79.内部类

 今天来说说一个非常特殊的类——内部类。

 

在实际的开发中,比较常用到的,一方面是Java图形开发的事件处理中,另一方面,在Spring应用开发中,会大量用到,在Spring框架本身,就大量使用了匿名内部类,通过回调方法实现了一些模板应用。

 

下面来具体看看内部类的内容。

 

所谓内部类,就是定义在一个类内部的类。


内部类的概念是在JDK1.1中被引入的。引入内部类,主要有以下一些考虑:


内部类对象能访问它所处类的私有数据;
内部类能够隐藏起来不为同一个包中的其他类访问;
匿名内部类可以方便的用在回调方法(callback method)中,典型应用是图形编程中的事件处理。 


1 内部类定义

 

 

 

内部类有时也称为“嵌套类(nested class)”,是定义在一个类内部的类。

 

 

 

这里所谓的“类的内部”,指的是在类定义的两个大括号之间,如下:
public class OuterClass{
 //可以在这个类的内部的任何地方定义内部类
}


在这个“类的内部”,包括类中的任何位置,甚至方法体中也可以定义内部类。注意,下面的放在同一个程序文件A.java中的类B不是类A的内部类,这是刚开始接触内部类的程序员比较容易引起混乱的地方:
public class A{
 //statements
}
class B{
 //statements
}


这两个类虽然写在用一个文件中,但它们之间是两个独立的类,它们之间并没有什么联系。将这个类文件编译之后,会产生两个class文件:A.class和B.class。


可以将一个内部类定义成一个静态的内部类,只需要在内部类定义前面加上static关键字。


为方便表述,我们将封装类相对于“内部类”称为“外部类”。

public class Outer1 {
 private int size;

 // 定义一个内部类,名为 "Inner"
 public class Inner {
  public void doStuff() {
   // 内部类可以访问外部类的私有属性
   size++;
  }
 }

 public void testTheInner() {
  Inner i = new Inner();
  i.doStuff();
 }
}


在这个Outer1类中,定义了一个内部类,这个内部类可以访问外部类的私有属性。


编译这个Outer1.java,将会产生两个class文件:Outer1.clas和Outer1$Inner1.class,前面一个类文件是Outer1类文件,后面一个是内部类类文件,它用“$”来分割开内部类和外部类。


在Outer类范围以外的其他应用中,实例化内部类必须首先有一个外部类实例存在,然后通过外部类实例来实例化内部类,实例化内部类的两种方法:


法一:
 Outer.Inner in=new Outer().new Inner();


法二:
 Outer o=new Outer();
 Outer.Inner I= o.new Inner();


当然,如果内部类是static的,也可以用下面方法:
 Outer.Inner in=new Outer.Inner();


我们来看一下,假设我们现在需要在另一个应用程序中使用刚才定义的类Outer1的内部类Inner,我们该如何来实现。为了更好的演示它,我们对上面的Outer类稍作修改,加入了一个用于获得Outer1类的私有属性size的方法getOuterSize():

public class Outer1 {
 private int size;

 // 定义一个内部类,名为 "Inner"
 public class Inner {
  public void doStuff() {
   // 内部类可以访问外部类的私有属性
   size++;
  }

  public int getOuterSize() {
   return size;
  }
 }

 public void testTheInner() {
  Inner i = new Inner();
  i.doStuff();
 }
}


然后我们再来看如何在另外一个类中使用Outer1的内部类:

public class TestInner {
 public static void main(String[] args) {
  Outer1.Inner oi = new Outer1().new Inner();
  oi.doStuff();
  System.out.println(oi.getOuterSize());
 }
}
编译

并运行这个程序,将在控制台上打印出size的值:1。

和一般的类不同,内部类可以使用protected和private来修饰,以限制在它的外部类以外的地方对它的访问,比如,上面的Outer1中的Inner内部类就可以定义成如下的样子:
public class Outer1 {
  private int size;
  /* 定义一个内部类,名为 "Inner" */
  private class Inner {
    public void doStuff() {
      // 内部类可以访问外部类的私有属性
      size++;
    }
    public int getOuterSize()
    {
     return size;
    }
  }
  public void testTheInner() {
    Inner i = new Inner();
    i.doStuff();
  }
}


此时,就不能在Outer1的范围之外使用Inner类了。


2 局部内部类

 

 

 

类的名字只能在定义的范围内使用,除非使用有效的全名。

 

 


Inner类也可以定义在方法的内部,或者类的一个自由块中,此时,内部类是一个局部的内部类,只能在方法体或者自由块中使用。

 

如果内部类被定义在方法中,那么方法中final类型的局部变量,都可以被Inner类的方法访问。定义在方法中的内部类只能在方法内被使用。

 

例如:

public class Outer {
 public void test(int i) {
  class LocalClass {
   public void localTest() {
    System.out.println("局部内部类的方法被调用");
    // System.out.println("i="+i); //错
    // 如果需要使用局部变量i,必须将它声明为final
   }
  }

  LocalClass lc = new LocalClass();
  lc.localTest();
 }

 public static void main(String[] args) {
  Outer o = new Outer();
  o.test(1);
 }
}


在这个类Outer中,定义了一个方法test(),在它的方法体内定义了一个局部内部类,这个内部类只能在这个方法体内使用。

 

如果在内部类中需要使用方法的局部变量(如方法的参数i),那么,需要将这个局部变量定义final的,否则,将会出现类似下面的错误(将i++前的注释去掉后的错误):

Outer.java:10: local variable i is accessed from within inner class; needs to be declared final
            System.out.println("i="+i);;
                                ^
1 error


如果将方法test()的参数i加上final就可以了。


3 匿名内部类

 

 

 

内部类的另外一个奇特的地方就是,可以不用给它指定一个类名,就可以直接拿来使用,这个用法经常用在图形界面的事件处理中。

 

 

 

4 内部类特性

 

 

 

内部类是一个复杂的应用,除了上面提到的一些特点外,它还有以下一些特点:

 

 


Inner class可以声明为抽象类 ,因此可以被其它的内部类继承。也可以声明为final的。


和外层类不同,Inner class可以声明为private或protected。


Inner class 可以声明为static的,但此时就不能再使用外层封装类的非static的成员变量。


非static的内部类中的成员不能声明为static的,只有在顶层类或static的内部类中才可声明static成员。

 

 

你可能感兴趣的:(内部类)