首先在了解抽象类和接口之前,我们需要知道什么是继承?什么是重写?
而在知道抽象类和接口之后,我们就需要知道什么是多态?
(主要是了解怎样用抽象类和接口去实现多态)
目录
进入正题:
1.继承
我们都知道 Java 是一门面向对象编程的语言,而对象说穿了就是 Java 中的 类(class)。
那继承是什么呢?
举个例子:
我先创建一个 Dog 类,其中有 name ,leg 属性字段,同时还有一个 cry 方法和一个eat方法。
在创一个Bird类,其中也有name,wing属性字段,同时还有一个 fly 方法和一个 eat 方法。
//这便是我们平常所创建的类
public class Dog {
public String name; //名字
public String leg; //几条腿
public void cry(){
System.out.println(this.name+"汪汪叫!");
}
public void eat(){
System.out.println(this.name+"吃东西");
}
}
public class Bird {
public String name; //名字
public String wing; //翅膀
public void fly(){
System.out.println(this.name+"会飞");
}
public void eat(){
System.out.println(this.name+"吃东西");
}
}
通过观察上面这两个类,我们可以发现它们有共同的属性和方法。可以想象一下,如果我们需要许多的类,恰巧这些类中有许多重复的代码,那么就会浪费我们大量的时间。
所以 继承 便出来了。
将上面的代码使用继承进行处理:
//Dog 子类继承于 Animal 父类
public class Dog extends Animal{
public String leg; //几条腿
public Dog(String name){
super(name);
}
public void cry(){
System.out.println(this.name+"汪汪叫!");
}
}
//Bird 子类继承于 Animal 父类
public class Bird extends Animal{
public String wing; //翅膀
public Bird(String name) {
super(name);
}
public void fly(){
System.out.println(this.name+"会飞");
}
}
//父类
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(){
System.out.println(name+"吃东西");
}
}
在上面的代码中,我们将 Dog 类 和 Bird 类中的重复性的属性和方法,都放到了一个单独的 Animal 类中,然后让 Dog 类和 Bird 类使用 extends 关键字继承 Animal 类。
这时,我们就称 Dog 类和 Bird 类为子类(或者叫派生类)
Animal 类称父类(或者叫基类,超类)
那么怎么使用父类的属性和方法呢?
//建一个测试用的类
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小白");
System.out.println(dog.name);
dog.eat();
Bird bird = new Bird("小黄");
System.out.println(bird.name);
bird.fly();
}
}
运行结果:
说明子类能够调用父类的方法和属性。
2.重写
通过上面的代码,我们可以发现子类可以调用所继承的父类的方法和属性。
但是如果父类的方法,在子类中也有呢?
这时调用该方法,运行的是子类的方法还是父类的方法?
下面来尝试一下:
//我们在Dog类和Bird类中都写上它们的eat方法
public class Dog extends Animal{
public String leg; //几条腿
public Dog(String name){
super(name);
}
public void cry(){
System.out.println(this.name+"汪汪叫!");
}
public void eat(){
System.out.println("我是子类Dog的eat方法!");
}
}
public class Bird extends Animal{
public String wing; //翅膀
public Bird(String name) {
super(name);
}
public void fly(){
System.out.println(this.name+"会飞");
}
public void eat(){
System.out.println("我是子类Bird的eat方法!");
}
}
//父类的eat方法不变
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(){
System.out.println(name+"吃东西");
}
}
然后调用测试类:
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小白");
System.out.println(dog.name);
dog.eat();
Bird bird = new Bird("小黄");
System.out.println(bird.name);
bird.eat();
}
}
运行结果:
说明只要子类和父类有相同的方法(返回类型相同,方法名相同,参数列表相同),子类调用该方法时,就会调用子类自己的方法。
我们称这一现象为:重写,就是说子类重写了父类的方法。
3.抽象类
在上面的例子中,Animal 这个父类里面拥有子类Dog和Bird共同的方法和属性。并且Animal这个父类唯一的作用就是被子类继承。
所以在Java中专门为这种类起了个名字,并且用一个关键字修饰 (abstract)——抽象类
抽象类不能被实例化,只能被别的实体类所继承。
抽象类中除了拥有普通的方法和属性,还拥有抽象方法,同样是使用 abstract 修饰。
那么什么是抽象方法呢?
用例子更好理解:
//这是一个abstract关键字修饰的抽象类
public abstract class Animal {
public String name; //普通成员变量
public Animal(String name) { //构造方法
this.name = name;
}
public void run(){ //普通方法
System.out.println("跑起来");
}
//这是一个被abstract关键字修饰的抽象方法,它没有身体,必须被子类重写。
public abstract void eat();
}
同时需要注意,有抽象方法的类一定是抽象类,而抽象类不一定有抽象方法。
4.接口
在上面我们知道了什么是抽象类,那么接口就很好理解了。
接口就是一种特殊的抽象类,为什么特殊?
因为如果一个类是接口,那么它一定是抽象类,反之一个类是抽象类,那么它不一定是接口。
(一个接口需要用关键字 interface 来修饰)
下面还是用Animal这个类来举例:
//这是一个interface关键字修饰的接口
public interface Animal {
//public static final 修饰的属性常量。
public static final String name = "小黄";
int age = 20; //在接口中无论你写不写,属性默认都是public static final修饰
//接口中只能有抽象方法,实体类实现这个接口后,必须要重写这个抽象方法
public abstract void eat();
//或者default修饰的方法,实体类可以不用重写这个方法
default void run(){
}
//或者是静态方法
public static void cry(){
System.out.println("叫声");
}
}
当我们知道了接口怎么定义后,就会想它到底能干嘛?
我们知道在Java中一个实体类,只能 继承 一个父类。所以有时候我们就会发现一个事物的父类有很多。所以为了解决这个问题,Java中就引入了一个概念——接口。
这里要说一下类之间的关系:
实体类与抽象类之间是继承关系。
实体类与接口之间是实现关系。
接口与接口之间也是继承关系。
这样的话,一个实体类就可以继承一个抽象类,并实现多个接口。从而满足它所需要的方法和属性
5.多态
概念性的讲,就是 “一个引用, 能表现出多种不同形态”
通过举例能更好的理解:
//定义一个图形的父类
abstract class Shape{
public abstract void print(); //这是一个需要被重写的抽象方法
}
//三角形
class Triangle extends Shape{
//重写父类的print方法
@Override
public void print() {
System.out.println("△");
}
}
//矩形
class Rectangle extends Shape{
//重写父类print方法
@Override
public void print() {
System.out.println("□");
}
}
//圆圈
class Circle extends Shape{
//重写父类print方法
@Override
public void print() {
System.out.println("○");
}
}
//使用多态需要满足两个条件
//1.向上转型:父类引用 引用 子类对象
// 例如 Shape x = new Triangle();
//2.重写父类方法。
public class Test {
public static void draw(Shape x){ //这里是用父类对象的引用接收
x.print();
}
public static void main(String[] args) {
Triangle triangle = new Triangle();
Rectangle rectangle = new Rectangle();
Circle circle = new Circle();
//调用draw方法,就发生了多态
draw(triangle); //传过去的是子类对象的引用
draw(rectangle);
draw(circle);
}
}
运行结果:
有时候理解一个概念,往往不需要用很复杂的例子,相反越简单,越通俗易懂。