一、内部类

在Java语言中,可以把一个类定义到另外一个类的内部,在类里面的这个类就叫做内部类,外面的类叫做外部类。在这种情况下,这个内部类可以被看成是外部类的一个成员(与类的属性和方法类似)。与之不同的好有一种类被成为顶层类(top-level),指的是类定义代码不嵌套在其他类定义中的类。

①类中套类,外层的叫外部类,内层的叫内部类
②内部类通常只服务于外部类,对外不具备可见性
③内部类对象通常在外部类中被创建
④内部类可以直接访问外部类的成员(包括私有的)
⑤内部类对象中有一个隐式的引用指向了创建它的外部类对象

需要注意的是嵌套类(Nested Class)与内部类(Inner Class)类似,只是嵌套类是C++的说法,而内部类是Java的说法。内部类可以分为很多种,主要有以下4种:静态内部类(static inner class)成员内部类(member inner class)局部内部类(local inner class)和匿名内部类(anonymous inner class)。它们的定义方法如下:

class outerClass() {
	static class innerClass{} //静态内部类
}
class outerClass() {
	class innerClass{} //成员内部类(普通内部类)
}
class outerClass() {
	public void memberFunction() {
		class innerClass{} //局部内部类
	}
}
public class MyFrame extends Frame{ //外部类
	public MyFrame() {
		addWindowListener(new WindowAdapter(){// 匿名内部类
			public void windowClosing(WindowEvent e) {
				dispose();
				System.exit(0);
			}
		});
	}
}

Java中规定,内部类只能访问外部类中的成员变量,不能访问方法中定义的变量,如果要访问方法中的变量,就要把方法中的变量声明为final(常量)的,因为这样可以使变量全局化,就相当于是在外部定义的,而不是在方法里定义的。

小结:
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。因为Java不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整


二、静态内部类

静态内部类是指被声明为static的内部类,它可以不依赖于外部类实例而被实例化,而通常内部类需要在外部类实例化后才能实例化。静态内部类不能与外部类有相同的名字,不能访问外部类的普通成员变量,只能访问外部类中的静态成员和静态方法(包括私有类型)


三、成员内部类

一个静态内部类去掉“static”关键字,就成为成员内部类。成员内部类为非静态内部类,它可以自由的引用外部类的属性和方法,无论这些属性和方法是静态的还是非静态的。但是它与一个实例绑定在了一起,不可以定义静态的属性和方法。只有在外部的类被实例化后,这个内部类才能被实例化。需要注意的是,非静态内部类不能有静态成员。


四、局部内部类

局部内部类指的是定义在一个代码块内的类,它的作用范围为其所在的代码块,是内部类中最少使用到的一种类型。局部内部类像局部变量一样,不能被public、protected、private以及static修饰,只能访问方法中定义为final类型的局部变量


五、匿名内部类

匿名内部类是一种没有类名的内部类,不使用关键字class、extends、implements,没有构造函数,它必须继承(extends)其他类或实现其他接口。 匿名内部类的好处是代码更加简洁、紧凑,但带来的问题是易读性下降。它一般应用于GUI(Graphical User Interface,图形用户界面)编程中实现事件处理等。在使用匿名内部类时,有以下几个原则:
(1)匿名内部类不能有构造函数。
(2)匿名内部类不能定义静态成员、方法和类。
(3)匿名内部类不能是public、protected、private、static。
(4)只能创建匿名内部类的一个实例。
(5)一个匿名内部类一定是在new的后面。这个匿名内部类必须继承一个父类或实现一个接口。
(6)因为匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。

①想创建一个类的对象,并且该类只被创建一次对象,该类不必命名,称之为匿名内部类。
说明:该类必须是子类(继承父类,实现接口)
匿名内部类中访问外部的数据,该数据必须为final的
③内部类与匿名内部类都有独立的.class
说明:(1)内部类有.class文件→外部类名$内部类名.class(2)匿名内部类也有独立.class文件→外部类名$1.class …外部类名$2.class …

匿名内部类的两种实现方法:
(1)继承一个类,重写其方法
(2)实现一个接口(可以是多个),实现其方法

示例1:不使用匿名内部类来实现抽象方法

package com.haobi;
/*
 * 不使用匿名内部类来实现抽象方法
 */
abstract class Person{
	public abstract void eat();
}
class Child extends Person{
	public void eat() {
		System.out.println("eat something");
	}
}
public class Test1 {
	public static void main(String[] args) {
		Person p = new Child();//向上造型
		p.eat();
	}
}

程序输出结果如下:
eat something

分析:用Child类继承了Person类,然后实现了Child类的一个实例,将其向上转型为Person类的引用。(如果Child类只使用一次,则独立编写为类会很麻烦)

示例2:匿名内部类的基本实现

package com.haobi;
/*
 * 匿名内部类的基本实现
 */
abstract class Person{
	public abstract void eat();
}
public class Test2 {
	public static void main(String[] args) {
		Person p = new Person() {
			@Override
			public void eat() {
				System.out.println("eat something");	
			}
		};
		p.eat();
	}
}

程序输出结果如下:
eat something

分析:直接将抽象类Person的方法在大括号中实现了,这样便可省略一个类的书写。

示例3:在接口上使用匿名内部类

package com.haobi;
/*
 * 在接口上使用匿名内部类
 */
interface Person{
	public void eat();
}
public class Test3 {
	public static void main(String[] args) {
		Person p = new Person() {
			@Override
			public void eat() {
				System.out.println("eat something");
			}			
		};
		p.eat();
	}
}

程序输出结果如下:
eat something

分析:由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现。

匿名内部类最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口,示例如下:

示例4:Thread类的匿名内部类实现

package com.haobi;
/*
 * Thread类的匿名内部类实现
 */
public class Test4 {
	public static void main(String[] args) {
		Thread t = new Thread() {
			@Override
			public void run() {
				for(int i=1;i<=5;i++) {
					System.out.print(i+" ");
				}
			}
		};
		t.start();
	}
}

程序输出结果如下:
1 2 3 4 5

示例5:Runnable接口的匿名内部类实现

package com.haobi;
/*
 * Runnbale接口的匿名内部类实现
 */
public class Test5 {
	public static void main(String[] args) {
		Runnable r = new Runnable() {
			@Override
			public void run() {
				for(int i=1;i<=5;i++) {
					System.out.print(i+" ");
				}
			}
		};
		Thread t = new Thread(r);
		t.start();
	}
}

程序输出结果如下:
1 2 3 4 5