JAVA 内部类的总结

前言

随着工作经验的增加,越发感觉到基础的重要性,所以最近上下班地铁上都在看一些基础的东西,前几天看了拭心大哥讲的内部类,由于自己对概念的理解有误,造成了一些困惑,自己查了一上午才弄明白(正好早上开无聊的周例会,我就在那自己查资料,还拉着大学同学一起讨论),所以决定抽空把这部分再梳理一下,做个分享,也可以加深自己对这部分的理解。

正文

定义在一个类中或者方法中的类称为内部类。

内部类可以分为以下四类:

  1. 成员内部类
  2. 局部内部类
  3. 静态内部类
  4. 匿名内部类

我们来逐个分解:

  • 成员内部类
    最普通的内部类,它定义在一个类的内部中,就如同一个成员变量一样。
    代码形式如下:
public class OuterClass {

    class InnerClass {
        private void method() {
            System.out.println("innerMethod");
        }
    }
}

调用方式:

    OuterClass out = new OuterClass();
    OuterClass.InnerClass in = out.new InnerClass();
    in.method();

注意:不是直接 new outClass.InnerClass()
成员内部类可以无条件的访问外部类的成员属性和成员方法(包括 private 和 static 类型的成员),这是因为在成员内部类中,隐式地持有了外部类的引用。

使用场景:
当类 A 需要使用类 B ,同时 B 需要访问 A 的成员/方法时,可以将 B 作为 A 的成员内部类。同时我们可以利用 private 内部类禁止其他类访问该内部类,从而做到将具体的实现细节完全隐藏。

  • 局部内部类
    在代码块或者方法中创建的类。局部内部类的作用域只能在其所在的代码块或者方法内,在其它地方无法创建该类的对象。可以把局部内部类理解为作用域很小的成员内部类。
    代码形式如下:
    public void test() {
        class PartInnerClass {
            private void method() {
                System.out.println("partInnerMethod");
            }
        }
        new PartInnerClass().method();
    }

使用场景:
局部内部类只用于当前方法或者代码块中创建、使用,属于一次性产品,使用场景比较少。

  • 静态内部类
    使用 static 关键字修饰的内部类就是静态内部类,静态内部类不持有外部类的引用,可以看作是和外部类平级的类。
    代码形式如下:
public class OuterClass {

    static class StaticInnerClass {
        private void method() {
            System.out.println("staticInnerMethod");
        }
    }
}

我们想在静态内部类中访问外部类的成员只能 new 一个外部类的对象,否则只能访问外部类的静态属性和静态方法。

使用场景:
1.当类 A 需要使用类 B,而 B 不需要直接访问外部类 A 的成员变量和方法时,可以将 B 作为 A 的静态内部类。

2.单例的实现:

public class Singleton {

    private Singleton() {}

    private static class InnerClass {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return InnerClass.INSTANCE;
    }
}

由于InnerClass是一个内部类,只在外部类Singleton的getInstance()中被使用,所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。初始化的时候由ClassLoader来保证同步,确保 INSTANCE 变量只能初始化一次,使INSTANCE成为一个真·单例。

  • 匿名内部类
    匿名内部类需要重点说一下,之前就是因为对它理解不到位,才造成了困惑。
    概括一下匿名内部类的特点:
    1.必须继承一个父类或实现一个接口,并不需要增加额外的方法,只是对继承方法的实现或是覆盖。
    2.只是为了获得一个对象实例,并不需要知道其实际的类型。
    3.匿名内部类的匿名指的是没有类名,而不是没有引用指向它。
    4.匿名内部类不能有构造方法,只能有初始化代码块。因为编译器会帮我们生成一个构造方法然后调用。
    5.匿名内部类中使用到的参数是需要声明为 final 的,否则编译器会报错。
    我之前理解出现偏差就是在第3条,好吧,其实第3条是我自己补充的,为了避免大家出现跟我一样的错误(之前和几个同学讨论的时候,大家对这一点的理解都很模糊)。
    代码形式如下:
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            
        }
    });

我们常用的按钮点击监听用到的就是匿名内部类。
上面这个例子很好理解,我们再来看看下面这个:

    @SuppressLint("HandlerLeak")
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ...
        }
    };

没错,这个new出来的 Handler 也是一个匿名内部类,我之前看到这个的时候懵逼了,判断不出它究竟是哪种内部类。所以记住,匿名内部类的匿名指的是没有类名,而不是没有引用指向它。你可别告诉我它的类型就是Handler啊,它只是Handler的一个子类,一个没有类名的子类。
顺便再说一下,这样的写法可能会造成内存泄露,因为这个匿名内部类会隐式地持有外部类的引用。举个例子:

    Message message = Message.obtain();
    message.what = 233;
    mHandler.sendMessageDelayed(message, 1000 * 60 * 10);
    finish();

我们发送了一个延迟10分钟的Message,在Message发送出去之前,我们结束了当前Activity。Message在消息队列中延迟了10分钟,然后才处理该消息。而这个Message引用了Handler,Handler又隐性地持有了Activity的引用,当发生GC时因为 Message – Handler – Acitivity 的引用链导致Activity无法被回收,所以发生了内存泄露。
解决办法就是使用弱引用WeakReference或者干脆将 Handler 设计为静态内部类

那么为什么匿名内部类中使用参数需要声明为final呢?
因为匿名内部类是创建一个对象并返回,这个对象的方法被调用的时机不确定,方法中有修改参数的可能,如果在匿名内部类中修改了参数,外部类中的参数是否需要同步修改呢?Java 为了避免这种问题,限制匿名内部类访问的变量需要使用 final 修饰,这样可以保证访问的变量不可变。

使用场景:
需要实现一个接口,但不需要持有它的引用。

补充

除了静态内部类,剩下的成员内部类、局部内部类、匿名内部类 都会默认隐式地持有外部类的引用。

再来两道典型题目融会贯通一下:

在?处填入合适的代码,使程序在控制台输出30,20,10。

public class OuterClass {

    private int num = 10;

    class InnerClass {
        int num = 20;

        void show() {
            int num = 30;
            System.out.println(?);
            System.out.println(?);
            System.out.println(?);
        }
    }

    public static void main(String[] args) {
        InnerClass in = new OuterClass().new InnerClass();
        in.show();
    }
}

.
.
.
.
.
答案:
1.num
2.this.num
3.OuterClass.this.num

补全代码 ,要求在控制台输出"HelloWorld"

interface InterfaceTest {
    void show();
}

public class TestClass {

    //补全代码

    public static void main(String[] args) {
        TestClass.method().show();
    }
}

.
.
.
.
.
答案:

interface InterfaceTest {
    void show();
}

public class TestClass {

    //补全代码
    public static InterfaceTest method() {
        return new InterfaceTest() {
            @Override
            public void show() {
                System.out.println("HelloWorld");
            }
        };
    }

    public static void main(String[] args) {
        TestClass.method().show();
    }
}

结语

学海无涯,我们不仅要向前迈进,还得时常回过头去,看看来时的路。

文中多有借鉴拭心大哥的文章,附上链接:
http://blog.csdn.net/d29h1jqy3akvx

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

推荐阅读更多精彩内容

  • Java 内部类 分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 1、成员内部类: 即作为外部类的一个成...
    ikaroskun阅读 1,219评论 0 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,565评论 18 399
  • 问:Java 常见的内部类有哪几种,简单说说其特征? 答:静态内部类、成员内部类、方法内部类(局部内部类)、匿名内...
    Little丶Jerry阅读 1,874评论 0 1
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,627评论 0 11
  • 连日绵绵阴雨洗,温阳初晴,苍寥云空里。 紫色杜鹃瑶笛起,层林绿染春之力。 先祖遥思应孝礼,碎步而行,峭径攀崖去。 ...
    明哥明说阅读 215评论 3 2