第一条 用静态工厂方法替代构造器
实例代码
/**
* Boolean 静态工厂方法
*/
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
public static void main(String[] args) {
//演示
Boolean a = valueOf(1==1);
System.out.println(a);
}
结果:
true
讲解:
1.比构造方法,更易于使用,可以规范命名易于理解
当需要创建不同类型的对象时,可以写多个工厂方法
2.可以设置静态对象,不必每次都创建一个新的对象
3.可以返回任意子类创建的对象,例如Map接口,可以返回它的任意实现
4.在创建参数化类型对象的时候,可以是代码变得简洁
例如
/**
* HashMap 静态工厂方法
*/
public static <k,v> HashMap<k,v> newInstance(){
return new HashMap<k,v>();
}
public static void main(String[] args) {
//演示
Map<String,String> map = newInstance();
map.put("xiaomi","小米");
System.out.print(map.get("xiaomi"));
}
结果:
小米
建议命名:
如果是获取一个新的对象可以叫getNewInstance()
如果是获取一个单例对象可以叫getInstance();
实际应用案例:
import java.util.UUID;
/**
* 学生类
*/
public class Student {
private String id;
private String name;
private int age;
private String className;
private String school;
//创建一个对象工厂方法
public static Student getNewInstance(String name,int age){
Student stu = new Student();
stu.id = UUID.randomUUID().toString();
stu.name = name;
stu.age = age;
return stu;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
//演示
public static void main(String[] args) {
Student xiaomi = Student.getNewInstance("小米",12);
System.out.println(xiaomi.getName());
System.out.println(xiaomi.getAge());
}
}
结果:
小米
12
第二条 需要多个构造函数的时候,建议使用构建器
代码如下:
/**
* 人类
*/
public class Person {
private final String name;
private final int age;
private final String sex;
//构造函数
public Person(Builder builder){
name = builder.name;
age = builder.age;
sex = builder.sex;
}
//类构建器
public static class Builder {
private String name;
private int age;
private String sex;
public Builder(String name){
this.name = name;
}
public Builder age(int age){
this.age = age; return this;
}
public Builder sex(String sex){
this.sex = sex; return this;
}
//创建对象返回
public Person build(){
return new Person(this);
}
}
@Override
public java.lang.String toString() {
return "Person{" +
"name=" + name +
", age=" + age +
", sex=" + sex +
'}';
}
}
测试代码:
public static void main(String[] args) {
Person hulin = new Person.Builder("胡林").age(12).sex("女").build();
System.out.println(hulin.toString());
Person xiaomi = new Person.Builder("小米").build();
System.out.println(xiaomi.toString());
}
运行结果:
Person{name=胡林, age=12, sex=女}
Person{name=小米, age=0, sex=null}
讲解:
先看结果,胡林和小米这两个对象,用以前思路的话,应该是创建两个构造函数,但是现在这种方法,就不需要啦!仔细观察代码,Builder这个构造器在编写的时候稍微复杂些,但是如果以后创建多个不同的对象,就规范精简很多啦!鱼和熊掌不可兼得嚒
第3条 用私有构造器或者枚举类型强化Singleton
类实现:
package com.qfh.object;
import java.util.Calendar;
import java.util.TimeZone;
/**
* 单例模式(Singleton)
*/
public class HelloUtil {
//在内存中只有一个实例,不可修改
private static final HelloUtil INSTANCE = new HelloUtil();
//私有化构造器
private HelloUtil(){}
//暴露共有获取静态对象方法
public static HelloUtil getInstance(){
return INSTANCE;
};
/**
* 判断时区,打印不同语言的世界你好
*/
public void sayHello(){
Calendar cal = Calendar.getInstance();
TimeZone timeZone = cal.getTimeZone();
if(timeZone.getDisplayName().indexOf("中国") >= 0){
System.out.print("世界你好!");
}else{
System.out.print("Hello World!");
}
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
HelloUtil.getInstance().sayHello();
}
}
运行结果:
世界你好!
优点:
1.私有化构函数,保证不会被创建
2.实例是static 和final 类型的保证全局唯一性
3.线程安全
4.通过反射机制也无法创建新的实例
缺点:
1.无法抵御反序列化攻击
修改如下:
package com.qfh.object;
import java.io.Serializable;
import java.util.Calendar;
import java.util.TimeZone;
/**
* 单例模式(Singleton)
*/
public class HelloUtil implements Serializable {
private static final long serialVersionUID = 810160916916358307L;
//在内存中只有一个实例,不可修改
private static final HelloUtil INSTANCE = new HelloUtil();
//私有化构造器
private HelloUtil(){}
//暴露共有获取静态对象方法
public static HelloUtil getInstance(){
return INSTANCE;
};
/**
* 判断时区,打印不同语言的世界你好
*/
public void sayHello(){
Calendar cal = Calendar.getInstance();
TimeZone timeZone = cal.getTimeZone();
if(timeZone.getDisplayName().indexOf("中国") >= 0){
System.out.println("世界你好!");
}else{
System.out.println("Hello World!");
}
}
/**
* 防止单例对象在序列化后生成“多例”
* @return
*/
private Object readResolve(){
return INSTANCE;
}
}
使HelloUtil可序列化,并加入readResolve方法,在序列化的时候直接返回内存中的实例
测试代码:
/**
* 序列化克隆
* @return
* @throws Exception
*/
public static HelloUtil deepCopy() throws Exception{
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(HelloUtil.getInstance());
InputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
HelloUtil test = (HelloUtil) ois.readObject();
return test;
}
public static void main(String[] args) {
/**
* 这样打印出的HelloUtil对象地址一致,
* 说明依然是单例对象
* 把readResolve方法注释掉再来一遍看看呢?
*/
HelloUtil hello01 = HelloUtil.getInstance();
HelloUtil hello02 = null;
try {
hello02 = deepCopy();
hello02.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
//如果两个对象的地址一样,说明依然是单例,没有创建新对象
System.out.println("hello01:"+hello01);
System.out.println("hello02:"+hello02);
if(hello01 == hello02){
System.out.println("是同一个实例对象");
}else{
System.out.println("不是同一个实例对象");
}
}
运行结果:
世界你好!
hello01:com.qfh.object.HelloUtil@4a5a12f5
hello02:com.qfh.object.HelloUtil@4a5a12f5
是同一个实例对象
对比实验:
注释掉readResolve方法:
/**
* 防止单例对象在序列化后生成“多例”
* @return
*/
/* private Object readResolve(){
return INSTANCE;
}*/
运行结果:
世界你好!
hello01:com.qfh.object.HelloUtil@61ae0436
hello02:com.qfh.object.HelloUtil@479747c9
不是同一个实例对象
但是以上方法不够精简,可以用枚举实现。
代码如下:
package com.qfh.object;
/**
* 枚举类型的单例模式
* 此方法好处有三点:
* 1.线程安全
* 2.不会因为反序列化而产生新实例
* 3.防止反射攻击
* 4.线程安全
*/
public enum GoodUtil {
INSTANCE {
@Override
protected void sayGood() {
System.out.println("棒棒哒!O(∩_∩)O哈哈~");
}
};
/**
* 此方法是内部继承的
*/
protected abstract void sayGood();
}
测试代码:
/**
* 序列化克隆
* @return
* @throws Exception
*/
public static GoodUtil deepCopy() throws Exception{
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(GoodUtil.INSTANCE);
InputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
GoodUtil test = (GoodUtil) ois.readObject();
return test;
}
public static void main(String[] args) {
/**
* 这样打印出的GoodUtil对象地址一致,
* 说明依然是单例对象
*/
GoodUtil hello01 = GoodUtil.INSTANCE;
GoodUtil hello02 = null;
try {
hello02 = deepCopy();
hello02.sayGood();
} catch (Exception e) {
e.printStackTrace();
}
//如果两个对象的地址一样,说明依然是单例,没有创建新对象
System.out.println("hello01:"+hello01);
System.out.println("hello02:"+hello02);
if(hello01 == hello02){
System.out.println("是同一个实例对象");
}else{
System.out.println("不是同一个实例对象");
}
}
运行结果:
棒棒哒!O(∩_∩)O哈哈~
hello01:INSTANCE
hello02:INSTANCE
是同一个实例对象