1、当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何条件。此外,内部类还拥有其外围类的所有元素的访问权。这是如何做到的呢?当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后,在你访问此外围类的成员时,就是用那个引用来选择外围类的成员。而这些所有细节,都是编译器帮你处理了。
如果不需要内部类对象和其外部类对象之间有关系,那么可以将内部类声明为static,静态内部类不会隐式的持有一个外部类对象的引用。静态内部类与普通内部类还有一个区别,普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含静态内部类,而静态内部类可以包含所有这些东西。
2、在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。示例:
A a= new A();
B b =a.new B();//其中A是外部类,B是内部类
但是,如果你创建的是嵌套类(静态内部类),那么它就不需要对外部类对象的引用。
3、匿名内部类
匿名内部类也就是没有名字的内部类
正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,形式如:new XXX(){};
public class Parcel{
public Wrapping wrapping(final int x){
return new Wrapping(){
private int value = x ;
public int value(){
return value++;
}}}}
a)Wrapping既可以是一个接口,也可以是一个已定义了的类 -- 匿名内部类既可以扩展类,也可以实现接口,但是 不能两者兼备。而且如果是实现接口,也只能实现一个接口;
b)在匿名类中定义字段时,还能够对其执行初始化操作;
c)如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的;
4、接口内部的类
正常情况下,不能在接口内部放置任何代码,但静态内部类可以作为接口的一部分。你放到接口中的任何类都会自动的是public和static。因为类是static的,只是将静态内部类置于接口的命名空间内,这并不违反接口的规则。你甚至可以在内部类中实现其外围接口,示例:
public interface ClassInterface{
void howdy();
class Test implements ClassInterface{
public void howdy(){
System.out.println("HowDy");
}
public static void main(String[] args){
new Test().howdy();
}}}
如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共有,那么使用接口内部的静态类会显得非常方便。
5、内部类有效的解决了“多重继承”问题
内部类允许继承多个非接口类型(类或者是抽象类)。为了看到更多的细节,让我们考虑这样一种情形:即必须在一个类中以某种方式实现两个接口。由于接口的灵活性,你有两种选择:使用单一类,或者使用内部类。
interface A {}
interface B {}
class X implements A,B{}
class Y implements A{
B makeB(){
return new B(){};
}}
如果拥有的是抽象类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
class D{}
abstract class E{}
class Z extends D{
E makeE(){ return new E(){};}
}
如果不需要解决“多重继承”的问题,那么自然可以用别的方式编码,而不需要使用内部类。
6、闭包
闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外部类对象的信息,还自动拥有一个指向此外部类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。
7、内部类的继承
因为内部类的构造器必须连接到指向其外部类的引用,所以在继承内部类的时候,事情会变得有点复杂。问题在于,那个指向外部类对象的“秘密”引用必须被初始化,而在子类中不再存在可连接的默认对象。要解决这个问题,必须使用特殊的语法来明确说明它们之间的关联:
class WithInner{
class Inner{}
}
public class InheriInner extends WithInner.Inner{
//InheriInner(){} won't compile
InheriInner(WithInner wi){
wi.super();
}}
可以看到,InheriInner只继承自内部类,而不是外围类。但是当要生成一个构造器时,默认的构造器并不算好,而且不能只是传递一个指向外部类对象的引用。此外,必须在构造器内使用如下语法:
enclosingClassReference,super();
这样才提供了必要的引用,然后程序才能编译通过。
8、内部类标识符
外部类的名字+$+内部类的名字。如果内部类是匿名的,编译器会简单地产生一个数字作为其标识符。如果内部类是嵌套在别的内部类之中,只需直接将它们的名字加在其外部类标识符与$的后面。
Counter.class
LocalInnerClass$1.class
LocalInnerClass$1LocalCounter.class
LocalInnerClass.class