Java SE 是什么,包括哪些内容(二十)?
本文内容参考自Java8标准
再次感谢Java编程思想对本文的启发!
代码示例:
下面的例子看起来会有点奇怪
// 匿名内部类
//接口Contents
interface Contents{
//方法value()
int value();
}
//外部类Parcel7
public class Parcel7{
//外部类方法contents()返回类型为Contents
public Contents contents(){
//return语句,返回一个Contents类的对象
//但是你会发现contents方法将返回值的生成与表示
//这个返回值的类的定义结合在一起。
//同时,这个类是匿名的,它没有名字
//看起来就像:你似乎正要创建一个类Contents的对象,
//但是在到达语句结束的分号之前,你却插入了一个类的定义
return new Contents(){
//int类型的类变量i
private int i;
//方法value
public int value(){
//返回类变量i的值
return i;
}
//这里要特别提醒一下,需要一个分号
};
}
//程序执行入口main方法
public static void main(String[] args){
//创建外部类Parcel7的对象实例
Parcel7 p = new Parcel7();
//调用方法contents()返回一个Contents类型的对象。
Contents c = p.contents();
}
}
从上面的代码示例中你会发现一个问题:之前不是不能使用接口去创建对象的吗?但是这里却出现了new Contents…代码…匿名内部类是可以直接使用接口的名称去创建对象的,这里并不是直接创建了Contents对象,仅仅是代表了这个匿名内部类是Contents类型的(这种奇怪的语法指的是:创建一个继承自类Contents的匿名类对象,通过new表达式返回的引用被自动向上转型为对Contents的引用)。
上面代码示例中匿名内部类的语法是下述形式的简化:
// 内部类的形式
//外部类Parcel7b
public class Parcel7b{
//内部类实现接口Contents
class MyContents implements Contents{
//int类型的类变量i
private int i;
//方法value
public int value(){
//返回类变量i的值
return i;
}
}
public Contents contents(){
return new MyContents();
}
//程序执行入口main方法
public static void main(String[] args){
//创建外部类Parcel7b的对象实例
Parcel7b p = new Parcel7b();
//调用方法contents()返回一个Contents类型的对象。
Contents c = p.contents();
}
}
上面的代码示例中的匿名内部类使用了默认的构造方法来生辰Contents,那么,如果你的父类需要一个有参数的构造方法,该如何处理:
代码示例:
// 使用带参数构造方法的匿名内部类
//父类Wrapping
public class Wrapping{
//int类型的类变量i
private int i;
//构造方法,带一个int类型的形式参数x
public Wrapping(int x){
//为类变量i赋值
i = x;
}
//方法value()
public int value(){
//返回类变量i的值
return i;
}
}
//外部类Parcel8
public class Parcel8{
//方法wrapping,带一个int类型的形式参数x,返回Wrapping类型
public Wrapping wrapping(int x){
//return语句,直接传入了参数x,表示调用了父类的构造方法
//这是匿名内部类调用构造方法的方式
return new Wrapping(x){
//实现方法value()
public int value(){
//返回父类value方法*47的值
return super.value()*47;
}
};
}
}
只需简单地传递合适的参数给基类的构造方法即可,这里是将x传进new Wrapping(x),尽管Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当做公共"接口"来使用。
你会注意到,类Wrapping拥有一个要求传递一个参数的构造器,这使得事情变得更加有趣了。
在匿名内部类的末尾的分号,并不是用来标记此内部类的结束的,实际上吗,它标记的是表达式的结束,这不过这个表达式证号包含了匿名内部类而已,因此,这与别的地方使用的分号是一致的。
在匿名内部类中定义字段时,还能够对其执行初始化操作:
代码示例:
// 初始化匿名内部类的字段
//外部类Parcel9
public class Parcel9{
//方法destination(final String dest),带一个final修饰的String
//类型的形式参数dest,因为dest内部类需要使用,所以必须是final的
public Destination destination(final String dest){
//return语句,创建一个匿名内部类
return new Destination(){
//为匿名内部类的类变量label赋值,使用的是外部类的dest参数
private String label = dest;
//方法readLabel(),返回类型为String类型
public String readLabel(){
//返回label的值
return label;
}
}
}
//程序执行入口main方法
public static void main(String[] args){
//创建外部类Parcel7b的对象实例
Parcel9 p = new Parcel9();
//调用方法destination(final String dest),获取一个
//实现了Destination接口类的对象实例。
Destination d = p.destination("Tamania");
}
}
如果定义一个匿名内部类,并且希望它使用一个在其外部类定义的变量或者是对象,那么编译器会要求其参数引用是final的,如果你忘记了,将会得到一个编译时错误消息。
为什么在一个外部类中,如果它的某个变量要被内部类使用到,就需要final修饰?final修饰的作用就是这个变量一旦被赋值,就不能再被改变,这是正确的,因为一旦内部类使用完毕之后,按照Java中代码是顺序执行的机制,在内部类使用了这个变量之后,你再去修改这个变量的值,那么内部类使用的这个值就不再是最新的了。所以需要一致,一旦内部类使用了,后面就不能再修改了,保证前后一致性。
如果你想在一个匿名内部类中执行类似构造方法的初始化,该怎么处理,因为匿名内部类不可能有命名的构造方法(因为这个类根本没名字),只有通过实例初始化,你才达到为匿名内部类创建一个构造方法的效果。
代码示例:
// 为匿名内部类实现一个构造方法初始化
//抽象类Base
abstract class Base{
//构造方法,带一个int类型的形式参数i
public Base(int i){
//打印字符串"Base constructor , i=" + i
System.out.println("Base constructor , i=" + i);
}
//抽象方法f()
public abstract void f();
}
//类AnonymousConstructor
public class AnonymousConstructor{
//static方法getBase(int i),带一个int类型的形式参数i
//返回类型为Base类型
public static Base getBase(int i){
//return语句,创建一个匿名内部类
return new Base(i){
{
//实例初始化,代表了匿名内部类的构造方法初始化,
//用一个{ }括起来
System.out.println("Inside instance initializer");
}
//方法f()
public void f(){
//打印字符串"In anonumous f()"
System.out.println("In anonumous f()");
}
};
}
//程序执行入口main方法
public static void main(String[] args){
//调用static方法getBase(int i),返回一个
//Base类型的匿名内部类对象
Base base = getBase(43);
//调用方法f()
base.f();
}
}
在上面的代码示例中,不要求变量i一定是final的,因为i是传递给匿名内部类的父类的构造方法,它并不会在匿名内部类被直接使用。
代码示例二:
下面的示例是带示例初始化的"parcel"形式(注意,方法destination()的参数必须是final的,因为他们是在匿名内部类直接使用的)。
// 实例初始化
//外部类Parcel10
public class Parcel10{
//方法destiantion(final String,final float price)
//因为两个形式参数都是在内部类里面直接使用的,需要final修饰
public Destination destination(final String,final float price){
//return语句,返回一个Destiantion类型,后紧接匿名内部类的创建
return new Destination(){
//int类型的类变量cost
private int cost;
{
//实例初始化部门,代表了匿名内部类的构造方法
cost = Math.round(price);
//if语句,如果cost的值大于100
if(cost>100){
//则打印字符串"Over budget!"
System.out.println("Over budget!");
}
}
//String类型的类变量label,声明的同时给它赋值
private String label = dest;
//方法readLabel()
public String readLabel(){
//返回label的值
return label;
}
};
}
//程序执行入口main方法
public static void main(String[] args){
//创建外部类Parcel10的对象实例
Parcel10 p = new Parcel10();
//调用方法destiantion(final String,final float price)
//返回一个Destiantion的类型.
Destination d = p.destination("Tasmania",101.395F);
}
}
在实例初始化的内部,可以看到有一段代码,它们不能作为字段初始化动作的一部分来执行(就是if语句)。所以对于匿名内部类而言,实例初始化的实际效果就是构造方法({ }里面的if语句不能划归到类变量初始化部分,所以只能是构造方法),当然,它受了限制,你不能重载实例初始化方法,所以仅有一个(也就是只能有一个{ })。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。