目录
Java中定义抽象类或者抽象方法使用 abstract 关键字声明
1. 抽象方法所在的类必须使用 abstract 声明为抽象类。
2. 若一个类使用 abstract 声明为抽象类,无法直接通过该类实例化对象。
3. 子类继承了抽象类,就必须强制子类覆写抽象类中的所有抽象方法(子类是普通类)。子类若是抽象类,可以选择性的覆写父类的抽象方法。
5. 在定义抽象方法时,abstract和final能否同时出现?
6. 在定义抽象类时,abstract和final能否同时出现?
抽象类
引言
现在有这样的需求∶描述形状的类Sharp。子类:三角形,正方形,圆形。
要求:创建一个方法可以接受 Sharp 以及其所有子类的对象,调用print()向上转型用在方法参数
//Crycle.java Crycle类单独的java文件 public class Crycle extends Sharp { // 快速覆写的快捷键 // alt + insert 或者直接写方法名 @Override public void print() { System.out.println("⚫"); } } //Triangle.java Triangle类单独的java文件 public class Triangle extends Sharp { public void print() { System.out.println("🔺"); } } //Square.java Square类单独的java文件 public class Square extends Sharp { @Override public void print() { System.out.println("⬛"); } } //Test.java Test类单独的java文件 public class Test { public static void main(String[] args) { fun(new Crycle()); fun(new Square()); fun(new Triangle()); } public static void fun(Sharp sharp) { //这个方法可以接受所有Sharp类,调用其print打印图形 sharp.print(); } }
若新建子类 Diamond,子类没有覆写 print 方法。
执行 fun(new Diamond()); 则不会打印。
package interface_Test.sharp; public class Diamond extends Sharp{ }
概念
若需要强制要求子类覆写方法,就要用到抽象类
现实生活中有很多的抽象类,这些类都是概念化的,没法具体到某个实例。这类抽象类可以描述这一类对象共同的属性和行为。例如:人类——>抽象类,没法对应到具体某个或者某一类人。中国人,日本人,印第安人,智人等都是人类。
此时 Sharp 类,包括 Sharp 类的 print() 方法都是抽象概念,应该设置为抽象类。
抽象类是普通类的"超集",只是比普通类多了一些抽象方法(0~n个)。换言之,普通类有的抽象类全有。
抽象方法所在的类必须是抽象类,子类若继承了抽象类,必须覆写所有抽象方法(前提:子类是普通类)。
Java中定义抽象类或者抽象方法使用 abstract 关键字声明
1. 抽象方法所在的类必须使用 abstract 声明为抽象类。
抽象方法指的是使用 abstract 关键字声明(只有方法声明,没有方法体)。它在抽象类中没有实现,延迟到子类实现。
//一个类若存在抽象方法,必须使用 abstract 声明为抽象类 public abstract class Sharp { public abstract void print();//只有方法声明,没有方法体{} }
问题:Java中没有方法体的方法就是抽象方法。
答:错误,本地方法不是抽象方法它也没有方法体。本地方法,不是抽象方法,这个方法由 JVM 实现,JVM是C++写的。
本地方法指的就是Java 通过JNDI(本地方法平台)调用C++的同名方法。
2. 若一个类使用 abstract 声明为抽象类,无法直接通过该类实例化对象。
当一个类是抽象类,不管他有没有抽象方法,这个类本身就是一个抽象的概念,没法具体到某个特定的实例。
只能通过子类向上转型变为抽象父类的引用。
例如:形状 Sharp 是抽象类,无法创建对象。
Sharp sharp = new Sharp(); //错误
例如:人类 Person 是抽象类,China 类为其普通子类。
Person per = new Person(); //错误 Person per = new China(); //正确,向上转型
3. 子类继承了抽象类,就必须强制子类覆写抽象类中的所有抽象方法(子类是普通类)。子类若是抽象类,可以选择性的覆写父类的抽象方法。
此时满足单继承局限,一个子类只能 extends 一个抽象类。
abstract class A { abstract void printA(); } //B是抽象类,可以选择性的覆写父类A的抽象方法 abstract class B extends A { abstract void printB(); } //普通类 C 是抽象类 B 的子类,必须覆写B中所有的抽象方法(包括继承来的方法) public class C extends B { @Override void printB() {} @Override void printA() {} }
4. 抽象类是普通类的超集
普通类有的内容,抽象类全都有,只是比普通类多了一些抽象方法而已。
抽象类虽然没法直接实例化对象,但是也可以存在构造方法。子类在实例化时,仍然遵从继承的规则,先调用父类(抽象类)的构造方法,而后调用子类构造方法。
问题:求输出结果
abstract class BaseTest { public BaseTest() { this.print(); } abstract void print(); } public class Fun extends BaseTest { private int num = 10; @Override void print() { System.out.println("num = " + num); } public static void main(String[] args) { new Fun(); } }
答:
5. 在定义抽象方法时,abstract和final能否同时出现?
不能!抽象方法必须被覆写,final修饰的方法无法被覆写,二者相互矛盾。
6. 在定义抽象类时,abstract和final能否同时出现?
不能!抽象类必须得有子类,final修饰的类没有子类,二者相互矛盾。
接口
引言
抽象类虽然没法直接实例化对象,子类仍然满足 is a 原则,子类和抽象父类之间仍然是满足"继承树"的关系。
例如:Person 对于 China、Sharp 对于 Cycle 。
一般来说,接口的使用表示两种场景
1. 接口表示一种规范或者标准。例如:"USB接口",5G标准。
2. 接口表示具备某种能力或行为。子类实现接口时不是 is a,而是具备这种行为或者能力。
例如:"游泳"——>能力或者行为,Person 满足游泳接口,Dog 也能满足游泳接口,Duck 也能满足游泳接口。
概念
接口中只有全局常量和抽象方法——>更加纯粹的抽象概念。其他东西统统没有。
接口使用关键字 interface 声明接口,子类使用 implements 实现接口。
1. 接口表示一种规范或者标准
USB接口表示一种规范。
package interface_Test.usb; //接口使用 interface 定义,只有全局常量、抽象方法 public interface USB { //插入 public abstract void plugIn(); //工作 public abstract void work(); }
子类使用 implements 实现接口,必须覆写所有的抽象方法。
鼠标,键盘外设都属于USB接口的子类。
//Mouse.java Mouse类单独的java文件 package interface_Test.usb; public class Mouse implements USB{ @Override public void plugIn() { System.out.println("安装鼠标驱动中"); } @Override public void work() { System.out.println("鼠标可以正常工作了"); } } //KetBoard.java KeyBoard类单独的java文件 package interface_Test.usb; public class KeyBoard implements USB{ @Override public void plugIn() { System.out.println("安装键盘驱动中"); } @Override public void work() { System.out.println("键盘可以正常工作了"); } }
电脑这个类算不算USB接口的子类?
所有带USB线插入到电脑的设备都应该满足USB规范。电脑是USB规范的使用者。public class Computer { //参数为何使用USB引用? //fun方法模拟电脑的USB的插口 //若fun方法参数为 Mouse 会如何? public void fun(USB usb) { usb.plugIn(); //插入 usb.work(); //工作 } }
对于电脑的使用者生产者来说,根本不关心到底哪个具体设备插入到我的电脑上,只要这个设备满足了USB接口,就能被电脑识别。
实现了一个接口接受无数种设备,只要这个设备满足USB接口,就可以插入到电脑且被电脑识别,兼容所有的USB子类对象。若fun方法参数为 Mouse 会如何?
fun(Mouse mouse) ——>这个插口只能插鼠标,键盘都无法识别,这是两个毫无关系的类。
运行测试
此时新增一个相机类
package interface_Test.usb; public class Computer { public static void main(String[] args) { Computer computer = new Computer(); Mouse mouse = new Mouse(); //插入鼠标 computer.fun(mouse); KetBoard ketBoard = new KetBoard(); //插入键盘 computer.fun(ketBoard); Camera camera = new Camera(); //插入相机 computer.fun(camera); } public void fun(USB usb) { usb.plugIn(); usb.work(); } }
子类 Camera,对于 Computer 这个类来说,没有影响。
开闭原则——所有设计模式的核心思想,程序应当对扩展开放,对修改关闭。
即方便扩展,不能影响已经写好的程序。
2. 接口表示具备某种能力或行为
接口允许多实现,一个类可能具备多个能力,同时实现多个父接口。
例如:表示子类实现了多个父接口
public class Duck implements ISwim,IRun,IFly{ }
若实现多个父接口,子类普通类,需要覆写所有的抽象方法
package interface_Test.animal; //飞,表示具有飞的行为或能力 public interface IFly { public abstract void fly(); } package interface_Test.animal; //跑步接口,表示具有跑步的行为或能力 public interface IRun { public abstract void run(); } package interface_Test.animal; //游泳接口,表示具有游泳的行为或能力 public interface ISwim { public abstract void swim(); }
//Rabbit.java Rabbit类单独的java文件 package interface_Test.animal; public class Rabbit implements IRun { @Override public void run() { System.out.println("兔子在跑"); } } //Dog.java Dog类单独的java文件 package interface_Test.animal; public class Dog implements IRun,ISwim{ @Override public void run() { System.out.println("狗在跑"); } @Override public void swim() { System.out.println("狗在游泳"); } } //Duck.java Duck类单独的java文件 package interface_Test.animal; public class Duck implements ISwim,IRun,IFly{ @Override public void run() { System.out.println("鸭子在跑"); } @Override public void swim() { System.out.println("鸭子在游泳"); } @Override public void fly() { System.out.println("鸭子在飞"); } }
package interface_Test.animal; public class Test { public static void main(String[] args) { //接口不能实例化对象,需要向上转型 IRun iRun1 = new Rabbit(); IRun iRun2 = new Dog(); ISwim iSwim = new Dog(); IFly iFly = new Duck(); iRun1.run(); iRun2.run(); iSwim.swim(); iFly.fly(); } }
由于接口中只有全局常量和抽象方法
因此接口中 public abstract(抽象方法)、static final(常量)全都可以省略。
package interface_Test.animal; public interface IFly { //全局常量 static final String test = "飞飞飞"; void fly(); }
在接口声明中,这些关键字都不用写,只保留最核心的方法返回值、方法参数列表、名称即可。
package interface_Test.animal; //跑步接口,表示具有跑步的行为或能力 public interface IRun { String test = "跑跑跑"; void run(); } package interface_Test.animal; public class Rabbit implements IRun { @Override public void run() { System.out.println(test); System.out.println("兔子在跑"); } } package interface_Test.animal; public class Test { public static void main(String[] args) { IRun iRun1 = new Rabbit(); iRun1.run(); } }
3. 接口和类之间的关系
接口和接口之间也存在继承关系。接口坚决不能继承—个类。
interface IA { void testA(); } interface IB { void testB(); } interface IC extends IA, IB { @Override void testA(); @Override void testB(); }
IC接口同时继承多个父接口,继承了所有的抽象方法,子类(普通类)在实现IC接口时,必须覆写所有的抽象方法。
class CImpl implements IC{ @Override public void testA() { } @Override public void testB() { } }
如果一个类既需要继承—个类,又要同时实现多个接口,如何实现?
先使用extends继承一个类,而后使用implements实现多个父接口。abstract class Person{ } class China extends Person implements IA,IB{ @Override public void testA() { } @Override public void testB() { } }
接口的命名规范
1. 为了区分接口和类,命名接口使用 l 开头,如:IRun,ISwim。
2. 子类实现一个接口时,命名以相应的接口开头,以 Impl 结尾。
eg :如果是IRun的子类,Runlmpl(不是强制要求,尽量能做到以此命名);如果子类实现多个父接口,不需要使用此规范来命名。