那些java中的内部类

java内部类是从JDK1.1开始出现的,因此,很多人都不陌生,但是又会觉得不熟悉。原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下。所以,这里将从四个方面做一个简单的总结:

一.内部类基础

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来简单了解一下这四种内部类的用法。

1.成员内部类

  成员内部类是最普通的内部类,它的定义为位于另一个类的内部,这里总结它的一些特性:

  • 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
  • 当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员
  • 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
  • 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象
  • 成员内部类中不能定义任何静态成员变量或静态成员方法,但可以有静态常量。

     1 public class InnerClassDemo {
     2     public static void main(String[] args) {
     3
     4         //必须先创建一个外部类对象,才能创建一个成员内部类的对象
     5         Outer outer = new Outer("Liming", 20);
     6         Outer.Inner inner = outer.new Inner();
     7         inner.show();
     8     }
     9
    10 }
    11
    12 class Outer{
    13
    14     private static String name;
    15     private int age;
    16     private int score = 100;
    17
    18     public Outer(String name, int age) {
    19         this.name = name;
    20         this.age = age;
    21
    22         //外部类访问内部类的成员,必须先创建一个成员内部类的对象
    23         Inner inner = new Inner();
    24         System.out.println(inner.score);//80
    25     }
    26
    27     class Inner{
    28
    29         int score = 80;
    30
    31         void show(){
    32             //内部类能够访问外部类的静态和私有成员
    33             System.out.println(name);//Liming
    34             System.out.println(age);//20
    35
    36             System.out.println(score);//80
    37             /*
    38              * 发生隐藏现象,访问的是内部类的score属性,若要访问外部类的score属性,则需要这样的格式:
    39              *     外部类.this.成员变量
    40              *    外部类.this.成员方法
    41              */
    42             System.out.println(Outer.this.score);//100
    43
    44         }
    45     }
    46 }

代码示例:

 1 public class InnerClassDemo {
 2     public static void main(String[] args) {
 3
 4         //必须先创建一个外部类对象,才能创建一个成员内部类的对象
 5         Outer outer = new Outer("Liming", 20);
 6         Outer.Inner inner = outer.new Inner();
 7         inner.show();
 8     }
 9 }
10
11 class Outer{
12     private static String name;
13     private int age;
14     private int score = 100;
15
16     public Outer(String name, int age) {
17         this.name = name;
18         this.age = age;
19
20         //外部类访问内部类的成员,必须先创建一个成员内部类的对象
21         Inner inner = new Inner();
22         System.out.println(inner.score);//80
23     }
24
25     /*
26      * 内部类也可以有一些访问控制修饰符,如private,public,protected
27      *     此处采用默认访问权限,只能在同一个包中访问
28      */
29     class Inner{
30         int score = 80; //static int score = 80; 直接报错,编译不通过
31         void show(){
32             //内部类能够访问外部类的静态和私有成员
33             System.out.println(name);//Liming
34             System.out.println(age);//20
35
36             System.out.println(score);//80
37             /*
38              * 此处发生隐藏现象,访问的是内部类的score属性,若要访问外部类的score属性,则需:
39              *    外部类.this.成员变量
40              *    外部类.this.成员方法
41              */
42             System.out.println(Outer.this.score);//100
43
44         }
45     }
46 }

注意:这段代码的执行顺序是:调用Outer构造器创建外部类对象 > 创建内部类对象 > 执行show方法 。

2.静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。

  • 静态内部类是不需要依赖于外部类的,即是在没有创建外部类对象的情况下也可以创建静态内部类的对象
  • 静态内部类只能访问外部类的静态成员,而不能访问外部类的非静态成员

代码示例:

 1 public class InnerClassDemo {
 2     public static void main(String[] args) {
 3
 4         //直接创建静态内部类的对象,不依赖于外部类
 5         Inner inner = new Inner();
 6         inner.show();
 7     }
 8 }
 9
10 class Outer{
11     public static String name = "Liming";
12     public int age = 20;
13
14     static class Inner{
15         void show(){
16             //静态内部类只能访问外部类的静态成员而不能访问非静态成员
17             System.out.println(name);//Liming
18 //            System.out.println(age); 直接报错,编译不通过
19         }
20     }
21
22 }

3.局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

代码示例:

 1 class People{
 2     public People() {
 3
 4     }
 5 }
 6
 7 class Man{
 8     public Man(){
 9
10     }
11
12     public People getWoman(){
13         class Woman extends People{   //局部内部类
14             int age =0;
15         }
16         return new Woman();
17     }
18 }

注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

4.匿名内部类

匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。

  • 匿名内部类不能有访问修饰符和static修饰符
  • 匿名内部类是唯一一种没有构造器的类,因为连类名都没有何来构造器
  • 匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调,“new 匿名内部类”,这个类首先是要存在的。

代码示例:

 1 scan_bt.setOnClickListener(new OnClickListener() {
 2
 3             @Override
 4             public void onClick(View v) {
 5                 // TODO Auto-generated method stub
 6
 7             }
 8         });
 9
10         history_bt.setOnClickListener(new OnClickListener() {
11
12             @Override
13             public void onClick(View v) {
14                 // TODO Auto-generated method stub
15
16             }
17         });

二.深入理解Java内部类

1.静态内部类与非静态内部类

  • 静态内部类的创建不依赖于外部类,并且可以定义静态成员
  • 非静态内部类依赖于外部类的创建而创建,并且不能定义任何的静态成员

解析:静态内部类不依赖于任何外部类,所以可以有自己的静态成员;而非静态内部类必须通过外部类创建,它的存在严格依赖于外部类,所以不能有自己的静态成员。

  • 静态内部类只能访问外部类的静态成员,而不能访问任何非静态成员

解析:静态内部类不依赖于外部类,所以可在未创建外部类对象的情况下创建静态内部类的实例对象,而没有外部类对象就不能访问外部类的非静态成员。

2.成员内部类可以无条件访问外部类的成员

之前,我们已经讨论过了成员内部类可以无条件访问外部类的成员,那具体究竟是如何实现的呢?下面通过反编译字节码文件看看究竟。

  • 事实上,编译器在进行编译的时候,会将内部类单独编译成一个字节码文件,命名方式为:外部类$内部类.class 或 外部类$X.class(X为一个整数)。

通过反编译工具,将内部类的字节码文件进行反编译,得到:

 1 E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner
 2 Compiled from "Outter.java"
 3 public class com.cxh.test2.Outter$Inner extends java.lang.Object
 4   SourceFile: "Outter.java"
 5   InnerClass:
 6    #24= #1 of #22; //Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes
 7 t2/Outter
 8   minor version: 0
 9   major version: 50
10   Constant pool:
11 const #1 = class        #2;     //  com/cxh/test2/Outter$Inner
12 const #2 = Asciz        com/cxh/test2/Outter$Inner;
13 const #3 = class        #4;     //  java/lang/Object
14 const #4 = Asciz        java/lang/Object;
15 const #5 = Asciz        this$0;
16 const #6 = Asciz        Lcom/cxh/test2/Outter;;
17 const #7 = Asciz        <init>;
18 const #8 = Asciz        (Lcom/cxh/test2/Outter;)V;
19 const #9 = Asciz        Code;
20 const #10 = Field       #1.#11; //  com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/t
21 est2/Outter;
22 const #11 = NameAndType #5:#6;//  this$0:Lcom/cxh/test2/Outter;
23 const #12 = Method      #3.#13; //  java/lang/Object."<init>":()V
24 const #13 = NameAndType #7:#14;//  "<init>":()V
25 const #14 = Asciz       ()V;
26 const #15 = Asciz       LineNumberTable;
27 const #16 = Asciz       LocalVariableTable;
28 const #17 = Asciz       this;
29 const #18 = Asciz       Lcom/cxh/test2/Outter$Inner;;
30 const #19 = Asciz       SourceFile;
31 const #20 = Asciz       Outter.java;
32 const #21 = Asciz       InnerClasses;
33 const #22 = class       #23;    //  com/cxh/test2/Outter
34 const #23 = Asciz       com/cxh/test2/Outter;
35 const #24 = Asciz       Inner;
36
37 {
38 final com.cxh.test2.Outter this$0;
39
40 public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);
41   Code:
42    Stack=2, Locals=2, Args_size=2
43    0:   aload_0
44    1:   aload_1
45    2:   putfield        #10; //Field this$0:Lcom/cxh/test2/Outter;
46    5:   aload_0
47    6:   invokespecial   #12; //Method java/lang/Object."<init>":()V
48    9:   return
49   LineNumberTable:
50    line 16: 0
51    line 18: 9
52
53   LocalVariableTable:
54    Start  Length  Slot  Name   Signature
55    0      10      0    this       Lcom/cxh/test2/Outter$Inner;
56
57
58 }

第11行~35行是常量池的内容,这里需要注意的是第38行的内容:

final com.cxh.test2.Outter this$0;

  这行是一个指向外部类对象的指针,看到这里想必大家豁然开朗了。也就是说编译器会默认为成员内部类添加了一个指向外部类对象的引用,那么这个引用是如何赋初值的呢?下面接着看内部类的构造器:

public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);

  从这里可以看出,虽然我们在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的Outter this&0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。

3.局部内部类和匿名内部类只能访问局部final变量

关于这个问题,我们先来看一段代码:

 1 /*
 2  * 局部内部类和匿名内部类只能访问局部final变量
 3  */
 4 public class InnerClass {
 5
 6     public static void main(String[] args) {
 7         test(11);
 8     }
 9
10     public static void test(final int a ){
11         final double b = 11.234;
12         //定义一个匿名内部类
13         new Thread(){
14             public void run() {
15                 System.out.println(a);
16                 System.out.println(b);
17             }
18
19         }.start();
20     }
21 }

编译后生成了两个文件,即匿名内部类单独生成一个文件:InnerClass$1.class

上段代码中,如果把变量a和b前面的任一个final去掉,这段代码都编译不过。我们可以考虑这样一个问题: 当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用“复制”的方法来解决这个问题。

详细介绍请参看:http://www.cnblogs.com/dolphin0520/p/3811445.html

4.静态内部类特殊的地方

  • 静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。
  • 静态内部类是不持有指向外部类对象的引用的,可以反编译静态内部类的class文件,发现是没有Outter this&0引用的。

三.内部类的作用和使用场景

关于内部类的作用和使用场景,这里总结了以下两点:

  • 每个内部类都能独立的继承一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。所以,内部类使得多重继承的解决方案变得完整
  • 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏
  • 其中,匿名内部类常用于事件监听和多线程程序

全文参考自:http://www.cnblogs.com/dolphin0520/p/3811445.html

时间: 2024-11-05 12:11:31

那些java中的内部类的相关文章

Java中的内部类(成员内部类、静态内部类、局部内部类、匿名内部类)

Java中的内部类(成员内部类.静态内部类.局部内部类.匿名内部类) 神话丿小王子的博客主页 我们先看这样一段话:人是由大脑.肢体.器官等身体结果组成.而组成我们人体的心脏它也有自己的属性和行为(血液.跳动),显然,此处不能单方面用属性或者方法表示一个心脏了,而需要一个类,但是这个类要依赖于人这个类存在(因为心脏不能脱离人体而存活,当然不考虑现今社会的发达科技,只从正常情况下来说),所心脏以需要写在人类的内部.内部类就相当于其中的某个器官之一. 首先看看什么是内部类:在类的内部定义另一个类.没错

【转 Java 中的内部类和匿名类

 Java 中的内部类和匿名类 2008-10-16 13:47:41 标签:Java 内部类 匿名类 休闲 职场 Java 中的内部类和匿名类* 为什么需要内部类? Java 内部类有什么好处?为什么需要内部类? 首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口.由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能. 不过你可能要质疑,更改一

java中的内部类和匿名内部类的使用

部分内容参考http://www.imooc.com/ 内部类: 什么是 Java 中的内部类 问:什么是内部类呢? 答:内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. 问:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!! 答:内部类的主要作用如下: 1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类 2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据 3. 内

【转】Java中的内部类和匿名类

Java内部类(Inner Class),类似的概念在C++里也有,那就是嵌套类(Nested Class),乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部类身上的确是用心良苦.学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构.下面从以下几个方面来介绍: 第一次见面 public interface Contents {    int value();} public interfac

浅谈Java中的内部类

最近在讲Java中的内部类,感觉内部类实际上对于初学者用得不多,那么内部类简单的说,其实就是在一个类的内部定义的类. 按照定义的情况分为:成员内部类,局部内部类,静态内部类,匿名内部类. 成员内部类,就是定义一个当作类的成员变量的类. 局部内部类,就是在类里面定义一个当作局部变量的类,通常是在方法或代码块中使用. 静态内部类,就是在类中定义一个静态变量,而该静态变量是一个类. 匿名内部类,通常是在创建一个接口对象,该对象是在创建对象的同时实现接口,而该接口的实现只是在此处使用一次,所以不需要定义

Java中的内部类怎么用

一.为什么需要内部类?java内部类有什么好处?为什么需要内部类? 首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口.由于内部类对外部类的所有内容都是可访问的,所以这 样做可以完成所有你直接实现这个接口的功能. 不过你可能要质疑,更改一下方法的不就行了吗? 的确,以此作为设计内部类的理由,实在没有说服力. 真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++

Java基础(十五):Java 中的内部类

问:什么是内部类呢? 答:内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. 问:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!! 答:内部类的主要作用如下: 1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类 2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据 3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便 问:内部类有几种呢? 答:内部

Java中的内部类(回调)

一.内部类的主要作用如下: 1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类 2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据 3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便. 二.内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 匿名内部类. 三.1,定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );

Java中的 内部类(吐血总结)

1.内部类分类 1)普通内部类 类的实例相关,可以看成是一个实例变量.内部类的类名由 "外部类.内部类" 确定. 普通内部类不能声明 static相关的变量或方法.内部类可以直接访问外部类的所有成员(包括 private成员),隐式或显式(外部类.this).而外部类可以 new 内部类,实例相关的可以直接 new,static 相关(类相关)需要使用实例的引用去 引用.new.内部类 class 前可以添加 private 与 protected 表示只对其外部类或其子类有访问权限.