5.5 抽象类
Posted weststar
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5.5 抽象类相关的知识,希望对你有一定的参考价值。
一、为什么要使用抽象类、抽象方法
当编写一个类时,常常会定义一些类用于描述该类的行为方式,那么这些方法都有具体的方法体。但在某些情况下,某个父类只知道其子类应该包含什么样的方法,但无法准确知道这些子类的如何实现这些方法。
例如:定义了一个Shape类,这个类应该提供一个计算周长的方法calPerimeter(),但不同的Shape子类对计算周长的方法是不一样的,即Shape类无法准确知道不同的子类计算周长的方法。但是如果不在Shape类中声明calPerimeter()方法,我们定义了Shape类型的三角形和矩形实例(都是从Shape类的子类派生出来的,且在子类中有定义calPerimeter()方法),这时我们就无法调用calPerimeter()方法,因为Shape类的实例不含calPerimeter()方法。如果必须调用calPerimeter()方法,我们就必须将两个实例强制转换为三角形和矩形类型。
如何既让Shape类里包含calPerimeter()方法,有无须提供其方法的实现呢?使用抽象类即可满足该要求:抽象方法只有方法签名,没有实现的方法。
二、抽象方法和抽象类
抽象类和抽象方法必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
抽象方法和抽象类的规则:
1、都必须必须由abstract修饰,抽象方法不能有方法体。
2、抽象类不能被实例化。即使抽象类里不包含抽象方法,这个抽象方法也不能创建实例。
3、抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类主要用于派生子类。
4、含有抽象方法的类只能被定义成抽象类。
抽象类可以用“有得有失”来形容,得——抽象类获得了抽象方法;失——抽象类不能创建实例,只能派生子类。
1 abstract class Shape 2 { 3 {System.out.println("执行Shape的初始化块");} 4 private String color; 5 //定义一个计算周长的抽象方法 6 public abstract double calPerimeter(); 7 //定义一个返回形状的方法 8 public abstract String getType(); 9 //定义Shape构造器,该构造器并不是用于创建Shape对象的 10 //而是用于被子类调用 11 public Shape(){} 12 public Shape(String color) 13 { 14 System.out.println("执行Shape的构造器..."); 15 this.color=color; 16 } 17 18 //color的setter()和getter()方法 19 public void setColor(String color) 20 { 21 this.color=color; 22 } 23 public String getColor() 24 { 25 return this.color; 26 } 27 }
上面的Shape类里的包含了两个初始化方法:calPerimeter()和getType(),所以这个类只能被定义成抽象类。Shape类既包含初始化块,也包含构造器,这些都不是在创建Shape对象时调用,而是在创建其子类的实例时被调用。
下面定义一个三角形类,三角形被定义成普通类,因此必须实现Shape类里的所有方法。该类可以创建实例
1 public class Triangle extends Shape 2 { 3 //定义三角形的三边 4 private double a; 5 private double b; 6 private double c; 7 public Triangle(String color,double a,double b,double c) 8 { 9 super(color); 10 this.setSides(a,b,c); 11 } 12 13 public void setSides(double a,double b,double c) 14 { 15 if(a>=b+c||b>=a+c||c>a+b) 16 { 17 System.out.println("三角形两边之和必须大于第三边"); 18 return; 19 } 20 else 21 { 22 this.a=a; 23 this.b=b; 24 this.c=c; 25 } 26 } 27 28 //重写父类的计算周长的抽象方法 29 @Override 30 public double calPerimeter() 31 { 32 return a+b+c; 33 } 34 //重写父类中的返回形状的抽象方法 35 public String getType() 36 { 37 return "三角形"; 38 } 39 }
下面再定义一个Circle普通类,Circle类也是Shape类的一个子类
1 class Circle extends Shape 2 { 3 private double radius; 4 public Circle(String color,double radius) 5 { 6 super(color); 7 this.radius=radius; 8 } 9 10 public void setRadius(double radius) 11 { 12 this.radius=radius; 13 } 14 //重写Shape类计算周长的抽象方法 15 @Override 16 public double calPerimeter() 17 { 18 return 2*Math.PI*radius; 19 } 20 @Override 21 //重写父类的返回形状的方法 22 public String getType() 23 { 24 return getColor()+"圆形"; 25 } 26 }
测试上面的类:
1 class ShapeTest 2 { 3 public static void main(String[] args) 4 { 5 //我们不能创建Shape的实例 6 //Shape s=new Shape("黄色");//ShapeTest.java:6: 错误: Shape是抽象的; 无法实例化 7 Shape s1=new Triangle("黑色",3,4,5); 8 Shape s2=new Circle("蓝色",3); 9 System.out.println(s1.getType());//三角形 10 System.out.println(s1.calPerimeter());//12.0 11 12 System.out.println(s2.getType());//矩形 13 System.out.println(s2.calPerimeter());//18.84955592153876 14 } 15 } 16 ---------- 运行Java捕获输出窗 ---------- 17 执行Shape的初始化块 18 执行Shape的构造器... 19 执行Shape的初始化块 20 执行Shape的构造器... 21 三角形 22 12.0 23 蓝色圆形 24 18.84955592153876 25 26 输出完成 (耗时 0 秒) - 正常终止
上面的main()方法定了两个Shape类型的引用变量,分别指向Triangle对象和Circle对象。由于在Shape类中定义了calPerimeter()方法和getType()方法,所以程序可以直接调用s1变量和s2变量的calPerimeter()和getType()方法,无需强制转换为其子类类型。
利用抽象类和抽象方法的优势,可以很好地发挥多态的优势。当使用abstract修饰类时,表面这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须有子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用。
注意:
(1)abstract只能修饰类、方法,不能修饰成员变量、构造器、初始化块。
(2)static修饰一个方法时,表明这个方法属于类本身,即通过该类就可以调用该类本身。但如果该方法被定义成抽象方法,则将导致通过类来调用方法时出现错误(调用一个没有方法体的方法肯定会引起错误)。因此static和abstract不能同时修饰一个方法,即没有所谓的类抽象方法。
(3)static和abstract并不是绝对互斥的,static虽然不能同时修饰某个方法,但他们可以同时修饰内部类。
(4)abstract关键字修饰的方法必须被子类重写才有意义,否则该方法将永运不会有方法体。因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。
三、抽象类的作用
抽象类不能创建实例,只能当成父类来继承。从语法角度来讲,抽象类是从多个具体的类中抽象出来的父类,它具有更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。
如果编写一个抽象父类,父类提供多个子类的通用方法,并把一个或多个方法留给其子类来实现,这就是一种模板模式。
以上是关于5.5 抽象类的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段