提起Java内部类(Inner Class)可能不少人不太熟悉,实际上相似的概念在C++里也有,那就是嵌套类(Nested Class),其实Java内部类与C++嵌套类最大的`不一样就在因而否有指向外部的引用上(下面会介绍到)`。java
内部类从表面上看,就是在类中又定义了一个类,而实际上并无那么简单,乍看上去内部相似乎有些多余,它的用处对于初学者来讲可能并非那么显著,可是随着对它的深刻了解,你会发现Java的设计者在内部类身上的确是用 心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可让你更优雅地设计你的程序结构。程序员
public interface Contents { int value(); } public interface Destination { String readLabel(); } public class Goods { private class Content implements Contents { private int i = 11; public int value() { return i; } } protected class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new GDestination(s); } public Contents cont() { return new Content(); } } class TestGoods { public static void main(String[] args) { Goods p = new Goods(); Contents c = p.cont(); Destination d = p.dest("Beijing"); } }
在这个例子里类Content和GDestination被定义在了类Goods内部,而且分别有着protected和private修饰符来控制访问级 别。Content表明着Goods的内容,而GDestination表明着Goods的目的地。它们分别实现了两个接口Content和 Destination。在后面的main方法里,直接用 Contents c和Destination d进行操做,你甚至连这两个内部类的名字都没有看见!这样,内部类的第一个好处就体现出来了——`隐藏你不想让别人知道的操做`,也即封装性。编程
同时,咱们也发现了在外部类做用范围以外获得内部类对象的第一个方法,那就是利用其外部类的方法建立并返回。上例中的cont()和dest()方法就是这么作的。那么还有没有别的方法呢?固然有,其语法格式以下:数组
outerObject=new OuterClass(Constructor Parameters); OuterClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
注意:`在建立非静态内部类对象时,必定要先建立起相应的外部类对象`。至于缘由,也就引出了咱们下一个话题。函数
对刚才的例子稍做修改:this
public class Goods { private valueRate=2; private class Content implements Contents { private int i = 11*valueRate; public int value() { return i; } } protected class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new GDestination(s); } public Contents cont() { return new Content(); } }
在这里咱们给Goods类增长了一个private成员变量valueRate,意义是货物的价值系数,在内部类Content的方法value()计算价值时把它乘上。咱们发现,value()能够访问valueRate,这也是内部类的第二个好处——`一个内部类对象能够访问建立它的外部类对象的内容, 甚至包括私有变量!`这是一个很是有用的特性,为咱们在设计时提供了更多的思路和捷径。`要想实现这个功能,内部类对象就必须有指向外部类对象的引用。`Java编译器在建立内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终能够访问其外部类对象,同时这也是为何在外部类做用范围以外向要建立内部类对象必须先建立其外部类对象的缘由。设计
有人会问,`若是内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了`,怎么办?没事,Java里用以下格式表达外部类的引用:code
OuterClass.this
有了它,咱们就不怕这种屏蔽的状况了。对象
和普通的类同样,内部类也能够有静态的。不过和非静态内部类相比,区别就在于`静态内部类没有了指向外部的引用`。这实际上和C++中的嵌套类很相像 了,Java内部类与C++嵌套类最大的不一样就在因而否有指向外部的引用这一点上,固然从设计的角度以及以它一些细节来说还有区别。继承
除此以外,`在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套能够不止一层)`。不过静态内部类中却能够拥有这一切。这也算是二者的第二个区别吧。
与接口相关方面,正常状况下,你不能在接口内部放置任何代码,但静态内部类能够做为接口的一部分,由于它是static 的。只是将静态内部类置于接口的命名空间内,这并不违反接口的规则。
静态内部类,意味着:
1. 建立一个static内部类的对象,不须要一个外部类对象;
2. 不能从一个static内部类的一个对象访问一个外部类对象;
Java内部类也能够是局部的,它能够定义在一个方法甚至一个代码块以内。
public class Goods1 { public Destination dest(String s) { class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new GDestination(s); } public static void main(String[] args) { Goods1 g= new Goods1(); Destination d = g.dest("Beijing"); } }
上面就是这样一个例子。在方法dest中咱们定义了一个内部类,最后由这个方法返回这个内部类的对象。若是咱们在用一个内部类的时候仅须要建立它的一个对象并创给外部,就能够这样作。固然,定义在方法中的内部类可使设计多样化,用途毫不仅仅在这一点。
下面有一个更怪的例子:
public class Goods2{ private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } } public void track() { internalTracking(true); } public static void main(String[] args) { Goods2 g= new Goods2(); g.track(); } }
你不能在if以外建立这个内部类的对象,由于这已经超出了它的做用域。不过在编译的时候,内部类TrackingSlip和其余类同样同时被编译,只不过它由它本身的做用域,超出了这个范围就无效,除此以外它和其余内部类并无区别。
当一个内部类的类声名只是`在建立此类对象时用了一次`,并且`要产生的新类需继承于一个已有的父类或实现一个接口`,才能考虑用匿名类,`因为匿名类自己无名,所以它也就不存在构造方法`,它须要显示地调用一个无参的父类的构造方法,而且重写父类的方法。
java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组同样,当你只须要建立一个类的对象并且用不上它的名字时,使用内部类可使代码看上去简洁清楚。它的语法规则是这样的:
new interfacename(){......}; 或 new superclassname(){......};
下面接着前面继续举例子:
public class Goods3 { public Contents cont(){ return new Contents(){ private int i = 11; public int value() { return i; } }; } }
这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。
在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:
frame.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } });
有一点须要注意的是,`匿名内部类因为没有名字,因此它没有构造函数(可是若是这个匿名内部类继承了一个只含有带参数构造函数的父类,建立它的时候必须带上这些参数,并在实现的过程当中使用super关键字调用相应的内容)`。若是你想要初始化它的成员变量,有下面几种方法:
若是是在一个方法的匿名内部类,能够利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final。
将匿名内部类改形成有名字的局部内部类,这样它就能够拥有构造函数了。
在这个匿名内部类中使用初始化代码块,以下图。
java内部类有什么好处?为何须要内部类?
首先举一个简单的例子,若是你想实现一个接口,可是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你 能够建一个内部类实现这个接口。因为内部类对外部类的全部内容都是可访问的,因此这样作能够完成全部你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法的不就好了吗?
的确,以此做为设计内部类的理由,实在没有说服力。
真正的缘由是这样的,`java中的内部类和接口加在一块儿,能够的解决常被C++程序员抱怨java中存在的一个问题——没有多继承`。实际上,C++的多继承设计起来很复杂,而java经过内部类加上接口,能够很好的实现多继承的效果。