Java的历程源远流长,代码写作方式五花八门,从今天开始,重新学习 java。每篇3点, 2021年4月20日
对象的创建和销毁
- 用静态工厂方法代替构造器
- 遇到多个构造器参数时要考虑使用构建器
- 用私有构造器或者枚举类型强化Singleton属性
1. 用静态工厂方法代替构造器
对于类而言,为了让客户端获取自身的一个实例,最传统的方法是提供一个公有的构造器,还有一个方法就是静态工厂方法(static factory method),它只是一个返回类的实例的静态方法,类似 Boolean中的一个对象引用 :
public static Boolean valueOf(boolean b) {
return b? Boolean.TRUE : Boolean.FALSE;
}
注意,静态工厂方法(static factory method)和设计模式中的工厂方法(Factory Method)模式是不同的。
静态工厂方法与构造器不同的第一大优势是,它们都有名称。如果构造器的参数本身没有明确的描述被返回的对象,那么具有适当名称的静态工厂方法会更容易使用,产生的代码更有易读性
静态工厂方法与构造器不同的第二大优势在于,不必再每次调用它们的时候都创建一个新对象.这个可以使不可变的类可以预先构建好实例,或者把构建好的实例缓存起来,进行重复的利用,从而避免重复创建对象,例如Boolean.valueOf(boolean) 方法说明了这个技术.这种方式类似于享元模式,减少了创建对象的代价,可以使性能极大的提升.
静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象,这样在选择返回对象的类时就有了更大的灵活性,例如 API可以返回对象,同时不会使对象的类变成公有的。以这种隐藏方式会让API变得非常简洁。这项技术可以用于基于接口的框架(interface-based framework),因为这种框架中,接口为静态工厂方法提供了自然返回类型。例如 java.util.Collections
public class Collections {
// Suppresses default constructor, ensuring non-instantiability.
private Collections() {
}
public static <T> List<T> singletonList(T o) {
return new SingletonList<>(o);
}
private static class SingletonList<E>
extends AbstractList<E>
implements RandomAccess, Serializable {
private static final long serialVersionUID = 3093736618740652951L;
.... //省略
}
..... //等等方法
}
说明 : Collections是一个私有的类,不可以被初始化,但是内部的方法 singletonList方法返回了一个 new SimgletonList<>(T) 对象,但是 SimgletonList这个类是私有的
静态工厂方法的第四大优势在于,所返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。 例如 :
public static User getUser(String name) {
return new User().setName(name); // 通过传递不同的参数,就可以返回不同的对象
}
静态工厂的第五大优势在于,方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不参与。这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础,例如: JDBC(Java数据库连接API)。服务提供者框架是指这样一个系统: 多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。
缺点 1. 类如果不含公有的或者受保护的构造器,就不能被子类化
public static class B {
private B(){
}
public static B getWhat() {
return new B();
}
}
public class b extends B {
}
new b(); // 错误 应为B并没有提供公有的构造方式,是用静态工厂的方法产出对象B
缺点 2. 我们很难发现它们,对于静态工厂方法来说,因为没有构造器,要想查明一个实例化的类是比较困难的
总的来说,构造器也好,静态工厂方法也好,各有各的好处,理解它们的长处,全面考虑,用到更合适的地方。
2. 遇到多个构造器参数时要考虑使用构建器
- 以前大多数人的实现
public class Book {
private Integer id;
private String name;
private List<Integer> lists;
public Book(Integer id,String name,List<Integer> lists) {
this.id = id;
this.name = name;
this.lists = lists;
}
}
private void test () {
Book name = new Book(1, "name", Arrays.asList(1));
}
过于复杂,对开发来说不是很友好
- 现在部分人的实现
public class Book {
private Integer id;
private String name;
private List<Integer> lists;
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setLists(List<Integer> lists) {
this.lists = lists;
}
}
private void test () {
Book book = new Book();
book.setId(1);
book.setName("name");
book.setLists(new ArrayList<>(0));
}
也还算比较不错,比如有很多个元素,就一直 book.setN
3.链式表达的实现
public class Book {
private Integer id;
private String name;
private List<Integer> lists;
public Book setId(Integer id) {
this.id = id;
return this;
}
public Book setName(String name) {
this.name = name;
return this;
}
public Book setLists(List<Integer> lists) {
this.lists = lists;
return this;
}
}
private void test () {
Book book = new Book().setId(1).setLists(new ArrayList<>(0)).setName("name");
}
使用起来应该清爽了许多了,并且再次看代码,可读性也很高.基于Idea使用lombok注解,更简单
3. 用私有构造器或者枚举类型强化Singleton属性
Singleton是指仅仅被实例化一次的类。Singleton通常被用来代表一个无状态的对象,或者是本质上唯一的系统组件。使类成为Singleton会使它的客户端测试变得非常困难,因为不可能给Singleton替换模拟实现,除非实现一个充当起类型的接口。
实现Singleton有两种常见的放阿飞。这两种方法都要求构造器一定是私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实例。
- 第一种 :
public class Book {
private long id;
public static final Book INSTANCE = new Book().setId(System.currentTimeMillis());
private Book() {
}
public static void main(String[] args) {
Book b = Book.INSTANCE;
Book b1= Book.INSTANCE;
Book b2 = Book.INSTANCE;
System.out.println(b.hashCode());
System.out.println(b1.hashCode());
System.out.println(b2.hashCode());
System.out.println(b.getId());
System.out.println(b1.getId());
System.out.println(b2.getId());
}
}
说明 :
私有构造方法只调用一次,来初始化公共静态 final Elvis.INSTANCE 属性。缺少一个公共的或受保护的构
造方法,保证了全局的唯一性:一旦 Elvis 类被初始化,一个 Elvis 的实例就会存在——不多也不少。客户端所做的
任何事情都不能改变这一点,但需要注意的是:特权客户端可以使用 AccessibleObject.setAccessible
方法,以反射方式调用私有构造方法
- 第二种
public class Book {
private long id;
public static final Book INSTANCE = new Book().setId(System.currentTimeMillis());
private Book() {
}
public static Book getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Book b = Book.getInstance();
Book b1= Book.getInstance();
Book b2 = Book.getInstance();
System.out.println(b.hashCode());
System.out.println(b1.hashCode());
System.out.println(b2.hashCode());
System.out.println(b.getId());
System.out.println(b1.getId());
System.out.println(b2.getId());
}
说明:
所有对 getInstance 的调用都返回相同的对象引用,并且不会创建其他的 Book 实例
- 第三种 基于多线程问题,有种更好的单例模式叫做Initialization Demand Holder (IoDH)的技术
public class Singleton {
private Singleton(){
System.out.println(Singleton.class);
}
private static class HolderClass{
private final static Singleton s = new Singleton();
}
public static Singleton getInstance(){
return HolderClass.s;
}
public static void main(String[] args) {
Singleton s = Singleton.getInstance();
}
}
- 第四重 双重锁校验技术,没啥好说的,自行百度吧