Java多态十六:匿名内部类

对于普通类,通过 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】的形式;

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页