优雅的创建对象
问题
-
考虑使用静态工厂方法替代构造方法
-
代码示例:
-
优点:
- 与构造方法不同,它们是有名字的。
- 它们不需要每次调用时都创建一个新对象。
- 它们可以返回其返回类型的任何子类型的对象。
- 他们返回对象的类可以根据输入参数的不同而不同。
-
缺点:
- 只提供静态工厂方法的主要限制是,没有公共或受保护构造方法的类不能被子类化。
- 静态工厂方法的第二个缺点是,程序员很难找到它们。
-
-
当构造方法参数过多时使用builder模式
- 图片示例:
- 上面是一个食品包装营养成分的类。
- 第一个图片是直接用伸缩的构造函数,通常这种方式你不得不为每一个参数都传递一个值,这种方式在参数不断增加后客户端很难编写代码,因为不知道每个参数都是什么意思,就比如ThreadPoolExecutor类,里面有七个参数,开始用的时候我必须去读一下源码才能理解每个参数到底该怎么去用。可读性比较差
- 当然我们还可以通过set的方式(即javaBean的方式)进行参数设置,但是这样就会改变对象不可变的性质,从而变成非线程安全的,在多线程下难以维护
- 第二个和第三个图片就是我们今天要说的builder模式创建对象
- 我们可以看到这种模式源对象只有一个私有的构造方法,里面只有一个builder参数
- builder对象每次build都会产生一个NutritionFacts对象,并且这个对象是不可变的。
- builder在设置伸缩参数的时候及其简单,代码可读性高
- 总结:比使用伸缩构造方法更容易读写,并且builder模式比 JavaBeans 更安全
- 图片示例:
-
使用私有构造方法或枚类实现Singleton属性
- 构造单例:
-
饿汉式单例:
-
饿汉式单例单例只要在类被加载时就会生成对象,所以有了懒汉式单例:
-
上面的懒汉式单例是存在着线程不安全的,当多线程并发执行时,会产生多个对象,故出现了双重检查锁单例:
-
思考:双重检查锁单例为什么要加volatile?
-
静态内部类创建单例,这种模式同时具有懒加载和线程安全的特性:
-
上面的双重检查锁和静态内部类都具有懒加载和线程安全的特性了,那是不是最完美的了呢?我们看下下面两个例子:
-
上面两个返回的结果都是false,当使用反射和反序列化攻击的时候还是会返回不同的对象,最佳的单例模式就是使用枚举来实现单例,java规定每一个枚举类型及其定义的枚举变量在jvm中都是唯一的,并且枚举具有对象的很多特性,比如定义方法:
-
思考:那为什么枚举就能防反射攻击和反序列化的问题了呢?
-
首先我们可以看下反射,报错了,我们进去newInstance方法看一下,发现反射在new对象时会进行判断是否枚举类型,如果是则报异常:
-
我们在看一下反序列化,下面的返回结果为true,说明枚举在反序列化之后返回的对象与原来的对象是同一个:
-
- 构造单例: