第13条 使类和成员的可访问性最小化

概要

1.使类和成员的可访问性最小化的意义

2.类和成员的可访问性类别

3.其他注意事项

4.最后的原则


一、使类和成员的可访问性最小化的意义

1. 概念:信息隐藏(或者"封装")

一个模块对外部模块隐藏其内部数据和其他实现细节,将它的API与它的实现细节清晰地隔离开来,模块之间只通过API进行通信,其他模块不需要知道该模块的内部工作情况。

2. 封装的作用

(1) 有效的解除组成系统的各模块之间的耦合关系,使得各个模块可以独立地开发,测试,优化,使用,理解和修改。

(2) 减轻程序员维护的负担。维护的模块的时候,只需要维护单个的模块,不用考虑其它模块的事情,只需要考虑模块对外提供的API。

(3) 模块复用,可以将模块移植到其他系统(模块拆分),重复使用

(4) 优化方便,检测性能的时候,可以确定出是哪个模块的问题,只需要专门针对该模块进行优化

二、类和成员的几种可访问性

1.顶层类(非嵌套)和接口,有两种访问级别,包级私有和公有的

顶层类,如果是包级私有,只有该包中的类才能使用它

接口,如果是包级私有,只有该包中的类能实现它

package test.topclass;

    class OneClass {

}

--------------------------------------

package test.topclass;

    interface OneInterface {

}

----------------------------------------

package test.topclass;

public class TestClass implements OneInterface{
    public static void main(String[] args) {
        OneClass one = new OneClass();
    }
}

2.私有嵌套类(或者接口)

如果一个包级私有的顶层类(或者接口)只是在某一个类的内部被用到,就应该考虑使它成为唯一使用它的那个类的私有嵌套类(或者接口)。这样可以将它的可访问范围从包中的所有类缩小到了使用它的那个类

package test.privateclass;

class TopClass {
    private interface TopInterface {

    }
   
    private class UserClass {
        private TopInterface oneInterface = new TopInterface(){};
        private TopClass oneClass = new TopClass();
    }
}

3.类成员(域,方法,嵌套类和嵌套接口),有四种可访问级别

私有的(private) -- 只有在声明该成员的顶层类内部才可以访问这个成员

包级私有的(package-private) -- 声明该成员的包内部的任何类都可以访问这个成员。从技术上讲,它被称为"缺省(default)访问级别",如果没有为成员指定访问修饰符,就采用这个访问级别

受保护的(protected) -- 声明该成员的类的子类可以访问这个成员,并且,声明该成员的包的内部的任何类也可以访问这个成员

公有的(public) -- 在任何地方都可以访问该成员

访问权限        类         包        子类        其他包

public              √           √           √               √

protected        √           √            √               ×

default            √           √            ×               ×

private             √            ×           ×                ×

对于接口的成员,只有一种访问级别,公有的。(下面例子是错误的)

package test;

public interface OneInteface {
    private int ten = 10;
    private void add();
}

三、其他注意事项

1.实例域决不能是公有的

如果域是非final的,或者是一个指向可变对象的final引用,那么一旦使这个域成为公有的,就放弃了对存储在这个域中的值进行限制的能力

2.静态公共域只能为final修饰的基本类型或者final修饰的不可变类

假设常量构成了类提供的整个抽象中的一部分,可以通过公有的静态final域来暴露这些常量

package test.other;


public class PublicClass {
    public static final String STRING_HELLO="HELLO";

}

final修饰引用类型的变量:test.staticpublic

3.长度非零的数组总是可变的,可被修改的

package test.other;


public class PublicArray {
    public static final int[] VALUES = { 1, 2, 3 };
    private static final float[] VALUES_FLOAT = {1.2f,1.3f};

    public static float[] getFloatArray(){
    return VALUES_FLOAT;
    }

    public static void main(String[] args){
    PublicArray.VALUES[0] = 10;
    PublicArray.getFloatArray()[0] = 10.2f;
        System.out.println("--------VALUE----------");
        for(int value:VALUES) {
            System.out.println(value+"");
        }
        System.out.println("--------VALUE_FLOAT----------");
        for(float valueFloat:VALUES_FLOAT) {
            System.out.println(valueFloat+"");
        }
    }
}



package test.other;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class PublicArrayTwo {
    private static final int[] VALUES = { 1, 2, 3 };
    private static final float[] VALUES_FLOAT = { 1.2f, 1.3f };

    public static final List<int[]> values() {
    return Collections.unmodifiableList(Arrays.asList(VALUES));
    }

    public static final float[] getFloatArray() {
    return VALUES_FLOAT.clone();
    }
}

package test.other;

import java.util.List;

public class TestPublicArrayTwo {
    public static void main(String[] args) {
        List<int[]> pat = PublicArrayTwo.values();
        if(pat.size() > 0){
            int[] publicArray = pat.get(0);
            for(int i = 0; i < publicArray.length; i++){
            int j = publicArray[i];
            System.out.println(j);
            }
        }
        System.out.println("------------------------------------------------");
        float[] oneFloat = PublicArrayTwo.getFloatArray();
        for(int i = 0; i < oneFloat.length; i++){
        System.out.println(oneFloat[i]);
        }
    }
}

四、最后的原则

总而言之,你应该始终尽可能地降低可访问性。你在仔细地设计了一个最小的公有API之后,应该防止把任何散乱的类、接口和成员变成API的一部分。除了公有静态final域的特殊情形之外,公有类都不应该包含公有域。并且要确保公有静态final域所引用的对象都是不可变的。

推荐阅读更多精彩内容