01 前言

昨天晚上,我把车停好以后就回家了。回家后才发现手机落在车里面了,但外面太冷,冷到骨头都能感受到寒意——实在是不想返回一趟去取了(小区的安保还不错,不用担心被砸车玻璃),于是打定主意过几个小时的“世外桃源”生活——别人找不到我,我也找不到别人,这种与世隔绝的状态非常适合读书写作。

把厚厚的《Java编程思想》摆在桌子上,正襟危坐,认认真真地读起了第十章——内部类。尽管我已经非常耐心和用心了,但内部类的这一章非常的枯燥,并且难以理解,我整个人几乎处于崩溃的边缘。

很早之前,有想要转行学习Java的朋友咨询我,有哪方面的书可以推荐,我郑重其事地介绍了《Java编程思想》,并且一再叮嘱他这是一本Java入门级的经典书,必须耐着性子读完它。现在想想,自己当时的推荐真是轻率!

我这样说,并不是为了否认《Java编程思想》这本书的价值,因为站在书本的角度,它可能会感慨说:这王二的学习能力有问题啊,读我竟然这么困难!

不是有那样一句话嘛:“如果你手里有一把锤子,所有东西看上去都像钉子。”我认为“内部类”这一章很难懂,其根本的原因在于我对“内部类”没有很好的理解。想要继续扎实Java的基础知识,唯一要做的就是——想尽一切办法搞懂“内部类”,并梳理成文。

02 内部类的定义

顾名思义,内部类就是放在另外一个类的内部定义的类。非常重要的一点是,内部类能够访问外部类的所有成员,包括private修饰的。

来看程序清单1-1:

public class Wanger {
    private int age;
    public Wanger(int age) {
        this.age = age;
    }     class Thought {
        public void know() {
            System.out.println("沉默王二的年龄" + age);
        }
    }     public Thought getThought() {
        return new Thought();
    }     public static void main(String[] args) {
        Wanger wanger = new Wanger(29);
        Wanger.Thought thought = wanger.getThought();
        thought.know(); // 输出:沉默王二的年龄29         // 使用.new的形式创建内部类对象
        Wanger.Thought thought1 = wanger.new Thought();
        thought1.know();
    }
}

程序清单1-1要表达什么意思呢?

答案是:我,沉默王二,已经29岁了,89年出生(有人说89年出生明明是30岁)。上了年纪了,总想装点嫩,理解一下。我读书不多,但特别爱思考,于是我就给自己创建了一个会思考的内部类Thought。

从程序清单1-1可以看得出,尽管Thought是内部类,但可以访问外部类Wanger的私有成员变量age。

如果想创建内部类的对象,需要先指明对象引用的类型,格式为OuterClassName.InnerClassName,就像main()方法中的Wanger.Thought那样。

紧接着,就要来创建内部类对象了,有两种形式。第一种形式是先在外部类中定义一个方法Thought getThought(),返回使用new关键字创建的内部类对象,然后使用外部类对象调用该方法wanger.getThought();第二种形式是直接通过外部类对象.new创建wanger.new Thought()

03 匿名内部类

以我的编程经验来看,匿名内部类使用最频繁的场合就是在创建线程的时候。

来看程序清单2-1:

public class Demo {

    public void test(String title) {
        Thread thread = new Thread(new Runnable() {             @Override
            public void run() {
                // title = "我不要吃鸡";
                // 改变时会提示错误
                // 在封闭范围中定义的局部变量必须是final的。
                System.out.println(title);
            }
        });
        thread.start();
    }     public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Demo demo = new Demo();
            demo.test("我要吃鸡" + i);
        }
    } }

在程序清单2-1中,test()方法内部有一个线程对象thread,是通过new Thread()创建的。new Thread()可以接收一个实现了Runnable接口类型的对象,这个对象要怎么创建呢?可以通过匿名内部类的形式来创建——new Runnable() {public void run(){......}}——这段简短的代码等同于:

// 实现Runnable接口
class MyRunnable implements Runnable {     @Override
    public void run() {     }
} // 向上转型
Runnable myRunnable = new MyRunnable();

匿名内部类的好处就在于不仅节省了定义实现类的过程,还能够自动向上转型

在程序清单2-1中,test()方法还有一个参数title,JDK1.8之前,编译器要求它必须是final类型的。但JDK1.8之后,如果我们在匿名内部类中需要访问局部变量,那么这个局部变量不再需要用final关键字修饰了。

但如果想要在匿名内部类中改变局部变量的值,编译器就会提醒你不能这样做,它会提示:“在封闭范围中定义的局部变量必须是final的。”

04 为什么需要内部类

Java的内部类让我很容易的想起来JavaScript的闭包,闭包就是定义在一个函数内部的函数——这听起来和Java的内部类定义一样一样的。本质上,闭包是将函数内部与函数外部连接起来的桥梁。内部类一样,它是将内部类与外部类连接起来的桥梁。

来看看什么是闭包吧:

function wanger() {
    var age = 30;
    function know() {
        console.log(age);
    }
} wanger();
// 控制台输出30

除此之外,内部类最引人注意的原因是:

内部类可以独立地继承一个抽象类或者实现一个接口,无论外部类是否也这样做了,对内部类都没有影响

总而言之,内部类让类的内容丰富多彩,就好像一个人的心中还可以住着另外一个人。

上一篇:Java代码复用的三种常用方式:继承、组合和代理

下一篇:Java String,看这篇就够了

Java的内部类真的那么难以理解?的更多相关文章

  1. 夯实Java基础系列18:深入理解Java内部类及其实现原理

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  2. java中内部类使用小结

    内部类是指在一个外部类中再定义一个类,类名不需要和文件名相同 内部类可以是静态的,类的修饰符可以是private,default,protect,public修饰 ,而外部类只能是public 和 d ...

  3. JAVA基础——内部类详解

    JAVA内部类详解 在我的另一篇java三大特性的封装中讲到java内部类的简单概要,这里将详细深入了解java内部类的使用和应用. 我们知道内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 ...

  4. java中内部类的积累

    放在一个类的内部的类我们就叫内部类. 二. 作用 1.内部类可以很好的实现隐藏 一般的非内部类,是不允许有 private 与protected权限的,但内部类可以 2.内部类拥有外围类的所有元素的访 ...

  5. JAVA动态代理的全面深层理解

    Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过 ...

  6. JAVA运行内部类的main方法

    运行内部类的main方法 定义两个线程: 一个线程的名字"thread1",线程功能输出1~10的阶乘. 另一个线程的名字"thread2",线程功能输出线程的 ...

  7. 【原创】这道Java基础题真的有坑!我也没想到还有续集。

    前情回顾 自从我上次发了<这道Java基础题真的有坑!我求求你,认真思考后再回答.>这篇文章后.我通过这样的一个行文结构: 解析了小马哥出的这道题,让大家明白了这题的坑在哪里,这题背后隐藏 ...

  8. 小白学Java:内部类

    目录 小白学Java:内部类 内部类的分类 成员内部类 局部内部类 静态内部类 匿名内部类 内部类的继承 内部类有啥用 小白学Java:内部类 内部类是封装的一种形式,是定义在类或接口中的类. 内部类 ...

  9. Java的内部类

    Java的内部类 首先我们来了解一下什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类可以是静态static的,也可用public,default,protected和private修饰 ...

随机推荐

  1. zabbix编译安装

    第一部分zabbix安装部署,实现分布式监控及网络知识 #yum install  lrzsz.x86_64   传送文件 安装mysql脚本 #!/bin/bash DIR = pwd NAME = ...

  2. stylus 样式

  3. Python学习最佳路线图

    python语言基础(1)Python3入门,数据类型,字符串(2)判断/循环语句,函数,命名空间,作用域(3)类与对象,继承,多态(4)tkinter界面编程(5)文件与异常,数据处理简介(6)Py ...

  4. VScode加文件头的方式

    在VScode中添加文件头,设置文件编辑者的方式,在软件中查询到file-header插件: 安装好,此时通过是可以生成默认的文件头.如果需要修改配置,在文件=>首选项=>设置中修改: 查 ...

  5. 京东B2B业务架构演变

    京东 B2B 业务的定位是让各类型的企业都可以在京东的 B 平台上进行采购.建立采购关系. 京东 B2B 的用户群体主要分为 2 类,一类是大 B 用户.另一类是小 B 用户.比如联通.移动公司跟京东 ...

  6. PBRT笔记(13)——光线传播1:表面反射

    采样反射函数 BxDF::Sample_f()方法根据与相应的散射函数相似的分布来选择方向.在8.2节中,该方法用于寻找来自完美镜面的反射和透射光线;在这里讲介绍实现其他类型的采样技术. BxDF:: ...

  7. ubuntu系统下matplotlib中文乱码问题

    参考 [ubuntu系统下matplotlib中文乱码问题 - CSDN博客](https://blog.csdn.net/jeff_liu_sky_/article/details/54023745 ...

  8. 读《31天学会CRM项目开发》记录4 - WEB服务配置

    好几天没有更新记录了,因为最近都在看本书的基础内容,然后跟着练习.等看到数据库部分,就晕菜了,只能草草浏览一遍,想在后面的实战中再加强. 下面是对IIS 和ASP.NET的配置! 一.什么是IIS? ...

  9. 将Redhat,CentOS,Ubuntu虚拟机的IP设为静态IP的方法

    一般在主机上创建的虚拟机默认是通过DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)网络协议来动态生成的,这样会导致你安装的虚拟机的IP地址是动态变化 ...

  10. Mem系列函数介绍及案例实现

      昨天导师甩给我们一个项目案例,让我们自己去看一看熟悉一下项目内容,我看到了这个项目里面大量使用memset(sBuf,0,sizeof(sBuf));这一块内存填充的代码,于是回想起以前查过Mem ...