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();
}});

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

《Effective Java》读书笔记三(类和接口)的更多相关文章

  1. Effective Java 读书笔记之三 类和接口

    一.使类和成员的可访问性最小化 1.尽可能地使每个类或者成员不被外界访问. 2.实例域决不能是共有的.包含公有可变域的类不是线程安全的. 3.除了公有静态final域的特殊情形之外,公有类都不应该包含 ...

  2. 《Effective Java》读书笔记 - 4.类和接口

    Chapter 4 Classes and Interfaces Item 13: Minimize the accessibility of classes and members 一个好的模块设计 ...

  3. Effective Java读书笔记--类和接口

    1.使类和成员的可访问性最小化不指定访问级别,就是包私有.protected = 包私有 + 子类一般private不会被访问到,如果实现了Serializable,可能会泄露.反射.final集合或 ...

  4. Effective java读书笔记

    2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...

  5. Effective Java读书笔记完结啦

    Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...

  6. [Effective Java]第四章 类和接口

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  7. Effective Java 读书笔记之五 枚举和注解

    Java1.5中引入了两个新的应用类型家族,新的类为枚举类型,新的接口为注解类型. 一.用enum代替int常量 1.枚举值由一组固定的常量组成合法值的类型. 二.用实例域代替序数 1.不要根据枚举的 ...

  8. 《Effective Java》笔记 使类和成员的可访问性最小化

    类和接口 第13条 使类和成员的可访问性最小化 1.设计良好的模块会隐藏所有的实现细节,把它的API与实现清晰的隔离开来,模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况: ...

  9. 初读"Thinking in Java"读书笔记之第九章 --- 接口

    抽象类和抽象方法 abstract void f();抽象方法是仅有声明而没有方法体的方法. 包含抽象方法的类叫做抽象类,如果一个类包含了一个抽象方法,则该类必须限定为抽象类. 抽象类和抽象方法可以使 ...

  10. Effective Java 读书笔记(一):使用静态工厂方法代替构造器

    这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...

随机推荐

  1. ImportError: No module named arcpy

    好久没写Python脚本了,今天一运行就报错:未找到名称为 arcpy 的模块(ImportError: No module named arcpy). 多半是环境变量出问题了,Python最令人讨厌 ...

  2. Unable to resolve superclass of

    因为用了和Google map相关的类,而Google map是单独的library,需要导入之后才能使用,因此在manifest.xml文件中的<application></app ...

  3. php之快速入门学习-3(print和echo)

    PHP echo 和 print 语句 echo 和 print 区别: echo - 可以输出一个或多个字符串 print - 只允许输出一个字符串,返回值总为 1 提示:echo 输出的速度比 p ...

  4. JAVA中MD5加密实现

    MD5加密实现  结 package com.pb; import java.io.UnsupportedEncodingException; import java.security.Message ...

  5. Deep compression code

    https://github.com/songhan/SqueezeNet-Deep-Compression import sys import os import numpy as np impor ...

  6. 安装调试Installing Odoo

    来自odoo的安装步骤 There are mutliple ways to install Odoo, or not install it at all, depending on the inte ...

  7. oracle 11g完美卸载

      oracle 11g完美卸载 CreateTime--2018年4月22日17:07:19 Author:Marydon 对于oracle数据库的卸载,一定要卸载干净,否则,再次想装oracle时 ...

  8. python之函数用法__getitem__()

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之函数用法__getitem__() #http://www.cnblogs.com/hongf ...

  9. Eclipse c++ 编译调试

    直接添加源文件方法: 右键选择工程->import->General->File System,在弹出的对话框中选择源文件目录,筛选文件后: 1.如果直接加到工程中,点Finish就 ...

  10. MyEclipse上的第一个java web

    简单解说怎样MyEclipse上开发一个web project 以及怎样部署的问题. 第一步: 创建一个web project File -> New -> Web Project 将出现 ...