zoukankan      html  css  js  c++  java
  • EffectiveJava学习笔记(二)

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

      java.lang.Math,java.util.Arrays这种工具类无需实例化,因为毫无意义,但是在缺少显式构造器的情况下,编译器会自动构建一个公有的无参构造器,这些工具类可能会在无意识的情况下被实例化,或是被继承,并且,企图将这种类做成抽象类限制实例化是行不通的,因为继承抽象类的子类仍然可被实例化,同时会造成误解。解决办法是让这种工具类包含一个私有的构造器,这样可以保证类不被实例化,同时也不可被继承,因为子类无法调用父类的构造器。

      第五条:优先考虑依赖注入来引用资源

      许多类会依赖一个或多个底层资源,同时要求底层资源是可以修改的(同一个类的多个实例),满足该需求的最简单的模式是,当创建一个新的实例时,就将资源传入类的构造器中(这就是依赖注入),实现如下。

    public class DependencyDemo {
    
        public static void main(String[] args) {
    
            new People(new Apple());
        }
    }
    
    class People {
    
        public People(Fruit fruit) {
            fruit.eat();
        }
    }
    
    interface Fruit {
    
        void eat();
    }
    
    class Apple implements Fruit {
        @Override
        public void eat() {
            System.out.println("eat apple");
        }
    }
    
    class Banana implements Fruit {
        @Override
        public void eat() {
            System.out.println("eat banana");
        }
    }

      不要使用单例或工具类实现一个需要依赖底层资源的类,因为资源的行为会影响类的行为,也不要直接创建资源,应使用依赖注入的方式。依赖注入的资源对象具有不可变性,因此多个客户端可以共享依赖对象,依赖注入的这种模式同样适用于静态工厂、构造器、构建器。依赖注入的另一个变体是将资源工厂传给构造器,客户端可创建任意子类型。

      第六条:避免创建不必要的对象

      将String s = new String("abc"),修改为String s = "abc";前者多次执行每次都构建一个新的对象,后者多次执行每次都使用缓冲区同一字符常量。

      对于同时提供了构造器和静态工厂方法的不可变类,应优先使用静态工厂方法,避免创建不必要的对象。

      有些对象创建成本昂贵,应缓存下来重复使用,如使用String.matches方法匹配正则表达式,其在内部为正则表达式创建Pattern实例,并且使用一次后就会被回收,为了提升性能,将正则表达式编译成一个Pattern实例,同时在创建类的实例时初始化,缓存起来,达到重用目的。

      自动装箱也会创建多余对象,在仅做计算无需判空的操作下,应优先使用基本类型而非包装类。

      同时,并非任何情况下都要避免创建对象,一些小对象的创建和回收是非常廉价的,无需维护在对象池中,对象池中维护的对象应是非常重量级的,典型如数据库连接池。

      第七条:消除过期的对象引用

      Java虽然有垃圾回收机制,但仍然有可能发生内存泄漏的情况,如下所示,我们手动实现一个栈。

    class Stack {
    
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITAL_CAPACITY = 16;
    
        public Stack() {
            this.elements = new Object[DEFAULT_INITAL_CAPACITY];
        }
    
        public void push(Object ele) {
            elements[size++] = ele;
        }
    
        public Object pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
            return elements[--size];
        }
    
        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, size * 2 + 1);
            }
        }
    }

      在这个例子中,如果一个栈先增长再收缩,弹出的对象将不会被当作垃圾回收,即使程序中不再使用这些对象,它们仍不会被回收,因为栈的内部维护着对这些对象的过期引用(即永远不会被解除的应用)。在本例中,凡事elements中下标小于size部分的引用都是过期应用,这些对象将造成内存泄漏。解决这个问题的方法很简单,只要清空引用即可,pop方法中添加如下处理。

    
    
    public Object pop() {
    if (size == 0) {
    throw new EmptyStackException();
    }
    Object ele = elements[--size];
    elements[size] = null;
    return ele;
    }
    
    

      清空引用的另一个好处是,如果这些过期对象又被错误的引用,程序将抛出NPE,可快速发现异常。

      还有一个内存泄漏的常见来源就是缓存,可以设置过期时间来避免。

      第三个会发生内存泄漏的常见来源是监听器和其他回调,如果没有取消注册或特殊处理将不断堆积,解决办法是只保存它们的弱引用,如将它们保存成WeakHashMap的键。

      第八条:避免使用终结方法和清除方法

      java不会保证一个程序终止前,清除方法或终结方法被及时的执行(或被执行),所以不应该依赖终结方法或清除方法更新状态。并且,如果在终结过程中抛出未被捕获的异常,终结过程将会停止也不会打印出异常栈信息。

      让类实现AutoClouseable可避免使用终止方法或清除方法,配合try-with-resourses可确保程序终止。使用方式及原理可见本博客另一篇文章https://www.cnblogs.com/youtang/p/11441959.html

      第九条:try-with-resources优先与try-finally

      https://www.cnblogs.com/youtang/p/11441959.html

  • 相关阅读:
    数据可视化需要简化编程
    设计模式之工厂模式
    LinCode落单的数
    怎样安装解压版MySQL
    程序阅读:简单C++学生信息管理系统
    中缀式变后缀式
    jquery动态创建表格
    Android笔记——Activity中的回传数据案例(装备选择)
    A mail sent to Google chromium.org Groups for Help
    Eclipse导入MyEclipseproject(web项目显示为java项目解决的方法)
  • 原文地址:https://www.cnblogs.com/youtang/p/11617919.html
Copyright © 2011-2022 走看看