java内部类可分为下面四种:
* 成员内部类
* 静态嵌套类
* 方法内部类
* 匿名内部类
为什么使用匿名类?内部类也会想普通类生成一个对应的class文件?带着这些问题往下看。
java中内部类可以访问外围类的成员,并且可以有多个内部类实例,多个内部类之间共享一个外围类实例。每当创建一个内部类对象时,内部类会保存一份指向外围类的引用,在内部类里面通过该引用访问外围类,这些编译器都已经帮我们做好了,所以在内部类可以直接访问外围类成员。
打个比方,将外围类比作一台计算机,其内存条可以看作是一个内部类,因为内存条是独立单位,一台计算机可以有多个内存条,每个内存条都可以使用外围类(计算机)的其他资源如CPU、磁盘等。下面代码展示了成员内部类的用法:
class Computer{
private String disk;
class Memeory{
public Memeory(){
}
public void writeDisk(String s){
disk = s;
}
}
public Computer(String d){
disk = d;
}
public Memeory createMemeory(){
return new Memeory();
}
public void printDiskContent(){
System.out.println(disk);
}
}
public class Test{
public static void main(String [] args){
Computer c = new Computer("Test0");
c.printDiskContent();
Computer.Memeory m1 = c.createMemeory();
m1.writeDisk("Test1");
c.printDiskContent();
Computer.Memeory m2 = c.new Memeory();
m2.writeDisk("Test2");
c.printDiskContent();
}
}
输出结果:
Test0
Test1
Test2
Computer
类有一个名为Memory
的内部类,在main
函数中,首先创建了一个Computer
类的实例,然后打印其磁盘内容。接着先后两次调用Computer
实例的createMemeory()
方法创建内部类Memeory
类的实例,有意思的是第二个Memory
实例调用writeDisk()
方法 覆盖了外围类中的”磁盘”内容 ,这一点说明内部类的不同实例之间是共享外围类成员的。
这里使用了两次方式来创建内部类,一种是在外围类中增加一个创建内部类的方法,另一种是通过外围类实例后面加.new
的方式直接创建内部类,由于已经有外围类实例了编译器知道去哪里找要创建的内部类,所以不需要使用c.new Computer.Memeory()
的方式来限定了(这样写编译不过)。
总结:
对于非静态的内部类,必须通过外围类的实例来创建内部类,并且可以创建多个内部类实例,这些内部类共享外围类实例的成员。
成员内部类的典型用途是用内部类来实现接口,看上去也可以直接使用外部类实现接口,但是使用内部类实现接口也有一些好处,例如在一个类中使用不同的内部类可以实现多个不同接口,也可以针对同一接口提供不同的内部类实现,而且这些内部类实现可以很好的隐藏起来。例如下面代码
interface Selector{
int getValue();
void next();
boolean isEnd();
}
class Sequence{
private int[] items;
private int index;
public Sequence(int size){
items = new int[size];
index = 0;
}
public void add(int item){
if (index < items.length){
items[index++] = item;
}
}
// 内部类
class SequenceSelector implements Selector{
private int curIndex = 0;
public int getValue() { return items[curIndex]; }
public void next() { if (curIndex < items.length) ++curIndex; }
public boolean isEnd() { return curIndex == items.length; }
}
public Selector getSelector(){
return new SequenceSelector();
}
}
public class Test{
public static void main(String [] args){
Sequence seq = new Sequence(10);
for (int i = 0; i < 10; ++i){
seq.add(i);
}
Selector selector = seq.getSelector();
while (!selector.isEnd()){
System.out.println(selector.getValue());
selector.next();
}
}
}
输出结果:
0
1
2
3
4
5
6
7
8
9
Sequence
是一个序列类,其内部类SequenceSelector
实现了Selector
接口,通过getSelector()
可以获取Selector
接口的一个实现版本,你也可以很方便的添加内部类提供其他版本的Selector
接口实现。
class OuterClass{
static class InnerClass{
}
}
public class Test{
public static void main(String [] args){
OuterClass out = new OuterClass();
// InnerClass in = new InnerClass(); // error
OuterClass.InnerClass in = new OuterClass.InnerClass();
}
}
只要将内部类声明为static
,那么该内部类的创建就不需要使用外围类实例了,可以直接通过new OuterClass.InnerClass()
方式创建,这种情况下内部类只能访问外部类中的静态成员。
静态嵌套类与C++中的嵌套类很相似,嵌套类提供了一个类的定义,只是类定义的位置是在另一个类的内部。
方法内部类和普通内部类唯一区别在于其作用域不同,下面例子中,在类Stuff
的方法中定义了一个内部类Triangle
,其实现了接口Shape
,Triangle
仅在方法getShape()
中可见。
interface Shape {
void area();
}
class Stuff {
public Shape getShape() {
class Triangle implements Shape {
public void area(){
System.out.println("Triangle.area()");
}
}
return new Triangle();
}
}
public class Test{
public static void main(String [] args){
Stuff stuff = new Stuff();
stuff.getShape().area();
}
}
输出结果:
Triangle.area()
匿名内部类顾名思义是没有名称的类。下面一个匿名内部类的例子,还是使用上面的代码稍加修改:
interface Shape {
void area();
}
class Stuff {
public Shape getShape() {
// class Triangle implements Shape {
// public void area(){
// System.out.println("Triangle.area()");
// }
// }
// return new Triangle();
return new Shape() { // 在这里定义
public void area() {
System.out.println("Triangle.area()");
}
};
}
public void test(){
}
}
public class Test{
public static void main(String [] args){
Stuff stuff = new Stuff();
stuff.getShape().area();
}
}
输出结果:
Triangle.area()
在getShape()
方法中返回了一个”实现了接口Shape
的匿名类”,在该匿名类创建的同时进行了定义,末尾的;
是必须的,他表示return
语句的结束。该匿名类和注释掉的代码是等价的,不过你不用再挠头皮去想给它起个什么名字了。
下面是一个含有带参构造函数的匿名类例子:
class Shape {
private int size;
public Shape(int x) {
size = x;
}
public int getSize() {
System.out.println("Shape.getSize()");
return size;
}
public void printSize() {
System.out.println(size);
}
}
class Stuff {
public Shape getShape(int x) {
return new Shape(x) {
{ System.out.println("initialize"); }
@Override
public int getSize() {
return super.getSize() * 10;
}
@Override
public void printSize() {
System.out.println(getSize());
}
};
}
}
public class Test{
public static void main(String [] args){
Stuff stuff = new Stuff();
stuff.getShape(99).printSize();
}
}
输出结果:
initialize
Shape.getSize()
990
在getShape()
类中创建并返回了一个”继承自基类Shape的匿名类”,该匿名类的输入参数传递给基类带参构造函数完成基类的初始化,然后进入匿名类的定义部分,首先执行匿名类的代码块,接着是两个重写了基类相同签名的方法。
注意:在匿名类内部中使用外围类变量时,改外围类变量必须是final
修饰的,否则编译器会提示错误。上面例子中虽然使用了外围变量,但是没有final
修饰,这是因为变量传递给了匿名类的构造函数,不在匿名内部类内。
每个内部类都能独立继承自一个(接口)实现,所以无论外围类是否已经继承了某个(接口)实现,对于内部类都没有影响。内部类为实现”多重继承”提供了解决方案,可以通过多个内部类独立继承自不同基类来实现。
使用外围类可以获得一些其他特性:
java中每个类编译后都会产生一个class文件,其中包含了创建该类型对象的全部信息,同样的,内部类也必须生成一个class文件以包含创建它们的信息。
内部类生成的class文件有严格的命名规则:外围类名称 + "$"
+ 内部类名称。内部类如果是一个匿名类,编译器会简单的生成一个数字作为匿名内部类的名称。
例如 下面的Outer.java
编译后会生成两个class文件:Outer.class
和Outer$Inner.class
public class Outer {
public class Inner {
}
public static void main (String [] args) {
return ;
}
}
[1] Java 编程思想