java学习笔记——接口、多态
2026/5/13 9:52:42 网站建设 项目流程

接口

一、介绍以及使用

1.接口
接口是更加彻底的抽象,它定义了一种规范/协议。

2.定义

// interface 关键字定义接口 public interface USB { // 抽象方法(public abstract 可以省略) void connect(); void disconnect(); }

3.实现

// implements 关键字实现接口 public class Mouse implements USB { @Override public void connect() { System.out.println("鼠标连接了"); } @Override public void disconnect() { System.out.println("鼠标断开了"); } } public class Keyboard implements USB { @Override public void connect() { System.out.println("键盘连接了"); } @Override public void disconnect() { System.out.println("键盘断开了"); } }

使用

// 接口作为类型(多态) USB device1 = new Mouse(); device1.connect(); // 鼠标连接了 USB device2 = new Keyboard(); device2.connect(); // 键盘连接了 // 可以用数组统一管理 USB[] devices = {new Mouse(), new Keyboard()}; for (USB d : devices) { d.connect(); }

ps:

接口用 interface 定义

实现类用 implements 实现接口

接口中的方法默认是 public abstract

接口 = 规范,实现类 = 具体实现

二、接口中的抽象方法

1.接口中抽象方法的特点:

接口中的方法默认都是 public abstract

实现类中重写的方法必须是 public

public interface MyInterface { // 以下四种写法完全等价(接口中) public abstract void method1(); // 完整版 abstract void method2(); // 省略 public public void method3(); // 省略 abstract void method4(); // 都省略(最常用) }

2.实现类必须重写

public class MyClass implements MyInterface { @Override public void method1() {} // 必须 public @Override public void method2() {} @Override public void method3() {} @Override public void method4() {} }

三、接口中的默认方法和静态方法

1.默认方法

public interface USB { // 抽象方法 void connect(); // 默认方法:有方法体,用 default 修饰 public default void checkStatus() { System.out.println("设备状态正常"); } } // 实现类可以不重写默认方法 public class Mouse implements USB { @Override public void connect() { System.out.println("鼠标连接"); } // checkStatus() 可以不重写,有默认实现 } // 实现类也可以重写默认方法 public class Keyboard implements USB { @Override public void connect() { System.out.println("键盘连接"); } @Override public void checkStatus() { System.out.println("键盘状态检查中..."); } }

好处:

接口升级:如果接口需要加新方法,原来已经实现该接口的类不用必须修改

提供默认实现:减少实现类的重复代码

2. 静态方法

public interface USB { // 静态方法:属于接口本身 public static void printInfo() { System.out.println("USB 3.0 标准"); } } // 调用:接口名直接调用 USB.printInfo(); // USB 3.0 标准 // 实现类不继承接口静态方法 // new Mouse().printInfo(); // 错误!

ps:

默认方法用 default,有方法体,可重写可不重写

静态方法用 static,接口名调用,实现类不继承

四、接口中的成员变量

1.特点

public interface MyInterface { // 以下四种写法完全等价 public static final int NUM1 = 10; // 完整写法 static final int NUM2 = 20; // 省略public public static int NUM3 = 30; // 省略final int NUM4 = 40; // 都省略(最常用) }

接口中的变量默认 public static final

实际上是常量,必须初始化,不能修改

通过接口名访问

例:

System.out.println(MyInterface.NUM1); // 10(接口名.常量名访问) // MyInterface.NUM1 = 100; // 错误!final 不能修改

五、接口特点以及和抽象类区别

1. 接口支持多实现

// 一个类可以实现多个接口! public class SmartPhone implements Phone, Camera, MP3 { @Override public void call() { } @Override public void takePhoto() { } @Override public void playMusic() { } }

2. 接口可以多继承接口

public interface A { void a(); } public interface B { void b(); } public interface C extends A, B { // 接口可以多继承 void c(); }

3. 类和接口的关系:

类与类:单继承 extends

类与接口:多实现 implements

接口与接口:多继承 extends

接口和抽象类区别

ps:

如果多个类有共同的属性和行为 → 抽象类

如果定义一种行为规范/能力 → 接口

多态

一、多态的介绍和基本使用

多态是面向对象三大特性之一,指同一行为在不同对象上表现出不同的形态

多态的前提条件

1.必须有继承或实现关系

2.必须有方法重写

3.父类引用指向子类对象

格式:

父类类型 变量名 = new 子类类型(); 变量名.方法名();

例子

// 父类 public class Animal { public void eat() { System.out.println("动物吃东西"); } } // 子类 public class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); } public void watchHouse() { System.out.println("狗看家"); } } public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void catchMouse() { System.out.println("猫抓老鼠"); } }

多态写法:

// 普通写法:子类引用指向子类对象 Dog d = new Dog(); d.eat(); // 狗吃骨头 // 多态写法:父类引用指向子类对象 Animal a1 = new Dog(); // 狗是动物(OK) Animal a2 = new Cat(); // 猫是动物(OK) a1.eat(); // 狗吃骨头(调用的是Dog重写后的方法) a2.eat(); // 猫吃鱼(调用的是Cat重写后的方法)

核心

Animal a = new Dog(); a.eat(); // 编译看左边(Animal):Animal 中有没有 eat 方法? // 运行看右边(Dog):实际调用的是 Dog 重写后的 eat 方法

ps:

多态三条件:继承 + 重写 + 父类引用指向子类对象

格式:父类 变量 = new 子类();

编译看左,运行看右

二、成员访问特点

例子

public class Father { int num = 10; public void method() { System.out.println("父类方法"); } } public class Son extends Father { int num = 20; // 和父类变量同名 @Override public void method() { System.out.println("子类方法"); } }

测试

Father f = new Son(); // 多态写法 // 成员变量:看左边(Father) System.out.println(f.num); // 10(父类的num!不是子类的20) // 成员方法:编译看左,运行看右(Son) f.method(); // 子类方法(调用重写后的方法)

为什么成员变量和成员方法的访问规则不同?

方法有重写,所以运行时用子类的版本

变量没有重写,只有"隐藏",所以访问父类的

ps:

变量看左边(父类)

方法:编译看左,运行看右(子类重写版)

变量没有重写,只有隐藏

三、多态的好处

好处一:提高代码的扩展性和灵活性

没有多态时:

public class AnimalKeeper { public void feedDog(Dog dog) { dog.eat(); } public void feedCat(Cat cat) { cat.eat(); } public void feedPig(Pig pig) { // 每增加一种动物,就要加一个方法 pig.eat(); } } // 使用 AnimalKeeper keeper = new AnimalKeeper(); keeper.feedDog(new Dog()); keeper.feedCat(new Cat()); // 不同动物要调用不同方法,很麻烦

用后

public class AnimalKeeper { // 参数是父类类型,可以接收所有子类对象 public void feed(Animal animal) { animal.eat(); // 自动调用对应子类的方法 } } // 使用:统一调用 AnimalKeeper keeper = new AnimalKeeper(); keeper.feed(new Dog()); // 狗吃骨头 keeper.feed(new Cat()); // 猫吃鱼 keeper.feed(new Pig()); // 猪吃饲料 // 不管什么动物,都用同一个 feed() 方法

好处二:简化代码,数组统一管理

// 有多态:用父类数组统一管理所有子类对象 Animal[] animals = {new Dog(), new Cat(), new Pig(), new Bird()}; for (Animal a : animals) { a.eat(); // 同一行代码,不同对象表现不同行为 } // 没有多态:每种动物一个数组,代码冗长 Dog[] dogs = {new Dog(), new Dog()}; Cat[] cats = {new Cat(), new Cat()}; // ... 每增加一种就多一个数组

好处三:降低耦合

// 调用方只依赖 Animal 父类,不依赖具体的 Dog 或 Cat public class Zoo { public void showAnimal(Animal a) { a.eat(); // 不关心具体是哪种动物 } } // 以后新增 Animal 的子类,Zoo 的代码完全不用改! public class Panda extends Animal { @Override public void eat() { System.out.println("熊猫吃竹子"); } } // 直接使用 zoo.showAnimal(new Panda()); // 完全兼容!

经典应用

// List 是接口,ArrayList 和 LinkedList 是实现类 List<String> list = new ArrayList<>(); // 多态写法 list.add("hello"); list.add("world"); // 以后想换实现类,只需改一处 // List<String> list = new LinkedList<>(); // 其他代码完全不用变 // 方法参数也用接口类型 public void printList(List<String> list) { for (String s : list) { System.out.println(s); } }

四、向上转型和向下转型

1.向上转型

子类对象 → 父类类型,自动完成,安全。

// 向上转型:自动发生,安全 Animal a = new Dog(); // Dog → Animal,自动转型 a.eat(); // 只能调用 Animal 中定义的方法 // 本质:把狗当成普通动物看待 // 优点:可以统一处理 // 缺点:丢失子类特有方法 // a.watchHouse(); // 编译错误!Animal 中没有此方法

2.向下转型

父类类型 → 子类类型,需要强制转换,有风险。

Animal a = new Dog(); // 先向上转型 // 向下转型:强制转换 Dog d = (Dog) a; // OK,因为 a 本质是 Dog d.watchHouse(); // 现在可以调用 Dog 特有的方法了

为什么需要向下转型?

多态写法只能调用父类中定义的方法

要调用子类特有的方法,必须向下转型

ps:

向上转型:子→父,自动,安全(丢失子类特有功能)

向下转型:父→子,强制,有风险(本质必须是该子类)

向下转型是为了调用子类特有方法

五、向下转型问题说明&类型判断

风险

Animal a = new Cat(); // a 本质是 Cat Dog d = (Dog) a; // 运行时错误!!! // ClassCastException: Cat cannot be cast to Dog // 类型转换异常:猫不能强制转换成狗!

错误原因:a 的本质是 Cat,强制转成 Dog 类型不兼容。

如何避免?——instanceof 类型判断:

// 语法:对象 instanceof 类型 → 返回 boolean Animal a = new Dog(); if (a instanceof Dog) { Dog d = (Dog) a; d.watchHouse(); } else if (a instanceof Cat) { Cat c = (Cat) a; c.catchMouse(); }

例子:

public static void handleAnimal(Animal a) { // 通用的操作 a.eat(); // 针对不同类型做不同处理 if (a instanceof Dog) { ((Dog) a).watchHouse(); } else if (a instanceof Cat) { ((Cat) a).catchMouse(); } } // 调用 handleAnimal(new Dog()); // 吃骨头 + 看家 handleAnimal(new Cat()); // 吃鱼 + 抓老鼠

注意

Dog d = new Dog(); System.out.println(d instanceof Dog); // true System.out.println(d instanceof Animal); // true(Dog是Animal的子类) System.out.println(d instanceof Object); // true(所有类都是Object的子类)

ps:

向下转型前必须用 instanceof 判断

instanceof:判断对象的本质类型

不用instanceof直接强转可能出ClassCastException

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询