Java抽象类方法调用机制
什么是抽象类?
使用abstract关键字修饰的类称为抽象类。
举个例子,你的老板说让你好好工作,等公司发了财人人有份,或者你的朋友说改天请示吃饭啊,这都可以看作是抽象类。所以,抽象类就是指光说不做的类,在实际中不能直接拿来用。
抽象类的作用
我们知道所有的对象都是通过类来描绘的,但并不是所有的类都是用来描绘对象的。抽象类用来定义不想被实例化的类,即不能使用new关键字创建抽象类的对象。
抽象类的定义:
abstract class A{
....
}
在这里我们要注意,有抽象方法的类必定是抽象类,反之,抽象类必有抽象方法(同时可以存在非抽象方法,这在编译时不会报错,虽然一般不这么用)。
下面我们来看一个例子:
abstract class grandFather{
abstract void grandFather_mehtod1();
abstract void grandFather_mehtod2();
grandFather(){
System.out.println("调用了grandFather的无参");
}
}
abstract class Father extends grandFather{
abstract void Father_method();
void father_function(){
System.out.println("hunt something!");
}
Father(){
System.out.println("调用了Father的无参");
}
}
class Son_1 extends grandFather{
void grandFather_mehtod1(){
System.out.println("Son_1覆写了grandFather_mehtod1");
}
void grandFather_mehtod2(){
System.out.println("Son_1覆写了grandFather_mehtod2");
}
void Son1_function(){
System.out.println("使用了Son_1自己的方法");
}
Son_1(){
System.out.println("调用了Son_1的无参");
}
}
class Son_2 extends Father{
void grandFather_mehtod1(){
System.out.println("Son_2覆写了grandFather_mehtod1");
}
void grandFather_mehtod2(){
System.out.println("Son_2覆写了grandFather_mehtod2");
}
void Father_method(){
System.out.println("Son_2覆写了Father_mehtod");
}
void Son2_function(){
System.out.println("使用了Son2_function");
}
Son_2(){
System.out.println("调用了Son_2的无参");
}
}
在这里我们定义了四个类,其中:
grandFather和Father是抽象类,他们的关系是Father继承grandFather。
Son_1和Son_2是两个普通类,其中Son_1继承grandFather,Son_2继承Father。
下面我们来实例化上述的类:
Son_1 s1 = new Son_1();
s1.grandFather_mehtod1();
s1.grandFather_mehtod2();
s1.Son1_function();
System.out.println("-----------------------------");
Son_2 s2 = new Son_2();
s2.grandFather_mehtod1();
s2.grandFather_mehtod2();
s2.Father_method();
s2.father_function();
s2.Son2_function();
System.out.println("-----------------------------");
grandFather gs1 = new Son_1();
gs1.grandFather_mehtod1();
gs1.grandFather_mehtod2();
((Son_1) gs1).Son1_function();
System.out.println("-----------------------------");
/* 直接使用gs1.Son1_function()会报错*/
grandFather gs2 = new Son_2();
gs2.grandFather_mehtod1();
gs2.grandFather_mehtod2();
((Son_2) gs2).Son2_function();
((Father) gs2).Father_method();
((Father) gs2).father_function();
System.out.println("-----------------------------");
/* 直接使用
gs2.Son2_function()
gs2.Father_method()
gs2.Father_function()
这三个方法会报错*/
Father fs2 = new Son_2();
fs2.grandFather_mehtod1();
fs2.grandFather_mehtod2();
fs2.Father_method();
fs2.father_function();
((Son_2) fs2).Son2_function();
System.out.println("-----------------------------");
/* 直接使用fs2.Son2_function()会报错*/
在这里我们创建了5个对象,分别是
**
Son_1创建的Son_1对象s1
Son_2创建的Son_2对象s2
grandFather创建的Son_1对象gs1
grandFather创建的Son_1对象gs2
Father创建的Son_2对象 fs2
**
接下来我们再来调用各个对象的方法,s1和s2就不用多说了,我们从gs1开始看。
这是我们惊奇的发现gs1这个对象并不能直接调用Son_1这个类独有中的方法,但是却可以用由Son_1这个类完成了的抽象类grandFather中的两个方法。
由于我们都知道,grandFather gs1 = new Son_1()
这种写法是通过父类来创建子类对象,本质上还是一个子类的对象,只是创建了一个父类的引用指向这个对象。相当于申请了一个Son_1的空间,所以按理来说应该可以使用Son_1这个类本身的方法。
但实际上会产生报错,解决方案是将gs1强制类型转换为Son_1,这时就相当于利用Son_1创建一个Son_1的对象。
控制台结果查看(myEclipse 10):
但这是为什么呢?
《Java编程思想》一书给出了答案
由于使用grandFather创建了一个Son_1类的对象,所以虽然是Son_1的对象,但是却把这个对象赋给了grandFather,所以在调用方法时,会按照grandFather的接口约束来调用,由于按照grandFather的寻址方式,所以看不到Son_1的方法。
如果这样说难以理解,我们可以换个方式来理解。
举个例子,现在有一匹狼想要混入羊群,它需要披上羊皮,把自己伪装成羊,才能成功的进入羊群。这时,它不能作出它实际上身为狼特有的动作,只能用羊拥有的动作。如果碰到狼和羊都有的动作,比如说嚎叫,这时狼也要按照羊的叫声来发声,才不会被发现,达到它混入羊群的目的。
本例中,可以把grandFather看作羊,把Son_1看作狼,就更容易理解了。
同理,对于gs2和fs2的方法调用也是如此,都是由于按照父类的接口约束而导致不能直接调用子类独有的方法。