Java内部类
一、 内部类概念
一个类定义在另一个类的内部,这个类就叫做内部类。
代码示例:
public class TestClass { /*内部类,可以用private/public/protected修饰*/ private class InnerClass{ } public static void main(String[] args) { } } |
内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。 内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类。
内部类生成的class文件名称是外部类类名加上”$”再加上内部类类名,如上内部类就是OuterClass$InnerClass.class。
除匿名内部类之外,其他的内部类都是在编译时直接生成而不是在调用时才生成。匿名内部类由于没有类名因此只有在运行时才会生成,而且只能创建一个实例。
为什么需要内部类?典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。
使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
二、 内部类的定义位置
正如上述所列的几种内部类:成员内部类、局部内部类、静态内部类、匿名内部类,这几种内部类的区分正是由于定义的位置不同。
成员内部类:定义在外部类的内部,非方法内,相当于外部类的一个成员变量。
局部内部类:定义在外部类的方法中,可以使静态方法和普通方法,注意局部内部类的修饰符只能是abstract和final。
静态内部类:又叫嵌套类,是定义位置同成员内部类,但是使用static修饰符修饰。静态内部类相当于外部类的静态成员变量(后面介绍)。
匿名内部类:主要出现在方法内,没有类名,当然也就没有构造方法。(后面介绍)
代码示例:
public class TestClass { /* 成员内部类,可以用private/public/protected修饰,不可以定义静态成员变量 */ protected class InnerClass01 { private String myName = "Vicky" ; } /* 静态内部类,可以用private/public/protected修饰,可以有静态变量以及静态方法 */ public static class StaticInnerClass { public static String myName = "Vicky"; public static void main(String[] args) { } } public void method() { /* 普通方法内的局部内部类,只能使用abstract/final修饰,可以定义静态成员变量 */ abstract class InnerClass02 { } } public static void method02() { /* 静态方法内的局部内部类,只能使用abstract/final修饰,可以重名 */ final class InnerClass01 { } } public static void main(String[] args) { /*匿名内部类*/ Thread th = new Thread(new Runnable(){ @Override public void run() { } }) ; } } |
注意:非静态内部类(成员内部类和局部内部类)外其他类型的内部类都不能定义静态成员,包括变量以及方法;静态内部类可以有静态方法(包括main())和静态变量。匿名内部类由于没有类名,故也没有构造方法,但可以非静态定义变量以及方法。
三、 普通内部类(成员内部类和局部内部类)的实例化方式
内部类作为定义在外部类的成员变量,如同变量的使用一样,需要一个外部类的实例来调用,除静态内部类外。
注意在不同的位置实例化内部类的方式有不同的实例化方式。
代码示例:
public class OuterClass { /* 外部类的变量可以使用内部类,使用内部类类名和new就可以实例化,同一般类的实例化 */ private InnerClass innerVar = new InnerClass(); /* 成员内部类,可以用private/public/protected修饰,不可以定义静态成员变量 */ protected class InnerClass { private String myName = "Vicky"; } public void method() { /* 普通方法内直接使用内部类类名和new就可以实例化,同一般类的实例化 */ InnerClass inner01 = new InnerClass(); /* 普通方法内的局部内部类,只能使用abstract/final修饰,可以定义静态成员变量 */ final class InnerClass02 { } /* 方法内的局部内部类,只能在方法内部使用,就如在方法内定义的变量只能在方法内部使用一样*/ InnerClass02 inner = new InnerClass02() ; } public static void method02() { /* 显然静态方法中不能使用非静态内部类 */ } public static void main(String[] args) { /* 我们知道main()方法和它所在的类没有任何关系,因此在main()方法中实例化内部类就同在其他类中一样,这样方便些 */ // 首先实例化外部类,得到一个外部类对象 OuterClass outer = new OuterClass(); // 通过外部类的对象实例化内部类 OuterClass.InnerClass inner = outer.new InnerClass(); } } |
我们可以看出定义在方法内的局部内部类只能在方法内部使用,就如定义在方法内部的变量一样。
四、 普通内部类(成员内部类和局部内部类)使用外部类的域
普通内部类可以直接访问外部类的所有域(包括private修饰的成员),如变量、方法等(但是有些地方需要注意)。普通内部类在实例化时会被自动添加一个指向外部类的引用,实际上是编译器在编译时为内部类构造了一个带有外部类类型参数的构造方法,如
public InnerClass(OuterClass outer){ } |
通过这个构造方法普通内部类就可以得到外部类的引用也就可以使用外部类的所有域。普通内部类(成员内部类/局部内部类)可以直接使用外部类包括静态成员变量在内的所有域(包括private修饰的)。静态内部类不是这样(后面介绍)。
代码示例:
public class OuterClass { private String myName = "Vicky"; private static int myAge = 21; public void method() { System.out.println("you are invoking outer class's common method ..."); } public static void staticMethod() { System.out.println("you are invoking outer class's static method ..."); } public void method01(){ /* 局部类变量 */ final class InnerClass { /* 内部类变量使用外部类普通变量 */ private String inner_myName = myName; /* 内部类变量使用外部类静态变量 */ private int inner_myAge = myAge; public void inner_method() { System.out.println("myName--" + inner_myName + ";myAge--" + inner_myAge); /* 内部类使用外部类普通方法 */ method(); /* 内部类使用外部类静态方法 */ staticMethod(); } } } /* 成员内部类*/ private class InnerClass { /* 内部类变量使用外部类普通变量 */ private String inner_myName = myName; /* 内部类变量使用外部类静态变量 */ private int inner_myAge = myAge; public void inner_method() { System.out.println("myName--" + inner_myName + ";myAge--" + inner_myAge); /* 内部类使用外部类普通方法 */ method(); /* 内部类使用外部类静态方法 */ staticMethod(); } } public static void main(String[] args) { OuterClass outer = new OuterClass() ; OuterClass.InnerClass inner = outer.new InnerClass() ; inner.inner_method() ; } } |
成员内部类和局部内部类在使用外部类的域时是一样的,可以直接调用。
五、 内部类中使用this
内部类中得到当前外围类对象的引用,可以使用.this关键字,注意与new的区别。
代码示例:
public class OuterClass { private String myName = "黄齐"; private static String myEnglishName = "Vicky"; public class InnerClass { public OuterClass getOuterClass01() { /*使用this获得外部类对象,这个对象就是在内部类创建时的外部类对象*/ return OuterClass.this; } public OuterClass getOuterClass02() { /*使用new创建外部类,这是新建的外部类对象*/ return new OuterClass(); } } public static void main(String[] args) { } } |
六、 嵌套类(静态内部类)
1、 嵌套类的定义
嵌套类就是static类型的内部类,即静态内部类,它有一些特殊之处。定义在前面已经展示过。
嵌套类相当于外部类的一个静态成员变量,创建时不需要外部类的对象,因此可以有静态成员变量以及方法。
代码示例:
public class OuterClass { /* 静态内部类 */ public static class StaticInnerClass { private String myName = "Vicky"; private static int myAge = 21; public void method() { } public static void main(String[] args) { } } public static void main(String[] args) { } } |
2、 嵌套类的实例化
代码示例:
public class OuterClass { /* 外部类的变量可以使用内部类,使用内部类类名和new就可以实例化,同一般类的实例化 */ private InnerClass innerVar = new InnerClass(); /* 静态内部类,可以用private/public/protected修饰,可以定义静态成员变量 */ public static class InnerClass { private static String myName = "Vicky"; } public void method() { /* 普通方法内直接使用内部类类名和new就可以实例化,同一般类的实例化 */ InnerClass inner01 = new InnerClass(); } public static void method02() { /* 普通方法内直接使用内部类类名和new就可以实例化,同一般类的实例化 */ InnerClass inner02 = new InnerClass(); } public static void main(String[] args) { /* 我们知道main()方法和它所在的类没有任何关系,因此在main()方法中实例化内部类就同在其他类中一样,这样方便些 */ // 直接通过内部类类名实例化,同一般类,注意类名依旧是使用外部类.内部类,貌似new后面直接使用内部类类名也是可以的 OuterClass.InnerClass inner01 = new OuterClass.InnerClass(); OuterClass.InnerClass inner02 = new InnerClass(); } } |
我们可以发现,静态内部类和普通内部类是外部类中的使用是一样,如定义一个变量时实例化、在方法中实例化等。但是静态内部类可以在部类的静态方法中使用,而普通内部类不可以;在外部类外面使用普通内部类需要使用一个外部类的对象才能创建内部类对象,同时需要使用外部类对象.new,如OuterClass.InnerClass inner =outer.new InnerClass();而静态内部类不需要使用外部类即可调用,如OuterClass.InnerClass inner01 = new OuterClass.InnerClass();
3、 嵌套类访问外部类的域
代码示例:
public class OuterClass { private String myName = "Vicky"; private static int myAge = 21; public void method01() { System.out.println("you are invoking outer class's common method ..."); } public static void staticMethod() { System.out.println("you are invoking outer class's static method ..."); } /* 静态内部类 */ public static class StaticInnerClass { // 不能使用外部类的非静态变量 // private String inner_myName = myName; // 能使用外部类的静态变量 private static int inner_myAge = myAge; public void method() { // 不能调用外部内的非静态方法 // method01; // 能调用外部内的静态方法 staticMethod(); } } public static void main(String[] args) { /* 在外部类之外实例化 */ OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass(); inner.method() ; } } |
可以看出同一般类中的静态方法只能使用静态变量,不能使用非静态变量一样,静态内部类也不能使用外部类的非静态变量以及方法。因为静态变量实例化不需要外部类的实例,因此当内部类实例创建之后,不一定存在外部类实例,所以无法使用相应的非静态变量以及方法。
前面说过普通内部类在创建时会得到一个外部类实例的引用,这样内部类就可以使用外部类的域,但是静态内部类不会传递外部内的实例过去,那么静态内部类是如何可以使用外部内的静态成员的呢?
七、 匿名内部类
匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,如下所示: new <类或接口> <类的主体> 这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。 如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。
代码示例:
public class OuterClass02 { public static void main(String[] args) { /* 匿名内部类 */ Thread th = new Thread(new Runnable() { public String var = "123"; public void method() { } @Override public void run() { } }); } } |
匿名内部类中没有构造方法(压根没类名,哪来的构造方法),也不能够定义静态成员,如变量及方法。