李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
Java匿名内部类(二)
Leefs
2019-11-20 PM
8380℃
7条
# Java匿名内部类(二) ### 前言 本篇将为大家带来《Java编程思想》10.6节,匿名内部类。 ### 一、匿名内部类和非匿名内部类对比 我们先看一段匿名内部类的代码: ```java //Returning an instance of an anonymous inner class interface Contents{ int value(); } public class Parcel7{ public Contents contents(){ return new Contents(){//Insert a class definition private int i =11; @Override public int value() { return i; } };//Semicolon required in this case } public static void main(String[] args){ Parcel7 p = new Parcel7(); Contents c = p.contents(); System.out.println(c.value()); } } ``` > 运行结果 ```java 11 ``` 在上述代码中,contents()方法将返回值的生成与表示这个返回值的类的定义结合在一起!同时,该类是匿名的。 "创建一个继承自Contents的匿名类的对象。"通过new表达式返回的引用被自动向上转型为对Contents的引用。 将上述匿名内部类加上名称,得到如下代码: ```java interface Contents{ int value(); } public class Parcel7b { class MyContents implements Contents{ private int i = 11; @Override public int value() { return i; } } public Contents contents(){ return new MyContents(); } public static void main(String[] args) { Parcel7b p = new Parcel7b(); Contents c = p.contents(); System.out.println(c.value()); } } ``` > 运行结果 ```java 11 ``` > 代码分析 1. 1.`MyContents`类实现了Contents接口 2. 2.在`Parcel7b`类中定义了一个`contents()`方法,用来创建`MyContents`对象 该段代码的执行流程很简单,大家可以试着执行一遍,小编就不带着大家分析了。 在这个匿名内部类中,使用了默认的构造器来生成Contents. ### 二、有参构造器 如果你的基类需要一个有参数的构造器,应该怎么办呢? > Wrapping类 ```java public class Wrapping { private int i; //有参构造器 public Wrapping(int x){ i = x; } public int value(){ return i; } } ``` > `Parcel8`类 ```java public class Parcel8 { public Wrapping wrapping(int x){ return new Wrapping(x){ public int value(){ return super.value() * 47; } }; } public static void main(String[] args) { Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); System.out.println(w.value()); } } ``` > 运行结果 ```java 470 ``` 在上述 代码中,尽管Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当作公共"接口"来使用,同时Wrapping拥有一个要求传递一个参数的构造器。 在`Parcel8`的匿名内部类中,只需要简单地传递合适的参数给基类的构造器即可,这里将`x`传进`new Wrapping(x)`。 在匿名内部类末尾的分号,并不是用来标记此内部类结束的。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类罢了。因此,这与别的地方使用的分号是一致的。 ### 三、字段初始化 在匿名类中定义字段时,还能够对其执行初始化操作: ```java interface Destination{ String readLabel(); } public class Parcel9 { public Destination destination(final String dest){ return new Destination(){ private String label = dest; @Override public String readLabel(){ return label; } }; } public static void main(String[] args) { Parcel9 p = new Parcel9(); Destination d = p.destination("Tasmania"); System.out.println(d.readLabel()); } } ``` > 运行结果 ```java Tasmania ``` 如果定义一个匿名内部类,并且希望它使用一个其外部定义的对象,那么编译器会要求其参数引用是final的,就像你在`destination()`的参数中看到的那样。如果忘记了,将会得到一个编译时错误信息。 如果只是简单地给一个字段赋值,那么此例中的方法是很好的。但是,如果想做一些类似构造器的行为,该怎么办呢?在匿名类中不可能有命名构造器(因为它根本没名字!), ### 四、构造器效果 通过**实例初始化**,就能达到为匿名内部类创建一个构造器的效果,就像这样: ```java abstract class Base{ public Base(int i){ System.out.println("Base constructor,i="+i); } public abstract void f(); } public class AnonymousConstructor { public static Base getBase(int i){ return new Base(i){ { System.out.println("In instance initializer"); } @Override public void f() { System.out.println("In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase(47); base.f(); } } ``` > 运行结果 ```java Base constructor,i=47 In instance initializer In anonymous f() ``` > 代码分析 1. 1.抽象类Base中有带参构造函数和抽象方法f() 2. 2.类`AnonymousConstructor`中有静态方法`getBase()` 3. 3.在静态方法`getBase()`中是匿名内部类 4. 4.在匿名内部类中有代码块{}和抽象方法实现 在此例中不要求变量i一定是final的。因为i被传递给匿名类的基类的构造器,它并不会在匿名类内部被直接使用。 #### 知识补充 在匿名内部类中代码块`{}`简单说明 ```java public class MyTest5 { public static void main(String[] args) { new C(); new C(); } } class C{ //每个C的实例被创建时,该代码块都会被执行一次 //随着每个实例的创建,该代码块都会被执行 { System.out.println("Hello"); } //加上static关键字,静态代码块只是在类加载时完成初始化阶段,进行调用,并且只执行一次 //不加上static表示一个实例化块,每一个对象被创建的时候,在构造方法执行之前该代码块都会被执行一次 static { System.out.println("static Java"); } //构造代码块 public C(){ System.out.println("c"); } } ``` > 运行结果 ```java static Java Hello c Hello c ``` 从运行结果可以看出静态代码块,实例化块和构造代码块的执行先后顺序是: ``` 静态代码块>实例化块>构造代码块 ``` ### 五、实例初始化 ```java public class Parcel10 { public Destination destination(final String dest,final float price){ return new Destination() { private int cost; { cost = Math.round(price); if(cost > 100){ System.out.println("Over budget!"); } } private String label = dest; @Override public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel10 p = new Parcel10(); Destination d = p.destination("Tasmania",101.395F); } } ``` > 运行结果 ```java Over budget! ``` 在上述是带实例化的"parcel"形式。注意destination()的参数必须是final的,因为它们是在匿名类内部使用的。 在实例初始化操作的内部,可以看到有一段代码,它们不能作为字段初始化动作的一部分来执行(就是if语句)。所以对于匿名类而言,实例初始化的实际效果就是构造器。当然它受到了限制--你不能重载实例初始化方法,所以你仅有一个这样的构造器。 ### 总结 匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。
标签:
Java
,
Java编程思想
,
JavaSE
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/210.html
上一篇
Java匿名内部类(一)
下一篇
Java匿名内部类(三)--再仿工厂方法
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
Filter
Linux
链表
Map
SpringCloudAlibaba
SQL练习题
数据结构
哈希表
并发线程
JavaScript
Hive
Flink
JVM
pytorch
Spark
人工智能
Spark Streaming
字符串
Elasticsearch
Jquery
Quartz
HDFS
Golang基础
Eclipse
Docker
ajax
Spring
前端
FileBeat
Elastisearch
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭