编写高质量代码之读书笔记1
2016-10-12 20:58
309 查看
背景:由于工作原因,经过好几个月的漫长时间,才将这本书看完,这里将阅读过程中觉得需要回味的内容记录,备份,以后经常看看,这里也分享给大家。
建议 9 : 少用静态导入
所谓静态导入就是类似:import static java.lang.Math.PI;
我们使用
建议22 :用整数类型处理货币
建议23 :不要让类型默默转换
int LIGTH_SPEED = 30 * 10000 * 10000;
long dis = LIGHT_SPEED * 60 * 8;//这个会先计算int,然后超过了int界限,变成负值,所以输出结果是负值,
long dis = 1L * LIGHT_SPEED * 60 * 8;//这个就没问题了,先强制转换成long,然后计算的时候,不会超出边界了,
建议24 :边界,边界,还是边界
if(order > 0 && order + cur <= LIMIT){
}
这个判断是存在问题的,假设order值为2147483647,那么这个就失效了,order>0满足条件,但是order + cur由于超出边界为负值,同 样满足条件,所以有时我们写的程序对于一般使用者好使,单独与高手来说,破解分分钟的事
建议26 :堤防包装类型的null值
List的类型中是可以是null的,每次取出需要注意是否为 null
public static int f(List<Integer> list){
int count = 0;
for(int i:list){
count +=i;
}
return count;
}
如果传入的list中有null值,则会报空指针错误,integer转换int的时候,需要调用intValue,如果包装类型为null,则会报错
建议28 :优先使用整数池
从这个可以看出来,当我们使用valueOf当输入介于(-128,128]之间的时候,返回的直接是缓存池中的,所以是同一个对象,
建议29 :优先选择基本类型
当基本类型long和包装类Long同时作为参数是,会优先使用long的函数,这个涉及到装箱顺序问题,
建议 30 :不要随便设置随机种子
当设置了种子new Random(10)之后,同一台机器每次运行随即出来的数是固定的
不设置种子new Random(),随即出来的值每次运行才不同
建议 36:使用构造代码块精炼程序
public class Client {
{
System.out.pprintln("执行构造代码块");
}
}
我写的这段代码会在所有的构造函数之前被调用,如果有多个构造函数,需要初始化同一段代码,使用可以简化代码(不要介意构造函数调
用构造函数的问题,不会重复调用的,编译器会单独处理这种情况)
建议 38 :使用静态内部类提高封装性
静态内部类与普通内部类区别
1.静态内部类不持有外部类的引用,所以更干净
2.静态内部类不依赖外部类,更独立
3.普通内部类,不能声明static方法和变量
建议 41 :让多重继承成为现实
java中可以多重实现,但不能多重继承,但是我们可以通过内部类和匿名内部来来解决这个问题
Interface Father {
public int strong();
}
Interface Mother {
public int kind();
}
class FatherImpl implements Father{
public int strong(){
return 8;
}
}
class MotherImpl implements Mother{
public int kind(){
return 8;
}
}
1.通过内部类实现多继承
class son extends FatherImpl implements Mother{
@Override
public int kind(){
return new MotherSpecial().kind();
}
private class MotherSpecial extends MotherImpl{
public int kind(){
return super.kind() -1;
}
}
}
2.通过匿名类来实现
class son extends FatherImpl implements Mother{
@Override
public int kind(){
return new MotherImpl(){
@Override
public int kind(){
return super.kind() -1;
}
}.kind();
}
}
之前运用过类似的手法实现类似的需求,但是这里看到总结出来,感觉还是豁然开朗。
建议 42 :让工具类不可实例化
1.通过私有化构造函数
2.为了防止反射,在私有化中再加入抛出异常处理。
建议 43 :避免对象的浅拷贝
这里需要记住clone的几个特性
1.基本类型 :这个会拷贝其值
2.对象 :拷贝地址引用(所以拷贝出来的成员对象,如果更改的话都会改掉)
3.String :这个也是地址,但是由于string的特殊性,在修改时,会在字符串池中重新生成新的字符串,所以结果和基本类型一致。
所以综上:一般拷贝的时候,对象会有问题,这是我们就可以通过下面来实现深度拷贝
public person clone(){
Person p = null;
try{
p = (Person)super.clone();
p.setFather(new Person(***));
}catch(CloneNotSupportedException e){
}
}
其实就是我们在拷贝的时候,new一个新的对象,这样就不是一个地址了,也不会一改都改了
建议 44 :推荐使用序列化实现对象的拷贝
这个可以方便的解决之前提到的浅拷贝问题,不需要每个对象都复写clone方法了
建议 47 :在equals中使用getClass进行类型判断(如果要是使用instanceof,就会发生违背传递性原则的问题)
建议 48 :覆写equals方法必须覆写hashCode方法
否则在获取HashMap中会出现问题,
建议 49 :推荐覆写toString方法
这个之所以纪录是因为覆写的格式,还是比较正规的,以后可以借鉴
publlic String toString(){
return String.format("%s.name=%s",this.getClass(),name);
}
建议 50 :使用package-info 类为包服务
这个之前确实是不知道,临时查看了一下,在android的framework中部分采用了这个文件进行讲解说明,这里记录一下,以后万一使用
呢
建议 52 :推荐使用Stirng直接量赋值
即:String str = "ddd"; 而不是 String str = new String("ddd");
这里设计了字符串池的概念,
str.intern():是会检查当前对象在对象池中有没有,如果有,则返回池中对象,如果没有,则把当前string放入池中。
建议 53 :注意方法中传递的参数要求
输入:
区别在于replaceAll参数要求是正则表达式而replace不需要
建议 54 :正确使用String,StringBuffer,StringBuilder
这是一个老生常谈的问题,
主要记住: StringBuffer:线程安全,但效率比StringBuilder低
StringBuilder:线程不安全,
二者其他的相同
建议 56 :自由选择字符串拼接方法
str += "c";
str = str.concat("c");
StringBuffer.append
三者效率越来越高,详情可查看各自源码
建议 57 :推荐在复杂字符串操作中使用正则表达式
这里我只引用一句话:正则表达式是恶魔,威力巨大,但难以控制
建议 58 :强烈建议使用UTF编码
Window默认编码是 GBK
Linux和Android和Mac则是 UTF
所以如果不统一,在显示的时候会出现问题。
建议 59 :对字符串排序持一种宽容的心态
汉子的排序有时是不准确的,这个如果汉子排序是核心算法(会有一些生僻字发生),建议使用开源项目(pinyin4j)
建议 60 :性能考虑,数组是首选
对基本类型进行求和计算时,数组的效率是集合的10倍
建议 61 :若有必要,使用变长数组
//每次增加addLen长度,这里并没有限制正负,最终返回一个新的数组
建议 62 :警惕数组的浅拷贝
Arrays.copyOf 都是数组的浅拷贝,更改拷贝后的数组,会影响源数组
注意,这里数组中如果是基本类型是没问题的,但是如果数组中是非基本类型,拷贝的其实是其地址。
建议 64 :多种最值算法,适时选择
这里想要说的是里面的一个小知识,
数组 ,List , TreeSet值之间的随意转换
Integer[] data = {};
List<Integer> dataList = Arrays.asList(data);
TreeSet<Integer> ts = new TreeSet<Integer>(dataList);//list转换为treeSet,注意这里的treeset自动排序
这里有一些隐藏的坑:
1. 数组转换List[注意啊,这里的data数组类型必须是装饰类,就是不能是8种基本数据类型]
2.这个dataList是长度不可变的了,这个大家要非常注意,如果add则会抛出异常UnsupportedOperationException();
建议 67 :不同列表选择不同的遍历方法
这里介绍两个List : ArrayList VS LinkedList
前者是继承RandomAccess 就是说明随机存取接口,
LinkedList则是双向链表形式的,前后值之间存在关联关系。
当我们使用ArrayList的时候,使用下标获取值的时候效率是更改的,
而在使用LinkedList的时候,使用foreach则是效率更高的。
所以排序算法可以如下形式:
public static int average(List<Integer> list){
int sum = 0;
if(list instanceof RandomAccess){
for(int i=0,size = list.size();i<size;i++){
sum += list.get(i);
}
} else {
for(int i:list){
sum += i;
}
}
return sum / list.size();
}
建议 68 : 频繁插入和删除时使用LinkedList,修改时使用ArrayList
建议 70 :自列表知识原列表的一个视图(并不会重新创建)
建议 71 : 推荐使用subList处理局部列表
因为subList修改的其实就是源列表,所以更加方便
例如删除一个列表下标从 20 - 30的值
List.subList(20,30).clear();
建议 72 :生成自列表后不要再操作原列表
建议 73 :使用Comparator进行排序(Comparable VS Comparator)
建议 74 :不推荐使用binarySearch对列表进行检索
binarySearch 与 indexOf 功能类似,都是返回某个值的下标,知识binarySearch是使用二分法查找,效率更高,
问题是,使用二分法,就需要对列表提前排序,1.使用者可能会忘记,2.加上排序的整体效率未必还有优势。
还有个不同就是indexOf是比较equals 来确定值的,而 binarySearch则是根据 compareTo
建议 76 :集合运算时使用更优雅的方法
这个确实在之前遇到过,但是我真的是使用for循环判断的,真是愚蠢。
1.并集 :把两个集合合起来(list1.addAll(list2));
2.交集:取两个集合中相同的值(list1.retainAll(list2));
3.差集:属于list1但不属于list2的(list1.removeAll(list2));
4.无重复的并集:list1 与 list2中相同的值,只能有一份
list2.removeAll(list1);
list1.addAll(list2);
建议 77 :使用shuffle打乱列表
Collecctions.shuffle(list1);//打乱列表的顺序
建议 78 :尽量让HashMap中的元素少量并简单
1.HashMap中存放的事Entry,
2.HashMap的扩容方式,
这两种会造成hashmap更容易发生outOfMemory
建议 80 :多线程使用Vector 或 HashTable
Vector是ArrayList的线程安全版本
HashTable是HashMap的线程安全版本
transient是类型修饰符,只能用来修饰字段。在对象序列化的过程中,标记为transient的变量不会被序列化
volatile也是变量修饰符,只能用来修饰变量。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的
值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同
一个值。
建议 83 :推荐使用枚举定义常量
这里只介绍用枚举的优点吧:1.枚举常量更简单
2.枚举常量属于稳态型
3.枚举具有内置方法
4.枚举可以自定义方法
建议 84 :使用构造函数协助描述枚举项(谁知枚举的构造函数,增加枚举类型到丰富性)
建议 85 :小心switch带来的空值异常
建议 87 :使用valueOf前必须进行校验
如果使用枚举的valueOf没有值的时候,则会直接返回非法参数异常。
建议 88 :用枚举实现工厂方法模式更简洁
这里直接上例子,看了就知道啦
这里是两款汽车
这是三种工厂模式的实现方法,前两种是使用枚举方法
这个是三种工厂模式的调用方法。
建议 89 :枚举项的数量限制在64个以内
详情需要查看源码(这个位操作还是比较麻烦的)
建议 9 : 少用静态导入
所谓静态导入就是类似:import static java.lang.Math.PI;
我们使用
public final class Math { public static final double E = 2.718281828459045; public static final double PI = 3.141592653589793; import之后,就会直接使用PI,而不需要使用Math.PI了,确实,如果直接使用PI的话,我们阅读代码时很难知道PI是哪里赋值的。 建议10 : 不要在本类中覆盖静态导入的变量和方法 这个是上面的延伸,如果本文件覆盖了静态导入的变量,则会使用本地的了,虽然编译不会报错,但可读性很差 建议11 :显示声明UID 这个就是在继承Serializable的时候,显示声明一下:private static final long serialVersionUID = XXXXXL; 显示声明是为了提高代码到健壮性,如果不显示声明,但发生版本不一致时,直接报invalidClassException,如果进行显示声明,则会进行 向上兼容, 建议12 :避免用序列化类在构造函数中为不变量赋值, 原因是反序列化时,构造函数不会执行(这个虽然没有使用过,但是如果面临这种情况,还是很容易犯错的) 建议13 :避免为final变量复杂赋值(主要还是针对序列化) 以下三种情况反序列化时final变量不会被重载 1.通过构造函数为final变量赋值 2.通过方法返回值为final变量赋值 3.final修饰的属性不是基本类型(没有序列化的基本类型) 建议14 :使用序列化类的私有方法巧妙解决部分属性持久化问题 添加:private void writeObject() 和 private void readObject()类似于parcelable的自己使用 建议16 :易变业务使用脚本语言编写 这个如果在android中使用的比较常用的事javaScript 建议18 :避免instanceof 非预期结果 instanceof两端必须是继承或实现关系,范型会作为Object类型 建议20 :不要只替换一个类 这个的意思就是不要只改一个jar包或发布包里的一个.class文件,这样会导致这个jar里面的class类中的final变量不会改变。使用ide编译 肯定不会有这个问题, 建议21 :用偶判断,不用奇判断 i % 2 == 0 ? "偶数":"奇数"
i % 2 == 1 ? "奇数":"偶数" 也许你会觉得这两个一样,但是第二个当输入负数的时候,都会返回偶数,不论什么值
建议22 :用整数类型处理货币
Log.v("XPC","oddOrEven="+(10.00 - 9.04)); 结果是 0.9600000000000009 很奇怪吧,原因是二进制转化小数的时候,是不精确的,所以使用的时候,都可以使用整数,最后再转化 为小数
建议23 :不要让类型默默转换
int LIGTH_SPEED = 30 * 10000 * 10000;
long dis = LIGHT_SPEED * 60 * 8;//这个会先计算int,然后超过了int界限,变成负值,所以输出结果是负值,
long dis = 1L * LIGHT_SPEED * 60 * 8;//这个就没问题了,先强制转换成long,然后计算的时候,不会超出边界了,
建议24 :边界,边界,还是边界
if(order > 0 && order + cur <= LIMIT){
}
这个判断是存在问题的,假设order值为2147483647,那么这个就失效了,order>0满足条件,但是order + cur由于超出边界为负值,同 样满足条件,所以有时我们写的程序对于一般使用者好使,单独与高手来说,破解分分钟的事
建议26 :堤防包装类型的null值
List的类型中是可以是null的,每次取出需要注意是否为 null
public static int f(List<Integer> list){
int count = 0;
for(int i:list){
count +=i;
}
return count;
}
如果传入的list中有null值,则会报空指针错误,integer转换int的时候,需要调用intValue,如果包装类型为null,则会报错
建议28 :优先使用整数池
public static Integer valueOf(int i) { return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128]; }
从这个可以看出来,当我们使用valueOf当输入介于(-128,128]之间的时候,返回的直接是缓存池中的,所以是同一个对象,
建议29 :优先选择基本类型
当基本类型long和包装类Long同时作为参数是,会优先使用long的函数,这个涉及到装箱顺序问题,
建议 30 :不要随便设置随机种子
当设置了种子new Random(10)之后,同一台机器每次运行随即出来的数是固定的
不设置种子new Random(),随即出来的值每次运行才不同
建议 36:使用构造代码块精炼程序
public class Client {
{
System.out.pprintln("执行构造代码块");
}
}
我写的这段代码会在所有的构造函数之前被调用,如果有多个构造函数,需要初始化同一段代码,使用可以简化代码(不要介意构造函数调
用构造函数的问题,不会重复调用的,编译器会单独处理这种情况)
建议 38 :使用静态内部类提高封装性
静态内部类与普通内部类区别
1.静态内部类不持有外部类的引用,所以更干净
2.静态内部类不依赖外部类,更独立
3.普通内部类,不能声明static方法和变量
建议 41 :让多重继承成为现实
java中可以多重实现,但不能多重继承,但是我们可以通过内部类和匿名内部来来解决这个问题
Interface Father {
public int strong();
}
Interface Mother {
public int kind();
}
class FatherImpl implements Father{
public int strong(){
return 8;
}
}
class MotherImpl implements Mother{
public int kind(){
return 8;
}
}
1.通过内部类实现多继承
class son extends FatherImpl implements Mother{
@Override
public int kind(){
return new MotherSpecial().kind();
}
private class MotherSpecial extends MotherImpl{
public int kind(){
return super.kind() -1;
}
}
}
2.通过匿名类来实现
class son extends FatherImpl implements Mother{
@Override
public int kind(){
return new MotherImpl(){
@Override
public int kind(){
return super.kind() -1;
}
}.kind();
}
}
之前运用过类似的手法实现类似的需求,但是这里看到总结出来,感觉还是豁然开朗。
建议 42 :让工具类不可实例化
1.通过私有化构造函数
2.为了防止反射,在私有化中再加入抛出异常处理。
建议 43 :避免对象的浅拷贝
这里需要记住clone的几个特性
1.基本类型 :这个会拷贝其值
2.对象 :拷贝地址引用(所以拷贝出来的成员对象,如果更改的话都会改掉)
3.String :这个也是地址,但是由于string的特殊性,在修改时,会在字符串池中重新生成新的字符串,所以结果和基本类型一致。
所以综上:一般拷贝的时候,对象会有问题,这是我们就可以通过下面来实现深度拷贝
public person clone(){
Person p = null;
try{
p = (Person)super.clone();
p.setFather(new Person(***));
}catch(CloneNotSupportedException e){
}
}
其实就是我们在拷贝的时候,new一个新的对象,这样就不是一个地址了,也不会一改都改了
建议 44 :推荐使用序列化实现对象的拷贝
public class CloneUtils { public static < T extends Serializable > T clone(T obj){ T cloneObj = null; ObjectOutputStream oos = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); cloneObj = (T)ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } }
这个可以方便的解决之前提到的浅拷贝问题,不需要每个对象都复写clone方法了
建议 47 :在equals中使用getClass进行类型判断(如果要是使用instanceof,就会发生违背传递性原则的问题)
建议 48 :覆写equals方法必须覆写hashCode方法
否则在获取HashMap中会出现问题,
建议 49 :推荐覆写toString方法
这个之所以纪录是因为覆写的格式,还是比较正规的,以后可以借鉴
publlic String toString(){
return String.format("%s.name=%s",this.getClass(),name);
}
建议 50 :使用package-info 类为包服务
这个之前确实是不知道,临时查看了一下,在android的framework中部分采用了这个文件进行讲解说明,这里记录一下,以后万一使用
呢
建议 52 :推荐使用Stirng直接量赋值
即:String str = "ddd"; 而不是 String str = new String("ddd");
这里设计了字符串池的概念,
str.intern():是会检查当前对象在对象池中有没有,如果有,则返回池中对象,如果没有,则把当前string放入池中。
建议 53 :注意方法中传递的参数要求
输入:
String input = "好是好"; Log.v("XPC", "input.replaceAll="+input.replaceAll("好","")); String input1 = "$是$"; Log.v("XPC", "input.replaceAll="+input1.replaceAll("$","")); String input2 = "好是好"; Log.v("XPC", "input.replace="+input2.replace("好","")); String input3 = "$是$"; Log.v("XPC", "input.replace="+input3.replace("$","")); 输出: V/XPC: input.replaceAll=是 V/XPC: input.replaceAll=$是$ V/XPC: input.replace=是 V/XPC: input.replace=是
区别在于replaceAll参数要求是正则表达式而replace不需要
建议 54 :正确使用String,StringBuffer,StringBuilder
这是一个老生常谈的问题,
主要记住: StringBuffer:线程安全,但效率比StringBuilder低
StringBuilder:线程不安全,
二者其他的相同
建议 56 :自由选择字符串拼接方法
str += "c";
str = str.concat("c");
StringBuffer.append
三者效率越来越高,详情可查看各自源码
建议 57 :推荐在复杂字符串操作中使用正则表达式
这里我只引用一句话:正则表达式是恶魔,威力巨大,但难以控制
建议 58 :强烈建议使用UTF编码
Window默认编码是 GBK
Linux和Android和Mac则是 UTF
所以如果不统一,在显示的时候会出现问题。
建议 59 :对字符串排序持一种宽容的心态
汉子的排序有时是不准确的,这个如果汉子排序是核心算法(会有一些生僻字发生),建议使用开源项目(pinyin4j)
建议 60 :性能考虑,数组是首选
对基本类型进行求和计算时,数组的效率是集合的10倍
建议 61 :若有必要,使用变长数组
//每次增加addLen长度,这里并没有限制正负,最终返回一个新的数组
public static <T> T[] expandCapacity(T[] datas , int addLen){ int newLen = datas.length + addLen; newLen = newLen > 0 : newLen ? 0; return Arrays.copyOf(datas,newLen); }
建议 62 :警惕数组的浅拷贝
Arrays.copyOf 都是数组的浅拷贝,更改拷贝后的数组,会影响源数组
注意,这里数组中如果是基本类型是没问题的,但是如果数组中是非基本类型,拷贝的其实是其地址。
建议 64 :多种最值算法,适时选择
这里想要说的是里面的一个小知识,
数组 ,List , TreeSet值之间的随意转换
Integer[] data = {};
List<Integer> dataList = Arrays.asList(data);
TreeSet<Integer> ts = new TreeSet<Integer>(dataList);//list转换为treeSet,注意这里的treeset自动排序
这里有一些隐藏的坑:
1. 数组转换List[注意啊,这里的data数组类型必须是装饰类,就是不能是8种基本数据类型]
2.这个dataList是长度不可变的了,这个大家要非常注意,如果add则会抛出异常UnsupportedOperationException();
建议 67 :不同列表选择不同的遍历方法
这里介绍两个List : ArrayList VS LinkedList
前者是继承RandomAccess 就是说明随机存取接口,
LinkedList则是双向链表形式的,前后值之间存在关联关系。
当我们使用ArrayList的时候,使用下标获取值的时候效率是更改的,
而在使用LinkedList的时候,使用foreach则是效率更高的。
所以排序算法可以如下形式:
public static int average(List<Integer> list){
int sum = 0;
if(list instanceof RandomAccess){
for(int i=0,size = list.size();i<size;i++){
sum += list.get(i);
}
} else {
for(int i:list){
sum += i;
}
}
return sum / list.size();
}
建议 68 : 频繁插入和删除时使用LinkedList,修改时使用ArrayList
建议 70 :自列表知识原列表的一个视图(并不会重新创建)
建议 71 : 推荐使用subList处理局部列表
因为subList修改的其实就是源列表,所以更加方便
例如删除一个列表下标从 20 - 30的值
List.subList(20,30).clear();
建议 72 :生成自列表后不要再操作原列表
建议 73 :使用Comparator进行排序(Comparable VS Comparator)
建议 74 :不推荐使用binarySearch对列表进行检索
binarySearch 与 indexOf 功能类似,都是返回某个值的下标,知识binarySearch是使用二分法查找,效率更高,
问题是,使用二分法,就需要对列表提前排序,1.使用者可能会忘记,2.加上排序的整体效率未必还有优势。
还有个不同就是indexOf是比较equals 来确定值的,而 binarySearch则是根据 compareTo
建议 76 :集合运算时使用更优雅的方法
这个确实在之前遇到过,但是我真的是使用for循环判断的,真是愚蠢。
1.并集 :把两个集合合起来(list1.addAll(list2));
2.交集:取两个集合中相同的值(list1.retainAll(list2));
3.差集:属于list1但不属于list2的(list1.removeAll(list2));
4.无重复的并集:list1 与 list2中相同的值,只能有一份
list2.removeAll(list1);
list1.addAll(list2);
建议 77 :使用shuffle打乱列表
Collecctions.shuffle(list1);//打乱列表的顺序
建议 78 :尽量让HashMap中的元素少量并简单
1.HashMap中存放的事Entry,
2.HashMap的扩容方式,
这两种会造成hashmap更容易发生outOfMemory
建议 80 :多线程使用Vector 或 HashTable
Vector是ArrayList的线程安全版本
HashTable是HashMap的线程安全版本
transient是类型修饰符,只能用来修饰字段。在对象序列化的过程中,标记为transient的变量不会被序列化
volatile也是变量修饰符,只能用来修饰变量。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的
值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同
一个值。
建议 83 :推荐使用枚举定义常量
这里只介绍用枚举的优点吧:1.枚举常量更简单
2.枚举常量属于稳态型
3.枚举具有内置方法
4.枚举可以自定义方法
建议 84 :使用构造函数协助描述枚举项(谁知枚举的构造函数,增加枚举类型到丰富性)
建议 85 :小心switch带来的空值异常
建议 87 :使用valueOf前必须进行校验
如果使用枚举的valueOf没有值的时候,则会直接返回非法参数异常。
建议 88 :用枚举实现工厂方法模式更简洁
这里直接上例子,看了就知道啦
interface Car{ } static class FordCar implements Car{ } static class BuickCar implements Car{ }
这里是两款汽车
enum CarFactory{ FordCar { public Car create(){ return new FordCar(); } }, BuickCar{ public Car create(){ return new BuickCar(); } }; public abstract Car create(); } enum CarFactory1 { FordCar,BuickCar; public Car create(){ switch (this){ case FordCar: return new FordCar(); case BuickCar: return new BuickCar(); default: throw new AssertionError("无效参数"); } } } public static class CarFactory2 { public static Car createCar(Class<? extends Car> c){ try { return (Car)c.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } }
这是三种工厂模式的实现方法,前两种是使用枚举方法
CarFactory.FordCar.create(); CarFactory1.BuickCar.create(); CarFactory2.createCar(FordCar.class);
这个是三种工厂模式的调用方法。
建议 89 :枚举项的数量限制在64个以内
详情需要查看源码(这个位操作还是比较麻烦的)
相关文章推荐
- 《编写高质量代码-web前端开发修炼之道》 读书笔记 (转)
- 编写高质量C#代码学习笔记(4)
- 《编写高质量代码改善Java程序的151个建议》学习笔记 第6章 枚举和注解
- 编写高质量iOS和OS X代码的52个有效方法(笔记六)
- 读汤姆大叔《深入理解javascript系列》笔记一编写高质量代码
- 编写高质量iOS和OS X代码的52个有效方法(笔记五)
- 编写高质量C#代码学习笔记(5)
- 编写高质量代码、学习笔记——CSS篇
- 笔记:编写高质量代码 改善Java程序的151个建议
- 编写高质量代码:改善java程序的151个建议-----笔记(1)
- 笔记 《Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法 》
- 深入理解javascript学习笔记(一) 编写高质量代码
- 读《编写高质量代码改善python的91个建议》笔记 建议7:
- 读书--编写高质量代码 改善C#程序的157个建议2
- 编写高质量代码、学习笔记——Javascript篇
- 编写高质量C#代码学习笔记(2)
- 深入理解javascript学习笔记(一) 编写高质量代码
- 《编写高质量代码-Web前端开发修改之道》笔记--第二章 团队合作
- 《编写高质量代码-Web前端开发修改之道》笔记--第三章 高质量的HTML
- 编写高质量iOS和OS X代码的52个有效方法(笔记七)