【java基础|第十六篇】面向对象(六)——抽象和接口
(四)面向对象
7、抽象
(1)为什么要有抽象?
父类中有些方法功能不明确,不明确的方法不知道如何定义方法体;在实际的使用中,本身也不会创建父类对象,一般都是一个具体的子类对象,实际开发中,创建父类对象意义不大,调用它的方法也是没有什么意义的。
那就要想办法不定义功能不明确的方法的方法体,还能被子类继承重写;并且要使父类不能被实例化,且子类不受影响,如果将父类的构造方法私有化,那么子类的构造方法调用父类的构造方法时就遇到了问题,因此就出现了抽象!
(2)抽象的概念:
抽象是一种将复杂的 概念和现实世界问题简化为更易于理解和处理的表示方法。
在计算机科学和编程中,抽象是一种关注问题的本质和关键特征,而忽略具体实现细节的方法。
在面向对象编程中,抽象是通过定义类、接口和方法来实现的。
(3)抽象方法:
使用的情形:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中 给出具体的实现,就可以将该方法定义为抽象方法。
抽象方法的定义格式: public abstract 返回值类型 方法名(参数列表);
注意:只有方法的定义,没有方法体。
(4)抽象类:
使用的情形:
只要类中有抽象方法,这个类就要声明为抽象类。
抽象类的定义格式:
【权限修饰符】 abstract class 类名{ }
抽象类的特点:
1、抽象类可以包含也可以不包含抽象方法;但包含抽象方法的类一定是抽象类
2、抽象类不能实例化创建对象
3、除了不能实例化外,其余与普通类一样,可以被继承,方法也可以被重写。
继承抽象类的子类的特点:
1、继承父类后,要重写父类中1所有的抽象方法
注意:如果不重写,或者不删除abstract,则子类中有了抽象方法,子类就变成了抽象类,无法实例化创建对象。
2、子类可以有自己特有的方法或成员
3、子类可以调用父类的构造方法
抽象类与多态
实际开发中,抽象类一般用于多态,抽象类引用指向子类对象。
(5)总结:
抽象带来的变化:1、父类无法再实例化
2、父类中有些未知具体功能的方法可以成为抽象方法,不需要在定义方法体,且子类继承时必须重写。
除了以上的两点,抽象类和普通类的用法并没有太大区别。
注意:abstract不能修饰属性,只能修饰类和方法
案例:
父类Animal类:
//该类中包含抽象方法,故而必须声明为抽象类
public abstract class Animal {//抽象类可以包含数据成员private String color;private int age;//抽象类中可以包含抽象方法public Animal() {}public Animal(String color, int age) {this.color = color;this.age = age;}@Overridepublic String toString() {return "Animal [color=" + color + ", age=" + age +"]";}//因为eat和sleep在父类中无法确定其具体功能,故而设置为抽象方法public abstract void eat();public abstract void sleep();
}
子类Cat类:
public class Cat extends Animal {//温顺度:1-10private int meekValue;public Cat() {}public Cat(String color, int age, int meekValue) {super(color,age);this.meekValue = meekValue;}//子类重写所有的抽象方法@Overridepublic void eat() {System.out.println("猫吃鱼");}@Overridepublic void sleep() {System.out.println("躺着睡");}@Overridepublic String toString() {String msg = super.toString();msg = "Cat: 温顺度=" + this.meekValue + ", " +super.toString();return msg;}
}
抽象子类Dog类:
//子类中包含抽象方法,故而必须声明为抽象类
//不可以实例化对象
//public class Dog extends Animal {
public abstract class Dog extends Animal {//忠诚度private int loyalValue;public Dog() {}public Dog(String color, int age, int loyalValue) {super(color,age);this.loyalValue = loyalValue;}//子类重写部分抽象方法@Overridepublic void eat() {System.out.println("狗吃肉");}//子类不重写sleep()抽象方法,则当前类仍旧包含抽象方法// @Override// public void sleep() {// System.out.println("趴着睡");// }@Overridepublic String toString() {String msg = super.toString();msg = "Dog: 忠诚度=" + this.loyalValue + ", " +super.toString();return msg;}
}
测试类::
public class Test {public static void main(String[] args) {//1.抽象父类 不可以实例化 对象//Animal a = new Animal(); //编译报错 error//2.抽象父类引用 可以指向 正常子类对象Animal c = new Cat("yellow", 2, 6);c.eat();c.sleep();System.out.println(c);System.out.println("------------");//3.子类如果是抽象类,不可以实例化对象// Animal d = new Dog("black", 2, 8); //编译报错// d.eat();// d.sleep();// System.out.println(d);}
}
8、接口
(1)概念:
接口是引用数据类型之一,他不是类,虽然他的编译结果也是.class文件
Java只支持单继承,拓展功能存在局限性,接口是对单继承的补充,接口可以多实现
接口也是一种抽象机制,提供了一种定义行为规范和实现多态性的方式
(2)优点:
合理使用接口,可以提高代码的可拓展性,可维护性和灵活性。
(3)接口的定义方式:
[修饰符] interface 接口名 {//数据成员,可以定义多个[public static final] 数据类型 数据成员 = 值;//抽象方法:可以定义多个[public abstract] 返回值类型 方法名(形参列表);
}
注意:修饰符是固定的,可以省略
接口不能被实例化
接口中的属性在实现接口的类中可以直接使用,但注意不能修改初始值,可以通过接口访问,也可以通过属性名访问
接口中只与抽象方法,没有构造方法,至于普通方法能否定义与版本有关
JDK8中,还允许在接口中编写静态方法和默认方法
JDK9中,还允许在接口中编写私有方法
(4)接口的继承:
Java中,类和类之间是单继承关系,接口和接口之间是多继承
[修饰符] interface 子接口 extends 父接口1,父接口2... {//新增成员或抽象方法
}
(5)接口的实现:
类和接口是实现关系,通过implements关键字表示,可以是单实现,也可以是多实现 ;子类还可以在继承一个父类的同时实现多个接口
//一个类可以同时实现多个接口
[修饰符] class 类名 implements 接口名1,接口名2,... {重写所有抽象方法
}
注意:接口的实现类可以是普通类也可以是抽象类。
接口实现多态的写法:接口引用指向实现类对象
接口名 对象名 = new 实现类();
注意:接口多态应用,依旧是编译看左边,运行看右边;
接口引用只能调用接口中包含的方法,成功调用后的是重写的方法。
案例:
interface IAction {int NUM = 10;void start();public abstract void end();
}//定义接口的实现类
class ActionImpl implements IAction {@Overridepublic void start() {System.out.println("start开始执行 ");}@Overridepublic void end() {System.out.println("执行完成,end结束");}
}public class Test {public static void main(String[] args) {//1.接口不能实例化对象,下面一行编译报错//IAction ic = new IAction();//2.接口引用 指向实现类对象IAction ac = new ActionImpl();//3.接口数据成员访问测试System.out.println(IAction.NUM);System.out.println(ac.NUM);System.out.println("-------------");//4.通过接口引用 调用 重写方法(多态体现)ac.start();ac.end();}
}
(6)接口与抽象类的区别:
语法结果的区别:
1. 定义方式:抽象类通过使用 abstract 关键字来定义,而接口使用 interface 关键字来定义
2. 实现方式:一个类可以继承(extends)一个抽象类,而一个类可以实现(implements)多个接口
3. 构造函数:抽象类可以有构造函数,而接口不能有构造函数
4. 方法实现:抽象类可以包含具体的方法实现,而接口只能包含抽象方法,即没有方法体的方法声明
5. 多继承:Java不支持多继承,一个类只能继承一个抽象类,但可以实现多个接口
6. 数据成员:抽象类可以包含普通数据成员和static数据成员,而接口只能包含 static final 修饰的数据成员
设计理念上的区别:
不同的实现类之间体现 like a 的关系 ,接口更加强调行为规范的定义,适用于多个类具有相同行为规范的情况。
例如:飞机具备飞翔的行为,鸟也具备飞翔的行为,此时我们就可以定义接口包含抽象方法fly(),然后让飞机和鸟分别去实现该接口。飞机 like a 鸟,因为它们都会fly()。
子类和抽象父类体现的是 is a 的关系 ,抽象类归根到底还是类,它比较特殊,不能被实例化,只能被继承。抽象类用于定义一种通用的模板或者规范,其中可包含了一些具体数据成员、方法实现和抽象方法声明。
例如:前面案例中的形状类Shape,它里面包含方法getArea(),但该方法功能不确定,所以定义成抽象方法,而包含了抽象方法的类Shape也必须被声明为抽象类。定义子类圆形类,其getArea()方法功能是明确的,则子类中重写方法。