Category: Java

Java中的switch语句 0

Java中的switch语句

很多人在学习Java语言的时候,应该就听说switch语句和if else差不多,其实事实上真的是这样吗?尤其是当你真的写代码的时候,是使用一个if else还是使用switch呢?让我们带着这些问题来仔细阅读以下本文的内容吧。 首先我们来看一个简单的代码,他们实现的功能类似,但分别使用的打包开关(packed switch),稀疏开关(sparse switch)以及if else。 这段代码非常简单,也许你的老师会告诉你,这些代码最终编译出来都是一样的,那么事实上真的如此吗?其实假如你看jvm的代码的话,会发现它们其实是不相同的。我们可以使用下面命令来看jvm的代码: 上面类的的jvm代码如下所示: 下面我们就来仔细看一下,首先来看if else, 和我们想象得差不多,一个一个地进行比较,要是有相等的就return。这个应该和我们想象得比价类似吧。 下面来看switch的情况,switch有两种情况,一种就是各个switch的值比较靠近,如下: 它编译的结果如下: 这里你会发现其实有一个tableswitch的东西,那这个是什么呢?在JVM spec中有讲到。 简单来讲,就是说其实类似做了一个key value的表,在key范围内的值,则会通过index直接找到相应的值,而范围之外,就直接返回默认值了。 而假如你使用是一个稀疏switch,也就是说各个case之间的值变化很大,他会怎么来实现呢? 他编译的结果如下: 这里我们看到他不再使用tableswitch了,它使用的是一个lookupswitch,其实说白了,他就是把所有的key排序,然后查找的时候就可以在一个有序表中进行查找了,这个效率其实相对tableSwtich就有一些差别了。当然和if else比则又不相同了。 好了,看了本文之后是不是才恍然大悟,原来我们平常看到的简单代码背后竟然有这么多不同的逻辑。 参考文章:https://www.dreamincode.net/forums/blog/1748/entry-4558-switch-statements-in-java/

0

Java程序员常犯的十个错误

本文总结了Java程序员常犯的十个错误: #1 把Array转变成ArrayList 为了把Array转变成ArrayList,程序员经常这样做: Arrays.asList返回的是Arrays内部的一个静态私有的ArrayList类,他不是Java.util.ArrayList类。Java.util.Arrays.ArrayList类有set(), get(), contains()这些方法,但是他没有任何加入元素的方法,所以他的大小是固定的。要创建一个真的ArrayList,你需要这样做: ArrayList的构造函数可以接收一个Collection的类型,这个类型是java.util.Arrays.ArrayList的父类。 #2 检查一个Array是否包含一个值 程序员经常这样写: 这个代码是可以工作的,但是我们没有必要把一个list转变成set。转变成set其实是需要消耗时间的,我们可以简化成这样: 或者 第一个的代码可读性比第二个稍微好一点。 #3 在一个循环中移除list中的一个元素 看下面这个在循环中移除元素的代码: 他的输出是: 这个代码有个眼中的问题,就是当一个元素被remove了,他的大小也变化了,同时index也发生了变化。所以,假如你想在循环中删除一个元素的话,那么使用index可能会出问题。 你也许会说,使用迭代是一个很好的在循环中删除元素的方式,你也许会觉得foreach在Java中其实就和迭代类似,其实事实上并不是这样,我们来看下面这个代码: 他会抛出ConcurrentModificationException。 改成下面这样就好了: .next()必须在.remove()之前调用。在foreach循环中,编译器会在移除元素之后调用.next(),这样就会导致ConcurrentModificationException。具体你可以看看ArrayList.iterator()的源码。 #4 Hanshtable 对比 HashMap 从算法定义的角度来说,hashtable是数据结构的名字。但是在Java中,数据结构的名字是HashMap。Hashtable和HashMap的最大的不同在于Hashtable是同步的。所以,很多时候,你并不需要使用Hashtable,HashMap就足够了。 具体可以参加这两篇文章: HashMap vs. TreeMap vs. Hashtable...

0

面试题之如何用Java设计一个自动售货机

如何用Java设计一个自动售货机程序是一个非常好的Java面试题。大多数情况会在面试比较senior的Java开发者的时候出现。在一个典型的代码面试中,你需要在一定的时间内根据对应的条件完成相关的代码。通常2到3小时内(面试哪有这么多时间,哈哈),你需要产生设计文档,可以工作的代码已经单元测试。这样的Java面试的好处就是你能够一次性检测面试者的很多能力。为了能够完成代码的设计,编码以及单元测试,面试者需要在这三个方面都比较精通。 另外,这种真实的问题可以提升你面向对象分析和设计能力的技能,假如你想成为一个很好的应用开发者,那么这个技能就很重要。 要想用Java或者别的面向对象的语言来设计一个自动售货机,你不仅仅需要了解最基本的东西,比如封装(Encapsulation),多态(Polymorphism)或者继承(Inheritance),你还需要理解如何使用抽象类和接口的细节,这样才能解决问题或者设计一个好的应用。 通常这种问题,还会给你一个使用设计模式的机会,因为在这个问题中你可以使用工厂模式去创建不同的售货机。我在20个Java软件开发的问题一文中曾今讨论过这个问题,那之后,我收到了很多反馈关于解决这个问题的方案。 这篇文章,我们将会提供一个自动售货机问题的解决方案。顺便说一下,其实这个问题有很多种解决的方案,你应当在看本文之前自己先尝试一下。你也需要先复习一下SOLID和OOPS的设计原则,我们会在代码中使用到他们。当你设计自动售货机的时候,你会发现我们会用到其中很多的相关内容。 另外,假如你对设计模式和原则感兴趣,我推荐你看看Udemy的“Java设计模式”这门课。这门课包括SOLID的设计模式,比如开闭原则(open closed)以及里氏替代(Liskov Substitution),当然也包括所有的面向对象的设计模式,比如装饰,观察,责任链等等。 问题陈述 你需要设计一个这样的自动售货机: 能够接收1分钱,5分钱,10分钱以及25分钱等等。 允许客户来选择产品,比如可乐(25), 百事(35),苏打(45) 允许用户取消请求来退钱 返回选择的产品,并且找零(假如需要的话) 允许售货机的提供商做reset的操作。 需求部分是这个问题最重要的部分。你需要仔细地阅读这个部分,然后对这个问题有一个高层的理解,然后思考如何来解决它。通常来说,需求部分都是不明确的,你需要通过阅读问题的陈述来列出一系列的你自己理解的需求。 我们喜欢指出基本的需求,因为他们很容易来跟踪。一些需求是很隐式的,我们最好把他们在你的列表中显式列出来。比如在这个问题中,假如售货机没有足够的零钱来找回,那么他就不应该接收相应的请求。 很不幸,没有什么课本或者课程来告诉你这些,你只有在真实的环境中做过这些才能知道。当然,有两本书曾帮助我改进我的面向对象分析和设计的能力,他们是《深入浅出面向对象分析和设计》 (Head First Object Oriented Design and Analysis),假如你没有相关的面向对象编程的经验,那么这本书非常值得推荐。 另外一本书是UML for Java Progrmmers,它是一本开发应用和系统设计方面非常好的书,值得推荐。它的做着是Robert C. Martin。我已经读过他的很多本书,比如Clean Code, Clean...

0

Java应用性能测试之堆内存

每一个性能工程师都需要知道Java中内存是如何工作的吗?假如你想完全解决性能瓶颈的话,我的答案是“必须的”。Java的性能管理对每一个性能工程师以及Java开发者来说都是一个梦魇,但同时又是写好Java应用必不可少的一部分。 这是一个申请新的对象和清除不使用对象(垃圾回收)的过程。Java有自动的内存管理,在后台有自动运行的垃圾回收机制来回收不使用的对象并释放内存。假如没有足够的知识和经验来了解JVM和垃圾回收是如何工作的,不知道Java的内存是如何创建的,我们工程师在执行Java应用程序的时候就很难发现对应的瓶颈是在哪里。 当分析性能瓶颈的时候,理解Java内存模块的运行是一个技术活。在我查阅了很多博客,以及结合我自身的工作经验来看,趟过了很多工作上的坑之后,慢慢理解了JVM各个部分都是如何工作的。当我开始做性能测试的时候,根本不知道什么是Java的堆,我甚至不关注Java中对象都是如何创建的,更不用说GC是如何把不同类型的不使用的对象释放的。 在我开始做Java性能测试的时候,我遇到了好几个内存相关的错误,比如 java.lang.OutOfMemoryError,也就是在那时,我开始了解Java性能测试中JVM堆和栈所扮演的不同角色。当你想要获得一些性能相关的工作时,很多公司和客户都会检查你对Java开发和Java性能调试上面的专业度,所以理解Java中内存是如何申请的是非常重要的,它可以让你写出高性能的应用,再也不会出现诸如OutOfMemoryError或者Memory Leaks的错误。 每一个性能工程师在调试JVM性能问题时都需要理解Java的性能管理内部是如何工作的。我们所创建一切,比如类,方法,对象,变量其实在JVM中都是内存。比如,我们创建一个局部变量,一个全局变量或者不同的类对象,他们都是存储在JVM堆内存中。 JVM中有很大的内存,它被分成了两大部分,一个是堆内存另一个是栈内存。首先,我们从堆内存开始来分析可能的读写内存问题。堆内存在Java中的定位已经目标是什么,这大概是每一个性能测试工程师中心中的疑问。 对所有的性能问题来说,这是Java程序中非常关键的部分。其实有很多不同的模式,方法,资源以及技巧有关堆内存,我们可以用之来优化Java程序。 堆内存 关于Java中堆内存有很多图表都对之进行了描述。每一个性能工程师都需要理解PermGen和Metaspace之间的区别,我们可以从下面的图中粗略了解一二: 堆内存被分为两代,一代是YOUNG,一代是OLD。YOUNG这一代的开始部分我们称之为EDEN空间,紧接着其后的第二部分称之为SURVIVOR,它有SURVIVOR0以及SURVIVOR1组成。现在我们来理解YOUNG每个部分的目的。无论何时,当我们创建一个新的对象时,他们都首先存放在EDEN空间。JVM中有一个自动的内存管理功能称之为垃圾回收(这里就不做详细介绍了)。 假如应用非常重创建了成百上千的对象,EDEN的内存就会被这些对象占满,这时候垃圾回收机制就会运行,把不使用或者没有引用的对象删除了,这个过程就称之为Minor GC。这个Minor GC会把所有的还存活的对象移动到SURVIVOR内存空间。Minor GC会自动在YOUNG中运行并释放内存空间。Minor GC运行的周期很短并且速度很快。 不同的类可能会创建很多对象,因此当对象的数目增大时,EDEN空间的内存也会增加。假设现在有GC1, GC2, GC3一直到GCN在运行(他们是由JVM自动调用垃圾回收操作创建的),他们会不停地检查不同的对象,并把他们立即转移到SURVIVOR内存中。 YOUNG中存活的对象会被转移到OLD中,当OLD也满的时候,Major GC就会启动。那什么时候Major GC会被启动呢?当OLD中内存完全满了的时候,Major GC就会启动。Major GC很长时候才会发生一次。当我们开发Java应用和Java自动框架的时候,假如程序员创建了很多的对象,那这时候就需要小心YOUNG和OLD这两个不同的概念了。 程序员不应该创建任何没有必要的对象,假如这种现象真的存在,那么垃圾回收应当在任务结束的时候即使销毁他们。Minor GC主要在YOUNG上运行,而Major GC则运行在OLD上。比如,你使用Amazon或者Walmart,我们知道将会有很多请求或者访问到这些页面,并且我们看到了超时的异常伴随着高的访问量。通常来说,在这种线上的商务网站,超时的异常一般是由于Major GC使用了大量的内存去销毁对象,从而使用相应的CPU以及内存的使用非常高。 这也表明了一些特定的类创建了太多了的对象。这种情况下,Major GC将会不停试图销毁不使用的对象。Major GC相比于Minor GC使用的时间会更长。 PermGen(Permanent...