文章目录
面向对象编程
前言
关于访问修饰符
从小到大依次为:
private(私有,当前类的内部可见) < default(啥也不写就是包权限,当前包的内部可见,不包含子包,同级目录可见) < protected(继承,不同包的有继承关系的类之间可见) < public(公有,当前项目可见)
Java中的“包” 就是操作系统的文件夹。
声明一个包使用 package
关键字,在实际中,创建package 包,在包的命名时,若存在多个文件夹的嵌套,使用“.”分隔,就会产生嵌套的“包”。(需要在旁边的设置的符号中,取消 Compact Middle Packages 这个选项)
包的存在就是为了解决类同名的问题
如何导入某个包中的某个类
使用import 导入类,import
语句只能导入相关包中的某个具体的类
此时若还要用到这个包中的类,不需要一行行写,
import java.util.*
;
此时将整个util
包下的所有类按需加载,此时导入的还是类,而不是把util
文件夹导入!!!
一般不推荐 import .*;,会有歧义,若程序中用到了两个相同名称的类,并且两个类的包都使用 import .*; 直接导入类的包。此时会报错。
在程序中用到了两个相同名称的类,有两种方法:
- 使用类的全名称, 包名.类名
- import 明确指定导入的是哪个包下的哪个类,例如:
import java.util.Date;
静态导入:
import static 可以导入包中的静态方法和静态属性
常见的系统包:
java.lang
:JDK的基础类,System,String,Object都在这个包下,JDK1.1之后,这个包下的所有类自动导入
java.lang.reflect
: 反射开发包
java.util
: 工具包(集合类都在这个包下,Arrays,Linked List,HashMap)
java.io
: I/O开发包,文件读取和写入
java.net
: 网络编程开发包,Socket
java.sql
: 数据库开发需要的包
继承
面向对象一共有三大特性:封装,继承和多态。
封装:具有保护性和易用性(封装就有很多种表达形式)。
private
实现属性或方法的封装只是封装的其中一种。
那么什么是方法重载呢?
方法重载就是在同一个类中定义了多个方法名称相同,参数列表不同,与返回值无关的一组方法,这样的一组方法就被称之前方法重载。
来看这几个代码:
public class Vehicle {
public String name;
public void speed(int num){
System.out.println(name + "的速度是" + num + "Km/h");
}
}
public class Car {
public String name;
public void speed(int num){
System.out.println(name + "的速度是" + num + "Km/h");
}
}
public class Airplane {
public String name;
public void speed(int num){
System.out.println(name + "的速度是" + num + "Km/h");
}
}
这三个类的代码完全相同,正常来说,所有的交通工具都具有name属性,以及他的speed(速度)属性。
在这三类中,汽车是交通工具,飞机也是交通工具。所以,car 和 airplane 都应该是Vehicle 的子类。
当类和类之间满足一个类(a) is a 另一个类(b),一定存在继承关系。a 继承了b
当一个类继承了另一个类,另一个类中的所有属性和方法(包括静态的属性和方法) 子类就天然具备了。
在java中使用extends表示类的继承。
Car extends Vehicle
Car 就是子类/派生类, Vehicle 就是父类/基类。
那么上面的代码就可以变为
public class Vehicle {
public String name;
public void speed(int num){
System.out.println(name + "的速度是" + num + "Km/h");
}
}
public class Car extends Vehicle {
}
public class Airplane extends Vehicle{
}
注意:不能为了省略代码就简单的使用继承,要想使用继承必须要满足继承的关系,不满足继承关系的类之间千万不能使用继承。
继承的规则:
- 要能使用继承,必须满足类之间的 is a 关系
- 一个子类只能使用extends 继承一个父类。(单继承)
Java中不允许多重继承,extends 后面只能跟父类,但是允许多层继承,没办法当儿子,可以当孙子。
满足继承关系的类之间一定是逻辑上垂直的关系
-
子类会继承父类的所有属性和方法,显示继承 :public属性 和方法可以直接使用。 隐式继承: private 属性和方法,子类其实继承了这个属性和方法,但是无法直接使用。要看父类是否会提供
set get
权限给子类。子类继承父类的所有属性和方法,包括静态的属性和方法,区别就在于是通过子类对象访问,还是直接通过类名称进行访问。
就好比你现在要让你爸告诉你 他银行卡的密码是什么,你作为儿子,你可以知道,但是需要你爸自己愿意告诉你,你才能知道。否则你肯定不会知道。
关于protected(继承权限)访问权限
protected 访问权限:
-
protected 作用域 :当前类的内部可见,不同包中的有继承关系的类之间可见。
在不同包的子类中访问protected 权限修饰的方法和属性时,只能通过该子类对象进行访问,其他子类对象和父类对象均无法访问
-
同包下的没有关系的类可见
那为什么
protected
继承权限在同包下的没有关系的类中可见呢?因为
private < default(包权限) < protected < public , 既然
protected
权限大于包权限,那么包权限可见的范围,protected
继承权限一样可见。
注意: protected
权限的属性和方法 在不同包中 没有继承关系的类中 是不可见的
super 关键字
复习一下 this
关键字:
-
修饰属性,表示直接从当前类中调用同名属性 this.name
-
修饰方法,表示直接调用当前类的方法
调用普通成员方法——this.方法()写不写this都一样,因为不写的话,编译器编译之后会默认加
this
,构造方法的互相调用——不同参数的构造方法之间出现了重复的调用,那么这个时候就可以使用
this(参数)
调用其他的构造方法-
this 调用其他的构造方法必须放在当前构造方法的首行。
-
this 调用构造方法不能成”环“。(就是这样规定的)
-
-
表示当前对象的引用
super
关键字:
- 修饰属性,表示直接从父类中去寻找同名属性
- 修饰方法,表示直接从父类中去寻找方法
在学习super关键字之前,我们要了解:
当我们要产生一个子类对象,默认首先产生父类对象。
父类
package InheritTest;
//父类
public class Vehicle {
public Vehicle(){
System.out.println("1 Vehicle 的无参构造");
}
}
子类
package InheritTest;
public class Car extends Vehicle {
public Car() {
System.out.println("2 Car的无参构造");
}
}
测试类
package InheritTest;
public class Test {
public static void main(String[] args) {
Car car = new Car(); // 产生一个子类对象
}
}
此时的输出结构是:
1 Vehicle 的无参构造
2 Car的无参构造
当调用new Car 无参构造一个子类对象之前,默认先调用父类的构造方法产生父类对象,才会执行子类的构造方法
来看这个代码应该输出多少?
父类:
package Question;
public class B {
public B(){
System.out.println("1 B 的构造方法");
}
{
System.out.println("2 B 的构造块");
}
static {
System.out.println("3 B 的静态块");
}
}
子类:
package Question;
public class D extends B{
public D(){
System.out.println("4 D 的构造方法");
}
{
System.out.println("5 D 的构造块");
}
static {
System.out.println("6 D 的静态块");
}
public static void main(String[] args) {
System.out.println("7 main开始执行");
new D();
new D();
System.out.println("8 main 结束执行");
}
}
输出结果是:
3 B 的静态块
6 D 的静态块
7 main开始执行
2 B 的构造块
1 B 的构造方法
5 D 的构造块
4 D 的构造方法
2 B 的构造块
1 B 的构造方法
5 D 的构造块
4 D 的构造方法
8 main 结束执行
解答:
由于main方法在子类中,要执行main方法就要加载子类,
加载子类时 要首先加载父类,此时执行父类的静态块(3),
然后加载子类,执行子类的静态块(6),
main方法开始执行,打印7,
new D(); 产生一个子类,产生子类对象先要产生一个父类对象,产生父类对象时,先调用构造块(2)
因为静态块只在类加载的时候执行一次,后面不在执行
构造块每次在产生一个对象是都要执行
然后再调用构造方法(1),父类对象产生
产生子类对象时,也是先调用构造块(5),再调用构造方法(4),子类对象产生。
又new D(); 重复上面的步骤,2 1 5 4
最后打印8。
所以结果就是 3 6 7 2 1 5 4 2 1 5 4 8
super 修饰属性
super修饰属性:表示在父类中寻找同名属性
当在有继承关系的类中,this关键字默认先在当前类中寻找同名属性,若没有找到,继续向上寻找父类中是否有同名属性。
若此时有继承关系的子类中有一个name属性为 儿子,父类中也有一个name属性为父亲,那么在子类中直接打印name, 打印的是子类中的儿子,那我们怎么能够打印父类中的父亲呢,这时就可以使用 super 关键字,直接在父类中寻找name属性。
看看代码会更好理解:
父类
package SuperTest;
public class Father {
protected String name = "父亲";
}
子类
package SuperTest;
public class Son extends Father{
public void fun(){
System.out.println(this.name);
}
public static void main(String[] args) {
Son son = new Son();
son.fun();
}
}
此时的子类使用this
关键字来打印子类中的name属性,但是子类中没有name属性,就从父类中去寻找name属性,输出结果就是
父亲
再看这个代码:
父类
package SuperTest;
public class Father {
protected String name = "父亲";
}
子类
package SuperTest;
public class Son extends Father{
public String name = "儿子";
public void fun(){
System.out.println(this.name);
}
public static void main(String[] args) {
Son son = new Son();
son.fun();
}
}
此时的子类代码中已经有了name属性,就直接从子类中找到了name属性,输出结果就是
儿子
那此时我们想在这个子类在访问到父类的name属性,我们就是使用super关键字,把this.name
改为 super.name
,此时 子类就访问到了 父类的name属性。
注意:
super 先从直接父类中寻找同名属性,若不存在再向上寻找
this 直接从当前类中直接寻找,若不存在再向上寻找
super 修饰构造方法
用法:
- super(父类构造方法的参数);
- super();
super 的位置和this 一样,也要处于构造方法的首行
若父类中不存在无参构造,则子类构造方法的首行必须使用 super(有参构造)
但是若父类存在无参构造,则子类构造方法不需要再写 super(); 调用无参构造,JVM会默认调用(隐式调用)
还要注意,在一个构造方法中,无法显式的使用 this()和super() 同时出现。两个都要在构造方法的首行,会产生矛盾。
当产生子类对象时,默认先产生父类对象,若父类对象还有父类,继续向上先产生祖类对象。
当我们在父类中写一个有参的构造方法,此时父类的无参构造就不在产生。那我们在在new 一个只有无参构造的子类对象时,先要产生父类对象,但是找不到父类的无参构造,此时就会出现编译报错。
那我们此时就要在子类无参构造的首行写入 super(参数); ,让子类能够找到父类的构造方法,产生父类对象。
看正确的代码
父类
package SuperTest;
public class Father {
protected String name = "父亲";
public Father(String name){
this.name = name;
System.out.println("Father 的有参构造");
}
}
子类
package SuperTest;
public class Son extends Father{
public String name = "儿子";
public Son(){
super("爸爸");
System.out.println("Son 的无参构造");
}
public static void main(String[] args) {
Son son = new Son();
}
}
如果不加 super(“爸爸”); 就会编译报错,
super 修饰普通方法
super 修饰普通方法和修饰属性一样,直接从父类中寻找同名方法。
当在有继承关系的类中,this关键字默认先在当前类中寻找同名方法,若没有找到,继续向上寻找父类中是否有同名方法。
父类
package SuperTest;
public class Father {
protected String name = "父亲";
public Father(String name){
this.name = name;
System.out.println("Father 的有参构造");
}
public void fun(){
System.out.println("Father 的fun 方法");
}
}
子类
package SuperTest;
public class Son extends Father{
public String name = "儿子";
public void fun(){
System.out.println("Son 的fun方法");
}
public void test(){
super.fun(); //调用父类的 fun方法
this.fun(); //调用子类的 fun方法,若子类没有
System.out.println(this); //打印当前对象的引用
System.out.println(super); //会报错,必须是 super.name 直接使用super 是错误的
}
public Son(){
super("爸爸");
System.out.println("Son 的无参构造");
}
public static void main(String[] args) {
Son son = new Son();
son.test();
}
}
super 可以访问父类的属性和方法,必须是super.name
, 不能直接使用super
final 关键字和继承的联系
final 关键字:
- 修饰属性,表示属性值不能变,常量,值定义之后无法被修改
- 修饰类,表示这个类无法被继承。
类和类的关系
类和类直接除了继承关系(狗 is a 动物),还有组合关系(has a),以及没有任何关系。
多态
多态:一个引用可以表现出多种行为或者特性就是多态性
最主要的多态:继承 + 方法重写
向上转型
正常我们new一个对象时,都是 类名称 类引用 = new 该类对象();
但是当我们在有继承关系的类之间时,我们可以 这样 父类名称 父类引用 = new 子类对象();,因为子类继承了父类,那么父类就可以指代所有的子类。就是向上转型。
向上转型发生在有继承关系的类之间,父类名称 父类引用 = new 子类实例();,不一定是直接子类,也可以是孙子类,子子孙孙无穷尽。
向上转型最大的意义在于参数统一化,降低使用者的使用难度
父类
package InheritTest;
public class Vehicle {
protected String name = "123";
public void fun(){
System.out.println("Vehicle的fun方法");
}
}
子类
package InheritTest;
public class Car extends Vehicle {
public void fun(){
System.out.println("Car的fun方法");
}
}
子类
package InheritTest;
public class Airplane extends Vehicle {
public void fun(){
System.out.println("Airplane的fun方法");
}
}
测试类:
package InheritTest;
public class Test {
public static void main(String[] args) {
funs(new Vehicle());
funs(new Car());
funs(new Airplane()); //扩展的,添加子类后,直接在使用时 用就即可,fun方法不需要做任何改变,因为最顶层的父类引用可以指代所有的子类对象
}
//只要是Vehicle 及其子类,都是天然的Vehicle对象,都满足 is a 关系
//通过Vehicle最顶层的父类引用,可以指代所有的子类对象
public static void funs(Vehicle vehicle){
vehicle.fun();
}
}
有向上转型后,最顶端的父类引用可以指代所有的子类对象。当父类有新的子类时,是非常容易扩展的!!!
那这个代码的输出是:
Vehicle的fun方法
Car的fun方法
Airplane的fun方法
funs中vehicle局部变量的引用调用fun方法时,当传入不同的对象时,表现出来了不同的fun方法行为,就是多态性。
同一个引用(变量名称),同一个方法名称,根据对象的不同表现出来了不同的行为,就是多态
那为何会出现多态这种情况呢?
就是方法覆写的问题。
方法重写
方法重载(overload):在一个类的内部,有多个方法名称相同的,参数列表不同,与返回值无关的一组方法。
方法重写(覆写)(override):发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全部相同的方法,这样的一组方法就是方法重写。
Vehicle ve1 = new Car();
Vehicle ve2 = new Airplane();
fun(ve1);
fun(ve2);
public static void fun(Vehicle vehicle){
vehicle.fun();
}
在调用时 调用的是谁的方法呢?
不需要看前半部分,只需要看当前new 的是哪个类的对象,new Car()
就是new 了一个Car类的对象,若Car类重写了相关的方法,则调用的是Car类中重写后的方法,若没有重写,则调用的是直接父类的方法。就近匹配原则。
当发生重写时, 子类权限 >= 父类权限才可以重写。 小于的话编译时就会报错
private < default < protected < public
那么父类方法中使用了private 权限,子类方法在使用public 权限,public > private,是否可以重写呢?
不能重写。private权限不包含在内。出了这个类,子类根本不知道有这个方法。
Java中有一个注解 @Override
,使用这个注解写在重写方法之前,帮你校验你的方法是否符合重写规则。
能否重写static 方法呢?
多态的本质就是因为调用了不同子类对象,这些子类对象所属的类 覆写相应的方法,才能表现出不同的行为。 而static 方法和对象无关,不能重写。
方法重写只发生在普通方法中
重写的方法返回值类型完全相同或者至少是向上转型类的返回值。
向上转型
向上转型发生的时机:
- 方法传参
- 直接赋值
Vehicle ve = new Car();
- 方法返回值
public static Vehicle test(){ //返回值为父类
Car car = new Car();
return car; //实际返回的类型是子类
}
当此时子类对象时,要先产生父类对象,若产生父类对象时,调用父类的构造方法内存在 子类覆写的方法,此时调用的是子类覆写后的方法,而不是子类覆写前的方法。
总结:
向上转型
父类名称 父类引用 = new 子类对象();
这是天然发生的向上转型,使用父类引用调用普通方法时,若子类重写了该方法,则调用该对象所在子类覆写后的方法。
注意: 类名称 引用名称 = new 类实例();
引用名称.方法名称();
能通过"."访问的方法是由 类名称说了算,能访问的这些方法必须在类中定义过,编译器会先在类中查找是否包含指定方法,若没有,会报错。
这个通过"." 访问的方法到底表现出哪个类的样子,是由new 的类实例所在的类说了算。
简单来说就是,能 . 哪些方法 前面说了算,至于 . 之后这方法有什么行为,后面new的对象所在的类说了算。
向下转型
来看这个例子:
Animal 是父类, Dog是子类
Animal animal = new Dog();
animal.play();
此时父类中的没有play()方法,在子类中拓展一个play()方法,直接使用父类引用是无法调用子类的拓展方法。
我们要使用子类的拓展方法就要把他还原为子类引用,通过子类引用来访问子类的拓展方法,就是向下转型。
//子类名称 子类引用 = (子类名称)父类引用
Dog dog = (Dog)animal;
将父类引用强制类型转化为子类引用,就是向下转型。
要发生向下转型,先要发生向上转型。
Animal animal = new Animal();
//animal 就是一个 Animal类产生的对象的引用,和其他的类毫无关系
Dog dog = (Dog)animal;
这个是会报错的,毫无关系的两个类直接没办法强制类型转换。
ClassCastException
就是类型转换异常。
Vehicle vehicle1 = new Car();
System.out.println(vehicle1);
Car car = (Car)vehicle1;
System.out.println(car);
打印父类引用 和 向下转型后的子类引用,会发现他们的输出地址都一样,说明只有一个对象,发生改变的只是引用的名字。
instanceof 关键字
当发生向下转型时会有风险,出现类型转换异常,那我们就可以使用 instanceof
关键字。
引用名称 instanceof 类 -> 就会返回一个布尔值,表示该引用名称指向的本质是不是该类的对象。
例子:
使用instanceof
关键字的返回值 搭配的分支语句进行类型转换。
package InheritTest;
public class Test {
public static void main(String[] args) {
Vehicle vehicle1 = new Car();//Car 继承了 Vehicle
Vehicle vehicle2 = new Vehicle();
if(vehicle1 instanceof Car){
Car car = (Car) vehicle1;
System.out.println("转换成功");
}else {
System.out.println("vehicle1 本质指向的不是Car这个类的引用");
}
if(vehicle2 instanceof Car){
Car car = (Car) vehicle1;
System.out.println("转换成功");
}else {
System.out.println("vehicle2 本质指向的不是Car这个类的引用");
}
}
}
输出结果:
转换成功
vehicle2 本质指向的不是Car这个类
总结:
只有在某个特殊的情况下,需要使用子类拓展的方法,才需要将原本向上转型的引用 进行向下转型 还原为子类引用。
抽象类
若需要强制要求子类覆写方法,就要用到抽象类。
其实现实生活中有很多的 抽象类,这些类都是概念化的,没法具体到某个实例,描述这一类对象共同的属性和行为。
交通工具 —> 抽象,没法对应具体的某个或者某一类的工具,汽车、自行车、飞机、火车等等
抽象类是普通类的"超集",只是比普通类多了一些抽象方法而已。普通类有的抽象类全都有!
抽象方法所在的类必须是抽象类,子类若继承了抽象类,必须覆写所有的抽象方法。(子类是普通类,子类若是抽象类,就不需要进行覆写)
Java中定义抽象类或者抽象方法使用 abstract
关键字。
抽象的使用 1
抽象方法所在的类必须使用abstract声明为抽象类.
抽象方法指的是使用abstract关键字声明,只有函数声明,没有函数实现的方法,称之为抽象方法。
package Abstract;
public abstract class Vehicle { //声明为抽象类
public abstract String name(); //抽象方法
}
抽象方法:使用关键字abstract声明,只有方法声明,没有方法体( “{}”)的方法。
抽象方法在抽象类中没有具体实现,延迟到子类实现。
来做一个判断题
java中,没有方法体的方法就是抽象方法。 error
因为本地方法也没有方法体,但他不是抽象方法。
抽象类的使用 2
若一个类使用abstract 关键字声明为抽象类,无法直接通过该类 实例化对象,哪怕该类中一个抽象方法也没有。
当一个类是抽象类,不管他有没有抽象方法,这个类本身就是一个抽象的概念,就没有办法具体到某个特定的实例,只能通过 子类 向上转型变为抽象父类的引用。
抽象类虽然没有办法直接实例化对象,子类仍然满足 is a 原则, 子类和抽象父类直接仍然是满足 继承树的关系
//此时Vehicle 是抽象类
Vehicle vehicle = new Car(); //这是正确的
Vehicle vehicle = new Vehicle(); // 这是错误的
抽象类的使用 3
子类继承了抽象类,就必须强制子类(子类是普通类)覆写 抽象类中的所有抽象方法(包括抽象类继承另一个抽象类中的方法),也满足单继承局限,一个子类只能继承一个抽象类。
package Abstract;
abstract class A{
public abstract void add();
}
//抽象类 可以不覆写或者选择性的覆写父类中的抽象方法
abstract class B extends A{
public abstract void del();
}
//普通类,必须覆写B中的所有抽象方法(包括继承来的抽象方法)
//若B中选择性覆写了他继承的抽象类的抽象方法,则B 覆写了的抽象方法,B的子类可以不进行覆写。!!!!!!!
public class Vehicle extends B{
@Override
public void del() {
}
@Override
public void add() {
}
}
若B中选择性覆写了他继承的抽象类的抽象方法,则B 覆写了的抽象方法,B的子类可以不进行覆写!!!!!!!
抽象类的使用 4
抽象类是普通方法的超集(普通类有的内容,抽象类都有),只是比普通类多了一些抽象方法而已,抽象类虽然没有办法直接实例化对象,但是也可以存在构造方法,子类在实例化对象时,仍然遵从继承的规则,先调用父类(抽象类)的构造方法,而后调用子类的构造方法!!
package Abstract;
abstract class A{
public abstract void add();
public A(){
System.out.println("A 的构造方法");
this.add();
}
}
public class Vehicle extends A{
@Override
public void add() {
System.out.println("num = " + num);
}
private int num = 10;
public static void main(String[] args) {
new Vehicle();
}
}
输出是:
A 的构造方法
num = 0
在主方法中new Vehicle(); 时,先调用父类(抽象类)的构造方法,构造方法中又调用add()方法,子类中覆写了add方法,所以这个add 方法是子类的,由于还没有执行子类的构造方法,所以num 还没有初始化 为 10。
若一个需求既可以使用抽象类的,也可以使用接口,那我们优先使用接口。抽象类仍然是单继承局限。
接口
一般来说,接口的使用表示两种场景
1 接口表示具备某种能力或行为,子类实现接口时 不是 is a, 而是具备这种行为或者能力。
”跑“ —> 是能力或者行为,狗满足跑接口,猫也满足跑接口。
2 接口表示一种规范或标准。USB接口、5G标准
接口中只有全局常量和抽象方法 —> 更加纯粹的抽象概念。其他东西统统没有
接口使用关键字 interface
声明接口(创建新的 java Class 文件时,选择interface ),子类(普通类)使用implements
实现接口。
接口和类的关系:
1 接口和接口之间也存在继承关系,接口坚决不能继承一个类。
若一个接口同时继承 了多个父接口,则这个接口也继承了所有的抽象方法,子类在实现这个接口时,必须覆写所有的抽象方法。
2 如果一个类既需要继承一个类,同时实现多个接口时, 先使用 extends
继承一个类,而后使用 implements
实现多个父接口。
class China extend 类名称 implements 接口名称{}
USB接口
package Interface_test;
//接口使用interface 关键字定义,只有全局常量(1%接口才有)和抽象方法(99%)
public interface USB {
//插入
public abstract void plugIn();
//工作
public abstract void work();
}
子类使用 implements
实现接口, 必须覆写所有的抽象方法
鼠标、键盘这些都属于USB接口的子类。
package Interface_test;
public class Mouse implements USB{
@Override
public void plugIn() {
System.out.println("鼠标驱动正在安装...");
}
@Override
public void work() {
System.out.println("鼠标驱动安装成功!!!");
}
}
package Interface_test;
public class KeyBoard implements USB{
@Override
public void plugIn() {
System.out.println("键盘驱动正在安装...");
}
@Override
public void work() {
System.out.println("键盘驱动安装成功!!!");
}
}
那么电脑这个类算不算USB接口的子类呢?
所有带USB线插入到电脑的设备都应该满足USB规范。 电脑应该叫做USB规范的使用者。
package Interface_test;
public class Computer {
public static void main(String[] args) {
Computer computer = new Computer();
Mouse mouse = new Mouse();
computer.fun(mouse);
KeyBoard keyBoard = new KeyBoard();
computer.fun(keyBoard);
}
public void fun(USB usb){
usb.plugIn();
usb.work();
}
}
此时为何方法的参数用的是USB接口引用?
fun方法就模拟电脑的接口,对于电脑的使用者和生产者来说,根本不关心到底哪个具体设备插到电脑上,只要满足了USB接口,就能够被电脑识别。就可以实现,一个接口可以接收无数种设备。兼容所有的USB对象。
假如fun方法的参数是(Mouse mouse),那么电脑的这个接口就只能插鼠标,电脑总不能设置很多个接口吧。
若此时电脑多了一个新的USB的子类,对于电脑这个类来说,一点都不影响,一行代码都不会改变。
这就是开闭原则,所有的设计模式的核心思想:程序应该对扩展开放,对修改关闭。 方便扩展,不能影响已经写好的程序。
接口允许多实现
接口表示能力,允许多实现,一个类可能具备多个能力,同时实现多个父接口
若实现多个父接口,子类(普通类)需要覆写所有的抽象方法。
package Interface_test.RunJump;
//跑的接口
public interface Run {
public abstract void run();
}
package Interface_test.RunJump;
//跳的接口
public interface Jump {
public abstract void jump();
}
子类实现多个父接口,多个父接口直接使用","分开
package Interface_test.RunJump;
//子类 猫 具有 跑和跳的能力,实现多个父接口,多个父接口直接使用","分开
public class Cat implements Run,Jump{
@Override
public void jump() {
System.out.println("猫会跳");
}
@Override
public void run() {
System.out.println("猫会跑");
}
}
package Interface_test.RunJump;
//测试
public class Test {
public static void main(String[] args) {
Run catRun = new Cat(); //向上转型
catRun.run(); //通过父类接口引用访问子类对象方法
Jump catJump = new Cat();
catJump.jump();
}
}
接口也不能直接实例化对象,需要向上转型,
由于接口中只有全局常量和抽象方法,因此在接口中 public abstract
抽象方法
static final
全局常量,这些关键字都可以省略。
以后在接口声明中,这些关键字都不需要写,只保留最核心的方法返回值,方法参数列表,名称即可。
要是对大家有所帮助的话,请帮我点个赞吧。