前言
关于 Java 的匿名内部类,是我入门 Java 的第一个重难点,刚上完课,就过来总结一下。
因为老师在课上讲的例子比较生动有趣,所以在这里我将对其进行模仿复现:先有一个叫动物的类,然后这个类中有一个子类鸟,接着在鸟类中实例出一个对象麻雀。动物类的方法是输出 “ 所有动物都会叫出声音~ ” ,而鸟类方法是输出 “ 叽叽喳喳!” 。现在我们要做的就是让麻雀 “ 叽叽喳喳!”
下面将有三个程序会被贴出来,我们可以分别体会到 多态、向上转型、匿名内部类 这三个概念。
一、 最直观写法——重写父类方法(多态)
我们用一个动物叫声的例子来进行问题的探讨,首先是正常写法完成程序代码:
public class AnimalSound {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Bird sparrow = new Bird(); //定义子类对象并实例化
sparrow.sound();
}
}
class Animal{
void sound() {
System.out.println("所有动物都会叫出声音~");
}
}
class Bird extends Animal{
void sound() { //重写父类的方法
System.out.println("叽叽喳喳!");
}
}
输出正确:
叽叽喳喳!
现在,我们来看一看这个程序:首先它分为了三个类,第一个是主类,第二个是普通类 Animal,第三个是继承于 Animal 的子类 Bird;然后在主类中进行了 Bird 类的实例化,出现了一个 sparrow 的对象,最后使用了该对象的方法 sound 。其实,这个程序就体现出了面向对象的三大特性之一:多态性,这怎么说?看子类 Bird 就知道了,在这里,我们在子类中重写了父类的方法,后来在主类中使用该方法后不就显示出了子类方法而非父类中的输出嘛,哈哈。
二、过渡写法——内部类+向上转型
向上转型就是说把子类对象赋值给父类对象:
public class AnimalSound {
public static void main(String[] args) {
// TODO 自动生成的方法存根
class Bird extends Animal{ //内部类
void sound() {
System.out.println("叽叽喳喳!");
}
}
Animal sparrow = new Bird(); //向上转型
sparrow.sound();
}
}
class Animal{
void sound() {
System.out.println("所有动物都会叫出声音~");
}
}
输出正确:
叽叽喳喳!
与上一个代码不同的是,这次的子类写在了主类里面,所以是个内部类,后来声明实例的时候是这样写的——Animal sparrow = new Bird();
,这句代码这样理解:等号左边是说定义一个父类对象,等号右边是说实例化一个子类,而等号其实是一个赋值运算。你想想啊,一个子类实例化后赋值给了它的父类对象,不就是儿子向上跑去找老爹了嘛,这就是向上转型。
另外说一下向上转型后产生的对象中方法的问题,方法用父类的还是子类的?这得看子类和父类的方法都有哪些,子类可精明了,自己有的就用自己的,用的顺手嘛(要换做是我,我也不愿意跟我爸换手机用,哈哈),而自己没有的,就直接用父类的(就像我每月都管我爸要生活费一样,我没得钱啊,手动狗头)。这样一来应该就清晰很多了吧。
三、最终写法——匿名内部类
其实说是最终写法,其实就是 Java 最常见写法,只不过是我们刚接触 Java ,对于面向对象编程还是不熟悉而已啦。
public class AnimalSound {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Animal sparrow = new Animal() { //获取匿名内部类对象实例
void sound() { //重写父类的方法
System.out.println("叽叽喳喳!");
super.sound(); //调用父类的 sound 方法
} //(单纯是我个人想记住 super 的用法)
}; //向上转型
sparrow.sound(); //调用匿名类的 sound 方法
}
}
class Animal{
void sound() {
System.out.println("所有动物都会叫出声音~");
}
}
输出正确:
叽叽喳喳!
所有动物都会叫出声音~
看吧,在这一个程序中,就找不到子类的名字,谁知道它叫啥呢,可这照样能够实例出一个对象 sparrow :Animal sparrow = new Animal()
,等号左边定义一个父类对象,等号右边是实例化一个父类,再赋值。这就是说啊,从一个动物的类中跳过了鸟这一子类,直接实例化出来了麻雀对象,这才有了下面的操作(重写父类方法并执行)。
总结
匿名匿名,既然子类名字都不出现,那再想找到它就是不可能的事了,所以只出现一次的类用起来匿名内部类就非常方便了,最后给出匿名内部类代码更简洁的方式:
public class AnimalSound {
public static void main(String[] args) {
// TODO 自动生成的方法存根
new Animal() {
void sound() {
System.out.println("叽叽喳喳!");
}
}.sound(); //向上转型
}
}
class Animal{
void sound() {
System.out.println("所有动物都会叫出声音~");
}
}
输出正确:
叽叽喳喳!
有没有跟我一样,直接哇塞了,这次连定义都省去了,直接实例出一个对象,然后把整个代码块(这个对象)当成这个对象的名字,直接调用方法,真牛掰!不过仔细想想也是哈,你叫张三去扫地的本质就是叫这个特定的人去扫地嘛,所以说,把张三给匿名掉,也不错嘛,管你张三还是李四, Ta 的工作只是扫地而已,扫好就行了呗。