个人博客 转载请注明原创出处。
内部类(inner class
)是定义在另外一个类内部的类。之因此定义在内部是由于内部类有一些普通类没有的“特权”,能够方便实现一些需求。java
先来看一个简单的例子:数组
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)); } }
能够看到这个类的名称是用外围类名称加内部类名称用$
符号分割,并且编译器在内部类的构造函数里自动添加了一个外围类的参数,这样内部类就能引用到外围类的域和参数了。app
不过这样还有一个问题,咱们彻底能够按普通的方式本身写一个构建方式来接收Apple
类而不用内部类的方式,不过这样的类却没法引用Apple
类的私有域和私有方法。编辑器
眼尖的同窗可能已经发现奥秘了,Apple.access$000(this.this$0)
这一条语句就是关键了。内部类在引用外围类的私有域和方法时编译器会在外围类内部生成一个静态方法access$XXX
,这个方法会返回外围类的私有域或调用私有方法,方法的第一个参数是外围类的引用。函数
不过这样就有了安全风险,任何人均可以经过调用Apple.access$000
方法很容易地读取到私有域size
。固然,access$000
不是Java
的合法方法名。但熟悉类文件结构的黑客可使用十六进制编辑器轻松地建立一个用虚拟机指令调用那个方法的类文件。因为隐秘地访问方法须要拥有包可见性,因此攻击代码须要与被攻击类放在同一个包中。ui
内部类有一些特殊的语法,好比获取传入的外围类引用的语法是OuterClass.this
,外围类的类名加上this
关键字。还有明确的使用内部类的构建函数outerObject.new InnerClass {construction parameters)
。在内部类中声明的静态域必须是不可变的,即必须用final
修饰符修饰,且不能有静态方法。例子:this
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(); } }
内部类也能够在一个方法内声明,这样定义的内部类就是局部内部类。局部内部类和内部类的区别在于局部内部类的做用域局限于定义它的方法块内,除了这个方法内部局部内部类都是不可见的。code
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
新手都去看看。