1、第12章 内部类,以前看到过这样一条新闻,说在一个鸡蛋中发现里面有一个小鸡蛋,这个小鸡蛋同样有蛋清和蛋黄,当时感觉很奇怪的。后来学习Java后,发现在Java中竟然也有这种奇怪的事,那就是内部类。内部类就好像刚提到的鸡蛋中的小鸡蛋一样,包含在另一个类中的。通过本章的学习,会发现内部类还有好多和该小鸡蛋相似的地方。读者通过本章的学习,应该完成如下几个目标。 了解什么是非静态内部类和如何进行非静态内部类和外部类之间的访问。 了解什么是局部内部类和如何进行局部内部类和外部类之间的访问。 了解什么是静态内部类和如何进行静态内部类和外部类之间的访问。 了解什么是匿名内部类和如何进行匿名内部类和外部类之间
2、的访问。,12.1 非静态内部类,当一个类作为另一个类的非静态成员,则这个类就是一个非静态内部类。在本节中就来学习如何创建和使用非静态内部类,同时也来讲解如何在内部类中访问外部类和在外部类中如何访问内部类。,12.1.1 创建非静态内部类,创建非静态内部类是很容易的,只需要定义一个类让该类作为其他类的非静态成员。该非静态内部类和成员变量或者成员方法没有区别,同样可以在非静态内部类前面加可以修饰成员的修饰符。 创建非静态内部类的基本语法如下所示。 class Wai class Nei /内部类成员 /外部类成员 ,12.1.2 在外部类中访问内部类,在内部类的程序中,是经常会进行外部类和内部类
3、之间访问的。在外部类中访问内部类是很容易的,只要把内部类看成一个类,然后创建该类的对象,使用对象来调用内部类中的成员就可以了。 【范例】示例代码是一个在外部类中访问内部类的程序。 01 class Wai 02 03 class Nei /创建非静态内部类 04 05 int i=5; /内部类成员 06 07 public void myVoid() /外部类成员 08 09 Nei n=new Nei(); /创建一个内部类对象 10 int ii=n.i; /访问内部类成员 11 System.out.println(“内部类的变量值为:“+ii); 12 13 14 public cl
4、ass NeiBuLei2 15 16 public static void main(String args) 17 18 Wai w=new Wai(); 19 w.myVoid(); 20 21 ,从程序的第16行主方法讲起,在main方法中,首先创建一个外部类对象,然后访问外部类的成员方法。在外部类的成员方法中,创建了一个内部类对象,然后使用内部类对象调用内部类的成员变量,从而得到结果。编译该程序将产生三个class文件,分别是主类、外部类和内部类。内部类产生的class文件的名称为Wai$Nei.class,在该名称中可以区分该内部类到底是哪一个类的内部类。,12.1.3 在外部类外
5、访问内部类,不但可以在外部类中访问内部类,还可以在外部类外访问内部类。读者肯定会觉得非常难的,要想访问类成员中的成员怎么访问呢。其实在Java中,是很容易做到的。在外部类外访问内部类的基本语法如下所示。 Wai.Nei wn=new Wai().new Nei(); 使用该方法就能够创建一个内部类对象,使用该内部类对象就可以访问内部类的成员。该方法是不容易理解的,该方法也是可以分为两条语句的。 Wai w=new Wai(); Wai.Nei wn=w.new Nei(); 这样就很容易理解了。首先是创建一个外部类的对象,然后让该外部类对象调用创建一个内部类对象。,一个在外部类外访问内部类的程
6、序,01 class Wai 02 03 class Nei /创建非静态内部类 04 05 int i=5; /内部类成员 06 int ii=6; 07 08 09 public class NeiBuLei3 10 11 public static void main(String args) 12 13 Wai.Nei wn1=new Wai().new Nei(); 14 Wai w=new Wai(); 15 Wai.Nei wn2=w.new Nei(); 16 System.out.println(“内部类中的变量i的值为:“+wn1.i); 17 System.out.pri
7、ntln(“内部类中的变量ii的值为:“+wn2.ii);图12-3 在外部类外访问内部类 18 19 ,在示例代码中使用了两种方法来从外部类外访问内部类。在外部类外访问内部类时,是不能够直接创建内部类对象的,因为内部类只是外部类的一个成员。所以要想创建内部类对象,首先要创建外部类对象,然后以外部类对象为标识来创建内部类对象。,12.1.4 在内部类中访问外部类,在内部类中访问外部类,就像所有的同一个类中成员互相访问一样,这样是没有限制的,包括将成员声明为private私有的。 【范例】示例代码是一个在内部类中访问外部类的程序。 示例代码 01 class Wai 02 03 int i=8;
8、 /外部类成员变量 04 class Nei /创建非静态内部类 05 06 public void myVoid() /内部类成员变量 07 08 System.out.println(“外部类中的成员变量值为:“+i); 09 10 11 12 public class NeiBuLei5 13 14 public static void main(String args) 15 16 Wai w=new Wai(); /创建外部类对象 17 Wai.Nei wn2=w.new Nei();/创建内部类对象 18 wn2.myVoid(); /调用内部类中成员 19 20 ,在示例代码中,
9、在内部类中定义了一个myVoid来访问外部类中的成员变量i。可以看到从内部类中访问外部类是非常容易的,不需要添加任何内容,就像成员方法间调用一样。有些读者学习完示例代码后,会有疑问,如果外部类中也有一个成员变量i怎么办呢?读者可以进行实验,从结果中可以看到得到的是内部类成员变量的值。下面通过示例代码解决这个问题。,一个在内部类和外部类中具有同名称变量访问的程序,01 class Wai 02 03 int i=8; /外部类成员变量 04 class Nei /创建非静态内部类 05 06 int i=9; 07 Wai ww=new Wai(); 08 public void myVoid(
10、) /内部类成员变量 09 10 System.out.println(“内部类中的成员变量值为:“+i); 11 System.out.println(“外部类中的成员变量值为:“+ww.i); 12 13 14 15 public class NeiBuLei6 16 17 public static void main(String args) 18 19 Wai w=new Wai(); /创建外部类对象 20 Wai.Nei wn2=w.new Nei(); /创建内部类对象 21 wn2.myVoid(); /调用内部类中成员 22 23 ,在本程序中的第3行定义了一个外部类的成员
11、变量,第6行定义了一个内部类的成员变量,这两个成员变量的名称是相同的。而在内部直接访问时,将访问的是内部类的成员变量。要想访问外部类成员变量,就需要首先创建一个外部类对象,然后使用该对象调用外部类成员变量。,12.2 局部内部类,在上一节中介绍了非静态成员内部类,以及如何对非静态成员内部类进行操作。在本节中就来学习局部内部类的知识,通过非静态成员内部类的学习,是很容易来学习局部内部类的。从名称就可以看出局部内部类是作为一个类的局部变量来定义的。,12.2.1 创建局部内部类,局部内部类的作用范围是和局部变量的作用范围相同的,只在局部中起作用,所以对局部内部类进行访问时,只能在该局部内部类的作用
12、范围内。 【范例】示例代码是一个创建和访问局部内部类的程序。 示例代码 01 class Wai 02 03 public void myVoid() 04 05 class Nei /定义一个局部内部类 06 07 int i=5; /局部内部类的成员变量 08 09 Nei n=new Nei(); 10 System.out.println(“局部内部类的成员变量为:“+n.i); 11 12 13 public class NeiBuLei8 14 15 public static void main(String args) 16 17 Wai w=new Wai(); /创建外部类
13、对象 18 w.myVoid(); /调用内部类中成员 19 20 ,在本程序中定义了一个局部内部类,并进行了对该局部内部类的访问。对该内部类进行访问必须在该内部类所在的方法中通过创建内部类对象来进行访问。这是因为这里的内部类是作为局部成员的形式出现的,只能在它所在的方法中进行调用。,12.2.2 在局部内部类中访问外部类成员变量,在局部内部类中访问外部类成员变量是很容易实现的,并不需要进行过多操作。在局部内部类中可以直接调用外部类的成员变量。 【范例】示例代码是一个在局部内部类中访问外部类成员变量的程序。 示例代码 01 class Wai 02 03 int i=9; /定义一个外部类的成
14、员变量 04 public void myVoid() 05 06 class Nei /定义一个局部内部类 07 08 public void myNeiVoid() 09 10 System.out.println(“外部类的成员变量值为:“+i);/访问外部类的成员变量 11 12 13 Nei n=new Nei(); /创建内部类对象 14 n.myNeiVoid(); /调用内部类中的成员方法 15 16 17 public class NeiBuLei9 18 19 public static void main(String args) 20 21 Wai w=new Wai(
15、); /创建外部类对象 22 w.myVoid(); /调用内部类中成员 23 24 ,在示例代码中定义了一个局部内部类,在该局部内部类中定义了一个方法来访问外部类的成员变量。从运行结果中可以看出在内部类中可以成功访问外部类的成员变量。在该程序中同样需要注意的是,对内部类进行访问需要和内部类在同一方法中。,12.2.3 在局部内部类中访问外部类的局部变量,和访问外部类的成员变量不同,在局部内部类中访问外部类中和局部内部类在同一局部的局部变量是不能够直接访问的。 【范例】示例代码是一个错误的访问外部类局部变量的程序。 示例代码 01 class Wai 02 03 public void myV
16、oid() 04 05 int i=9; /定义一个外部类的局部变量 06 class Nei /定义一个局部内部类 07 08 public void myNeiVoid() 09 10 System.out.println(“外部类的局部变量值为:“+i);/访问外部类的成员变量 11 12 13 Nei n=new Nei(); /创建内部类对象 14 n.myNeiVoid(); /调用内部类中的成员方法 15 16 17 public class NeiBuLei10 18 19 public static void main(String args) 20 21 Wai w=new
17、 Wai(); /创建外部类对象 22 w.myVoid(); /调用内部类中成员 23 24 ,运行该程序是会发生错误的,错误信息为“从内部类中访问局部变量i;需要被声明为最终类型”。在局部内部类中访问外部类的局部变量是不能够访问普通的局部变量的,必须将该局部变量声明为final。,12.2.4 静态方法中的局部内部类,局部内部类定义在非静态方法和静态方法中是不同的,在前面的两小节中都是将局部内部类定义在非静态方法中,在本节中就来学习静态方法中定义局部内部类的情况。在静态方法中定义的局部内部类要想访问外部类中的成员,该程序必须是静态成员。静态成员和非静态成员之间的访问是不变的。 注意:在静态
18、方法中定义的局部内部类要想访问外部类中的成员,该程序必须是静态成员。静态成员和非静态成员之间的访问是不变的。,一个错误的访问成员的程序,01 class Wai 02 03 int i=3; 04 public static void myVoid() 05 06 class Nei /定义一个局部内部类 07 08 public void myNeiVoid() 09 10 System.out.println(“外部类的局部变量值为:“+i);/访问外部类的成 /员变量 11 12 13 Nei n=new Nei(); /创建内部类对象 14 n.myNeiVoid(); /调用内部类中
19、的成员方法 15 16 17 public class NeiBuLei12 18 19 public static void main(String args) 20 21 Wai w=new Wai(); /创建外部类对象 22 w.myVoid(); /调用内部类中成员 23 24 ,运行该程序是会发生错误的,错误信息为“无法从静态上下文中引用非静态变量i”。该程序主要错误原因是第三行定义的外部类变量是一个非静态成员变量。而本程序中定义的局部变量是定义在静态的方法中,所以是不能够正常访问的。如果想正常访问,就需要将程序修改成示例代码12-13的形式。,12.3 静态内部类,在第一节中已经
20、讲解了非静态内部类,在本节中就来讲解什么是静态内部类。静态内部类就是在外部类中扮演一个静态成员的角色。在本节中就来学习如何创建静态内部类和关于静态内部类访问的问题。,12.3.1 创建静态内部类,创建静态内部类的形式和创建非静态内部类的形式很相似的,只是需要将该内部类使用static修饰成静态的形式。使用static修饰类,这在正常类中是不可能的。定义静态内部类的语法如下所示。 class Wai static class Nei /内部类成员 /外部类成员 ,12.3.2 在外部类中访问静态内部类,在外部类中访问静态内部类和在外部类中访问非静态内部类一样的,只需要从成员间访问的角度就可以考虑
21、到这一点。 【范例】示例代码是一个在外部类中访问静态内部类的程序。 示例代码 01 class Wai 02 03 static class Nei /创建静态内部类 04 05 int i=5; /内部类成员 06 07 public void myVoid() /外部类成员 08 09 Nei n=new Nei(); /创建一个内部类对象 10 int ii=n.i; /访问内部类成员 11 System.out.println(“静态内部类的变量值为:“+ii); 12 13 14 public class NeiBuLei15 15 16 public static void mai
22、n(String args) 17 18 Wai w=new Wai(); 19 w.myVoid(); 20 21 ,12.3.3 在外部类外访问静态内部类,通过上一小节的学习,知道在外部类中访问静态内部类和访问非静态内部类是相同的,但是在外部类中访问静态内部类和非静态内部类就不再相同。因为静态内部类是外部类的静态成员,静态成员是不需要外部类对象而存在的,所以在外部类外,对静态内部类进行访问时是不需要创建外部类对象的。 注意:因为静态内部类是外部类的静态成员,静态成员是不需要外部类对象而存在的,所以在外部类外,对静态内部类进行访问时是不需要创建外部类对象的。,12.4 匿名内部类,在所有的内
23、部类中最难的就应该是匿名内部类。匿名内部类从名字上看就知道是没有类名的类。在本节中就来介绍如何创建匿名内部类和如何进行关于匿名内部类的访问问题。,12.4.1 创建匿名内部类,在创建匿名内部类中将使用到继承父类或者实现接口的知识,匿名内部类是没有名字的,所以在创建匿名内部类时同时创建匿名内部类的对象。创建匿名内部类的语法格式如下所示。 new NeiFather() /匿名内部类 ; 在创建匿名内部类的语法中,NeiFather是匿名内部类继承的父类的类名,使用new同时创建了匿名内部类的对象。在匿名内部类中可以重写父类中的方法,也可以定义自己的方法。,12.4.2 匿名内部类的初始化,匿名内
24、部类是没有名称的,所以匿名内部类也是不可能具有构造器的,这就出现一个问题。有时在匿名内部类中也是要定义成员变量的,但是该成员变量应该放在什么位置呢。这里的解决方法就是创建一个非静态语句块,将所有的初始化的成员变量都放在该非静态语句块中。这样在匿名内部类中的方法中就可以来调用这些成员变量。,12.5 综合练习,在下面的程序中运行哪一条语句可以正常运行。 01 public class LianXi1 02 03 static int h=1; 04 private int i=2; 05 public void myVoid() 06 07 final int j=3; 08 int k=4;
25、09 class Nei 10 11 public void myNeiVoid() 12 13 /System.out.println(h); 14 /System.out.println(i); 15 /System.out.println(j); 16 /System.out.println(k); 17 18 19 Nei n=new Nei(); 20 n.myNeiVoid(); 21 22 public static void main(String args) 23 24 LianXi1 lx=new LianXi1(); 25 lx.myVoid(); 26 27 ,在该程序的第13行到第16行注释了4条访问程序中变量的语句,释放这些语句,可以发现访问h、i和j都是能够正常访问的,但是访问变量k就会发生编译异常。这是因为在局部内部类中,只能访问方法中的最终变量。,12.6 小结,在本章讲解了Java中的内部类,内部类包括非静态内部类、局部内部类、静态内部类和匿名内部类,并分别对这些内部类进行了讲解。在讲解每一种内部类时,首先讲解如何创建该内部类,并且讲解了如何对内部类进行访问。,