Java基础-内部类详解

我的博客 转载请注明原创出处。

内部类(inner class)是定义在另一个类内部的类。之所以定义在内部是因为内部类有一些普通类没有的“特权”,可以方便实现一些需求。

内部类

先来看一个简单的例子:

public class Apple {

    private int size = 16;

    private class Book {
        public void print() {
            System.out.println(size);
        }
    }

}

Book类就是定义在Apple类中的一个内部类,Book类引用了Apple类的私有域size却没有报错,这就是上文提到的特权了,内部类可以引用外围类的所有域和方法包括私有的。那么为什么内部类可以做到这样神奇的事情呢?原来是编译器在背后偷偷干的好事!

把上文的例子编译后可以看到编译器会额外生成一个Apple$Book.class文件:

class Apple$Book {
    private Apple$Book(Apple var1) {
        this.this$0 = var1;
    }

    public void print() {
        System.out.println(Apple.access$000(this.this$0));
    }
}

可以看到这个类的名称是用外围类名称加内部类名称用$符号分割,而且编译器在内部类的构造函数里自动添加了一个外围类的参数,这样内部类就能引用到外围类的域和参数了。

不过这样还有一个问题,我们完全可以按普通的方式自己写一个构建方式来接收Apple类而不用内部类的方式,不过这样的类却无法引用Apple类的私有域和私有方法。

眼尖的同学可能已经发现奥秘了,Apple.access$000(this.this$0)这一条语句就是关键了。内部类在引用外围类的私有域和方法时编译器会在外围类内部生成一个静态方法access$XXX,这个方法会返回外围类的私有域或调用私有方法,方法的第一个参数是外围类的引用。

不过这样就有了安全风险,任何人都可以通过调用Apple.access$000方法很容易地读取到私有域size。当然,access$000不是Java的合法方法名。但熟悉类文件结构的黑客可以使用十六进制编辑器轻松地创建一个用虚拟机指令调用那个方法的类文件。由于隐秘地访问方法需要拥有包可见性,所以攻击代码需要与被攻击类放在同一个包中。

特殊的语法

内部类有一些特殊的语法,比如获取传入的外围类引用的语法是OuterClass.this,外围类的类名加上this关键字。还有明确的使用内部类的构建函数outerObject.new InnerClass {construction parameters)。在内部类中声明的静态域必须是不可变的,即必须用final修饰符修饰,且不能有静态方法。例子:

public class Apple {

    private int size = 16;

    private class Book {
        public void print() {
            System.out.println(Apple.this.size);
        }
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        Apple.Book book = apple.new Book();
    }

}

局部内部类

内部类也可以在一个方法内声明,这样定义的内部类就是局部内部类。局部内部类和内部类的区别在于局部内部类的作用域局限于定义它的方法块内,除了这个方法内部局部内部类都是不可见的。

public class Apple {

    private int size = 16;

    private void print() {
        class Book {
            public void print() {
                System.out.println(size);
            }
        }
        Book book = new Book();
        book.print();
    }

}

匿名内部类

顾名思义,匿名内部类是一种没有类名的类。因为有时候我们只需要有一个一次性使用的类的对象,匿名内部类可以方便我们实现。通常的语法格式为:

SuperType superType = new SuperType(construction parameters) {
    inner class methods and data
}

如果SuperType是一个接口,那么就需要在大括号里实现接口定义的抽象方法。如果SuperType是一个类,可以在大括号里扩展这个类。因为匿名内部类没有类名,所以是不能定义构建函数的。在Java8以后,使用lambda表达式会比匿名内部类更加方便。

双括号初始化

利用匿名内部类的特殊语法的特殊初始化技巧,比如初始化一个数组:

List<String> arrayList = new ArrayList<String>() {{
    add("test");
    add("test2");
}};

不过就这个例子来说这样更好:List<String> arrayList = Arrays.asList("test", "test2");

静态内部类

上文说到内部类都会有一个外围类的引用,不过有时我们只是想把类放在另一个类内部并不需要引用它,这时就可以用到静态内部类。例子:

public class Apple {

    private int size;

    private int price;

    public Apple(int size, int price) {
        this.size = size;
        this.price = price;
    }

    public static void main(String[] args) {
        Apple apple = AppleBuilder.builder().setPrice(20).setSize(16).build();
    }

    static class AppleBuilder {

        private int size;

        private int price;

        static AppleBuilder builder() {
            return new AppleBuilder();
        }

        Apple build() {
            return new Apple(size, price);
        }

        AppleBuilder setSize(int size) {
            this.size = size;
            return this;
        }

        AppleBuilder setPrice(int price) {
            this.price = price;
            return this;
        }

    }

}

后记

一周一篇的第八篇了,接下来再复习一下并发相关的内容就准备去看看JVM相关的内容了。知识学是学不完的,只希望自己能坚持学到老,不要待在舒适区变成一个曾经讨厌的老顽固。这次同样是参考《Java核心技术 卷1》,这可真是一本好书,建议Java新手都去看看。


程序道路
记录一些东西
495 声望
23 粉丝
0 条评论
推荐阅读
HashMap的实现原理笔记
HashMap是Java中常用的Map接口的实现类,因为在日常工作中非常频繁的出现,所以在大部分的Java面试中都会问几个关于HashMap的问题。掌握HashMap的实现原理,已经是Java程序员的基础操作了。

Yuicon1阅读 1.9k

一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。
前言秒杀和高并发是面试的高频考点,也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目,提交PR,提高竞争力。早日上岸,升职加薪。知识点详解秒杀系统架构图秒杀流程图秒杀系统设计这篇文章一万多...

王中阳Go32阅读 2.4k评论 1

封面图
计算机网络连环炮40问
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

程序员大彬14阅读 1.7k

万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide8阅读 1.6k

封面图
花了半个小时基于 ChatGPT 搭建了一个微信机器人
相信大家最近被 ChatGPT 刷屏了,其实在差不多一个月前就火过一次,不会那会好像只在程序员的圈子里面火起来了,并没有被大众认知到,不知道最近是因为什么又火起来了,而且这次搞的人尽皆知。

Java极客技术12阅读 3.1k评论 3

封面图
数据结构与算法:二分查找
一、常见数据结构简单数据结构(必须理解和掌握)有序数据结构:栈、队列、链表。有序数据结构省空间(储存空间小)无序数据结构:集合、字典、散列表,无序数据结构省时间(读取时间快)复杂数据结构树、 堆图二...

白鲸鱼9阅读 5.2k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go11阅读 2.7k评论 4

封面图
495 声望
23 粉丝
宣传栏