effective java学习笔记

总共657条建议,随笔记录些
第一条:考虑用静态工厂来代替公开的构造方法
第二条:通过私有的构造函数来强化单例属性

两种单例模式:
模式一:
/**
* 通过构建一个公开静态的对象实例,来保持单例。
* 缺点:想改变单例为多例时,改动较大,若是想一直保持该类为单例的话,没问题,性能上也较高
*/
public static final Singleton2 instance = new Singleton2();

private Singleton2(){}

模式二:
/**
* 通过构建一个公开静态的方法来获得实例。
* 优点:想改变单例为多例时,修改方便
*/
private static final Singleton1 instance = new Singleton1();

private Singleton1(){}

public static Singleton1 getInstance()
{
return instance;
}

第三条:通过私有的构造函数来强化不可实例化

比如说我们需要编写一些全是静态方法或静态属性的类,该类不被实例化。
最好的例子就是工具类。java.util.Collections包。只提供公开,静态的方法来访问。
第四条:避免创建重复的对象
4.1
String s = new String(“haha”); // don’t do this 该语句每次都会new 一个新的对象出来,在循环中将会new出一堆的新对象
String ss = “haha”;这个方式新建的对象把放入JVM的池中,在循环中中再次调用时,不会new 一个新对象。

4.2
Boolean b = Boolean.valueOf(“true”); 这个是利用静态工厂来获得对象,只会新建一个新的对象
Boolean b1 = new Boolean(“true”); 通过构造方法来获得对象的方式则会新建一个对象

在新建一个类的时候,尽量使用对方提供的静态工厂来获得实例,因为在其中还可以做到延迟加载的效果。

4.3
对于一些不变的变量,特别是定义在循环中的变量,如果其值是不变,那么可以考虑设置为static final 。
在static 块中进行赋值。能够大大减少系统的开销。至少可以减少100倍的耗时。在1000000次循环的情况下。
4.4
对于现代的JVM来说,小对象的创建和回收是非常廉价的,建立多使用。不过在循环中例外。

第五条:消除过期的对象引用
例如说在一个栈中,我们pop一个元素后,在数组中指向该对象的引用起始已经是无效了,但是JVM不知道,还在这个数组中保持着对该对象的引用,不会去清理该片内存,如果一直操作这个栈的话就可能会引起内容泄露。
良好的做法就是设置数组pop的这个位置的引用指向null。
即,只要一个类自己管理一片内容的话,就需要考虑内存泄露的问题。

内存泄露还有可能就是缓存

第六条:避免使用终结函数

暂时没看明白

第七条:改写equals尽量遵守的约定
7.1 在进行比较时,不需要进行 null 值比较,可以直接采用instanceof 中为null则直接返回false
7.2 域的比较顺序也会影响性能,最新比较的域应该是最容易发生改变的域。
7.3 改写equals的时候一般要改写hashCode()
7.4 不要让equals方法过于聪明
7.5 不要让equals依赖于不可靠的因素
7.6 不要把其中的Object替换成其他的对象
如:
@Override
public boolean equals(OutOfMenery arg0)
{
return false;
}

一个相对较好的equals方法
@Override
public boolean equals(Object arg0)
{
if (!(arg0 instanceof OutOfMenery)) return false; //这里已经做了null值判断
if (arg0 == this) return true;
OutOfMenery cMenery = (OutOfMenery)arg0;
return cMenery.size == this.size;
}

第八条:重写equals必须重写hashCode
8.1规范
1. 同一个程度中如果对象的值没有变化,那么多次调用hashCode方法必须返回同一个整数
2. 如果两个对象的equals方法相同,那么调用hashCode的方法也必须相同
3. 如果两个对象的equals方法不用,不要求hashCode的方法返回相同的值,不过为了散列跟均匀,建议返回相同的值。

8.2 非常不建立使用下种方式来实现hashCode,因为如此会将一个散列表整成一个链表。导致线性运行的程序成平方运行时间。
@Override
public int hashCode()
{
return 42;
}

8.3 方法实现的小建议

1. 把某个非0常数,比如17.保存在一个较results的变量中
2. 对于对象的每一个域做一下操作 计算散列码 c
2.1 boolean —> c = f?0:1
2.2 byte char short int — > (int)f;
2.3 long 类型 –> (int)(f^f >>> 32);
2.4 float 类型 — > Float.floatToIntBits()转化为int
2.5 double 类型 — > Double.doubleToLongBits() 转化为int
2.6 对象的引用,那么直接调用对象的hashCode()方法,具体在对象中自己实现。如果对象为null 那么就设置为0
2.7 数组,把数组中的每一个值作为单独的域来处理。递归的引用上面的规则。
3. 按照下面的公式来套用2中计算得到的 c
result = result*37 +c ; //选择37是因为37是一个素数

下列就是按照上面的规则进行的计算
@Override
public int hashCode()
{
int result = 17; //设置常量
result = result*37 + area; //转为int 计算
result = result*37 + exchange;
result = result*37 +extention;
return result; //返回自己需要的值
}
4.若是计算的hashCode非常复制,计算代价太大,那么可以将它放到系统缓存中。

private volatile static int hashCode = 0; //这样是作为一个缓存,并做lazy-initialized
@Override
public int hashCode()
{
if ( 0 == hashCode)
{
int result = 17;
result = result*37 + area;
result = result*37 + exchange;
result = result*37 + extention;
hashCode = result;
}
return hashCode;
}

第九条:总是要改写toString()

建议一定要改写该方法。
最好是指定一种格式来来覆盖该方法,可以的话提供一个公开静态的方法来解析toString后的方法。

第十条:谨慎的改写clone()方法

首先需要实现cloneanle接口,标示该类是允许clone的

作者: inter12

在这苦短的人生中,追求点自己的简单快乐

发表评论

电子邮件地址不会被公开。 必填项已用*标注