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方法

静态内部类

/**
 * <pre>
 *     author : 杨丽金
 *     time   : 2018/10/24
 *     desc   : 静态内部类【在外部类中访问静态内部类私有成员;在静态内部类中访问外部类私有成员】
 *     version: 1.0
 * </pre>
 */
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方法

匿名内部类

/**
 * <pre>
 *     author : 杨丽金
 *     time   : 2018/10/25
 *     desc   : 匿名内部类
 *     version: 1.0
 * </pre>
 */
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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342