• 《Effective Java》笔记 :(一)创建和销毁对象


    一 .考虑用静态工厂方法代替构造器

        1. 静态工厂方法与设计模式中的工厂方法模式不同,注意不要混淆

          例子:

    public static Boolean valueOf(boolean b){
        return b ? Boolean.TRUE : Boolean.FALSE;
    } 

        2. 采用静态工厂方法比构造器方法的优势

        ①有名称

        ② 不必在每次调用他们的时候都创建一个新的对象

        ③可以返回原类型的任何子类型的对象

        ④在创建参数化实例的时候,使代码更加简洁

    比如

    Map<String,List<String>> m = new HashMap<String,List<String>>();

    当参数越多的时候会越复杂,可以考虑用静态工厂方法

    public static<K,V> HashMap<K,V> newInstance(){
        return new HashMap<K,V>();
    }

    Map<String,List<String>> m = HashMap.newInstance();

    3.采用静态工厂方法的缺点:类如果不含公有的或者受保护的构造器,就不能被子类化;与其他的静态方法实质上没区别
    静态工厂方法惯用名词:valueOf/of/getInstance/newInstance/getType/newType

    二.遇到多个构造器参数时考虑用构建器(建造器Builder)

    重构构造器模式可行,但是参数很多的时候,客户端代码很难编写,容易出错,且较难阅读维护

     替代办法一: 使用JavaBeans模式,调用无参构造器创建对象,然后调用setter设置参数 

    public class NutritionFacts {
        // Parameters initialized to default values (if any)
        private int servingSize = -1; // Required; no default value
        private int servings = -1; // "     " "      "
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;
    
        public NutritionFacts() {
        }
    
        // Setters
        public void setServingSize(int val) {
            servingSize = val;
        }
    
        public void setServings(int val) {
            servings = val;
        }
    
        public void setCalories(int val) {
            calories = val;
        }
    
        public void setFat(int val) {
            fat = val;
        }
    
        public void setSodium(int val) {
            sodium = val;
        }
    
        public void setCarbohydrate(int val) {
            carbohydrate = val;
        }
    
        public static void main(String[] args) {
            NutritionFacts cocaCola = new NutritionFacts();
            cocaCola.setServingSize(240);
            cocaCola.setServings(8);
            cocaCola.setCalories(100);
            cocaCola.setSodium(35);
            cocaCola.setCarbohydrate(27);
        }
    }
    View Code

    这种办法有缺陷:① 构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态 ②JavaBeans模式阻止了把类做成不可变的可能

    替代办法二:使用Builder模式

    public class NutritionFacts {
        private final int servingSize;
        private final int servings;
        private final int calories;
        private final int fat;
        private final int sodium;
        private final int carbohydrate;
    
        public static class Builder {
            // Required parameters
            private final int servingSize;
            private final int servings;
    
            // Optional parameters - initialized to default values
            private int calories = 0;
            private int fat = 0;
            private int carbohydrate = 0;
            private int sodium = 0;
    
            public Builder(int servingSize, int servings) {
                this.servingSize = servingSize;
                this.servings = servings;
            }
    
            public Builder calories(int val) {
                calories = val;
                return this;
            }
    
            public Builder fat(int val) {
                fat = val;
                return this;
            }
    
            public Builder carbohydrate(int val) {
                carbohydrate = val;
                return this;
            }
    
            public Builder sodium(int val) {
                sodium = val;
                return this;
            }
    
            public NutritionFacts build() {
                return new NutritionFacts(this);
            }
        }
    
        private NutritionFacts(Builder builder) {
            servingSize = builder.servingSize;
            servings = builder.servings;
            calories = builder.calories;
            fat = builder.fat;
            sodium = builder.sodium;
            carbohydrate = builder.carbohydrate;
        }
    
        public static void main(String[] args) {
            NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                    .calories(100).sodium(35).carbohydrate(27).build();
        }
    }
    View Code

    三.用私有构造器或者枚举类型强化Singleton属性

     单元素的枚举类型已经成为实现Singleton的最佳方法

    public enum Elvis {
        INSTANCE;
    
        public void leaveTheBuilding() {
            System.out.println("Whoa baby, I'm outta here!");
        }
    
        // This code would normally appear outside the class!
        public static void main(String[] args) {
            Elvis elvis = Elvis.INSTANCE;
            elvis.leaveTheBuilding();
        }
    }

    四.通过私有构造器强化不可实例化的能力

    企图通过将类做成抽象类来强制该类不可实例化,是行不通的 --可以子类化,子类可以实例化

    让这个类包含私有构造器,就不能实例化了

    public class UtilityClass {
        // Suppress default constructor for noninstantiability
        private UtilityClass() {
            throw new AssertionError();
        }
    }

    五.避免创建不必要的对象

    String s = new String("hello")------->两个实例

    换成

    String s = "hello"; ------>                一个实例

    优先使用静态工厂方法,而不是构造器,避免创建不必要的对象

    public class Person {
        private final Date birthDate;
    
        public Person(Date birthDate) {
            // Defensive copy - see Item 39
            this.birthDate = new Date(birthDate.getTime());
        }
    
        // Other fields, methods omitted
    
        // DON'T DO THIS!
        public boolean isBabyBoomer() {
            // Unnecessary allocation of expensive object
            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
            Date boomStart = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
            Date boomEnd = gmtCal.getTime();
            return birthDate.compareTo(boomStart) >= 0
                    && birthDate.compareTo(boomEnd) < 0;
        }
    }
    
    //上面的程序每次被调用都会创建Date ,Calendar,TimeZone没必要
    //下面的程序使用了一个静态初始化器,避免了效率低下
    
    
    
    class Person {
        private final Date birthDate;
    
        public Person(Date birthDate) {
            // Defensive copy - see Item 39
            this.birthDate = new Date(birthDate.getTime());
        }
    
        // Other fields, methods
    
        /**
         * The starting and ending dates of the baby boom.
         */
        private static final Date BOOM_START;
        private static final Date BOOM_END;
    
        static {
            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
            BOOM_START = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
            BOOM_END = gmtCal.getTime();
        }
    
        public boolean isBabyBoomer() {
            return birthDate.compareTo(BOOM_START) >= 0
                    && birthDate.compareTo(BOOM_END) < 0;
        }
    }
    View Code

    优先使用基本类型而不是装箱基本类型,当心无意识的自动装箱

    //声明成Long而不是long,意味着构造了很多Long实例。应该改成long
    public class Sum {
        // Hideously slow program! Can you spot the object creation?
        public static void main(String[] args) {
            Long sum = 0L;
            for (long i = 0; i < Integer.MAX_VALUE; i++) {
                sum += i;
            }
            System.out.println(sum);
        }
    }

    六.消除过期的对象引用

    //下面的例子存在着内存泄漏的可能,因为过期引用并没有被回收
    public class Stack {
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        public Stack() {
            elements = new Object[DEFAULT_INITIAL_CAPACITY];
        }
    
        public void push(Object e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public Object pop() {
            if (size == 0)
                throw new EmptyStackException();
            return elements[--size];
        }
    
        /**
         * Ensure space for at least one more element, roughly doubling the capacity
         * each time the array needs to grow.
         */
        private void ensureCapacity() {
            if (elements.length == size)
                elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
    
    //修复方法
    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
            elements[size] = null;
            return result;
    }
    View Code

     内存泄漏的另一个常见来源是缓存,处理方法是使用WeakHashMap代表缓存

    内存泄漏的第三个常见来源是监听器和其他回调,,处理办法是保存他们的弱引用,例如只将它们保存成WeakHashMap中的键

    七.避免使用终结方法

    而使用显式终止方法,比如InputStream/OutputStream和java.sql.Connection上的close()方法。

    通常与try-finally结合使用,在finally子句中调用显式终止方法。

  • 相关阅读:
    1030 完美数列 (25 分)
    1029 旧键盘 (20 分)
    数据库命令失败原因汇总
    代码有中文括号,导致错误
    win10笔记本触控板使用指南
    (已解决)vsC#控制台应用添加System.Windows.Forms引用失败(精通C#)
    ildasm中Ctrl+M闪退的问题(已解决, 精通C# 15.1)
    C#控制台应用(.NET Core)添加System.Windows.Forms失败(已解决)
    知识点_指针_增加对指针的理解
    自己写出的Bug_应是%f却写成%d
  • 原文地址:https://www.cnblogs.com/qingdaofu/p/6814936.html
Copyright © 2020-2023  润新知