一、内部类作用
二、使用
a)通用规则
1.类相关、实例相关
1)普通内部类、匿名内部类、局部内部类对象的创建依赖于外部类对象,普通内部类、匿名内部类、局部内部类对象中持有外部类引用。(由编译器在字节码文件中自动添加外部类类型的变量,并通过带参的构造函数将实例传入)
2)静态内部类对象的创建独立于外部类对象(静态内部类是类相关的)
普通内部类对象中持有外部类对象的引用
参见下文
匿名内部类对象中持有外部类对象的引用
对于下文中匿名内部类的代码,其对应的字节码文件中:
局部内部类对象中持有外部类对象的引用
对于下文中局部内部类的代码,其对应的字节码文件中:
2.外部类/内部类中私有字段的可访问性
内部类类型 | 外部类中可否访问内部类的私有数据(包括变量、方法) | 内部类中可否访问外部类的私有数据(包括变量、方法) |
---|---|---|
普通内部类 | √ | √ |
静态内部类 | √ | √ |
匿名内部类 | × | √ |
局部内部类 | × | √ |
Q:为什么可以访问到私有数据?
普通内部类
public class InnerClassTest {
private int outField1=1;
int outField2=2;
protected int outField3=3;
public int outField4=4;
private void outF1(){
}
public InnerClassTest(){
InnerClassA innerObj=new InnerClassA();
System.out.println("create "+this.getClass().getSimpleName()+"object");
System.out.println("the value of inner class field1="+ innerObj.field1);
System.out.println("the value of inner class field2="+ innerObj.field2);
System.out.println("the value of inner class field3="+ innerObj.field3);
System.out.println("the value of inner class field4="+ innerObj.field4);
innerObj.innerF1();
}
// nor static inner class
public class InnerClassA{
private int field1=5;
int field2=6;
protected int field3=7;
public int field4=8;
// static int field5=1;// compile error
public InnerClassA(){
System.out.println("create"+this.getClass().getSimpleName()+"object");
System.out.println("the value of outer class field1="+outField1);
System.out.println("the value of outer class field2="+outField2);
System.out.println("the value of outer class field3="+outField3);
System.out.println("the value of outer class field4="+outField4);
outF1();
}
private void innerF1(){
}
}
public static void main(String[] args) {
InnerClassTest test = new InnerClassTest();
}
}
普通内部类对应的字节码文件中,编译器自动添加了外部类类型的变量
,并添加了一个带参的构造函数将外部类实例传入【故普通内部类对象中持有外部类对象的引用】
当在普通内部类中有代码访问外部类中私有数据(变量、方法)时(
System.out.println("the value of outer class field1="+outField1);
和outF1();
),编译器会在外部类对应的字节码中添加静态方法access()
,该方法需要传入一个外部类对象,在该方法中返回私有字段值,或调用私有方法。
若在普通内部类中无访问外部类私有数据的代码,则字节码文件中不会生成access
方法
当在外部类中有代码访问普通内部类中私有数据(变量、方法)时(
System.out.println("the value of StaticInnerClass field1="+obj.staticInnerField1);
和innerObj.innerF1();
),编译器会在普通内部类对应的字节码中添加静态方法access()
,该方法需要传入一个普通内部类对象,在该方法中返回私有字段值,或调用私有方法。
若在外部类中无访问普通内部类私有数据的代码,则字节码文件中不会生成access
方法
静态内部类
/**
*
* author : 杨丽金
* time : 2018/10/24
* desc : 静态内部类【在外部类中访问静态内部类私有成员;在静态内部类中访问外部类私有成员】
* version: 1.0
*
*/
public class StaticInnerClassTest {
private int outField1=1;
int outField2=2;
protected int outField3=3;
public int outField4=4;
public StaticInnerClassTest(){
System.out.println("create "+this.getClass().getSimpleName()+" object");
// must create StaticInnerClassTest object manually
StaticInnerClass obj = new StaticInnerClass();
// System.out.println("the value of StaticInnerClass field1="+obj.staticInnerField1);
System.out.println("the value of StaticInnerClass field2="+obj.staticInnerField2);
System.out.println("the value of StaticInnerClass field3="+obj.staticInnerField3);
System.out.println("the value of StaticInnerClass field4="+obj.staticInnerField4);
}
// static class
public static class StaticInnerClass{
private int staticInnerField1=1;
int staticInnerField2=2;
protected int staticInnerField3=3;
public int staticInnerField4=4;
public StaticInnerClass(){
System.out.println("create "+this.getClass().getSimpleName()+" object");
// must create StaticInnerClassTest object manually
StaticInnerClassTest obj=new StaticInnerClassTest();
//System.out.println("the value of out class field1="+obj.outField1);
System.out.println("the value of out class field2="+obj.outField2);
System.out.println("the value of out class field3="+obj.outField3);
System.out.println("the value of out class field4="+obj.outField4);
}
}
}
当在静态内部类中有代码访问外部类中私有数据(变量、方法)时(
System.out.println("the value of out class field4="+obj.outField4);
),编译器会在外部类对应的字节码中添加静态方法access()
,该方法需要传入一个外部类对象(手动创建StaticInnerClassTest obj=new StaticInnerClassTest();
),将该对象的私有字段值返回。
若在静态内部类中无访问外部类私有字段的代码,则外部类字节码文件中不会生成access
方法
当在外部类中有代码访问静态内部类中私有数据(变量、方法)时(
System.out.println("the value of StaticInnerClass field1="+obj.staticInnerField1);
),编译器会在静态内部类对应的字节码中添加一个静态方法access()
,该方法需要传入一个静态内部类对象,将该对象的私有字段值返回。
若在外部类中无访问静态内部类私有字段的代码,则静态内部类字节码文件中不会生成access
方法
匿名内部类
/**
*
* author : 杨丽金
* time : 2018/10/25
* desc : 匿名内部类
* version: 1.0
*
*/
public class AnonymousInnerTest {
private int outField1=1;
int outField2=2;
protected int outField3=3;
public int outFiled4=4;
// 定义接口
public interface OnCliclListener {
void onClick(Object obj);
}
// 匿名内部类:实现接口或继承父类
private void anonymousClassTest(){
OnCliclListener listener=new OnCliclListener() {
// 定义属性,但外部类访问不到(因为外部类获取不到匿名内部类类名,所以也就无法创建匿名内部类对象)
int innerField=1;
@Override
public void onClick(Object obj) {
System.out.println("对象 "+obj+"被点击");
System.out.println(outField1);
System.out.println(outField2);
System.out.println(outField3);
System.out.println(outFiled4);
}
};
listener.onClick(new Object(){
@Override
public String toString() {
return "Myobj";
}
});
}
public static void main(String[] args) {
AnonymousInnerTest test = new AnonymousInnerTest();
test.anonymousClassTest();
}
}
在匿名内部类对应的字节码文件中:1)该类中虽然定义了私有数据,但不可被外部类访问到;
在外部类对应的字节码文件中:1)为匿名内部类中访问的私有数据提供了相应的静态方法供调用。
局部内部类
public class LocalInnerTest {
private int outField1=1;
int outField2=2;
protected int outField3=3;
public int outField4=4;
// 外部类的私有方法
private void outF1(){
}
private void localInnerClassTest(){
// 方法内部类
class A{
public A(){
System.out.println("create A object");
System.out.println(outField1);
System.out.println(outField2);
System.out.println(outField3);
System.out.println(outField4);
outF1();
}
}
A a=new A();
if(true){
class B{
public B(){
System.out.println("create B object");
System.out.println(outField1);
System.out.println(outField2);
System.out.println(outField3);
System.out.println(outField4);
outF1();
}
}
B b=new B();
}
}
public static void main(String[] args) {
new LocalInnerTest().localInnerClassTest();
}
}
在外部类对应的字节码文件中:1)为匿名内部类中访问的私有数据(变量、方法)提供了相应的静态方法供调用。
3.静态成员不能访问实例成员
- 静态内部类不能访问外部类的实例成员
因为:要想访问外部类的实例成员,必须通过外部类实例进行。无论是静态内部类还是静态内部类对象中只持有外部类的引用,但不持有外部类对象的引用,所以直接调用外部类的实例成员会造成错误。
- 外部类的静态成员(静态变量、静态方法、静态初始化块)不能访问普通内部类、匿名内部类、局部内部类(不可用于定义变量、创建实例)
A:访问普通内部类、匿名内部类、局部内部类这些普通成员,必须通过外部类实例进行。外部类的静态成员中不持有外部类对象的引用,所以直接调用会造成错误。
4.不允许在普通内部类、匿名内部类、局部内部类中定义静态成员
a)针对个体的规则
1.普通内部类
变量访问规则
如何在外部类之外的类访问普通内部类
创建普通内部类实例
2.匿名内部类
new 实现接口()|父类构造器(实参列表)
{
// 匿名内部类的实体部分
}
3.局部内部类
参考文献
回归Java基础,详解 Java 内部类
疯狂Java