Effective Java 第2章 创建和销毁对象 精简总结 (1)

1 篇文章 0 订阅
1 篇文章 0 订阅
第一条 用静态工厂方法替代构造器

实例代码

     /**
     * 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
是同一个实例对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值