欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

79.内部类

程序员文章站 2022-03-02 11:28:30
...

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

 

在实际的开发中,比较常用到的,一方面是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成员。