概述
背景
问题(动机、需求)
访问控制(或隐藏具体实现)与“最初实现并不恰当”有关。 —— 摘自JAVA编程思想
1. 代码会被重构。最初实现的代码并不是最好的(可维护性角度考虑)。
2. 消费者(客户端程序员)需要代码在某些方面不变。内在实现可变,但是接口调用方式不变,即不破坏消费者的代码。
要解决以上办法,就得知道哪些接口,哪些域已经被消费者使用。
解决办法
使用权限控制。
正文
理论知识
访问权限修饰词的等级(Accesss level modifiers)决定了其他类是否可以访问到某个特殊的变量或者函数可将权限控制划分为两个等级:
一. 最高级别(At the top level)——public或者no-modifier。
如果一个类(top-level)被声明为public,意味着该类对其它类都可见;如果一个类没有访问修饰词(no-modifier or the default or package-private),那么这个类只能被同一个包内的其他类访问。二.成员级别(At the member level)—— public,private,protected,no-modifier。
用修饰词public和no-modifier修饰成员或top-level class的含义是一样的; 如果用private修饰成员,则该成员只在同一个类内可见;如果用protected修饰成员,则该成员在包内可见,此外还可被处于任意包内的子类所访问到。
简言之
1. Visible to the package, the default(no-modifier).
2. Visible to the class only (private).
3. Visible to the world (public).
4. Visible to the package and all subclasses (protected).
看表理解
Modifier | Class | Package | Subclass | World |
---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
no modifier | Yes | Yes | No | No |
private | Yes | No | No | No |
这张表描述了成员在各个【访问权限修饰词】的修饰下的可见性。
- Class那一列:表示在同一类内,该类总是可以访问它自己的成员。
- Package那一列:表示在同一个包内,成员的可见性。
- Subclass那一列: 表示不同包内,子类可以访问父类的public、protected成员。
- World那一列: 表示所有类都可以访问某个类的public成员。
如果还不懂,可以看下面例子理解。
看例子理解
Modifier | Alpha | Beta | AlphaSub | Gamma |
---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
no modifier | Yes | Yes | No | No |
private | Yes | No | No | No |
包结构如下
├─java
│
├─one
│ └─ Alpha.java
│ └─ Beta.java
└─two
└─ AlphaSub.java
└─ Gamma.java
package one;
/**
* 测试同一个类内的成员访问权限
* public > protected > no-modifier > private
* 结果: 在同一个类内,所有成员都可见
*/
public class Alpha {
/**成员变量*/
public String publicStr;
protected String protectedStr;
String noModifierStr;
private String privateStr;
/**成员函数*/
public void publicMethod() {}
protected void protectedMethod() {}
void noModifierMethod() {}
private void privateMethod() {}
/**测试方法*/
public void test(){
//成员变量
System.out.println( this.publicStr );
System.out.println( this.protectedStr );
System.out.println( this.noModifierStr );
System.out.println( this.privateStr );
//成员函数
this.publicMethod();
this.protectedMethod();
this.noModifierMethod();
this.privateMethod();
}
}
package one;
/**
* 同一个包内,Alpha成员仅private修饰不可见
*/
public class Beta {
public void test(){
//Alpha实例能访问Alpha哪些成员
Alpha alpha = new Alpha();
//成员变量
System.out.println( alpha.publicStr );
System.out.println( alpha.protectedStr );
System.out.println( alpha.noModifierStr );
//成员函数
alpha.publicMethod();
alpha.protectedMethod();
alpha.noModifierMethod();
}
}
package two;
import one.Alpha;
/**
* pre-codition: 不同包,但是是Alpha的子类
* AlphaSub可以访问Alpha的public、protected成员
*/
public class AlphaSub extends Alpha {
public void test(){
//成员变量
System.out.println( super.publicStr );
System.out.println( super.protectedStr );
//成员函数
super.publicMethod();
super.protectedMethod();
}
}
package two;
import one.Alpha;
/**
* 不同包内,Gamma只能访问Alpha的public成员
*/
public class Gamma {
public void test(){
Alpha alpha = new Alpha();
//成员变量
System.out.println( alpha.publicStr );
//成员函数
alpha.publicMethod();
}
}
一些建议
如果接口对外开发,而你又不想因消费方的滥用而出现问题,访问权限修饰词可以帮你
- 使用最严格的访问权限修饰词private,除非你有不那么用的更好的理由。
- 避免成员变量使用public修饰。因为public成员会和你的实现联系起来,这不利于你灵活地改变代码,而且可能该成员变量还被消费者随意更改。
注意的地方
- 父类声明的方法是public,在子类重写(Override)的话也必须声明为public.
- 父类声明的方法是protected,如果子类重写,必须声明为protected或者public,不能为private。
- 父类声明的方法是private,这不存在继承,是否不用考虑任何限制。
简言之就是子类重写方法的访问权限修饰词必须比父类的等级要低,即要求不能更加严格。
访问权限修饰词并不是万能的
JAVA反射机制可以绕过访问权限修饰词的作用,比如
public class FooBar {
private String mutableMember;
public String getMutableMember() {
return mutableMember;
}
public static void main(String[] args) {
try {
Field field = FooBar.class.getDeclaredField("mutableMember");
field.setAccessible(true);
/**即使mutableMember是private,也可以改变其值*/
FooBar fooBar = new FooBar();
field.set(fooBar, "无中生有的值");
System.out.println(fooBar.getMutableMember());
} catch (Exception e) {
e.printStackTrace();
}
}
}
output
无中生有的值
参考来源
【1】 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
【2】https://www.tutorialspoint.com/java/java_access_modifiers.htm
【3】http://www.cydiasubstrate.com/id/c17c554f-b603-4e3b-8f99-ebb3528e3ef8/