首先在了解抽象类和接口之前,我们需要知道什么是继承?什么是重写?
而在知道抽象类和接口之后,我们就需要知道什么是多态?
(主要是了解怎样用抽象类和接口去实现多态)
目录
1.继承
2.重写
3.抽象类
4.接口
5.多态
进入正题:
我们都知道 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(); } }
运行结果:
说明子类能够调用父类的方法和属性。
通过上面的代码,我们可以发现子类可以调用所继承的父类的方法和属性。
但是如果父类的方法,在子类中也有呢?
这时调用该方法,运行的是子类的方法还是父类的方法?
下面来尝试一下:
//我们在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(); } }
运行结果:
说明只要子类和父类有相同的方法(返回类型相同,方法名相同,参数列表相同),子类调用该方法时,就会调用子类自己的方法。
我们称这一现象为:重写,就是说子类重写了父类的方法。
在上面的例子中,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(); }
同时需要注意,有抽象方法的类一定是抽象类,而抽象类不一定有抽象方法。
在上面我们知道了什么是抽象类,那么接口就很好理解了。
接口就是一种特殊的抽象类,为什么特殊?
因为如果一个类是接口,那么它一定是抽象类,反之一个类是抽象类,那么它不一定是接口。
(一个接口需要用关键字 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中就引入了一个概念——接口。
这里要说一下类之间的关系:
实体类与抽象类之间是继承关系。
实体类与接口之间是实现关系。
接口与类之间也是继承关系。
这样的话,一个实体类就可以继承一个抽象类,并实现多个接口。从而满足它所需要的方法和属性
概念性的讲,就是 “一个引用, 能表现出多种不同形态”
通过举例能更好的理解:
//定义一个图形的父类 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); } }
有时候理解一个概念,往往不需要用很复杂的例子,相反越简单,越通俗易懂。