JAVA类成员访问权限
如表1-1所示,不详述,这里列出方便下文引用。
修饰词 | 本类 | 同一包类 | 子类 | 其他类 |
---|---|---|---|---|
private | 是 | 否 | 否 | 否 |
default | 是 | 是 | 否 | 否 |
protected | 是 | 是 | 是 | 否 |
public | 是 | 是 | 是 | 是 |
内部类基本知识点
我们首先看一个内部类的例子,并借助javap工具观察编译器对内部类的处理方式。代码1-1如下:
package df;
public class OuterClassTest1 {
private String outer;
public class InnerClassTest1 {
private int inner;
public InnerClassTest1(int p){
this.inner = p;
}
public void access(){
System.out.println(outer);
System.out.println(inner);
}
}
public static void main(String[] args){
OuterClassTest1 outerClass = new OuterClassTest1();
//外部类的静态方法创建内部类的时候,不需要指定具体类型
InnerClassTest1 innerClass1 = outerClass.new InnerClassTest1(1);
//除外部类外,其他地方创建内部类对象需要指定具体类型
OuterClassTest1.InnerClassTest1 innerClass2 = outerClass.new InnerClassTest1(1);
}
}
javap查看编译结果如图1-1:
- 编译器为内部类添加了指向外部类对象的引用this$0,通过构造函数初始化。因此创建内部类时,需要首先创建外部类对象,然后再创建内部类对象。
- 编译器为外部类添加了access$0静态方法,猜测应该是当内部类access方法访问外部类成员变量outer时,内部类会调用access$0方法并传参this$0,然后访问到outer的值。编译器通过向外部类添加静态成员方法,实现了内部类对外部类所有成员的访问权,更准确的说是间接访问权。
- 除外部类外,任何地方创建内部类对象,需要指定内部类的具体类型:OuterClassTest1 .InnerClassTest1。
注:java编程思想对这一知识点描述有所不同:
If you want to make an object of the inner class anywhere except from within a non-static method of the outer class, you must specify the type of that object as OuterClassName.InnerClassName.
但从代码1-1可以看出,外部类的静态方法也可以不用前缀OuterClassName.。
- this$0变量对源码不可见,内部类访问外部类对象引用方法:OuterClassTest1.this。
- 除外部类的非静态方法,其他地方创建内部类方法:OuterClassObject.new。
内部类可见性
本小节我们讨论一下当不同访问权限修饰符(private、default、protected、public)作用于内部类时,内部类对外可视性。代码1-2如下:
package df;
public class OuterClassTest {
private class PrivateInnerClass{
private void private_method() {
};
void default_method() {
};
protected void protected_method() {
};
public void public_method() {
};
}
class DefaultInnerClass{
private void private_method() {
};
void default_method() {
};
protected void protected_method() {
};
public void public_method() {
};
}
protected class ProtectedInnerClass{
private void private_method() {
};
void default_method() {
};
protected void protected_method() {
};
public void public_method() {
};
}
public class PublicInnerClass{
private void private_method() {
};
void default_method() {
};
protected void protected_method() {
};
public void public_method() {
};
}
}
决定内部类可视性的因素
创建内部类的方法是:OuterClass.InnerClass obj = OuterClassObject.new InnerClass()。因此能否成功创建内部类对象,依赖两方面的可视性:
- OuterClass.InnerClass类型的可视性,由内部类的修饰符决定。如PrivateInnerClass由private修饰,则决定了只有外部类可以创建PrivateInnerClass类型对象。
- InnerClass的构造函数的可视性,可以显示声明构造函数并指定权限修饰符,也可以由编译器默认生成。默认构造函数的修饰符和类的修饰符一致,如PrivateInnerClass类的默认构造函数的修饰符就是private。我们可以借助于javap -p 查看默认构造函数,如下图1-2:
外部类访问内部类
代码1-3:
package df;
public class OuterClassTest {
//...省略代码
public static void main(String[] args) {
OuterClassTest out = new OuterClassTest();
PrivateInnerClass privateIn = out.new PrivateInnerClass();
privateIn.private_method();
}
}
- private内部类仅对外部类可见,外部类可以访问内部类的所有成员,包括私有成员。同样default\protected\public内部类对外部类都完全可见。
相同包的类对内部类的访问
代码1-4:
package df;
public class SamePackage {
public static void main(String[] args) {
OuterClassTest out = new OuterClassTest();
//ERROR
//OuterClassTest.PrivateInnerClass privateIn = out.new PrivateInnerClass();
OuterClassTest.DefaultInnerClass defaultIn = out.new DefaultInnerClass();
//ERROR
//defaultIn.private_method();
defaultIn.default_method();
}
}
- 除外部类外,private内部类对其他类不可见。
- default内部类可以被相同包下的类访问,对default内部类对象成员的访问遵从开头JAVA类成员访问权限,即可以访问内部类default/protected/public修饰的成员,但不能访问private修饰的成员。
外部类的子类
代码1-5:
package test;
import df.OuterClassTest;
public class OtherPackage extends OuterClassTest{
public static void main(String[] args){
OuterClassTest out = new OuterClassTest();
//ERROR
//OuterClassTest.DefaultInnerClass defaultIn = out.new DefaultInnerClass();
//ERROR
//OuterClassTest.ProtectedInnerClass protectedIn = out.new ProtectedInnerClass();
}
}
- 尽管OtherPackage 是外部类的子类,因处于不同的包下,无法创建default内部类DefaultInnerClass。
- ProtectedInnerClass对象创建失败的原因是:ProtectedInnerClass的默认构造函数是protected的,OtherPackage继承OuterClassTest只能保证OuterClassTest.ProtectedInnerClass的可视性,不能保证ProtectedInnerClass构造函数的可视性,如果想让上述代码创建成功,可以显示声明ProtectedInnerClass的构造函数,并且用public修饰。