Java 匿名类(匿名内部类)
学习的参考博文:无恨之都
1. 初识 匿名类
标准说法: 内部类包括:成员类、局部类、匿名类(匿名内部类)。
匿名类概念:
- 匿名类可以使你的代码更加简洁 (JDK8之后Lambda更简洁)。
- 你可以定义一个类的同时对其进行实例化。
- 它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要使用一次,就可以使用匿名类代替局部类。
- 匿名类是表达式,而非常规的类
匿名类的使用场景:
- 一个局部类只需要使用一次的时候
- 由于匿名类没有类名,那么除了定义它的地方,其他地方无法调用,所以匿名类也可以叫匿名内部类
2. 通过示例分析局部类和匿名类区别
sayHello
方法中有局部类和匿名类分别实现HelloWorld
接口的方法
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
/**
* 1、局部类:EnglishGreeting实现了HelloWorld接口
*/
class EnglishGreeting implements HelloWorld {
String name = "无参";
@Override
public void greet() {
greetSomeone(name);
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("局部类:" + name);
}
}
// 创建局部类EnglishGreeting的实例化对象,使用接口类型接收
HelloWorld englishGreeting = new EnglishGreeting();
// 局部类:无参方法
englishGreeting.greet();
// 局部类:带参方法
englishGreeting.greetSomeone("带参");
/**
* 2、匿名类实现HelloWorld接口并创建了实例化对象:frenchGreeting
*/
HelloWorld frenchGreeting = new HelloWorld() {
String name = "无参";
@Override
public void greet() {
greetSomeone(name);
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("匿名类:" + name);
}
};
// 匿名类:无参方法
frenchGreeting.greet();
// 匿名类:带参方法
frenchGreeting.greetSomeone("带参");
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
}
【输出】
局部类:无参
局部类:带参
匿名类:无参
匿名类:带参
【分析】
代码里局部类和匿名类实现的功能是一样的,内部的方法实现的代码是也一样的,区别只在实现HelloWorld
接口的地方
局部类的格式是:
- 创建局部类并且实现接口:
class EnglishGreeting implements HelloWorld {...}
- 创建局部类的实例化对象并用接口类型接收:
HelloWorld englishGreeting = new EnglishGreeting();
- 调用实例化对象的方法
匿名类的格式是:
- 创建匿名类实现接口同时对其进行实例化:
HelloWorld frenchGreeting = new HelloWorld() {...}
- 调用实例化对象的方法
【区别】
- 局部类
EnglishGreeting
实现HelloWorld
接口,有自己的类名:EnglishGreeting
,定义完成后需要再对其实例化对象:englishGreeting
才能可以使用方法 - 匿名类在定义时就已经实例化成对象:
frenchGreeting
,定义完了就可以直接使用方法 - 匿名类是一个表达式,因此在定义的最后用分号结束
3. 匿名内部类的语法
3.1 匿名类实现接口
其实上面的示例中的匿名类就是实现接口的方式,这个示例将实现更复杂的功能
public class InterfaceTest {
public static void main(String[] args) {
TomInterface tif = new TomInterface() {
String name = "汤姆";
@Override
public void getName() {
System.out.println(name);
}
TomInterface setName(String name){
this.name = name;
return this;
}
}.setName("杰瑞");
tif.getName();
}
}
interface TomInterface{
void getName();
}
【结果】
杰瑞
【分析】
main
方法创建匿名类实现TomInterface
接口并实例化:new TomInterface{...}
- 调用匿名类对象的
setName
方法,将杰瑞
赋值给匿名类的成员变量name
,并返回当前实例this
给接口变量tif
main
方法调用匿名类对象的方法tif.getName()
,而此时的匿名类的成员变量name
的值已经被替换成杰瑞
,所以最后输出杰瑞
而不是汤姆
3.2 匿名类继承父类 (匿名子类)
匿名类继承父类,调用父类构造,重写父类方法
public class ExtendTest {
public static void main(String[] args) {
String name = "李四";
// 创建父类对象,打印原始name值
PartherClass partherClass = new PartherClass();
System.out.println("父类的getName方法=" + partherClass.getName());
// 使用匿名类继承父类,并打印name值
PartherClass pc = new PartherClass(name){
@Override
public String getName(){
return "匿名类 - "+super.getName();
}
};
System.out.println(pc.getName());
}
}
class PartherClass{
private String name = "张三";
public PartherClass(){}
public PartherClass(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
【结果】
父类的getName方法=张三
匿名类 - 李四
【分析】
- 创建父类对象并调用
getName
方法,这个不用细说 - 创建匿名类继承父类并实例化对象:
pc
,本次匿名类调用的是父类的带参构造,将参数赋值给了父类的name
- 调用匿名类重写的
getName
方法,得到新的name
值
3.3 区别
Demo demo = new Demo(xxx){...}
- 操作符:new
- 一个要实现的接口或要继承的类,示例3.1 是实现接口,示例3.2 是继承类
- 一对括号,如果是匿名子类,那么父类有构造参数就填,不带参就空着;如果匿名类是实现接口,那么括号里需要空着
{...}
,括号里括着的是匿名类的声明主体- 末尾的
;
号,因为匿名类的声明是一个表达式,是语句的一部分,所以需要分号结尾 - 表面上看匿名类没有类名,没有构造参数。但其实在编译的时候,编译器会给匿名类分配类名和构造器,只是我们无法操作也不能复用。如需验证,可以看编译后的class文件,多出一个命名格式:
匿名类定义类$?.class
的文件。例如示例3.2,匿名类的class文件就是:ExtendTest$1.class