对于普通类,通过 class 类名的方式定义类;然后,通过 类名 对象名 = new 构造方法();的方式获取类对象:
设想一下这种场景:在程序的逻辑中,对某个类的实例只会使用一次,而此时,这个类的名字对于整个程序就可有可无了。此时,就可以将类的定义与类的创建放到一起完成,简化程序的编写。像这种没有名字的类就成为匿名内部类。通常可以通过匿名内部类来简化对于抽象类和接口实现的操作。这篇博客主要介绍匿名内部类应用于抽象类(其实,对于非抽象类一样可以)。(其实接口也是一样的)
下面将通过实例演示匿名内部类,
1.不使用匿名内部类的情况(1这部分是废话,纯铺垫的,主要在2部分)
Person抽象父类:里面有个抽象方法read():正常情况是,弄一个抽象父类的子类,然后在子类中实现read()抽象方法,然后通过创建子类的实例对象就可以去调用子类中的实现后的read()方法。。。。从而实现最终的功能调用。
从这个过程可以发现,对于抽象父类需要明明白白的写一个子类,然后明明白白的使用子类去创建子类实例对象,然后去调用方法。这也是以前常见的策略。
// 抽象父类
public abstract class Person {
private String name;
public Person(){}
// 抽象父类的抽象方法
public abstract void read();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Man子类,继承Person类,实现了其中的read()抽象方法:
public class Man extends Person {
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("男生看科幻类书籍");
}
}
Woman子类,继承Person类,实现了其中的read()抽象方法:
public class Woman extends Person {
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("女生喜欢读言情小说");
}
}
PersonTest测试类,有两种方式调用read()方法:
import com.imooc.one.Man;
import com.imooc.one.Person;
import com.imooc.one.Woman;
public class PersonTest {
// 需求:根据传入的不同的人的类型,调用对应的read方法
// 不使用匿名内部类的方案一:通过方法重载来实现,这种方式较复杂
public void getRead(Man man){
man.read();
}
public void getRead(Woman woman){
woman.read();
}
// 不使用匿名内部类的方案二:通过多态技术的简略方式
public void getRead(Person person){
person.read();
}
public static void main(String[] args) {
// 不适用匿名内部类的方案一和方案二的测试
PersonTest test = new PersonTest();
Man one = new Man();
Woman two = new Woman();
test.getRead(one);
test.getRead(two);
}
}
上面的例子非常简单,是常见的方式,没有使用匿名内部类,在调用read方法的过程中,通过两个子类获取了类的实例对象,然后通过实例对象去调用方法。
2.那么上述功能,如何通过匿名内部类的方式去实现?
首先,依旧是一个抽象类,其中有个抽象方法:目标是完成抽象方法的实现并调用实现后的方法
Person抽象父类:里面有个抽象方法read():
// 抽象父类
public abstract class Person {
private String name;
public Person(){}
// 抽象父类的抽象方法
public abstract void read();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对此,不再写其子类了,直接在测试类采用匿名内部类的方式去实现:匿名内部类核心部分
import com.imooc.one.Person;
public class PersonTest {
// 需求:根据传入的不同的人的类型,调用对应的read方法
public void getRead(Person person){
person.read();
}
public static void main(String[] args) {
PersonTest test = new PersonTest();
// 匿名内部类的实现方式,实现抽象类Person类中的抽象方法,并调用之
test.getRead(new Person(){
// 按照其错误提示,重写read()方法
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("男生看科幻类书籍,匿名内部类");
}
});
// 匿名内部类的实现方式,实现抽象类Person类中的抽象方法,并调用之
test.getRead(new Person(){
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("女生喜欢读言情小说");
}
});
}
}
注:上面如果不重写read方法的错误提示如下图:看看就行
可以发现
(1)匿名内部类没有类型名称、没有实例对象名称;
(2)由于匿名内部类的特殊性,无法在匿名内部类前加访问修饰符,即private、protected、public、abstract(自然匿名内部类中不允许出现抽象方法)、static都不能加;
(3)由于匿名内部类没有名字,所以无法在匿名内部类中编写构造方法;但如果有属性想要进行初始化操作,可以使用构造代码块:
(4)匿名内部类中不能出现静态成员,静态属性和静态方法都不能出现;
(5)自然,匿名内部类可以继承父类,也可以实现接口,但不可兼得,只能是其一;
这儿说继承父类,而没有特殊强调父类必须是抽象类,实测发现,即使是一般类(不是抽象类),匿名内部类可以同样使用,只是,匿名内部类中的方法就是对父类中方法的重写而已。
(6)匿名内部类对于接口也是可以的,采取同样的策略:
注:(1)匿名内部类优点:匿名内部类对内存的损耗和系统性能的影响小;
(2)匿名内部类缺点:但,由于没有类名和对象名,匿名内部类只能在局部使用一次,当下一次想重复调用时,只能重新写一遍。
(3)匿名内部类适用场景
(4)匿名内部类编译后的文件名称:
下面的PersonTest$1.class和PersonTest$1.class两个文件,分别是两个匿名内部类编译后的文件。
即编译后的文件是【外部类$数字.class】的形式;