当编写一个类时,我们往往会为该类定义一些方法,这些方法是用来描述该类的功能具体实现方式,那么这些方法都有具体的方法体。
但是有的时候,某个父类只是知道子类应该包含怎么样的方法,但是无法准确知道子类如何实现这些方法。比如一个图形类应该有一个求周长的方法,但是不同的图形求周长的算法不一样。那该怎么办呢?
分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是方法功能声明相同,但方法功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法。
一.抽象类
抽象类:使用abstract 关键字修饰的类叫做抽象类
抽象方法:使用abstract修饰的方法叫抽象方法,抽象方法不允许有方法体。
示例:
public abstract class Shape {
/**
* 抽象方法
* @return
*/
public abstract double daC();
}
为什么使用抽象类
抽象类专门用于继承关系,在继承关系中充当父类。
抽象类规则
(1)抽象类不允许被实例化,即不让new
抽象类不是一个完整的类,因为抽象类中可能有抽象方法,而抽象方法没有方法体,没有方法体的方法是半成品,所以不允许实例化。
(2)抽象类中可以有属性,普通方法,构造方法,main方法
(3)如果一个类中包含抽象方法,那么这个类必须是抽象类
(4)抽象类中可以没有抽象方法
(5)父类可以通过抽象方法要求子类实现抽象方法。
示例1:
public abstract class Shape {
//抽象类中可以定义属性
double param1;
double param2;
static final double PI = 3.14;
//抽象类中可以定义普通方法
public void sayHello(){ }
//抽象类中可以定义抽象方法
public abstract double daC();
//抽象类中可以定义main方法
public static void main(String[] args) {
}
}
示例2:
abstract class Shape {
public static final Double PI = 3.1415926;
//定义抽象方法
public abstract double calcC(double n1,double n2);
}
class Rectangle extends Shape{
//实现抽象方法
@Override
public double calcC(double n1, double n2) {
return (n1 + n2) * 2;
}
}
class Circle extends Shape{
//实现抽象方法
@Override
public double calcC(double n1, double n2) {
return 2 * Shape.PI * n1;
}
}
public class TestAbs{
public static void main(String[] args) {
//实例化Rectangle对象
Shape shape1 = new Rectangle();
double c1 = shape1.calcC(3, 4);
System.out.println(c1);
//实例化Circle对象
shape1 = new Circle();
double c2 = shape2.calcC(3, 4);
System.out.println(c2);
}
}
不与abstract共存
- private:私有的方法子类是无法继承到的,也不存在覆盖,而abstract和private一起使用修饰方法,abstract既要子类去实现这个方法,而private修饰子类根本无法得到父类这个方法。互相矛盾。
- final:final修饰的类不允许被继承,与abstract相悖。
- static:static是静态,只有一份,用于共享。abstract是为了让子类重写,是多份,用于子类私有。
优缺点
优点:抽象类中己经实现的方法可以被其子类使用,使代码可以被复用,同时提供了抽象方法,保证了子类具有自身的独特性。
缺点:在有些应用场合,仅仅使用抽象类和抽象方法会有一定的局限性。
二.接口
接口是interface,相当于抽象类,在继承关系中充当父类的角色,在接口中通过定义抽象方法来指定规范,子类去实现接口,要实现接口中的所有抽象方法。
作用
- 多继承
- 不同子类实现相同的行为
定义接口与定义规范
接口定义时需要使用interface关键字。
接口定义语法格式:
public interface 接口名 {
抽象方法1;
抽象方法2;
抽象方法3;
}
定义接口就是定义规范,接口中的抽象方法就是具体的规范。
实现类与遵循规范
接口实现语法格式:
class 类 implements 接口 {
重写接口中方法
}
在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
示例:
interface IShout extends IFly{//extends Object 接口不是类,所以没有默认继承Object
//接口没有最高层,类的最高层是Object
//接口中不允许有普通方法,所以接口中的方法都是抽象的
/*public void sayHello(){
}*/
//接口不是类,所以没有构造
/*
public IShout(){
}
*/
//定义常量
String TYPE="动物";
//定义抽象方法
void shout();
}
class Rabbit implements IShout{
//实现shout()方法
@Override
public void shout() {
}
//实现fly()方法
@Override
public void fly() {
}
}
实现接口就是遵循接口的规范。
接口规定
- 接口中可以定义变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量,其值不能改变。
- 接口中可以定义方法,方法也有固定的修饰符,public abstract
- 接口不可以new,不可以实例化。
- 子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类。
- 接口不能继承类
- 接口可以继承接口
- 接口没有最高层,类的最高层是Object
- 接口中不允许有普通方法,所以接口中的方法都是抽象(除了default方法和static方法)
- 接口不是类,所以没有构造
接口特点
实现接口与接口多继承,接口与类的实现
1.8接口新特性
1、default方法
示例:
interface IShout {
//定义默认方法,有方法实现,子类可以直接使用,也可以重写
public default void method(){
System.out.println(" interface default method ");
}
}
public class Rabbit implements IShout{
@Override
public void method() {
System.out.println(" rabbit method is running. ");
}
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
rabbit.method();
}
}
2、static方法
示例:
interface IShout {
//定义抽象方法,有方法实现,可以直接使用接口名调用
public static void method(){
System.out.println(" interface static method ");
}
}
public class Rabbit implements IShout{
public static void main(String[] args) {
IShout.method();
}
}
三.抽象类和接口的区别
相同点
(1)都不能被实例化
(2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。
不同点
(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
(3)接口强调特定功能的实现,而抽象类强调所属关系。
(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。