抽象方法和抽象类
抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
抽象方法和抽象类的规则如下:
- 抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。
- 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。
- 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)五种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
- 含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。
注意:
归纳起来,抽象类可用“有得有失”4个字来描述。“得”指的是抽象类多了一个能力:抽象类可以包含抽象方法;“失”指的是抽象类失去了一个能力:抽象类不能用于创建实例。
定义抽象方法只需在普通方法上增加abstract修饰符,并把普通方法的方法体(也就是方法后花括号括起来的部分)全部去掉,并在方法后面增加分号即可。
注意:
抽象方法和空方法体的方法不是同一个概念。例如,public abstract void test();是一个抽象方法,它根本没有方法体,即方法后面没有一对花括号;但public void test(){}方法是一个普通方法,它已经定义了方法体,只是方法体为空,即它的方法体什么也不做,因此这个方法是不可使用abstract来修饰。
定义抽象类只需在普通类上增加abstract修饰符即可。甚至一个普通类(没有包含抽象方法的类)增加abstract修饰符后也将变成抽象类。
下面定义一个Shape抽象类。
public abstract class Shape {
{
System.out.println("执行Shape的初始化块。。。");
}
private String color;
//定义一个计算周长的抽象方法
public abstract double calPerimeter();
//定义一个返回形状的抽象方法
public abstract String getType();
//定义Shape的构造器,该构造器并不是用于创建Shape对象
//而是被子类调用
public Shape(){
}
public Shape(String color){
System.out.println("执行Shape的构造器。。。");
this.color=color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
上面的Shape类里包含了两个抽象方法:calPerimeter()和getType(),所以这个Shape类只能被定义成抽象类。Shape类里既包含了初始化块,也包含了构造器,这些都不是在创建Shape对象时被调用的,而是在创建其子类的实例时被调用。
抽象类不能用于创建实例,只能当做父类被其他子类继承。
下面定义一个三角形类,三角形类被定义成普通类,因此必须实现Shape类里的所有抽象方法。
public class Triangle extends Shape {
//定义三角形的三边
private double a;
private double b;
private double c;
public Triangle(String color,double a,double b,double c){
super(color);
this.setSides(a,b,c);
}
public void setSides(double a,double b,double c){
if(a>=b+c||b>a+c||c>a+b){
System.out.println("三角形的两边之和必须大于第三边");
return;
}
this.a=a;
this.b=b;
this.c=c;
}
//重写Shape类的计算周长的抽象方法
public double calPerimeter(){
return a+b+c;
}
//重写Shape类的返回形状的抽象方法
public String getType(){
return "三角形";
}
}
上面的Triangle类继承了Shape抽象类,并实现了Shape类中的两个抽象方法,是一个普通类,因此可以创建Triangle类的实例,可以让一个Shape类型的引用变量指向Triangle对象。
下面再定义一个Circle普通类,Circle类也是Shape的一个子类。
public class Circle extends Shape {
private double radius;
public Circle(String color,double radius){
super(color);
this.radius=radius;
}
public void SetRadius(){
this.radius=radius;
}
//重写Shape类的计算周长的抽象方法
public double calPerimeter(){
return 2*Math.PI*radius;
}
//重写Shape类的返回形状的抽象方法
public String getType(){
return getColor()+"圆形";
}
public static void main(String[] args) {
Shape s1=new Triangle("黑色", 3, 4, 5);
Shape s2=new Circle("黄色",3);
System.out.println(s1.getType());
System.out.println(s1.calPerimeter());
System.out.println(s2.getType());
System.out.println(s2.calPerimeter());
}
}
上面的main()方法中定义了两个Shape类型的引用变量,它们分别指向Triangle对象和Circle对象。由于在Shape类中定义了calPerimeter()方法和getType()方法,所以程序可以直接调用s1变量和s2变量的calPerimeter()方法和getType()方法,无须强制类型转换为其子类类型。
利用抽象类和抽象方法的优势,可以更好的发挥多态的优势,使得程序更加灵活。
当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用。
注意:
abstract不能用于修饰成员变量,不能用于修饰局部变量,即没有抽象变量、没有抽象成员变量等说法;abstract也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器。
除此之外,当使用static修饰一个方法时,表明这个方法属于该类本身,即通过类就可以调用该方法,如果过该方法被定义成抽象方法,则将导致通过该类来调用该方法时出现错误(调用了一个没有方法体的方法肯定会引起错误)。因此static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法。
注意:
static和abstract并不是绝对互斥的,static和abstract虽然不能同时修饰某个方法,但他们可以同时修饰内部类。
abstract关键字修饰的方法必须被其子类重写才有意义,否则这个方法将永远不会有方法体,因此abstract方法不能被定义为private访问权限,即private和abstract不能同时修饰方法