ThinkInJava之内部类
一:内部类概述
将一个类的定义放在另一个类的内部,这就是内部类。内部类是Java一种非常有用的特征,因为他允许你把一些逻辑相关的数据组织在一起,并控制它的可见性。
二:内部类的创建
我们都知道类的创建语法(简略)如下
[public |...] class 类名 [implements|....]{ 定义属性(注意不同的修饰符(如public ....)) 定义方法语法(构造方法或普通方法) }
而内部类的创建就是把该类放在外部类的 同属性的位置或方法(包括构造方法)内 定义例下(希望大家可以看懂)
public class OutClass{
public OutClass(){ //构造方法内创建内部类
class C{}
} (修饰符)class A{} #局部内部类(同属性的位置) public B getB(){ #简称方法内部类(方法的内部) class B{}
return new B(); } }
####在次都忽略了修饰符,如果加上了修饰符如(static | public |private),会影响内部类的可见范围或和外围类的关系(如static),详细信息可参考修饰
符(和修饰符的用法及其相识)的讲解,和下文,因为篇幅有限不可能全面讲到。
三:外部类访问(创建)内部类(和内部类的修饰符有关系(如private public static 。。。。))
内部类的创建方式有两种
方法一:在外围类环境下(),直接通过new 关键字和普通类一样创建
方法二:通过外围类实例创建,具体细节如下
public class Parcel2 { class Contents{ private int i = 11; public int values() { return i; } } public Contents contents() { return new Contents(); #在外部类环境内创建(方法一) } public static void main(String[] args) { Parcel2 parcel2 = new Parcel2(); #注意这是两种创建内部类的方法 Contents contents = parcel2.contents(); #在外部类环境内直接创建 Contents contents = parcel2.new Contents();#通过外部类对象和关键字new创建内部类 (方法二) } }
备注:其它很多博客对内部类做了分类,这些分类也不外乎就是定义内部时用的修饰符和内部类定义位置的不同给内部起的名字而已。例如
成员内部类:同属性一样定义(修饰符一般是public或private)。
匿名内部类:即一个方法接受一个接口(interfaceA)类型的类。在其它类中调用这个方法,直接通过new interfaceA()这个接口并实现了该接口的所有方法,new interfaceA就是匿名内部了。
方法内部类:就是定义在外类普通方法内的内部类。
局部内部类:就是定义在构造方法内的内部类。
嵌套内部类:就是把定义内部了时用static关键字修饰的内部类。这个类我想多说点,普通内部类对象隐式的保存了一个外部类对象的引用,然而内部类通过static修饰就不一样了,该内部类就和外部类完全分离了,即不需要通过外部类对象创建内部类,内部了没有关联的外部类引用了。
接口内部类:外部类不是一个类,而是一个接口,接口内定义了一个内部类,该内部类可以实现自己,案例如五 接口内部了
四:关键字this 和new
4.1 new 的用法已经介绍过(通过外部类实例创建内部类实例)
4.2 this 通过OutClass.this 方式可访问创建自自己的的外部类实例(非常重要注意理解,有助于帮助我们理解为什么外部类对象的属性(包括private)完全报漏给内部类,检验如下
public class OutClass { void print() { System.out.println("id:"+ this.hashCode()+"我是外部类的方法"); } class InnerClass{ void print() { System.out.println("我是内部类的方法"); System.out.println("___________"); OutClass.this.print();//内部类内访问创建它的外部类实例,并调用外部类的方法 } } public static void main(String[] args) { OutClass outClass = new OutClass(); System.out.println("id:" + outClass.hashCode()); InnerClass innerClass = outClass.new InnerClass(); innerClass.print(); } } 结果: id:865113938 我是内部类的方法 ___________ id:865113938我是外部类的方法
分析结果,我们发现打印的hashCode()值相同,可知两次打印都是同一个对象的引用地址。
五:接口内部类
正常情况下,不能在接口内放置任何代码,但嵌套类可以作为接口的一部分,你放入接口中的任何类都自动转化为public和static,因为类是static,只是将
嵌套类置于接口的命名空间内,这并不违反接口规则。你可以在内部类中实现外围接口如下
public interface ClassInInterface {
void howdy(); class Test implements ClassInInterface{ @Override public void howdy() { System.out.println("我是接口内部类接口"); }; public static void main(String[] args) { new Test().howdy(); } } }
注意:在编译器内(eclipse),无法直接运行该main方法,可通过Javac编译代码,然后用Java命令运行
备注:如果你想创建某些公共代码,只想让默些特定类(实现了该接口的类)拥有。就可用该接口内部了特性。
测试:ClassInInterface
public class ClassInInterfaceImpl implements ClassInInterface{
@Override
public void howdy() {
new Test().howdy();
System.out.println("我自己的方法");
}
public static void main(String[] args) {
ClassInInterfaceImpl classInInterfaceImpl = new ClassInInterfaceImpl();
classInInterfaceImpl.howdy();
}
}
结果:
我是接口内部类接口
我自己的方法
六:关于使用内部类的一些小结
6.1:解决了java多重继承的问题。比如有个类需要继承其它两个类,然而Java只支持单继承,故我们可以通过编写一个内部类来继承另一个类的我们需要的类(当然我们也可以通过组合的方式来完成相识功能)
6.2:封装一些功能,只让特定的类拥有该方法。例如接口内部类,只有实现了该接口的类才拥有使用特定功能。
6.3:一个类中要实现一个接口方法的不同功能。
何时(什么情况下)使用内部类?请注意这个问题(引用ThinkInJava),如果只是需要一个接口的引用,为什么不通过外围类实现那个接口呢?答案是:如果这能满足需求,那么就行该这样做(即不用内部类)
同时,因为内部类引用了外部类的对象的地址,导致外部类始终有一个对象在引用他,如果不刻意手动清空内部类,就会导致内存泄漏。如下
public class Clear { @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); System.out.println("垃圾回收器要清理我"); } public static void main(String[] args) { Clear clear = new Clear(); 代码1.//InnerClass innerClass = clear.new InnerClass(); 代码2.//innerClass = null
代码3.//new Clear().new InnerClass()
clear = null;//
System.gc();
} class InnerClass{ } }
结果:垃圾回收器要清理我
如果我们把代码1注射打开,就不会执行finalize()方法,必须同时把代码2也要打开。如果是代码3的写法?那么垃圾怎么回收外部类呢??(就导致了内存泄漏)
备注:在thinkInJava中内部类这一节讲到了闭包的概念,他是这样定义闭包的:闭包是一个可调用的对象,它记录了一些信息(即外部类对象的一些信息,这些信息并没有显现的展示给内部了对象,而是以一种特殊的形式OutClass_name.this的形式展示给了自己),这些信息来自创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包(即内部类就是java中一种闭包),它不仅包含外围类对象的信息,还自动拥有一个指向外部类的引用,在此作用域内,内部类可以有权操作所有成员,包括private成员。