Java内部类与外部类的那些事
昨天去笔试的时候遇到了Java的内部类的创建方式与访问权限的问题,我不懂,没写,故今天起来特意去试验一下,就有了这篇总结性的文章。
Java中的内部类又分为非静态内部类(匿名内部类也是非静态的内部类)和静态内部类,两者与外部类的关系是不一样的,创建的方式也不一样。
1 非静态内部类
Java的非静态内部类在构造的时候,会将外部类的引用传递进来,并且作为内部类的一个属性,因此,内部类会隐式地持有其外部类的引用。也就是非静态内部类在构造的时候需要有一个外部类的实例传递进来进行构造,不能像普通的Java类那样直接就可以通过 new来生成;一个简单的例子如下:
1 import java.util.ArrayList; 2 import java.util.LinkedList; 3 import java.util.List; 4 import java.util.Queue; 5 import java.util.Scanner; 6 7 public class Main { 8 9 /** 10 * @param args 11 */ 12 public int k=3; 13 private static String string="Java"; 14 protected float j=1.5f; 15 public static void show(){ 16 System.out.println("show"); 17 } 18 private void add(){ 19 System.out.println("add"); 20 } 21 public static void main(String[] args) { 22 // TODO Auto-generated method stub 23 Main m=new Main(); 24 //合法的非静态内部类的构造方式 25 Child c=m.new Child(); 26 //Child c=new Child() 这是不合法的构造方式 27 c.test(); 28 29 } 30 //内部类Child 31 class Child{ 32 public int i; 33 public void test(){ 34 System.out.println("k=:"+k); 35 System.out.println("string:"+string); 36 add(); 37 System.out.println("j=:"+j); 38 show(); 39 } 40 41 } 42 43 }
并且非静态的内部类可以访问外部类的所有成员变量与方法,包括静态的成员变量与方法,执行内部类Child的test()方法就可以得到下面的结果:
1 k=:3 2 string:Java 3 add 4 j=:1.5 5 show
2 静态内部类
Java中的静态内部类在构造的时候并不需要外部类的引用,因此静态的内部类不会持有外部类的引用,并且静态内部类只能访问外部类的静态成员变量和方法。一个简单的例子为(上面的代码做一些简单的改动):
1 public class Main { 2 3 /** 4 * @param args 5 */ 6 public int k=3; 7 private static String string="Java"; 8 protected float j=1.5f; 9 public static void show(){ 10 System.out.println("show"); 11 } 12 private void add(){ 13 System.out.println("add"); 14 } 15 public static void main(String[] args) { 16 // TODO Auto-generated method stub 17 Main m=new Main(); 18 //Child c=m.new Childe();//非法 19 Child c=new Child(); 20 c.test(); 21 22 } 23 static class Child{ 24 public int i; 25 26 public void test(){ 27 // System.out.println("k=:"+k);//不能访问外部类非静态变量 28 System.out.println("string:"+string); 29 //add(); //不能访问外部类的非静态方法 30 // System.out.println("j=:"+j); 31 show(); 32 } 33 34 } 35 36 }
从上面的代码可以看到,静态内部类的创建方式与普通的Java类的创建方式一样,执行第21行代码就可以得到如下结果:
1 string:Java 2 show
3 杂谈
Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏(你认为垃圾回收器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾回收器不能正常工作)。为了避免这种情况的发生,你可以在内部类的内部显示持有一个外部类的软引用(或弱引用),并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收;
关于内存泄漏的这一点,有参考技术小黑屋的两篇文章:Android中Handler引起的内存泄露和避免Android中Context引起的内存泄露