内部类(inner class)是定义在另一个类中的类。为什么需要使用内部类呢?其主要原因有以下三点:
内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
内部类可以对同一个包中的其他类隐藏起来。
当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
内部类可以分为:
成员内部类
局部内部类
静态内部类
匿名内部类
成员内部类:就是在外部类的内部再定义一个类
public class OuterClass1 { private int i = 10; private int j = 20; private static int count = 0; public static void func1(){} public static void func2(){} //成员内部类中,不能定义静态成员 //成员内部类中,可以访问外部类中的所有成员 class InnerClass { //内部类中不允许定义静态变量 //static int inner i = 100; int j = 100;//内部类和外部类的实例变量可以共存 int k = 1; void innerFunc1(){ System.out.println("内部类中k值为" + k); //在内部类中访问内部类自己的变量直接用变量名 System.out.println("内部类中j值为" + j); //在内部类中访问自己的变量名也可以用this.变量名 System.out.println("内部类中j值为" + this.j); //在内部类中访问外部类与内部类重名的实例变量用“外部类名.this.变量名” System.out.println("外部类中j值为" + OuterClass1.this.j); //如果内部类中没有与外部类同名的变量,则可以直接用变量名访问外部类变量 System.out.println("外部类中count值为:" + count); func1(); func2(); } } //外部类的非晶态方法访问成员内部类 public void func3(){ InnerClass inner = new InnerClass(); inner.innerFunc1(); } public static void main(String[] args) { //内部类的创建原则是,首先创建外部类对象,然后通过此类创建内部类对象 //静态的内部类则不需要外部类对象的引用 OuterClass1 out = new OuterClass1(); OuterClass1.InnerClass outin1 = out.new InnerClass(); outin1.innerFunc1(); //也可以将创建代码合并在一块 OuterClass1.InnerClass outin2 = new OuterClass1().new InnerClass(); outin2.innerFunc1(); } }
局部内部类不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
局部类有一个优势,即对外部世界可以完全地隐藏起来。即使所在外部类的其他代码也不能访问它
与其他内部类相比较,局部类还有一个优点。它们不仅能够访问包含他们的外部类,还可以访问局部变量。不过,那些局部变量必须被声明为final。
public class OutClass2 { private int s = 10; private int k = 0; public void func1(){ final int s= 20; final int j= 1; //局部内部类 class InnerClass{ int s = 30;//可以定与外部类同名的变量 //static int m = 20;//不可以定义静态变量 void innerFunc(){ //如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量 System.out.println("外围类成员" + k); //可以访问外部类的局部变量(即方法内的变量),但是必须声明为final System.out.println("常量:" + j); //如果内部类中有与外部类同名的变量,直接访问的是内部类中的变量 System.out.println("常量:" + s); //用this.变量名访问的也是内部类的变量 System.out.println("常量:" + this.s); //用外部类名this.内部变量名访问的是外部变量 System.out.println("外部变量成员:" + OutClass2.this.s); } } new InnerClass().innerFunc(); } public static void main(String[] args) { //访问局部内部类必须先定义外部类对象 OutClass2 out = new OutClass2(); out.func1(); } }
/** * 静态内部类 * 静态内部类可以用public、protected、private修饰, * 不能从嵌套类的对象中访问非静态类的外围对象 * 静态内部类与普通内部类的区别: * 1.静态内部类:内部类对象可以与外部类对象没有联系,在静态内部类中不能访问外部类的非静态成员 * 2.普通内部类:对象隐含的保存了一个引用,指向创建他的外围类对象。 * */ public class OutClass3{ public static int i = 1; public int j = 10; public static void func1(){ } public void func2(){ } //静态内部类可以用static、public、private修饰 //静态内部类中可以定义静态或非静态的成员 static class InnerClass{ static int inner_i = 100; int inner_j = 200; static void innerFunc1(){ //静态内部类只能访问外部类的静态成员(包括静态变量和静态方法) System.out.println("outer.i" + i); func1(); } void innerFunc2(){ //静态内部类不能访问外部类的非静态成员(包括非静态变量和非静态方法) // System.out.println("outer.j" + j); //func2(); } } public static void func3(){ //外部类访问内部类的非静态成员:实例化内部类即可 InnerClass inner =new InnerClass(); inner.innerFunc1(); } public static void main(String[] args) { new OutClass3().func3(); //静态内部类的对象可以直接生成 OutClass3.InnerClass inner = new OutClass3.InnerClass(); inner.innerFunc2(); } }
有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为static,以便取消产生的引用。
注释:声明在接口中的内部类自动成为static和public类。
假如一个类只有在当前方法中使用一次,只创建这个类的一个对象,就不必命名了。这种类被称为匿名内部类(anonymous)。
public void start(int interval , final boolean beep){ ActionListener listener = new TimerPrinter() { public void actionPerformed(ActionEvent e){ Date date = new Date(); SimpleDateFormat f = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"); System.out.println("现在时间:" + f.format(date)); if(beep)Toolkit.getDefaultToolkit().beep(); } }; Timer t = new Timer(interval , listener); t.start(); }它的含义是:创建一个实现 ActionListener 接口的类的新对象,需要实现的方法 actionPerformed 定义在括号 {} 内。
通常的语法格式为:
new SuperType(Constructionparameters)
{
inner class methods and data
}
其中,SuperType可以使接口,于是内部类就要实现这个接口;也可以是一个类,于是内部类就要扩展它。
由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给超类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。不仅如此,还要提供一对花括号。
注释:下面的技巧称为“双括号初始化”(double braceinitialization),这里利用了内部类语法。假设想构造一个数组列表,并将它传递到一个方法:
ArrayList<String> strs = new ArrayList<String>(); strs.add("STR1"); strs.add("STR2"); strs.add("STR3");
如果不再需要这个数组列表,最好让它作为一个匿名列表。
ArrayList<String> sts = new ArrayList<String>(){{add("a");add("b");}};