《Java编程思想》第十章 内部类

Ursula ·
更新时间:2024-09-20
· 870 次阅读

 

前言:

本系列是我本人阅读java编程思想这本书的读书笔记,主要阅读第五章到第十七章以及第二十一章的内容,今天的笔记是第十章

java中有一种类叫内部类,这一章我们就来了解一下什么是内部类以及内部类的一些特性。

1.创建内部类

内部类的创建方式很简单,就是把类的定义放在外部类里面

public class A{ class B{ } }

classB就是classA的内部类,那么,如何来使用内部类呢,当在外部类的内部使用内部类的时候,用法和普通的类并没什么区别,但是还有一种情况,外部类有一个方法,这个方法返回了内部类的一个引用,就像这样

public class A { //内部类B class B { } public B b() { return new B();//返回B的一个引用 } public static void main(String[] args) { A a = new A(); A.B b = a.b();//创建内部类对象 } }

当在外部类的非静态方法之外的任意位置想要创建内部类的对象的时候,就要使用外部类.内部类的形式具体的指明对象的类型

内部类对象在生成的时候有与外部类有了一种联系,这是因为外部类在创建内部类对象的时候,内部类对象会捕获一个外部类对象的引用,当你在内部类访问外部类成员的时候,使用的就是这个引用,所以,内部类具有外部类所有成员的访问权限。

2. 使用.this和.new

说完了怎么在外部类的非静态方法之外创建内部类对象,现在来说说怎么在内部类调用外部类的引用,可以用外部类名字后跟.this来指向外部类引用,这样产生的引用自动具有正确的类型,像这样

public class DoThis{ class Inner{ public DoThis outer(){ return DoThis.this;//这句话返回了外部类的引用 } } }

  那么如果要在内部类没有返回自己的引用的时候,外部类要怎么去创建内部类对象呢,要像这样

public class DoThis{ class Inner{ public static void main(String[] args){ DoThis.Inner dn=dn.new Inner(); } } }

还有一种内部类,他是在方法内部使用的,作用域仅在方法内,这种内部类叫局部内部类,就像这样

public class A{ public A aa(){ class B(){ } } }

这个时候这个classB的作用范围仅在aa()方法内。

3.匿名内部类

看一下这段代码

public class Parcel{ public Contents contents(){ return new Contents(){ private int i=1; public int value(){ return i; } }; } }

是不是觉得很奇怪,contents()方法把返回值的生成和返回值的类的定义结合在了一起,但是这个类没有名字,是匿名的这就是匿名内部类,可能你也注意到了,在return语句的最后有一个分号,这不是用来标记类的结束的。实际上,这标记的是表达式的结束,只是这个表达式刚好包含了匿名内部类而已。

关于匿名内部类的使用,有个要点要注意,如果你想在匿名内部类中使用一个外部定义的对象,那么这个参数引用就必须是final类型的,但是有一种例外情况,那就是给匿名内部类创建构造器,那不需要参数一定是final的,因为参数并不会被匿名内部类直接使用。

匿名内部类从本质上来说,会隐式的继承一个父类或者接口,换句话说匿名内部类就是一个继承了该类或者实现了该接口的一个子类对象,只不过这个子类对象没有名字。也就是说要想使用匿名内部类首先这个类要有一个父类或者接口,其实java中很多地方都用到了匿名内部类,最能体现匿名内部类使用的地方就是多线程了

public class A{ public static void main(String[] args){ //继承Thread类 Thread thread=new Thread(){ @override public void run(){ ...... } } //或者是实现Runnable接口 Runnable thread=new Runnable(){ @override public void run(){ ...... } } } }

这些都是使用匿名内部类的典型例子

4. 静态内部类

如果不需要内部类和外部类有链接,那么就可以把内部类声明为static,也就是静态内部类,也叫作嵌套类,但是有一点要记住,静态内部类不可以访问外部类的非静态成员。

静态内部类还可以放到接口中,因为放到接口中的任何类都自动地成为static和public的。因为类是static的,所以只是将静态内部类置于接口内部,不违反接口的规则。而且你还可以在静态内部类中实现接口的方法,这样,当你想创建一些公共的代码,让所有实现该接口的类使用的话,使用静态类就会显得很方便了(jdk1.8开始接口中已经可以有默认的实现方法了)。

5.为什么要使用匿名内部类

一般来说,内部类继承自某个类或者实现某个接口,内部类操作创建它们的外部类的对象,那么如果说只是需要一个对接口的引用的话,为什么不直接通过外部类实现那个接口呢,内部类的特点是,每个内部类都能独立地继承自一个实现。内部类的好处就是更有效的实现了“多重继承”,考虑一下,有这样一种情况,必须在一个类中以某种形式实现两个接口,那该怎么做呢

很简单,两种方法,一种是用一个类实现两个接口,一种是用一个类实现一个接口再用一个静态内部类实现另一个接口,比如说

interface A(); interface B(); //第一种方法 class X implements A,B{ } class Y implememts A{ //第二种方法,这里就是一个匿名内部类 B makeB(){ return new B(){}; } }

这样看起来,好像用内部类并没有体现出有什么好处,但是现在有了一个新情况,那就是现在不是两个接口,而是两个具体的类或者抽象类,这个时候,由于java不允许继承多个类,就只能用内部类来实现了

class C{} abstart D{} //继承一个类 class Z extend c{ //匿名内部类继承另一个抽象类 E makeE(){ return new E(){}; } } 6.内部类的继承

既然是类,那么自然也可以继承,内部类也是可以继承的,但是因为内部类的构造器必须要连接到指向外部类的引用,所以内部类的继承稍微有点复杂。那就是,隐式的指向的那个外部类的引用必须被初始化,而在子类中将不再存在可连接的默认对象。

为了解决这个问题,需要一个特殊的语法,下面来看看这个特殊的语法

class WithInner{ class Inner{ } } public class InheritInner extends WithInner.Inner{ InheritInner(WithInner wi){ wi.super(); } public static void main(String[] args){ WithInner wi=new WithInner(); InheritInner ii=new InheritInner(wi); } }

可以看到,InheritInner只是继承与内部类,但是当腰生成一个构造器的时候,默认的构造器并不算好,而且并不能只是传递一个指向外部类对象的引用。所以,必须在构造器中使用引用.super()这种语法提供一个必要的引用,然后程序才能编译通过。

说了那么多内部类的特性,我们知道每个普通类都会产生一个.class文件,其中包含了如何创建该类型对象的全部信息,那么内部类会不会产生一个.class文件呢,答案是肯定的,内部类也会产生一个.class文件,但是内部类的.class文件和普通类的额稍微有些不同,内部类的.class文件的命名是以外围类的名字后面加上$,再加上内部类的名字来命名的,当内部类是匿名内部类时,编译器会产生一个数字加到$的后面,如果内部类是嵌套在别的内部类之中,那么就在它的外部类的$后面直接加上它的名字。

总结

通过本章的学习,我们熟悉了内部类的特性,知道了如何创建内部类,如何使用内部类,知道了匿名内部类和静态内部类,还探讨了用内部类的好处以及什么时候用内部类,还了解了内部类的继承和内部类的标识符,到此,本章就结束了。


作者:wangjimmy1994



java编程思想 内部类 java编程 JAVA

需要 登录 后方可回复, 如果你还没有账号请 注册新账号