Java内部类

一、内部类作用

二、使用

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

你可能感兴趣的:(Java内部类)