zoukankan      html  css  js  c++  java
  • 《Effective Java》读书笔记三(类和接口)

    No13 使类和成员的可访问性最小化

    要区别设计良好的模块与设计不好的模块,最重要的因素在于,这个模块对于外部的其他模块而言,是否隐藏其内部数据和其他实现细节。模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况。这个概念被称为信息隐藏(infomation hiding)或封装(encapsulation),是软件设计的基本原则之一。

    对于顶层的(非嵌套的)类和接口,只有两种可能的访问级别:

    1. 包级别的(package-private)。
    2. 公有的(public)。

    对于成员(域、方法、嵌套类和嵌套接口)有四种可能的访问级别:

    1. 私有的(private)。
    2. 包级私有的(package-private),缺省default访问级别。
    3. 受保护的(protected)。
    4. 公有的(public)。

    实例域决不能是公有的。同样的建议也适用于静态域(常量例外)。

    安全漏洞之一:

    // Potential security hole!
    public static final Thing[] VALUES = {...};

    注意:引用本身不能被修改,但是它所引用的对象却可以被修改—这会导致灾难性的后果。

    建议:

    private static final Thing[] PRIVATE_VALUES = {...};
    
    public static final Thing[] values() {
             return PRIVATE_VALUES.clone();
    }

    No16 复合(composition)优先于继承(inheritance)

    对普通的具体类(concrete class)进行跨越包边界的继承,是非常危险的,特指当一个类扩展另一个类的时候(不考虑同一程序员的情况,也不考虑专门为继承而设计的类的情况)。主要原因是因为继承可能导致不确定的风险,需要你深入了解父类细节。

    与方法调用不同的是,继承打破了封装性。换句话说,子类依赖于其超类中特定功能的实现细节。超类的实现有可能会随着发行版本的不同而有所变化。

    只有当子类和超类之间确实存在子类型关系时,使用继承才是恰当的。换句话说,对于两个类A和B,只有当两者之间确实存在“is-a”关系的时候,类B才应该扩展类A;否则类B就不应该扩展类A,而是让B包含A的一个私有实例。

    No20 类层次优于标签类

    考虑下面这个类,它能够表示圆形或者矩形:

    // Tagged class - vastly inferior to a class hierarchy!
    class Figure {
        enum Shape { RECTANGLE, CIRCLE };
    
        // Tag field - the shape of this figure
        final Shape shape;
    
        // These fields are used only if shape is RECTANGLE
        double length;
        double width;
    
        // This field is used only if shape is CIRCLE
        double radius;
    
        // Constructor for circle
        Figure(double radius) {
            shape = Shape.CIRCLE;
            this.radius = radius;
        }
    
        // Constructor for rectangle
        Figure(double length, double width) {
            shape = Shape.RECTANGLE;
            this.length = length;
            this.width = width;
        }
    
        double area() {
            switch(shape) {
              case RECTANGLE:
                return length * width;
              case CIRCLE:
                return Math.PI * (radius * radius);
              default:
                throw new AssertionError();
            }
        }
    }

    这种标签类(tagged class)有着许多缺点。它们中充斥着样板代码,包括枚举声明,标签域以及条件语句,破坏了可读性。

    我们再看看下面的代码:

    // Class hierarchy replacement for a tagged class
    abstract class Figure {
        abstract double area();
    }
    class Circle extends Figure {
        final double radius;
    
        Circle(double radius) { this.radius = radius; }
    
        double area() { return Math.PI * (radius * radius); }
    }
    class Rectangle extends Figure {
        final double length;
        final double width;
    
        Rectangle(double length, double width) {
            this.length = length;
            this.width  = width;
        }
        double area() { return length * width; }
    }

    这个类层次纠正了前面提到过的标签类的所有缺点。代码简单且清楚。另一种好处,有助于增强灵活性,比如扩展一个正方形,只需要扩展长方形即可:

    class Square extends Rectangle {
        Square(double side) {
            super(side, side);
        }
    }

    No22 优先考虑静态成员类

    嵌套类(nested class)是指被定义在另一个类的内部的类。嵌套类存在的目的应该只是为它的外围类(enclosing class)提供服务。如果嵌套类将来可能会用于其他的某个环境中,它就应该是顶层类(top-level class)。

    嵌套类有四种:静态成员类(static member class)、非静态成员类(nonstatic member class)、匿名类(anonymous class)和局部类(local class)。除了第一种以外,其他三种被称为内部类(inner class)。

    静态成员类是最简单的一种嵌套类。最好把它看作是普通的类,只是碰巧被声明在另一个类的内部而已,它可以访问外围类的所有成员,包括那些被声明为私有的成员。静态成员类是外围类的一个静态成员,与其他的静态成员末端,也遵守同样的可访问性规则。如果它被声明为私有的,它就只能在外围类的内部才可以访问。

    静态成员类的一种常见用法是作为公有的辅助类,仅当它与它的外部类一起使用时才意义。

    非静态成员类的一种常见用法是定义一个Adapter,它允许外部类的实例被看作是另一个不相关的类的实例。示范:

    // Typical use of a nonstatic member class
    public class MySet<E> extends AbstractSet<E> {
             // Bulk of the class omitted
    
             public Iterator<E> iterator() {
                      return new MyIterator();
             }
      
    
             private class MyIterator implements Iterator<E> {
                 // ...
             }
    }

    如果成员类的每个实例都需要一个指向其外围实例的引用,就要把成员类做成非静态的;否则,就做成静态的。

    匿名类可以出现在代码中任何允许存在表达式的地方。由于匿名类出现出现在表达式当中,它们必须保持简短—大约10行或者更少些—否则会影响程序的可读性。

    匿名类的一种常见用法是动态地创建函数对象,例如:

    Arrays.sort(stringArray, new Comparator<String>(){
             public int compare(String s1, String s2) {
                       return s1.length() – s2.length();
    }});

    局部类是四种嵌套类中用得最少的类。在任何“可以声明局部变量”的地方,都可以声明局部类,并且局部类也遵守同样的作用域规则。

  • 相关阅读:
    forfiles
    windows 安装 keras
    windows上使用tensorboard
    python类定义与c#的一些区别
    iis https 客户端证书
    DevExpress历史安装文件、源码、注册破解下载
    AE开发,执行GP操作的时候的错误
    USB无线网卡和PCI-E无线网卡如何选择(转)
    .NET 对实现IPersistStream接口的对象进行保存和读取
    搜索
  • 原文地址:https://www.cnblogs.com/nayitian/p/3245474.html
Copyright © 2011-2022 走看看