首页 > 技术文章 > 学习Android之内部类

rosepotato 2013-11-10 10:14 原文

java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类。内部类又分为:常规内部类、局部内部类、匿名内部类和静态嵌套类四种。我们内部类的知识在Android手机开发中经常用到。

 

一、常规内部类

 

所谓常规内部类,或者说内部类,指的就是除去后面三种之外的内部类(这算什么解释。。。)

 

先写一个最简单的内部类的例子,大家感觉一下:

 

view sourceprint?1 public class Outer {

 

2     public class Inner{

 

3     }

 

4 }

 

编译一下,我们看到目录中出现了两个class文件,其中有一个文件名叫做Outer$Inner.class,带了一个$符号,这个特点让我们很容易的认出来这是内部类编译后的class文件。

 

 

 

再写一个稍微复杂一点的内部类:

 

view sourceprint?01 public class Outer {    

 

02  

 

03     private int x=1;

 

04  

 

05     public Outer(){

 

06         System.out.println("Outer initial");

 

07     }

 

08  

 

09     public class Inner{

 

10  

 

11         public Inner(){

 

12             System.out.println("Inner initial");

 

13         }

 

14  

 

15         private int x=2;

 

16  

 

17         public  void add(){

 

18             int x=3;

 

19             System.out.println(x);

 

20             System.out.println(this.x);

 

21             System.out.println(Outer.this.x);

 

22         }

 

23  

 

24     }

 

25  

 

26     public static void main(String[] args){

 

27         Inner inner = new Outer().new Inner();

 

28         inner.add();

 

29     }

 

30 }

 

我们编译以后,运行一下看看:

 

 

 

在上面的例子里我们可以清晰的看到:

 

内部类就像一个实例成员一样存在于外部类中。

内部类可以访问外部类的所有成员就想访问自己的成员一样没有限制。

内部类中的this指的是内部类的实例对象本身,如果要用外部类的实例对象就可以用类名.this的方式获得。

内部类对象中不能有静态成员,原因很简单,内部类的实例对象是外部类实例对象的一个成员。

下面我们再小结一下内部类的创建方法:

 

在外部类的内部,可以用 Inner inner = new Inner(); 方法直接创建

在外部类外部,必须先创建外部类实例,然后再创建内部类实例,除了上面 Inner inner = new Outer().new Inner()的写法以外,还有 Outer outer = new Outer(); Inner inner = outer.new Inner();的写法

 

 

二、局部内部类

 

我们也可以把类定义在方法内部,这时候我们称这个类叫局部内部类。

 

我们再看一个例子:

 

view sourceprint?01 public class Outer {

 

02  

 

03     int x =1;

 

04     public void doSomething(){

 

05         final int y=2;

 

06         class Inner{

 

07             int x =3;

 

08             void print(){

 

09                 int x=4;

 

10                 System.out.println(x);

 

11                 System.out.println(this.x);

 

12                 System.out.println(Outer.this.x);

 

13                 System.out.println(y);

 

14             }

 

15         }

 

16         Inner inner = new Inner();

 

17         inner.print();

 

18     }

 

19  

 

20     public static void main(String[] args){

 

21         Outer outer = new Outer();

 

22         outer.doSomething();

 

23     }

 

24 }

 

运行程序,查看结果:

 

 

 

我们通过上面这里例子也可以看到下面几点:

 

局部内部类的地位和方法内的局部变量的位置类似,因此不能修饰局部变量的修饰符也不能修饰局部内部类,譬如public、private、protected、static、transient等

局部内部类只能在声明的方法内是可见的,因此定义局部内部类之后,想用的话就要在方法内直接实例化,记住这里顺序不能反了,一定是要先声明后使用,否则编译器会说找不到。

局部内部类不能访问定义它的方法内的局部变量,除非这个变量被定义为final 。

是不是有点不好理解?关于为什么用final修饰以后就可以用了,我打算专门在番外篇里专门写一篇博客给你讲清楚,先记住吧。

 

三、匿名内部类

 

当我们把内部类的定义和声明写到一起时,就不用给这个类起个类名而是直接使用了,这种形式的内部类根本就没有类名,因此我们叫它匿名内部类。

 

我们再看一个有趣的例子:

 

view sourceprint?01 public class Dog {

 

02  

 

03     public interface Pet {

 

04  

 

05         public void beFriendly();

 

06         public void play();

 

07  

 

08     }

 

09  

 

10     public static void main(String[] args){

 

11  

 

12         Pet dog = new Pet(){

 

13             @Override

 

14             public void beFriendly() {

 

15                 System.out.println("蹭蹭你^_^");

 

16             }

 

17             @Override

 

18             public void play() {

 

19                 System.out.println("把飞盘叼给你,逼你把飞盘丢出去,然后它再捡回来让你继续扔,连续500次^_^");

 

20             }

 

21         };

 

22  

 

23         dog.beFriendly();

 

24         dog.play();

 

25  

 

26     }

 

27 }

 

编译和运行程序,查看结果:

 

 

 

竟然编译和运行都很正常,我们知道抽象类和接口肯定无法实例化的,因此刚才的例子肯定有点意思:

 

第一匿名内部类可以是个接口,这个没什么好奇怪的哈。

第12行到第21行是一个语句,就是定义了一个对象,因此21行大括号后面有个分号。

匿名内部类用 new Pet(){ … } 的方式把声明类的过程和创建类的实例的过程合二为一。

匿名内部类可以是某个类的继承子类也可以是某个接口的实现类。

好吧我们再看一个例子,方法参数内的匿名内部类:

 

view sourceprint?01 public class Dog {

 

02  

 

03     static abstract class Ball {

 

04         abstract String getName();

 

05     }

 

06  

 

07     void play(Ball b){

 

08         System.out.println(b.getName());

 

09     }

 

10  

 

11     public static void main(String[] args){

 

12         Dog dog = new Dog();

 

13  

 

14         dog.play(new Ball(){

 

15             @Override

 

16             String getName() {

 

17                 return "qiu qiu";

 

18             }});

 

19     }

 

20 }

 

编译和运行以后的截图我就不给你了,返回值就是“qiu qiu”。

 

从第14行到第18行是一句话,就是执行一个play方法,而这个方法的参数就由一个匿名内部类的实例来提供。

 

四、静态嵌套类

 

为了让你感觉舒服一些,我们也把最简单的内部类放在最后讲。

 

当一个内部类前面用static修饰时,我们称之为静态嵌套类或者说静态内部类。

 

上面的例子里其实我们已经看到过静态嵌套类了,下面我们再举一个例子:

 

view sourceprint?01 public class Outer {

 

02  

 

03     static int x =1;

 

04  

 

05     static class Nest {

 

06  

 

07         void print(){

 

08             System.out.println("Nest "+x);

 

09         }

 

10     }

 

11  

 

12     public static void main(String[] args){

 

13         Outer.Nest nest = new Outer.Nest();

 

14         nest.print();

 

15     }

 

16 }

 

因为静态嵌套类和其他静态方法一样只能访问其它静态的成员,而不能访问实例成员。因此静态嵌套类和外部类(封装类)之间的联系就很少了,他们之间可能也就是命名空间上的一些关联。上面例子中你需要注意的就是静态嵌套类的声明方法 new Outer.Nest() 连续写了两个类名,以至于我们都怀疑前面的Outer是个包名了,好在包名一般都小写的,要不还真分不清……

 

再强调一遍,内部类在Android中应用的非常多,理解和使用好显得蛮重要。

 

 

匿名类不等同于匿名内部类

 

匿名类不等同于匿名内部类,后者也经常使用,匿名内部类是内部类的一种,使用的时候更加简洁,但是不是任何情况下都需要使用。

在thinking in java 3th 是这么阐述的:

 

简单地说:匿名内部类就是没有名字的内部类。什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较

合适的:

  ·只用到类的一个实例。
  ·类在定义后马上用到。
  ·类非常小(SUN推荐是在4行代码以下)
  ·给类命名并不会导致你的代码更容易被理解。
  在使用匿名内部类时,要记住以下几个原则:

  ·匿名内部类不能有构造方法。
  ·匿名内部类不能定义任何静态成员、方法和类。
  ·匿名内部类不能是public,protected,private,static。
  ·只能创建匿名内部类的一个实例。
·一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
  ·因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。 

 

 

推荐阅读