很久以前买的一本《Effective Java》,最近开始读啦!从第二章开始看(第一章是引言),每一章都有若干条建议,翻了一下感觉对日常编码有很强的指导意义呀,勤劳的我决定写个读书笔记记录一下。
笔记里面会记录每一章的所有条建议,略过我觉得不太重要的细节,加入一些小例子。如果有书里讲的不太清楚的,会贴一些其他的资料。
说到静态工厂方法,书里给的例子是包装类型,也就是平时常会用到的Integer.valueOf(),Integer.parseInt()想必也算,返回的都是Integer类型的对象。对于自定义的类而言,我写了个简单的例子:
class Student{
private int id;
private String name;
private Student(int id, String name){
this.id = id;
this.name = name;
}
public static Student newInstance(int id, String name){
return new Student(id, name);
}
}
当我们想创建一个Student对象时,最常见的一种方法就是用构造器方法,也就是:
Student s = new Student(1, "ZhangSan");
但是在上面的代码示例里,将构造方法私有化,对外只提供一个静态方法,创建对象时需要这么写:
Student s = Student.newInstance(1, "ZhangSan")
那么这样有什么好处呢?
缺点:
关于建造者模式,在前面的博客里已经详细说过了,这里不再赘述
同上,这个在前面的博客(单例模式)里也详细说了。
对于一些类来说,实例化是没有意义的。比如在我写的一个商城项目里封装了一些工具类,比如PropertiesUtil、MD5Util、JedisUtil,我只需要调用这些类的静态方法,不需要实例化对象。
这个时候就可以把构造函数私有化,如果要防止在类的内部显示调用构造方法,可以在私有的构造方法里加入这样的方法体:
throw new AssertionError();
依赖注入这个概念在看设计模式和spring的时候都接触过。所谓依赖就是A类有个B类的成员变量,那么A依赖于B;比如车里有车轮,那么车这个类可以这么写:
class Car{
private Wheel wheel;
public Car(){
this.wheel = new Wheel();
}
}
上面的代码里,Car类在初始化的时候要负责wheel对象的创建。那么问题来了,当Wheel的构造方法变了,比如要指定一个形参size代表车轮尺寸,那么不仅要改Wheel类的代码,Car类的代码(以及其他所有依赖于Wheel类的代码)都要更改,不符合开闭原则和单一职责原则。如果车依赖于车轮,车轮依赖于螺丝,那么螺丝的构造方法更改了,所有直接或间接依赖于螺丝的都要更改,是非常难以维护的。
为了解耦,把一个类(称为A)所依赖的其他类的对象创建过程与A的实现解耦,就要用到依赖注入。听上去挺高级的,其实用起来很简单:
class Car{
private Wheel wheel;
public Car(Wheel wheel){
this.wheel = wheel;
}
}
上面的代码就是依赖注入中非常常见的一种——构造器注入,以前没听过依赖注入这个概念,但是平时其实都是这么写的。
对于不可变的对象,以及创建开销非常大的对象,可以采用缓存、对象池这样的方法进行重用。
缓存最常见的例子就是Java提供的字符串缓存池了(基本类型也是有缓存池的,比如int类型会缓存-128至127),如果写这么一句代码:
String s = "abc";
那么s这个引用指向的不是堆里产生的新对象,而是去字符串常量池里找“abc”这个字面量,找得到就指向这个字面量,找不到就生成一个并且指向他。如此一来所有值为“abc”的字符串引用指向的是同一块内存地址,重用的前提是String是final类型不用担心数据安全问题。
至于创建开销特别大的对象,数据库的连接算是比较典型的例子,可以用连接池进行联结的重用。
这一条针对的主要是内存泄漏问题,书中提到的例子是栈(top指针后面的元素都属于内存泄漏)、缓存(解决方法一般是lru)、监听器和其他回调(弱引用解决)。
除此之外,我记得Threadlocal对象也是有内存泄漏问题的,解决方法是弱引用+手动调用remove()。
finalize()方法在看JVM垃圾回收的时候看到过,对象被标记为垃圾对象后不会立即被回收,会检查一下是否需要调用finalize()方法,但是这个方法不是很稳定,不能确保执行。
从Java9开始,Finalizer机制被弃用,被Cleaner机制代替,仍然是不可预测、运行缓慢、通常不必要的。
这两个我都没用过,他们的问题主要在于:
但是这两个机制也是有好处的,用途主要有二: