java嵌套类(Nested Classes)总结
Nested Classes定义
在java语言规范里面,嵌套类(Nested Classes)定义是:
A nested class isany class whose declaration occurs within the body of another class orinterface. A top level class is a class that is not a nested class.
说的简单一点,就是定义在类里面的类。一般把定义内部类的外围类成为包装类(enclosing class)或者外部类
嵌套类分类
根据nestedclass定义的地方,可以分为membernested class,local nested class , anonymous nested class
member nested class(成员嵌套类):成员嵌套类作为 enclosing class 的成员定义的,成员嵌套类有enclosing class属性(这个是什么意思?)
local nested class (局部嵌套类):局部嵌套类定义在 enclosing class 的方法里面,局部嵌套类有enclosing class 属性和enclosing method 属性
anonymous nested class(匿名嵌套类):匿名嵌套类没有显示的定义一个类,直接通过new 的方法创建类的实例。一般回调模式情况下使用的比较多
member nested class 可以使用public,private,protected访问控制符,也可以用static,final关键字(使用static修饰的成员嵌套类成为“顶级嵌套类”:当你在其它类的外面声明一个类时,Java就认为该类是一个顶级类。如果你在一个顶级类中声明一个类,并且在该嵌套类的声明前加上static的修饰符,你就得到了一个顶级嵌套类)
local nested class 可以使用final关键字
anonymous nested class 不使用任何关键字和访问控制符
见下面的代码
public class EnclosingClass { public static final class NestedMemberClass { } public void nestedLocalClass() { final class NestedLocalClass { } } public void nestedAnonymousClass() { new Runnable() { @Override public void run() { } }; } }
在大多数情况下,一般把nested classes 分为两种:
Static Nested Classes(静态嵌套类): 就是用static修饰的成员嵌套类(或者叫静态成员类)
InnerClass:静态嵌套类之外所有的嵌套类的总称,也就是非静态嵌套类,也就是没有用static定义的nested classes,Inner Classes 不能定义为static,不能有static方法和static初始化语句块。(不然即使你定义了外部也访问不了)在JLS(java语言规范)里面是这么定义的:
Aninner class is a nested class that is not explicitly or implicitly declaredstatic. Inner classes may not declare static initializers (§8.7) or memberinter- faces
其中Inner Class又可以分为三种:
1 inner member classes :没有用static 修饰的成员内部类(所谓“成员”就是类的成员,跟成员变量,成员方法一样的)
2 local inner classes :定义在方法里面的内部类,方法可以是static的也可以是非static的,也可以是构造器方法。
3 anonymous inner classes :定义在方法里面匿名类,方法可以是static的也可以是非static的
嵌套类访问规则
Static Nested Classes 以及 inner classes 有一些限制规则,下面介绍一下这些规则。
· Static Nested Classes访问规则
用Static修饰的Nested Classes,只能访问外部类的非static变量。(这一点不懂的话是对static的理解不够深入,可以参考这篇文章:http://blog.csdn.net/a512592151/article/details/38453811)对于public的 static Nested Classes 可以用new 外部类.内部类()的方式直接创建。而默认的static Nested Classes 可以在同一包名下,用 new 外部类.内部类()的方式创建。其实和外部类的方式差不多。静态成员类可以使用访问控制符,可以使用static修饰,可以是abstract抽象类
我的理解:对于上面为什么可以直接这样new 外部类.内部类()创建这个嵌套类呢,从这里我引出了一个队static 的个人理解,在嵌套类前面加static 不是用来修饰这个嵌套类的,而是为了让其上层类知道这个类是静态的,从而可以直接引用,而不必先实例化上层类。其他静态XX我想也是类似的。(初学者的理解,大牛勿喷)
public class StaticNestedClass { // 私有局部 private int i = 0; // 静态 public static int j = 0; // 不变值 private final int k = 0; // static final private static final int m = 0; // 静态嵌套内,这里不是innerclass,可以直接new出来 public static class PublicNestedClass { private void test1() { // System.out.println(i); 非innerClass不能访问enclosing类的非static属性 System.out.println(j); System.out.println(m); // System.out.println(k); 非innerClass不能访问enclosing类的非static属性 } // 可以定义static方法 private static void test2() { } } // 静态嵌套内,这里不是innerclass,由于是私有的,不可以直接new出来 private static class PrivateNestedClass { } }
下面的例子演示了static Nested class的创建
public class TestClass { public static void main(String[] args) { //任何地方都可以创建 StaticNestedClass.PublicNestedClass publicNestedClass = new StaticNestedClass.PublicNestedClass(); //编译错误,无法访问内部内 //StaticNestedClass.PrivateNestedClass privateNestedClass = new StaticNestedClass.PrivateNestedClass(); } }
· Inner Class访问规则
inner member classes(内部成员类)可以访问外部类的所有实例属性,静态属性(而静态内部类是不能这样访问的,因为引用静态内部类根本不需要实例化上一层类,所以也不会存在什么实例属性了)。因为内部成员类持有一个外部对象的引用(从jvm的机制方面解释一下?),内部类的实例可以对外部类的实例属性进行修改。如果是public的 inner member classes,可以通过外部类实例.new 内部类()的方式进行创建,当调用内部类的构造器的时候,会把当前创建的内部类对象实例中持有的外部对象引用赋值为当前创建内部类的外部类实例。内部成员类可以是使用访问控制符,可以定义为final,也可以是抽象类。
public class MemberInnerClass { // 私有局部 public int i = 0; // 静态 private static int j = 0; // 不变值 private final int k = 0; // static final private static final int m = 0; public class PublicMemberInnerClass { // enclosing Class的属性都可以访问 public void test() { System.out.println(i); System.out.println(j); System.out.println(m); System.out.println(k); } public MemberInnerClass getOutterClass() { return MemberInnerClass.this; } // 这里会报错,不允许定义static方法 // private static final void test(); } // 私有的innerclass 外部不能访问 private class PrivateMemberInnerClass { } // 公开局部类,外部可以访问和创建,但是只能通过OutterClass实例创建 class DefaultMemberInnerClass { public MemberInnerClass getOutterClass() { return MemberInnerClass.this; } } } |
下面例子演示了内部成员类的创建
public class TestClass { public static void main(String[] args) { // 任何地方都可以创建 MemberInnerClass t = new MemberInnerClass(); // 可以创建,pmic里面保存对t的引用 MemberInnerClass.PublicMemberInnerClass pmic = t.new PublicMemberInnerClass(); // 可以在同一package下创建,dmic保存对t的引用 MemberInnerClass.DefaultMemberInnerClass dmic = t.new DefaultMemberInnerClass(); // 编译错误,无法访问内部内 // MemberInnerClass.PrivateMemberInnerClass pmic = t.new // PrivateMemberInnerClass(); // 下面验证一下outterClass是同一个对象 System.out.println(pmic.getOutterClass() == t); System.out.println(dmic.getOutterClass() == t); } } |
运行程序,打印结果:
|
2 local inner classes(局部类)
局部类 定义在类方法里面。这个方法既可以是静态方法,也可以是实例方法,也可以是构造器方法或者静态初始化语句块。
局部类可以定义在一个static上下文里面和非static上下文里面。局部类不能有访问控制符(private,public,protected修饰)(方法中不需要定义成员可见性),可以是抽象的,也可以定义为final
定义在static上下文(static 字段初始化,static初始化块,static方法)里面的local inner classes 可以访问类的静态属性,如果定义在静态方法里面的局部类,还可以方法里面定义的final变量。在static上下文定义的局部类,不能引用父类的实例变量,因为static方法不属于类的实例,属于类本身,局部类不能在外部进行创建,只能在方法调用的时候进行创建
package com.hxw.T2; public class LocalInnerClass { // 私有局部 private int i = 0; // 静态 public static int j = 0; // 不变值 private final int k = 0; // static final private static final int m = 0; public static void test() { final int a = 0; int b = 0; // local inner class不能够有访问控制符比如public private abstract class LocalStaticInnerClass { private int d = 0; public LocalStaticInnerClass() { // 可以访问方法里面定义的final 变量 System.out.println(a); // 不能访问b 因为b不是final // System.out.println(b); // 定义在static上下文里面的local inner class 不能访问外部类的非static字段 // System.out.println(i); // System.out.println(k); System.out.println(j); System.out.println(m); } // local inner class不能定义静态方法为什么? // public static void test(){} } } public void test2() { final int a = 0; int b = 0; final class LocalNonStaticInnerClass{ public LocalNonStaticInnerClass() { //定义在非static上下文的local inner class 可以访问外部类的所有属性 // private static int c; /*local inner class不能定义静态属性(因为如果要外界访问该方法的话是如此形式的, * LocalInnerClass.LocalNonStaticInnerClass.c; * 但是实际上对于非静态内部类我们的访问方法是这样的: LocalInnerClass localinner=new LocalInnerClass() LocalInnerClass.LocalNonStaticInnerClass inner =localinner.new LocalNonStaticInnerClass(); 不难解释,因为LocalNonStaticInnerClass不是静态成员怎么直接访问呢,必须实例化后做一个外部引用才可以访问的。 这样一来, */ System.out.println(i); System.out.println(k); System.out.println(j); System.out.println(m); } } } } |
提问:为什么上面的例子,b不是final,就不能访问呢?答案见:http://blog.csdn.net/a512592151/article/details/38468953
3 anonymousinner classes (匿名类)也是定义在方法里面,匿名类和局部类访问规则一样,只不过内部类显式的定义了一个类,然后通过new的方式创建这个局部类实例,而匿名类直接new一个类实例,没有定义这个类。匿名类最常见的方式就是回调模式的使用,通过默认实现一个接口创建一个匿名类然后,然后new这个匿名类的实例。
public class AnonymousInnerClass { //访问规则和局部类一样 public void test() { //匿名类实现 new Thread(new Runnable() { @Override public void run() { } }).start(); //非匿名类实现 class NoneAnonymousClass implements Runnable{ public void run() { } } NoneAnonymousClass t = new NoneAnonymousClass(); new Thread(t).start(); } } |
嵌套类的层次
嵌套类是可以有层次的,也就是说嵌套类里面还是定义类,成为嵌套类中的嵌套类。虚拟机如何保证嵌套类正确的嵌套层层次?
对于merber class,内部嵌套类的可以表示为 A$B 其中A为外部类,B为内部成员类,如果B里面又有成员为C的嵌套类,那么C就可以表示为A$B$C,如果A定义了两个同名member class,那么编译器就会报错。如果B里面又包含了为名B的nested class,则编译器会报错.
对于local inner Class,局部类可以表示为A$1B的方式,其中A为外部类,B为第一个局部类如果在不同的方法里面定义了同名的局部类B,编译器是可以编译通过的,那么定义的第二个局部类B可以表示为A$2B,如果在同一个方法里面同定义两个相同的局部类B,那么编译错是要报错的。如果B里面又定义了同名的成员类,则可以表示为A$1B$B。
对于anonymous inner classes,匿名类可以表示为A$1的方式,代表程序里面有一个匿名类。如果有N个,可以表示为A$N的方式(N为自然数).
看看下面的例子
public class NestedClassLevel { class A { // 编译器会报错,A里面不能在定义名为A的nested classes // class A{} public void test() { class B { } } } //可以在继续定义B class B { public void test(){ //可以无限定义匿名类 new Runnable() { public void run() { //可以无限定义匿名类 new Runnable() { public void run() { } }; } }; } } // 只能定义一个B // class B{} public void test() { // 可以定义A class A { public void test() { //可以有同名的局部类B和成员类B class B { public void test() { } } //局部类A里面不能在定义A //class A{} } } //可以有同名的局部类B和成员类B class B { } } } |
对于定义在非static上下文里面的nested类层次,比如A$B$1C ,则最内层的嵌套类C有一个指向B实例的引用,B有一个指向A实例的引用,最终最内层的嵌套类可以访问A中的属性可以方法,一般把B成为A的直接嵌套类。但是A不可以访问B或者C中属性或者方法(因为没有实例化)。
nested interface
由于interface默认是定义为一个 public static的特殊类,所以interface可以直接作为 static member class。可以通过A.B的方式进行访问。
nested class的应用
在java提供的基本类库里面,大量使用nested classes。比如我们知道的map类。其中 Map类里面有一个定义了Entry类abstract inner class。所以我们在遍历map的时候,一般使用
for (Map.Entry entry:map.entrySet()){
}
总结:nested类是java里面比较复杂的一个概念,必须详细了解jvm中对于嵌套类的实现以及java编译器对嵌套类的处理才可以深入了解嵌套类细节。