《Effective Java》学习总结

来源:互联网 发布:淘宝来图印花定制内裤 编辑:程序博客网 时间:2024/05/20 13:14

第一章:代码应该被重用,而不是被拷贝。模块之间的依赖性应该尽可能地降到最小。错误应该尽早被检测出来,最好是在编译时刻。


第2条:处理多参数构造器

public class NutritionFacts {private int servingSize;private int servings;private int calories;private int fat;public NutritionFacts(int servingSize, int servings) {this(servingSize, servingSize, 0); //调用了有三个参数的构造函数}public NutritionFacts(int servingSize, int servings, int calories) {this(servingSize, servings, calories, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat) {this.servingSize = servingSize;this.servings = servings;this.calories = calories;this.fat = fat;}}

第3条:实现Singleton的两种方式

①方式一

public class Elvis {public static final Elvis INSTANCE = new Elvis();//public域private Elvis(){//私有的构造函数}}

②方式二

public class Elvis {private static final Elvis INSTANCE = new Elvis(); //private的域private Elvis(){//私有的构造函数}public static Elvis getInstance(){return INSTANCE;}}

第5条:避免创建不必要的对象

String s = new String("stringette"); //Don't do this
该语句每次被执行的时候都创建一个新的String实例,但是这些创建对象的动作全都是不必要的。传递给String构造器的参数"stringette"本身就是一个String实例,功能各方面等同于构造器创建的所有对象。

改进后的版本如下:

String s = "stringette";

基本类型和装箱基本类型:

public static void main(String[] args) {Long sum = 0L; //这意味着程序会构造大约2的31次方个多余的Long实例for (long i = 0; i < Integer.MAX_VALUE; i++) {sum += i;}System.out.println(sum);}
将sum的声明从Long改为long,运行时间从43秒减少到了6.8秒。结论很明确,要优先使用基本类型而不是装箱基本类型。

第6条:一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。

public Object pop() {if (size == 0) {throw new EmptyStackException();Object result = elements[--size];elements[size] = null;// 清空对象引用return result;}}

第8条:

①使用instanceof操作符检查“参数是否为正确的类型”

②覆盖equals时总要覆盖hashCode


第13条:使类和成员的可访问性最小化


第19条:接口应该只被用来定义类型,它们不应该被用来导出变量


第24条:消除非受检警告

可以用@SuppressWarnings("unchecked")注解来禁止警告


第41条:慎用重载

“能够重载方法”并不意味着就“应该重载方法”。一般情况下,对于多个具有相同参数数目的方法来说,应该尽量避免重载方法。


第43条:返回零长度的数组或者集合,而不是null

对于一个返回null而不是零长度数组或者集合的方法,客户端每次都要判断方法返回的值是否为null这种曲折的处理方式。


第45条:将局部变量的作用域最小化

要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明。几乎每个局部变量的声明都应该包含一个初始化表达式。


第46条:for-each循环优先于传统的for循环

对集合和数组的遍历首选做法如下:

for(Element e : elements){    doSomething(e);}
注意:这样做不会有性能上的损失,在某些情况下,比起普通的for循环,甚至还稍有性能优势。

第48条:如果需要精确的答案,请避免使用float和double

float和double类型尤其不适合用于货币计算。解决的办法是使用BigDecimal、int或者long进行货币计算。BigDecimal有两个缺点:与使用基本运算类型相比,这样做很不方便,而且很慢。在使用int和long时,需要自己处理十进制小数点。

另外,float和double都是线程不安全的。


第49条:基本类型优先于装箱基本类型

基本类型和装箱基本类型之间有三个主要区别:

①基本类型只有值,而装箱基本类型则具有与它们的值不同的同一性。

Comparator<Integer> naturalOrder = new Comparator<Integer>(){public int compare(Integer first,Integer second){return first < second ? -1 : (first == second);}};
对于上面这段代码,如果比较Integer(42)和Integer(42),这两个Integer实例都表示相同的值42,因此这个比较器应该返回0,而事实上,它的输出却为1。这是因为还要对这两个对象引用进行同一性比较。当程序使用==操作符比较两个装箱基本类型时,它做了个同一性比较,这几乎肯定不是你所希望的;

②基本类型只有功能完备的值,而每个装箱基本类型除了它对应基本类型的所有功能之外,还有个非功能值:null;

③基本类型通常比装箱基本类型更节省时间和空间。


第51条:当心字符串连接的性能

为连接n个字符串而重复地使用字符串连接操作符(+, String concatenation operator),需要n的平方级的时间。如果数量巨大的字符串连接,为了获得可以接受的性能,请使用StringBuilder替代String。


第52条:通过接口引用对象

应该优先使用接口而不是类来引用对象,这样会使程序更加灵活。


第66条:同步访问共享的可变数据

Java语言规范保证读或者写一个变量是原子的(atomic),但是long和double类型除外,这两个类型变量不是线程安全的。

增量操作符(++)不是原子的。

当多个线程共享可变数据的时候,每个读或者写数据的线程,都必须执行同步。


第69条:并发工具优先于wait和notify

java.util.concurrent中更高级的工具分成三类:Executor Framework、并发集合(Concurrent Collection. 如ConcurrentMap)以及同步器(Synchronizer)

对于间歇式的定时,始终应该优先使用 System.nanoTime,而不是使用System.currentTimeMills。 System.nanoTime更加准确也更加精确,它不受系统的实时时钟的调整所影响。


第72条:不要依赖于线程调度器

线程优先级是Java平台上最不可移植的特征了。