5.内部类
最常使用内部类和匿名类的地方是GUI[图形用户界面Graphic User Interface]。由于匿名类并没有名称,因此生成的类文件会用数字表示,如Outer$1.class
如果内部类是匿名的,那么编译器会简单地生成数字,把它们作为内部类标识符使用。若内部类嵌套于其他内部类中,则它们的名字简单地追加在一个$以及外部类标识符的后面。
这种生成内部名称的方法除了非常简单和直观以外,也非常“健壮”,可适应大多数场合的要求。由于它是Java的标准命名机制,所以产生的文件会自动具备“与平台无关”的能力(注意Java编译器会根据情况改变内部类,使其在不同的平台中能正常工作)。
5.1内部类描述
1. 内部类的定义和使用:
内部类有如下特性:
◇ 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称.名字不能与包含它的类名相同。
◇ 可以使用包含它的类的静态和实例成员变量,也可以使用它所在方法的局部变量。
◇ 可以定义为abstract。
◇ 可以声明为private或protected。
◇若被声明为static,就变成了顶层类,不能再使用局部变量。
◇ 若想在Inner Class中声明任何static成员,则该Inner Class必须声明为static。
2.内部类(inner).可分为三种:
(1) 在一个类(外部类)中直接定义的内部类;
(2) 在一个方法(外部类的方法)中定义的内部类;
(3) 匿名内部类.
另外一个概念是:嵌套类,Java中的四种嵌套类:嵌套静态类、成员类、本地类和匿名类
public class String {
private static classCaseInsensitiveComparator implements Comparator, Serializable {//嵌套静态类
//…
}//就是静态内部类
}
classPeerFixer implements AdjustmentListener, java.io.Serializable {
//…成员类,很像嵌套静态类,只是不带 static 关键字。在一个类(外部类)中直接定义的内部类
}
}
本地类是在代码块中声明的,一般来说,本地类在方法中进行声明。
成员类和本地类之间最重要的差别是,本地类只能访问 final 的变量或者参数。
匿名类允许我们把类的定义和实例的分配组合在一起。我们可以直接用 new SomeClass() 实例化对象,并且在实例化的位置把整个类的实现都放在花括号中。
3. 内部类的优缺点:
◇ 优点:节省编译后产生的字节码文件的大小
◇ 缺点:使程序结构不清楚
内部类使用的其它的问题
内部类同其它类一样也可以继承外部其它包的类和实现外部其它地方的接口.同样它也可以继承同一层次的其它的内部类,甚至可以继承外部类本身.下面我们给出最后一个例子做为结束:
public class Layer // 测试第二个内部类 |
5.2在外部类中定义内部类
class Outer {
private static int size;//静态变量
public class Inner{
private int size;
public void doStuff(int size) {
size++; // 存取局部变量
this.size++;//存取其内部类的成员变量
Outer.this.size++; // 存取其外部类的成员变量(可以是静态变量)
System.out.println(size + " " + this.size + " " + Outer.this.size);
}
}// 内部类Inner结束
public voidtestInner() {/** 类Outer中定义的实例方法testInner()方法 */
Inner i = new Inner();
i.doStuff(5);}
public static voidmain(String[] a) {
Outer o = new Outer();
o.testInner();
}}// 类Outer结束
例2:
import java.awt.* ;
import java.awt.event.*;
public class InnerClass{
private Frame f;
private TextField tf;
public InnerClass(){
f=new Frame("Inner classes example");
tf=new TextField(30);
}
public voidi launchFrame(){
Label label=new Label("Click and drag themouse");
f.add(label,BorderLayout.NORTH);
f.add(tf,BorderLayout.SOUTH);
f.addMouseMotionListener(newMyMouseMotionListener());/*参数为内部类对象*/
f.setSize(300,200);
f.setVisible(true);
}
class MyMouseMotionListener extends MouseMotionAdapter{/*内部类开始*/
public void mouseDragged(MouseEvent e) {
String s="Mouse dragging:x="+e.getX()+"Y="+e.getY();
tf.setText(s); }
}
public static void main(String args[]) {
InnerClass obj=new InnerClass();
obj.launchFrame();
}
}
如下所示代码为在外部类中定义两个内部类及它们的调用关系:
class Outer } |
内部类InnerOne及InnterTwo只在类Outer的作用域内是可知的,内部类的变量成员只在内部类内部可见, 若外部类或同层次的内部类需要访问, 需采用示例程序中的方法, 不可直接访问内部类的变量.
5.3在方法中定义内部类
如下所示代码为在方法内部定义一个内部类:
package inner; System.out.println(str2); |
从上面的例程我们可以看出定义在方法内部的内部类的可见性更小, 它只在方法内部可见, 在外部类(及外部类的其它方法中)中都不可见了.同时,方法内的内部类只能访问所在方法的final型成员.同时另一个需引起注意的是方法内部定义成员, 只允许final修饰或不加修饰符, 其它像static等均不可用.
5.4静态内部类
如果你不需要内部类对象与其外围类对象之间有联系,那你可以将内部类声明为static。这通常称为嵌套类(nestedclass)。Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。想要理解static应用于内部类时的含义,你就必须记住,普通的内部类对象隐含地保存了一个引用,指向创建它的外围类对象。然而,当内部类是static的时,就不是这样了。嵌套类意味着:
1. 嵌套类的对象,并不需要其外围类的对象。
2. 不能从嵌套类的对象中访问非静态的外围类对象。
如下所示代码为定义一个静态嵌套类
public class StaticTest{ // 静态内部类中可以定义静态或者非静态的成员 Private Static String x=“as”; // 静态内部类不能访问外部类的非静态成员(包括非静态变量和非静态方法) //静态内部类只能访问外部类的静态成员(包括静态变量和静态方法) // 外部类访问内部类的非静态成员:实例化内部类即可 person.display(); //System.out.println(mail);//不可访问 System.out.println(Person.x);// 外部类访问内部类的静态成员:内部类.静态成员 |
在静态嵌套类内部, 不能访问外部类的非静态成员, 这是由Java语法中"静态方法不能直接访问非静态成员"所限定.注意, 外部类访问内部类的的成员有些特别, 不能直接访问, 但可以通过内部类实例来访问, 这是因为静态嵌套内的所有成员和方法默认为静态的了.同时注意, 内部静态类Person只在类StaticTest范围内可见, 若在其它类中引用或初始化, 均是错误的.
一 . 静态内部类可以有静态成员,而非静态内部类则不能有静态成员。
二 . 静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;
三 . 非静态内部类的非静态成员可以访问外部类的非静态变量。
生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类(正常情况下,你不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,因为它是static 的。只是将嵌套类置于接口的命名空间内,这并不违反接口的规则)
5.5 匿名内部类
匿名类是一种特殊的内部类,它是在一个表达式内部包含一个完整的类定义。所谓的匿名就是该类连名字都没有,匿名内部类不可以有构造器,因为匿名内部类是要扩展或实现父类或接口。匿名内部类可以继承其他类,因为是类就是继承自Object类的。
匿名内部类实现接口指的是类本身定义的时候实现接口还是直接定义匿名内部类的时候实现接口,前者肯定是可以的,后者应该是不行的,光从语法上都不能实现,如:
class B { }
class C{
public void z(){
Thread t=new Thread(new A(){//这里定义了一个无名内部类,要实现接口,语法上都是不能实现的
public void run() { }
});
}
class A extends B implements Runnable,Serializable,Cloneable
{ //如果在new 以后写继承其他类或是实现接口都是错误的
public void run(){ }
}
}
public class A { int i = 1; public A() { Thread thread = new Thread() {//匿名内部类 public void run() { for(;;) { A.this.run();//用到了外部类的 run 函数,用外部类的类名加上 this 引用来说明要调用的是外部类的方法 run try { sleep(1000); } catch(InterruptedException ie) { } }//for }//run };//Thread thread.start(); } //A() public void run() { System.out.println("i = " + i); i++; } public static void main(String[] args) throws Exception { new A(); } }
本地内部类与匿名内部类的区别在于本地内部类有构造函数,而匿名内部类只能实例初始化。
匿名内部类:通常用在Java的事件处理上
package inner; System.out.println(str); } |
在上面的例子中, 方法addMouseListener接受一个对象型的参数表达式, 于是, 在参数里, 我们定义了一个匿名内部类, 这个类是一个MouseAdapter类型的类, 同时在这个类中定义了 一个继承的方法mousePressed, 整个类做为一个参数. 这个类没有名称, 但是 当执行这个 表达式 时它被自动实例化.