使用枚举定义常量时,会伴有大量的switch语句判断,目的是伪类每个枚举项解释其行为,例如: public class Client { public static void main(String[] args) { doSports(null); } public static void doSports(Season season) { switch (season) { case Spring: System.out.println("春天放风筝"); break; case…
一.分析  使用枚举定义常量时,会有伴有大量的switch语句判断,目的是为每个枚举解释其行为. 我们知道,目前的Java的switch语句只能判断byte.short.char.int类型(JDK7已经允许使用string类型),为什么枚举也能跟在switch后面呢?很简单,因为编译的时候,编译器判断出switch后面的参数是枚举类型,然后就会根据枚举的排序值继续匹配.如下: public static void doSports(Season season){ switch(season.o…
switch的后跟枚举类型,case后列出所有的枚举项,这是一个使用枚举的主流写法,那留着default语句似乎没有任何作用了,程序已经列举出了所有的可能选项,肯定不会执行到default语句,. 错了...这个default还是很有用的,以我们定义的日志级别来举例说明,这是一个典型的枚举常量.如下所示: public class Client { public static void main(String[] args) { LogLevel logLevel = LogLevel.INFO…
Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本,这些概念我 们都很清楚,也被前辈嘱咐过很多次,但我们经常会逃避使用Vector和HashTable,因为用 得少,不熟嘛!只有在真正需要的时候才会想要使用它们,但问题是什么时候算真正需要呢?我们来看一个例子,看看使用线程安全的Vector是否可以解决问题,代码如下: import java.util.ArrayList; import java.util.List; import java.util.R…
建议16: 易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP.Ruby.Groovy.JavaScript等,这些“入侵者”都有一个共同特征:全是同一类语言—脚本语言,它们都是在运行期解释执行的.为什么Java这种强编译型语言会需要这些脚本语言呢?那是因为脚本语言的三大特征,如下所示: 1.灵活.脚本语言一般都是动态类型,可以不用声明变量类型而直接使用,也可以在运行期改变类型. 2.便捷.脚本语言是一种解释型语言,不需要编译成二进制代码,也不需要像Java一样生成字节…
建议35: 避免在构造函数中初始化其他类 构造函数是一个类初始化必须执行的代码,它决定着类的初始化效率,如果构造函数比较复杂,而且还关联了其他类,则可能产生意想不到的问题,我们来看如下代码: public class Client { public static void main(String[] args) { Son s = new Son(); s.doSomething(); } } // 父类 class Father { Father() { new Other(); } }//…
建议5:别让null值和空值威胁到变长方法 public class Client { public void methodA(String str,Integer... is){ } public void methodA(String str,String... strs){ } public static void main(String[] args) { Client client = new Client(); client.methodA("China", 0); cli…
开发中经常用到Arrays和Collections这两个工具类. 在数组和列表之间进行切换.非常方便.但是也会遇到一些问题. 看代码: import java.util.Arrays; import java.util.List; public class Client { public static void main(String[] args) { int[] data = {1,2,3,4,5}; List list = Arrays.asList(data); System.out.p…
注解的写法和接口很类似,都采用了关键字interface,而且都不能有实现代码,常量定义默认都是pulbic static final类型的. 他们的主要不同点是:注解在interface前加上@字符,而且不能继承,不能实现,这经常会给我们的开发带来一些障碍. 分析一个ACL(Access Contorl List ,访问控制列表)设计案例..看看如何避免这些障碍. ACL中有三个重要的元素: 1.资源,有哪些信息是要被控制起来的. 2.权限级别,不同的访问者在规划在不同的级别中. 3.控制器(…
工厂方法模式(Factory Method Patter)是"创建对象的接口",让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类.工厂方法模式在我们的开发工作中,经常会用到. 下面以汽车制造为例,看看一般的工厂方法模式是如何实现的,代码如下: public class Client { public static void main(String[] args) { //生产车辆 Car car = CarFactory.createCar(FordCar.class); }…
线程启动完毕后,在运行可能需要终止,Java提供的终止方法只有一个stop,但是不建议使用此方法,因为它有以下三个问题: (1)stop方法是过时的 从Java编码规则来说,已经过时的方式不建议采用. (2)stop方法会导致代码逻辑不完整 stop方法是一种"恶意" 的中断,一旦执行stop方法,即终止当前正在运行的线程,不管线程逻辑是否完整,这是非常危险的. 看如下代码: public class Client { public static void main(String[]…
建议33: 不要覆写静态方法 我们知道在Java中可以通过覆写(Override)来增强或减弱父类的方法和行为,但覆写是针对非静态方法(也叫做实例方法,只有生成实例才能调用的方法)的,不能针对静态方法(static修饰的方法,也叫做类方法),为什么呢?我们先看一个例子,代码如下: public class Client { public static void main(String[] args) { Base base = new Sub(); //调用非静态方法 base.doAnythi…
建议26: 提防包装类型的null值 我们知道Java引入包装类型(Wrapper Types)是为了解决基本类型的实例化问题,以便让一个基本类型也能参与到面向对象的编程世界中.而在Java 5中泛型更是对基本类型说了“不”,如想把一个整型放到List中,就必须使用Integer包装类型.我们来看一段代码: public class Client { // 计算list中所有元素之和 public static int f(List<Integer> list) { int count = 0…
枚举和注解都是在Java1.5中引入的,虽然他们是后起之秀,但是功能不容小觑,枚举改变了常量的声明方式,注解耦合了数据和代码. 建议83:推荐使用枚举定义常量 一.分析 常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量.不过,在1.5版之后有了改进,即新增了一种常量声明方式,枚举常量.代码如下: enum Season{ Spring,Summer,Autumn,Winter; } JLS(Java Language Specification,…
建议11: 养成良好习惯,显式声明UID 我们编写一个实现了Serializable接口(序列化标志接口)的类, Eclipse马上就会给一个黄色警告:需要增加一个Serial Version ID.为什么要增加?它是怎么计算出来的?有什么用?本章就来解释该问题. 类实现Serializable接口的目的是为了可持久化,比如网络传输或本地存储,为系统的分布和异构部署提供先决支持条件.若没有序列化,现在我们熟悉的远程调用.对象数据库都不可能存在,我们来看一个简单的序列化类: public clas…
在Java中一涉及到中文处理就会冒出很多的问题来,其中的排序也是一个让人头疼的问题,看代码: import java.util.Arrays; public class Client { public static void main(String[] args){ String[] strs = {"张三(Z)","李四(L)","王五(W)"}; //排序,默认是升序 Arrays.sort(strs); int i=0; for(Strin…
一.分析  字符串的操作,诸如追加.合并.替换.倒序.分隔等,都是在编码过程中经常用到的,而且Java也提供了append.replace.reverse.split等方法来完成这些操作,它们使用起来确实方便,但是更多的时候,需要使用正则表达式来完成复杂的处理. 二.场景  统计一篇文章中的单词的数量,代码如下: import java.util.Scanner; public class Client { public static void main(String[] args) { //接…
我们知道Set与List的最大区别就是Set中的元素不可以重复(这个重复指的equals方法的返回值相等),其他方面则没有太大的区别了,在Set的实现类中有一个比较常用的类需要了解一下:TreeSet,该类实现了类默认排序为升序的Set集合,如果插入一个元素,默认会按照升序排列(当然是根据Comparable接口的compareTo的返回值确定排序位置了),不过,这样的排序是不是在元素经常变化的场景中也适用呢?我们来看例子: import java.util.SortedSet; import…
在系统开发中我们经常会使用HashMap作为数据集容器,或者是用缓冲池来处理,一般很稳定,但偶尔也会出现内存溢出的问题(OutOfMemory错误),而且这经常是与HashMap有关的.而且这经常是与HashMap有关的.比如我们使用缓冲池操作数据时,大批量的增删改产操作就可能会让内存溢出,下面建立一段模拟程序,重现该问题,看代码: import java.util.HashMap; import java.util.Map; public class Client { public stati…
在初中代数中,我们经常会求两个集合的并集.交集.差集等,在Java中也存在着此 类运算,那如何实现呢? 一提到此类集合操作,大部分的实现者都会说:对两个集合进行遍历,即可求出结果.是的,遍历可以实现并集.交集.差集等运算,但这不是最优雅的处理方式.下面来看看如何进行更优雅.快速.方便的集合操作. (1) 并集,(2)交集,(3)差集(补集),(4)无重复并集. 也叫做合集,把两个集合加起来即可,这非常简单,代码如下: import java.util.ArrayList; import java…
对一个列表进行检索时,我们使用的最多的是indexOf方法,它简单好用,而且也不会出错,虽然它只能检索到第一个符合条件的值,但是我们可以生成子列表后再检索.这样也就可以查找到所有符合条件的值了. Collections工具类也提供了一个检索的方法:binarySearch,这个是干什么的?该方法也是对一个列表进行检索的,可以查找出指定的索引值,但是在使用这个方法时就有一些注意事项,看代码: import java.util.ArrayList; import java.util.Collecti…
List接口提供了subList方法,其作用是返回一个列表的子列表.这与String类的subString有点类似.但是他们的功能是否相同?看代码: import java.util.ArrayList; import java.util.List; public class Client { public static void main(String[] args) { //定义一个包含两个字符串的列表 List<String> c = new ArrayList<String>…
来看一个判断列表相等的例子,看代码: import java.util.ArrayList; import java.util.Vector; public class Client { public static void main(String[] args) { ArrayList<String> strs = new ArrayList<String>(); strs.add("A"); Vector<String> strs2 = new…
一.场景: 我们来看一个场景,统计一个省的各科高考科目考试的平均分. 当然使用数据库中的一个SQL语句就能求出平均值,不过这个不再我们的考虑之列,这里只考虑使用纯Java的方式来解决.(由于我的机器配置比较好,用80万的数据量有点小,各个统计结果不明显,不能更加显著的说明明问题...这里改为800万) 看代码: import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.uti…
上一个建议之处了asList方法在转换基本类型数组时候存在的问题,在看下asList方法返回的列表有何特殊的地方.看代码: import java.util.Arrays; import java.util.List; public class Client { //枚举,声明一个星期 enum Week{Sun,Mon, Tue, Wed,Thu,Fri,Sat} public static void main(String[] args) { //工作日 Week[] workDays =…
为了更好的使用枚举,Java提供了两个枚举集合:EnumSet和EnumMap,这两个集合的使用方法都比较简单,EnumSet表示其元素必须是某一枚举的枚举项,EnumMap表示Key值必须是某一枚举的枚举项,由于枚举类型的实例数量固定并且有限,相对来说,EnumSet和EnumMap的效率会比其他Set和Map要高. 虽然EnumSret很好用,但是它有一个隐藏的特点.项目中可能定义非常多的枚举项,然后通过EnumSet访问,遍历,但它对不同的枚举数量有不同的处理方式.为了进行对比,我们定义两…
每个枚举都是java.lang.Enum的子类,都可以访问Enum类提供的方法,比如hashCode(),name(),valueOf()等..... 其中valueOf()方法会把一个String类型的名称转变为枚举项,也就是枚举项中查找出字面值与该参数相等的枚举项,虽然这个方法很简单,但是JDK却做了一个对于开发人员来说并不简单的处理: 看代码: import java.util.Arrays; import java.util.List; public class Client { pub…
上一个建议解释了为什么要使用forName,本建议就说说哪些地方不适合使用动态加载. 如果forName要加载一个类,那它必须是一个类------8中基本类型就排除在外.它们不是一个具体的类. 其次它必须具有可追溯的类路径...否则就会报ClassNotFoundException. 在Java中,数组是一个非常特殊的类,虽然它是一个类,单没有定义类路径. public class Client { public static void main(String[] args) throws Ex…
动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,对Java程序来说,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否需要加载一个类,比如从Web上接受一个String参数作为类名,然后在JVM中加载并初始化,这就是动态加载,此动态加载通常是通过Class.forName(String)实现的,只是这个forName()方法到底是什么意思? 我们知道一个类文件只有在被加载到内存中后才可能生成实例对象,也就是说一个对象的生成必…
Java的Class类提供了很多的getDeclaredxxx方法和getxxx方法,例如getDeclaredmethod和getMethod成对出现,getDeclaredConstructors和getConstructors也是成对出现. 那这两者有什么区别呢? import java.lang.reflect.Method; public class Client { public static void main(String[] args) throws Exception { /…