Java内部类机制详解

Java允许在一个类里面定义另一个类,类里面的类就是内部类。内部类看似简单,其实里面大有乾坤,下面我们就来好好聊一聊内部类。代码示例在最下面。

初识内部类

内部类作用及其一些共性和特点:
使用内部类最吸引人的原因是:每个内部类都能独立地实现某一接口,所以无论外部类是否已经实现了某个接口,对于内部类都没有影响。接口和内部类配合使用,使得多继承的解决方案变得更加完整。

① 我们都知道类一般都不声明成private和protected,但是内部类可以,所以通过内部类可以很好地隐藏我们的信息。
② 内部类它可以直接访问外部类的成员变量和方法(甚至是私有的),利用这个特性,配合接口,我们可以更好地实现多继承的效果。
③ 内部类声明成静态的,就不能随便访问外部类的成员数据了,此时内部类只能访问外部类的静态成员数据。
④ 在编译成功之后,它就与外部类是不同的类,是一个独立的类,当然他们之间还是有联系的。在编译之后内部类会被编译成独立的.class文件,前面冠以外部类的类名和$符号。

在这里我要详细解释下上面的第二点和第三点:

  • 静态内部类虽然定义在外部类的里面, 但是它只是在形式上(写法上)和外部类有关系,其实在逻辑上和外部类并没有直接的关系,它并不依赖外部类。虽然它也能访问外部类的静态数据,这是因为在编译的时候,就已经做到数据共享了。

  • 而一般的内部类,不仅在形式上和外部类有关系(写在外部类的里面), 在逻辑上也和外部类有联系。这种逻辑关系主要表现在:内部类对象的创建依赖于外部类对象,内部类对象持有指向外部类对象的引用。

至于为什么会持有外部类对象的引用,以后会再专门拿出篇幅进行说明,这里就先不做介绍了。

根据不同的区分方法,总的来说可以分为成员内部类(普通内部类)、静态内部类、局部内部类、匿名内部类。

一、成员内部类

概念:
成员内部类是跟外部类的成员变量和方法同级的内部类。成员内部类的修饰词跟外部类的成员变量及方法的权限修饰词的作用是一样的。

用法特征:
① 成员内部类与外部类的实例相联系,可以访问外部类的所有成员数据(包括外部类中的 private成员)。正因为成员内部类与外部类的实例联系,因此不能在其内部定义静态成员变量。
② 非静态内部类的创建需要依赖于外部类。

二、静态内部类

概念:
跟成员内部类不同的是:他是由static来修的类,叫做静态内部类。

用法特征:
静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,但是静态内部类却没有。没有这个引用就意味着:
① 静态内部类不能直接访问外部类的非静态成员,静态内部类可以直接访问外部类的静态成员数据。
③ 静态内部类可以直接创建实例,不需要依赖于外部类。

三、局部内部类

概念:
即在方法中定义的内部类,与局部变量类似,其范围为定义它的代码块。

用法特征:
① 局部内部类可以访问外部类的所有成员数据和方法内的数据。
② 局部内部类访问局部变量和形参时,局部变量和形参必须修饰为final。
③ 局部内部类和普通内部类是相似的,因为他们都不能定义静态成员数据(静态常量除外)。如果局部内部类在静态方法内被定义,那么这个局部内部类就只能访问方法的静态成员。
④ 不能在局部内部类中声明接口,因为接口本身就是静态的。
⑤ 局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。

在这里解释下上面的第二点:
局部内部类访问局部变量和形参时,局部变量和形参必须修饰为final。这是因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。

在Java SE8中,不再需要这样了,只要局部变量和形参不被二次赋值即可。

四、匿名内部类

概念:
没有名字的内部类。形式参考代码示例。
语法:
new 实现接口()|父类构造器(实参列表)
{
//匿名内部类的实体部分
}
用法特征:
① 匿名内部类特别适合于只使用一次的类。
② 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
③ 匿名内部类内部不能有构造方法。
④ 匿名内部类中不能存在任何的静态成员变量和静态方法。
⑤ 匿名类和局部内部类一样,可以访问外部类的成员(必须是final或没有二次赋值的成员)。

java8以前,Java要求被局部内部类,匿名内部类访问的局部变量必须使用final修饰,java8以后,这个限制取消了!

应用场景及注意事项:
当我们只需要类的一个实例,且类在定义以后会马上被用到,代码量少,写上匿名内部类会优化程序结构,并符合以上的那些用法特征的时候,可以考虑用匿名内部类。

  • 对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。

  • 匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

  • 有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种方法:
    ① 如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数。
    ② 将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
    ③ 在这个匿名内部类中使用初始化代码块。

五、代码示例

//OuterClass类
public class OuterClass {
    
    private static String outerStaticStr;
    private int outerInt;
    
    // 普通方法
    public void outerDisplay(){
        System.out.println("OuterClass outerDisplay Method");
    }
    
    // 静态方法
    public static void outerStaticDisplay(){
        System.out.println("OuterClass outerStaticDisplay Method");
    }

    /*成员内部类*/
    public class InnerClass{
        public String innerStr; //成员内部类不能声明静态变量
        static final int innerInt = 100; // 静态常量
        public void innerDisplay(){
            outerStaticStr = "Tom";  // 使用外部类的成员变量
            System.out.println("成员内部类 " + outerStaticStr);
            outerDisplay();// 使用外部类的方法
            outerStaticDisplay();
        }
    }

    /*静态内部类*/
    public static class StaticInnerClass {
        private String innerStr;
        public static String innerStaticStr; // 与成员内部类不同,静态内部类可以声明静态变量
        public void innerDisplay() {
            //静态内部类可以直接访问外部类的静态成员数据。
            outerStaticStr = "Jerry";
            System.out.println("静态内部类 " + outerStaticStr);
            outerStaticDisplay();
        }
    }

    /*局部内部类*/
    public void outPut(String string) {
        int a = 20;
        outerInt = 999;
        class LocalInnerClass { // 此时局部内部类与局部变量同一等级,局部不能加private等权限访问修饰词修饰。
            static final int b = 20; //静态常量
            public void localOutPut() {
                //string = "局部内部类"; //不能再赋值
                //a = 30; //不能再赋值
                System.out.println(string);
                System.out.println(outerInt);
            }
        }
        // 只能在方法内实例化
        LocalInnerClass localInner = new LocalInnerClass();
        localInner.localOutPut();
    }

    /*匿名内部类*/
    public void test(Person per){
        System.out.println(per.getName() + "今天走了" + per.walk() + "米。");
    }
    

    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    public StaticInnerClass getStaticInnerClass(){
        return new StaticInnerClass();
    }

}
public class Main {

    public static void main(String[] args) {
        
        OuterClass outer = new OuterClass();
        //OuterClass.InnerClass inner = outer.getInnerClass();
        OuterClass.InnerClass inner = new OuterClass().new InnerClass();// 非静态内部类的创建需要依赖于外部类。
        inner.innerDisplay();

        OuterClass.StaticInnerClass staticInner = outer.getStaticInnerClass();
        staticInner.innerDisplay();

        outer.outPut("局部内部类");

        /*匿名内部类*/
       outer.test(new Person() {

            public int walk() { // 实现抽象方法
                return 500;
            }

            public String getName() { // 重写方法
                return "小明";
            }
        });

       /*
       * test()方法接受一个Person类型的参数,同时我们知道一个抽象类是没有办法直接new的。
       * 我们必须要先有实现类才能new出来它的实现类实例。
       * 所以在方法中直接使用匿名内部类来创建一个Person实例。
       * 由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。
       * */

    }
}
public abstract class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract int walk();
}

写完喽!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ


知识重在总结和梳理,只有不断地去学习并运用,才能化为自己的东西。当你能为别人讲明白的时候,说明自己已经掌握了。

欢迎转载,转载请注明出处!

如果有错误的地方,或者有您的见解,还请不啬赐教!

喜欢的话,麻烦点个赞!

本文部分参考了:http://blog.csdn.net/chenssy/article/details/13170015

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

推荐阅读更多精彩内容

  • Java 内部类 分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 1、成员内部类: 即作为外部类的一个成...
    ikaroskun阅读 1,213评论 0 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,620评论 0 11
  • 高铁一路飞驰,窗外延绵的农田快速闪过。靠窗的张茂的凝神望着远处,满脸心事重重。 他的心从来没有这么忐忑和无主过。他...
    林四月阅读 1,310评论 0 2
  • 关于宫崎骏的动画,人们可以写出很多的溢美之词,我不多赘述。然而在看过宫老大多数的动画电影之后,对他的片中流露出的对...
    魔鬼的赞歌阅读 2,052评论 9 18