改善Java程序的151个建议笔记

来源:互联网 发布:今天淘宝怎么用不了了 编辑:程序博客网 时间:2024/05/16 14:53
第 1 页
编写高质量代码:改善Java程序的151个建议笔记
第1章 Java开发中通用的方法和准则
1、 包名全小写,类名首字母全大写,常量全部大写并且用下划线分隔,变量采用骆驼命名法命名等。
2、 字母“l”(还包括大写字母“O”)尽量不要和数字混用。如果字母和数字必须混合使用,字母“l”务必大写,字母“O”则增加注释。
3、 三元操作符的类型务必一致:
public class Client {
public static void main(String[] args) {
int i = 80;
String s = String.valueOf(i < 100 ? 90 : 100);
String s1 = String.valueOf(i < 100 ? 90 : 100.0);
System.out.println("两者是否相等:" + s.equals(s1));
}
}
4、 Java对自加是这样处理的:首先arr的值(注意是值,不是引用)拷贝到一个临时变量区,然后对arr变量加1,最后返回临时变量区的值。
◆每种语言对自增的实现方式各不同。
5、 少用静态导入。
6、 对于静态导入,一定要遵循两个规则:
(1)不使用*(星号)通配符,除非是导入静态常量类(只包含常量的类或接口)。
(2)方法名是具有明确、清晰表象意义的工具类。
7、不要在本类中覆盖静态导入的变量和方法。
◆编译器有一个“最短路径”原则:如果能够在本类中查找到的变量、常量、方法,就不会到其他包或父类、接口中查找,以确保本类中的属性、方法优先。
8、记住在case语句后面随手写上break,养成良好的习惯。
9、JCP(Java Community Process)提出了JSR223规范,只要符合该规范的语言都可以Java
平台上运行(它对JavaScript是默认支持的)。
10、从Java6版本它开始支持动态编译了,可以在运行期直接编译.java文件,执行.class。
11、instanceof关键字:
(1)instanceof只能用于对象的判断,不能用于基本类型的判断。
(2)若左操作数是null,结果就直接返回false,不再运算右操作数是什么类。
(3)(String)null instanceof String:返回值是false,不要看这里有个强制类型转换就认为结果是true,不是的,null是一个万用类型,也可以说它没有类型,即使做类型转换还是个null。
(4)instanceof操作符的左右操作数必须有继承或实现关系,否则编译会失败。
第 2 页
第2章 基本类型
1、用偶判断,不用奇判断。
2、用整数类型处理货币。(把参与运算的值扩大100倍,并转化为整型,然后在展现时再缩小100倍)
3、不要让类型默默转换。
4、Java是先运算然后再进行类型转换的。
5、在单元测试中,有一项测试叫做边界测试(也叫做临界测试),如果一个方法接收的是int类型的参数,那以下三个值是必测的:0、正最大、负最小,其中正最大和负最小是边界值,如果这三个值都没有问题方法才是比较安全可靠的。
6、通过valueOf产生包装对象时,如果int参数在-128和127之间,则直接从整型池中获得对象,不在该范围的int类型则通过new生成包装对象。
7、自动装箱有一个重要的原则:基本类型可以先加宽,再转变成宽类型的包装类型,但不能直接转变成宽类型的包装类型。
8、在Java中,随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:
(1)种子不同,产生不同的随机数;
(2)种子相同,即使实例不同也产生相同的随机数。
9、除非必要,否则不要设置随机种子。
第3章 类、对象及方法
1、在接口中不要存在实现代码。
2、静态变量一定要先声明后赋值。
◆静态变量是在类初始化时首先被加载的,JVM会去查找类中所有的静态声明,然后分配空间,注意这时候知识完成了地址空间的分配,还诶有赋值,之后JVM会根据类中静态赋值(包括静态类赋值和静态块赋值)的先后顺序来执行。
3、一个实例对象有两个类型:表面类型和实际类型,表面类型是声明时的类型,实际类型
是对象产生时的类型。对于非静态方法,它是根据对象的实际类型来实行的。
4、如果是通过对象调用静态方法,JVM则会通过对象的表面类型查找静态方法的入口,继而执行之。
5、子类实例化时,会首先初始化父类(注意这里是初始化,可不是生成父类对象),也就是初始化父类的变量,调用父类的构造函数,然后才会初始化子类的变量,调用子类自己的构造函数,最后生成一个实例对象。
6、避免在构造函数中初始化其他类。
7、在Java中一共有四种类型的代码块:
(1)普通代码块
(2)静态代码块
(3)同步代码块
(4)构造代码块(在类中没有任何的前缀或后缀,并使用“{}”括起来的代码片段)
注意:
(1)编译器会把构造代码块插入到每个构造函数的最前端。若有super,则插在super
后!如果遇到this关键字,则不插入构造代码块!
(2)构造代码块会在每个构造函数内首先执行(需要注意的是:构造代码块不是在构
第 3 页
造函数之前执行的,它依托于构造函数的执行)
8、只有在是静态内部类的情况下才能把static修复符放在类前,其它任何时候static都是不能修饰类的。
9、静态内部类与普通内部类的区别:
(1)静态内部类不持有外部类的引用,普通内部类持有外部类的一个引用!
(2)普通内部类与外部类之间是相互依赖的关系,内部类实例不能脱离外部类实例。静态内部类是可以独立存在的,即使外部类消亡了,静态内部类还是可以存在的。
(3)普通内部类不能声明static的方法和变量,常量(也就是final static 修饰的属性)还是可以的。
10、匿名函数虽然没有名字,但也是可以有构造函数的,它用构造函数块来代替。
11、一般类(也就是具有显式名字的类)的所有构造函数默认都是调用父类的无参构造的。匿名类在初始化时直接调用了父类的同参数构造,然后再调用了自己的构造代码块。
12、浅拷贝方式并不会把对象的所有属性全部拷贝一份,而是有选择性的拷贝,它的拷贝规则如下:
(1)如果变量是基本类型,则拷贝其值;
(2)如果变量是一个实例对象,则拷贝地址引用;
(3)String 字符串,拷贝的也是一个地址,是个引用,但是在修改时,它会从字符串池中重新生成新的字符串,原有的字符串对象保持不变。
13、不要主动进行垃圾回收。
第4章 字符串
1、在字符串池中所容纳的都是String字符串对象,它的创建机制是这样的:创建一个字符
串时,首先检查池中是否有字面值相等的字符串,如果有,则不再创建,直接返回池中
该对象的引用,若没有则创建之,然后放到池中,并返回新建对象的引用。
2、创建字符串的两种方式:
方式一:String str1 = “ddd”;
方式二:String str2 = new String(“ddd”);
直接声明一个String对象是不检查字符串池的,也不会把对象放到池中,如方式二;方
式一会检查当前的对象在对象池中是否有字面值相同的引用对象,如果有则返回池中对
象,如果没有则放置到对象池中,并返回当前对象。
◆建议在开发中使用直接量赋值方式,除非确有必要才新建一个String对象!
3、实现从原始字符串中删除与之匹配的所有字符串:
public class StringUtils{
public static String remove(String source, String sub){
return source.replace(sub, "");
}
}
4、String类是不可改变的量;StringBuffer对象的值是可改变的。StringBuffer是线程
安全的;StringBuilder是线程不安全的。
5、在字符串拼接方式中,append方法最快,concat方法次之,加号最慢。
第 4 页
第5章 数组和集合
1、经验测试验证,扩容1.5倍即满足了性能要求,也减少了内存消耗。
2、Set的子类TreeSet还能自动排序。
3、避开基本类型数组转换列表陷阱。(Arrays.asList())
4、asList方法产生的List对象不可更改。
5、Java中的foreach语法是iterator(迭代器)的变形用法。
6、经过实际测试得知,LinkedList的插入效率比ArrayList快50倍以上。
7、在实际测试中得知,处理大批量的删除动作,LinkeedList比ArrayList快40倍以上。
8、修改元素值时,LinkedList输给了ArrayList。
9、subList产生的列表只是一个视图,所有的修改动作直接作用于原列表。
10、推荐使用subList处理局部列表。
11、删除索引位置为20~30的元素:
public static void main(String[] args){
List<Integer> initData = Collections.nCopies(100, 0);
ArrayList<Integer> list = new ArrayList<Integer>(initData);
list.subList(20,30).clear();
}
12、生成子列表后不要再操作原列表。
13、集合操作:
// 并集
public static ArrayList get_orSet(ArrayList dataList1, ArrayList dataList2) {
ArrayList dataListCopy1 = new ArrayList();
dataListCopy1 = (ArrayList) dataList1.clone();
ArrayList dataListCopy2 = new ArrayList();
dataListCopy2 = (ArrayList) dataList2.clone();
dataListCopy1.addAll(dataListCopy2);
return dataListCopy1;
}
// 交集
public static ArrayList get_andSet(ArrayList dataList1, ArrayList dataList2) {
ArrayList dataListCopy1 = new ArrayList();
dataListCopy1 = (ArrayList) dataList1.clone();
ArrayList dataListCopy2 = new ArrayList();
dataListCopy2 = (ArrayList) dataList2.clone();
// retainAll方法会删除dataListCopy1中没有出现在dataListCopy2中的元素
dataListCopy1.retainAll(dataListCopy2);
return dataListCopy1;
}
// 差集
public static ArrayList get_notSet(ArrayList dataList1, ArrayList dataList2) {
ArrayList dataListCopy1 = new ArrayList();
dataListCopy1 = (ArrayList) dataList1.clone();
ArrayList dataListCopy2 = new ArrayList();
第 5 页
dataListCopy2 = (ArrayList) dataList2.clone();
dataListCopy1.removeAll(dataListCopy2);
return dataListCopy1;
}
// 无重复并集(注意:不能用HashSet实现)
public static ArrayList get_onlyorSet(ArrayList dataList1,
ArrayList dataList2) {
ArrayList dataListCopy1 = new ArrayList();
dataListCopy1 = (ArrayList) dataList1.clone();
ArrayList dataListCopy2 = new ArrayList();
dataListCopy2 = (ArrayList) dataList2.clone();
dataListCopy2.removeAll(dataListCopy1);
dataListCopy1.add(dataListCopy2);
return dataListCopy1;
}
14、尽量让HashMap中的元素少量并简单。(HashMap中元素多了可能产生内存溢出问题)
15、如果哈希吗相同,HashMap的查找效率就与ArrayList没什么两样了。
16、多线程使用Vector或HashTable。Vector是ArrayList的多线程版本,HashTable是
HashMap的多线程版本。Vector、HashTable是线程安全的!ArrayList、HashMap是线程
不安全的!
17、当一个集合在被多个线程修改并访问时,就可能会出现
ConcurrentModificationException异常。
18、TreeSet类实现了默认排序为升序的Set集合。(根据Key值自动排序)
19、所有的集合底层存储的都是数组。
20、数组的工具类是java.util.Arrays和java.lang.reflect.Array,集合的工具类是java.util.Collections。
第6章 枚举和注释
1、枚举和注释都是在Java1.5中引入的。
2、推荐使用枚举定义常量。
3、JLS(Java Language Specification,Java语言规范)提倡枚举项全部大写,字母之间
用下划线分隔。
4、接口常量(或类常量)必须定义值,否则编译通不过。
5、枚举常量属于稳态型。
6、枚举具有内置方法。
7、枚举可以自定义方法。
8、枚举类型是不能有继承的。
9、小心switch带来的空值异常。
10、目前Java中的switch语句只能判断byte、short、char、int类型(JDK7已经允许使用String类型)。
11、Enum类定义的方法基本上都是final类型的。
12、迪米特原则(Law of Demeter,简称为LoD),也就是最少知识原则:一个对象应该对其它对象有最少的了解。
第 6 页
13、枚举项的数量限制在64个以内。
第7章 泛型与反射
1、Java的泛型在编译期有效,在运行期被删除。
2、泛型:
(1)泛型的class对象是相同的。
(2)泛型数组初始化时不能声明泛型类型。
(3)instanceof不允许存在泛型参数。
3、不能初始化泛型参数和数组。
4、初始化泛型数组和泛型参数的方法:
class Foo<T>{
private T t;
private T[] tArray;
private List<T> list = new ArrayList<T>();
public Foo(){
try{
Class<?> tType = Class.forName("");
t = (T)tType.newInstance();
tArray= (T[])Array.newInstance(tType,5);
}catch(Exception e){
e.printStackTrace();
}
}
}
5、类的成员变量是在类初始化前初始化的,所以要求在初始化前它必须具有明确的类型,
否则就只能声明,不能初始化。
6、asList所生成的List长度是不可变的!
7、Java泛型支持通配符,可以单独使用一个“?”表示任意类,也可以使用extends关键字表示一个类(接口)的子类型,还可以使用super关键字表示某一个类(接口)的父类型。
(1)泛型结构只参与“读”操作则限定上界(extends关键字)。
(2)泛型结构只参与“写”操作则限定下界(使用super关键字)。
8、null是一个万用类型,它可以是所有类的实例对象。
9、建议采用的顺序是List<T>、List<?>、List<Object>。
10、在Java的泛型中,可以使用“&”符号关联多个上界并实现多个边界限定,而且只有上界才有此限定,下界没有多重限定的情况。
11、Java语言是先把Java源文件编译成后缀为class的字节码文件,然后再通过ClassLoader机制把这些类文件加载到内存中,最后生成实例执行的。
12、Class类的特殊地方:
(1)无构造函数
(2)可以描述基本类型
(3)其对象都是单例模式
13、Class对象是在加载类时由Java虚拟机通过调用类加载器中的defineClass方法自动
第 7 页
构造的。
14、一个Class的实例对象描述一个类,并且只描述一个类,反过来也成立,一个类只有一个Class实例对象。
15、一般获得一个Class对象有三种途径:
(1)类属性方式
(2)对象的getClass()方法
(3)forName方法加载
16、getMethod方法获得的是所有public访问级别的方法,包括从父类继承的方法,而getDeclaredMethod获得是自身类的所有方法,包括公用(public)方法、私有(private)方法等,而且不受限于访问权限。
17、阅读源代码是理解的最好方式。
18、在大量的反射情况下,设置Accessible为true可以提升性能20倍以上。
19、一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在
运行时再决定是否要加载一个类。
20、如果使用的是import关键字产生的依赖包,JVM在启动时会自动加载所有依赖包下的类文件。
21、动态加载不适合数组。
22、AOP为Aspect Oriented Programming的缩写,是OOP的延续。意为:面向方面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
第8章 异常
1、不要在finally块中处理返回值。
2、不要在构造函数中抛出异常。
3、多使用异常,把性能问题放一边。(异常有一个缺点:性能比较慢)
第9章 多线程和并发
1、不推荐覆盖start方法。
2、启动线程前stop方法是不可靠的。
3、终止一个线程不能使用stop方法,使用自定义的标志位决定线程的执行情况。
4、线程优先级只使用三个等级。
5、Java的线程有10个级别(准确地说是11个级别,级别为0的线程是JVM的,应用程序
不能设置该级别)。
7、 优先级只是表示线程获得CPU运行的机会,并不代表强制的排序号。优先级差别越大,运行机会差别越明显。
8、 Java的优先级只是代表抢占CPU的机会大小,优先级越高,抢占CPU的机会越大,被优先执行的可能性越高,优先级差别不大,则抢占CPU的机会差别也不大。
9、 建议使用优先级常量,而不是1到10随机的数字。
10、线程优先级推荐使用MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY三个级别,不建议使用其他7个数字。
11、volatile不能保证数据同步。
第 8 页
12、Lock与synchronized是不一样的。
13、显示锁是对象级别的锁,而内部锁是类级别的锁,也就是说Lock锁是跟随对象的,
Synchronized锁是跟随类的。
第10章 性能和效率
1、提升Java性能的基本方法:
(1)不要在循环条件中计算
(2)尽可能把变量、方法声明为final static 类型
(3)缩小变量的范围
(4)频繁字符串操作使用StringBuilder或StringBuffer
(5)使用非线性检索
(6)覆写Exception的fillInStackTrace方法
(7)不建立冗余对象
2、运行一段程序需要三种资源:CPU、内存、I/O。
3、若非必要,不要克隆对象。通过clone方法生成一个对象时,就会不再执行构造函数了,
只是在内存中进行数据块的拷贝。
4、克隆对象并不比直接生成对象效率高。
5、一个好的性能衡量标准应该包括以下KPI(Key Performance Indicators):
(1)核心业务的响应时间
(2)重要业务的响应时间
6、在JVM中有两种内存:栈内存(Stack)和堆内存(Heap),栈内存的特点是空间比较小,速度快,用来存放对象的引用及程序中的基本类型;而堆内存的特点是空间比较大,速度慢,一般对象都会在这里生成、使用和消亡。
7、没有慢的系统,只有不满足业务的系统;没有慢的系统,只有构架不良的系统;没有慢的系统,只有懒惰的技术人员;没有慢的系统,只有不愿意投入的系统。
第11章 开源世界
第12章 思想为源
1、以下20条建议可以逐步把我们向技术人员方向培养:
(1)熟悉工具
(2)使用IDE
(3)坚持编码
(4)编码前思考
(5)坚持重构
(6)多写文档
(7)保持程序版本的简单性
(8)做好备份
(9)做单元测试
(10)不要重复发明轮子
第 9 页
(11)不要拷贝
(12)让代码充满灵性
(13)测试自动化
(14)做压力测试
(15)“剽窃”不可耻
(16)坚持向敏捷学习
(17)重里更重面
(18)分享
(19)刨根问低
(20)横向扩展
原创粉丝点击