Java的抽象类 和它的实体
抽象,就是从许多具体事物中舍弃个别的、特殊的属性,抽出共同的、本质的属性。
定义任何类,若在关键词class 之前加上修饰符 abstract,它就成了抽象类。抽象类不允许直接被实例化。就是说,在任何一个能实例化的类的定义前,放上关键字 abstract ,给定类就被看作是抽象类,进而不能直接被实例化了。显然/尽管,这种抽象类中没有抽象的方法。常见的情况是,在定义一个类的时候, 一旦出现了抽象方法,这个类就是抽象的, 必须在关键字class之前放上修饰符 abstract ,否则会有编译错误。
定义抽象方法的必要性:比如,定义类 Person的时候,各种人的吃饭方式各有不同:中国人用筷子吃饭, 西方人用刀叉吃饭, 或印度人用手抓着吃饭。 因此,定义吃饭方法 havingDinner()的时候,就要从多种人中舍弃个别的、具体的操作(如用筷子吃饭, 用刀叉吃饭, 或用手抓着吃饭),抽出共同的、本质的操作:吃饭, 产生了抽象方法 havingDinner()。 做法是,忽略方法体(即不包括一对花括号和其中的代码块)而仅给出方法的签名。就是说,只有方法的特征没有方法的实现,以构成抽象方法。
正是因为有了这一个抽象方法:吃饭, 所以 Person 就必须是抽象类,即在类定义之前必须冠以 abstract class Person, 以示它为 抽象类, 不可直接使用 new 操作符来创建实体。
然而,抽象类 Person 可以有实体。它可以通过它的子类(派生类)产生它的对象。所以,可以这样回答楼主的提问:"Java抽象类可以被实例化吗?" Java抽象类不可以直接 借助 操作符 new 被实例化,但它可以 通过完善了它的抽象方法定义的子类(派生类),借助 操作符 new ,产生它 (Person) 的对象。这种案例,被称作"向上转型 Upcasting"。其优点是,可以将多种不同子类对象的引用,储存于单一种类 (抽象类/父类 Person) 的 引用之中。案例见如下代码。
从这个案例也可见,在抽象类 Person 的定义之中, 定义一个静态的方法 getInstane(), 调用它也可以返回一个 Person 类的对象。表面上,似乎借用抽象类 Person 的名字来调用它的静态方法,也可以实例化抽象类 Person。但进一步追究这个方法的定义就会发现,它仍然要使用 操作符 new 和一个匿名(子)类来达到实例化抽象类 Person 的目的。
/*因为类的定义中有一个抽象方法:吃饭,
* 所以它必须是抽象类
*/
abstract class Person {
protected String name;
public Person(String name){
this.name=name;
}
/* 不同人吃饭方法不一样,放在一起定义太麻烦,
* 或不可能,因此只能冠以修饰符 abstract,
* 随后给出方法的签名
*/
abstract String havingDinner();
static Person getInstane(){
return new Person("Noname"){
String havingDinner(){
return name + " is using unknown manner to eat. 无名氏用未知方式吃饭。";
}
};
}
}
class Chinese extends Person{ // 具体定义中国人
public Chinese(String name){
super(name);
}
public String havingDinner(){ //吃饭具体化
return name + "用筷子吃饭。";
}
}
class American extends Person{ // 具体定义美国人
public American(String name){
super(name);
}
public String havingDinner(){ //吃饭具体化
return name + "用刀叉吃饭。";
}
}
class Indian extends Person{ //具体定义印度人
public Indian(String name){
super(name);
}
public String havingDinner(){ //吃饭具体化
return name + "用手抓着吃饭。";
}
}
public class AbstractTest {
public static void main(String args[]){
Person p[]=new Person[4];
p[0] = new Chinese("李明");
p[1] = new American("特朗普");
p[2] = new Indian("桑贾伊·甘地");
p[3] = Person.getInstane();
for (Person r:p)
System.out.println(r.havingDinner());
}
}
输出:
李明用筷子吃饭。
特朗普用刀叉吃饭。
桑贾伊·甘地用手抓着吃饭。
Noname is using unknown manner to eat. 无名氏用未知方式吃饭。
顺便提一点: "向上转型 Upcasting" 一般都可行,因为,无论中国人、美国人、还是印度人,他/她们都是人。然而,"向下转型 Downcasting",即 将 Person 类型的引用,强制转换成其子类的引用,是有条件的。条件是:Person 的引用,必须是调用这个给定的子类的构造方法而创建的。这是因为:一个人不一定就是 中国人、美国人、或是印度人。
参考: 看深入jvm遇到的问题,为什么输出是外部类的方法结果