Java中的内部类

内部类(inner class)是定义在另一个类中的类。使用内部类的两个主要原因:

  • 内部类可以对同一个包中的其他类隐藏。
  • 内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据。

使用内部类访问对象状态

一个内部类方法可以访问自身的数据字段,也可以访问创建它的外围类对象的数据字段。为此,内部类的对象总有一个隐式引用,指向创建它的外部类对象,这个引用在内部类的定义是不可见的。

外围类的引用在内部类构造器中设置。编译器会修改所有的内部类构造器,添加一个对应外围类引用的参数。

内部类的特殊语法规则

内部类中外围类的引用表示为:

OuterClass.this

可以采用以下语法更明确地编写内部类对象地构造器:

outerObject.new InnerClass(construction parameters)

在外围类的作用域外,要用以下方式引用内部类:

OuterClass.InnerClass

内部类中声明的所有静态字段都必须是final,并初始化为一个编译时常量。
内部类不能有static方法。

内部类是否有用、必要和安全

内部类是一个编译器现象,与虚拟机无关。编译器将会把内部类转换为常规的类文件,用$(美元符号)分隔外部类名与内部类名,而虚拟机则对此一无所知。

编译器生成了一个额外的实例字段this$0,对应外围类的引用。(名字this$0是编译器合成的,在自己编写的代码中不能引用这个字段)

既然内部类会转换成常规类,内部类如何得到那些额外的访问权限呢?
编译器在外围类添加了静态方法access$0。它将返回作为参数传递的那个对象的字段。(方法名可能稍不同,取决于编译器。)

这样做存在安全风险。任何人都可以通过调用access$0方法容易地读取到私有字段。当然access$0不是Java的合法方法名。但熟悉类文件结构的黑客可以使用十六进制编辑器轻松地创建一个类文件,其中利用虚拟机指令调用那个方法。

局部内部类

当某个类只是在方法中使用一次,可以在一个方法中局部地定义这个类。
声明局部类时不能有访问说明符。局部类的作用域被限定在声明这个局部类的块中。局部类有一个很大的优势,即对外部世界完全隐藏,甚至外围类中其他代码也不能访问它。

由外部方法访问变量

局部类不仅能够访问外部类的字段,还可以访问局部变量,不过,那些局部变量必须是事实最终变量。这说明,它们一旦赋值就绝不会改变。编译器检测对局部变量的访问,为每一个变量建立相应的实例字段,并将局部变量复制到构造器,从而能初始化这些实例字段。

匿名内部类

假如只想创建这个类的一个对象,,甚至不需要为类指定名字。这样一个类被称为匿名内部类(anonymous inner class)。

由于构造器的名字必须与类名相同,而匿名内部类没有类名,所以,匿名内部类不能有构造器。但可以提供一个对象初始化块。

静态内部类

有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类有外围类对象的一个引用。为此,可以将内部类声明为static,这样就不会生成那个引用。

下面是一个想要使用静态内部类的典型例子。考虑这样一个任务:计算数组中的最小值和最大值。当然,可以编写两个方法,一个方法用于计算最小值,另一个方法由于计算最大值。在调用这两个方法的时候,数组被遍历两次。如果只遍历数组一次,并能够同时计算出最小值和最大值,那么就可以大大地提高效率了。

double min = Double.POSITIVE_INFINITY;
double max = Double.NEGAITVE_INFINITY;
for (double v : values) {
	if (min > v) min = v;
	if (max < v) max = v;
}

然而这个方法必须返回两个数,为此,可以定义一个包含两个值的类Pair:

class Pair {
	private double first;
	private double second;
	public Pair(double f, double s) {
		first = f;
		second = s;
	}
	public double getFirst() { return first; }
	public double getSecond() { return second; }
}

minmax方法可以返回一个Pair类型的对象。

class ArrayAlg {
	public static Pair minmax(double[] values) {
		...
		return new Pair(min,max);
	}
}

当然,Pair是一个十分大众化的名字。这样就会产生名字冲突,解决这个问题的办法是将Pair定义为ArrayAlg的一个公共内部类。此后,就可以通过ArrayAlg.Pair访问它了:

ArrayAlg.Pair p = ArrayAlg.minmax(d);

不过,在Pair对象中不需要任何其他对象的引用,为此,可以将这个内部类声明为static,从而不生成那个引用:

class ArrayAlg {
	public static class Pair{
		...
	}
	...
}

只要内部类不需要访问外围类对象,就应该使用静态内部类。
与常规内部类不同,静态内部类可以有静态字段和方法。
在接口中声明的内部类自动是static和public。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值