Day12
接口
当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义为接口;接口也是一种引用数据类型,他比抽象类还要抽象;
- 接口存在的两个重要意义
- 规则的定义
- 程序的扩展性
接口的定义和特点
- 接口用关键字interface来定义
- public interface 接口名{}
- 接口不能实例化
- 接口和类之间是实现关系,通过implements关键字表示
- public class 类名 implements 接口名{}
- 接口的子类(实现类)
- 要么重写接口中的所有抽象方法
- 要么是抽象类
- 注意:接口和类的实现关系,可以单实现,也可以多实现。
- public class 类名 implements 接口名1,接口名2{}
示例:
给所有的动物制定两个规范,(1)所有的动物必须能吃饭;(void eat()),(2)所有的动物必须能睡觉(void sleep());已知猫和狗两个类都遵循了上面的规范,请使用代码描述出猫类和狗类,并编写测试类,创建猫类和狗类的对象,调用吃饭和睡觉的方法;
接口Animal.java
package animal;
public interface Animal {
public abstract void eat();
public abstract void sleep();
}
实现类Dog.java
package animal;
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃狗粮");
}
@Override
public void sleep() {
System.out.println("狗在门口睡觉");
}
}
实现类Cat.java
package animal;
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
@Override
public void sleep() {
System.out.println("猫趴在草垛上睡觉");
}
}
测试类
package animal;
public class AnimalTest {
public static void main(String[] args) {
Dog d = new Dog();
Cat c = new Cat();
d.eat();
d.sleep();
c.eat();
c.sleep();
}
}
运行结果:
接口中成员的特点
- 成员变量:只能是常量,系统会默认加入三个关键字:public、static、final
- 构造方法:没有
- 成员方法:只能是抽象方法,系统会默认加入两个关键字:public、abstract
JDK8版本中接口成员的特点
- JDK8版本后
- 允许在接口中定义非抽象方法,但是需要使用关键字default修饰,这些方法就是默认方法(作用:解决接口升级问题)
- 接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表){}
- 范例:public default void show(){}
- 接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
- 接口中允许定义static静态方法
- 接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表){}
- 范例:public static void show(){}
- 接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
示例:
接口A
package jdk8;
public interface InterA {
public default void show(){
System.out.println("这里是接口A");
}
public static void show1(){
System.out.println("InterA...show");
}
}
接口B
package jdk8;
public interface InterB {
public default void show(){
System.out.println("这里是接口B");
}
public static void show1(){
System.out.println("InterB...show");
}
}
测试类
package jdk8;
public class Test {
public static void main(String[] args) {
Inter1Impl i1 = new Inter1Impl();
i1.show();
Inter2Impl i2 = new Inter2Impl();
i2.show();
//i2.show1();
InterA.show1();
InterB.show1();
}
}
class Inter1Impl implements InterA,InterB{
@Override
public void show() {
System.out.println("这里是实现类");
}
}
class Inter2Impl implements InterA{
}
运行结果:
接口的使用思路
- 若发现一个类中所有的方法都是抽象方法,那么就可以将该类改进为一个接口
- 涉及到接口大面积更新方法,不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法
- 希望默认方法调用的更加简洁,可以考虑设计为static静态方法。(需要去掉default关键字)
- 默认方法中出现了重复的代码,可以考虑抽取出一个私有方法。(需要去掉default关键字)
类和接口的关系
- 类和类的关系
- 继承关系,只能单继承,但是可以多层继承
- 类和接口的关系
- 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系
- 继承关系,可以单继承,也可以多继承
多态
概述
同一个对象,在不同时刻表现出来的不同形态;
多态的前提和体现
- 有继承/实现关系
- 有方法重写
- 有父类引用指向子类对象
多态中成员访问特点
- 构造方法:同继承一样,子类会通过super访问父类构造方法
- 成员变量:编译看左边(父类),执行看左边(父类)
- 成员方法:编译看左边(父类),执行看右边(子类)
- 成员方法和成员变量访问不一样的原因:
- 成员方法有重写,而成员变量没有
示例:
package duotai;
public class DuotaiTest {
public static void main(String[] args) {
Fu f = new Zi();//形成多态
System.out.println(f.num);
f.show();
}
}
class Fu{
int num=10;
public void show(){
System.out.println("show...fu");
}
}
class Zi extends Fu{
int num=20;
public void show(){
System.out.println("show...zi");
}
}
运行结果:
多态的好处和弊端
- 好处:提高了程序的扩展性
- 具体体现:定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的任意子类对象
- 弊端:不能使用子类的特有功能;
示例:
package duotai;
public class DuotaiTest1 {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ //Animal a = new Dog(),Animal a = new Cat()
a.eat();
//a.watchHome();//多态弊端,不能调用子类特有的方法
}
}
abstract class Animal{
public abstract void eat();
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("够吃肉");
}
public void watchHome(){
System.out.println("狗子看家");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
}
运行结果:
多态中的转型
- 向上转型:父类引用指向子类对象
- 向下转型:父类引用转为子类对象
多态中转型存在的风险
- 概述:如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
- 关键字instanceof
- 使用格式:变量名 instanceof 类型
- 判断关键字左边的变量是否是右边的类型,返回boolean类型结果
示例:
package duotai;
public class DuoTest2 {
public static void main(String[] args) {
useAnimal(new Dog1());
useAnimal(new Cat1());
}
public static void useAnimal(Animal1 a){
a.eat();
//Dog1 d = (Dog1)a;
//d.watchHome();//此处报错ClassCastException
if (a instanceof Dog1){
Dog1 d = (Dog1)a;
d.watchHome();
}
}
}
abstract class Animal1{
public abstract void eat();
}
class Dog1 extends Animal1{
@Override
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看门");
}
}
class Cat1 extends Animal1{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
运行结果:
练习1
1.请定义“员工(类)”:
属性:姓名、性别、年龄(全部私有)
行为:工作(抽象)
无参、全参构造方法
get/set方法
2.请定义“绘画(接口)”
抽象方法:绘画
3.请定义“Java讲师类”继承自“员工类”
4.请定义”UI讲师类”,继承自“员工类”,并实现“绘画”接口。
要求:
请按上述要求设计出类结构,并实现相关的方法,并进行调用测试。
员工类Yuangong.Java
package lian1;
public abstract class Yuangong {
private String name;
private String gender;
private int age;
public abstract void work();
public Yuangong(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public Yuangong() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
绘画接口Huihua.java
package lian1;
public interface Huihua {
public abstract void hua();
}
Java讲师类JavaTeacher.java
package lian1;
public class JavaTeacher extends Yuangong {
@Override
public void work() {
System.out.println(getAge()+"岁的"+getGender()+"老师"+getName()+"正在给学生上Java课程");
}
public JavaTeacher(String name, String gender, int age) {
super(name, gender, age);
}
public JavaTeacher() {
}
}
UI讲师类UITeacher.java
package lian1;
public class UITeacher extends Yuangong implements Huihua {
@Override
public void work() {
System.out.print(getAge()+"岁的"+getGender()+"老师"+getName()+"正在给学生上UI课程,");
}
@Override
public void hua() {
System.out.println("并在UI课上教学生绘画");
}
public UITeacher(String name, String gender, int age) {
super(name, gender, age);
}
public UITeacher() {
}
}
测试类Test.java
package lian1;
public class Test {
public static void main(String[] args) {
JavaTeacher jt = new JavaTeacher("贾玉","男",31);
jt.work();
UITeacher ut = new UITeacher("林玉","女",28);
ut.work();
ut.hua();
}
}
运行结果:
练习2
请模拟生活中"养殖场老板指挥员工饲养动物"的场景;员工类中包含饲养动物的方法,而老板类(就是测试类)可以面向员工对象调用员工类的方法;代码关系示意图如下:
创建动物类Animal,定义抽象方法eat
package lian1;
public abstract class Animal {
public abstract void eat();
}
创建Dog、Cat、Pig类继承Animal类,重写eat方法
package lian1;
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗子吃狗粮。");
}
}
package lian1;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫猫吃小鱼干。");
}
}
package lian1;
public class Pig extends Animal {
@Override
public void eat() {
System.out.println("小猪吃饲料。");
}
}
创建员工类Worker,定义饲养动物的方法raiseAnimal
package lian1;
public class Worker {
//将动物父类作为形式参数,方法可以接收任意子类动物
public void raiseAnimal(Animal a){
a.eat();
}
}
创建老板类Boss,在该类中进幼崽即创建指定动物对象,并安排员工饲养动物,即调用raiseAnimal方法;
package lian1;
public class Boss {
public static void main(String[] args) {
Worker w = new Worker();
Dog d = new Dog();
Cat c = new Cat();
Pig p = new Pig();
//调用方法将指定动物作为实际参数传递过去
w.raiseAnimal(d); //相当于Animal a = new Dog();父类引用指向子类对象
w.raiseAnimal(c);
w.raiseAnimal(p);
}
}
运行结果:
练习3
按照要求完成以下内容:
- 已知电脑类(Computer), 有开机和关机的功能,以及使用鼠标和键盘的功能;
- 已知鼠标类(Mouse), 也有连接电脑和断开电脑的功能,connet():打印鼠标连接了;disconnet():打印鼠标断开了;
- 已知键盘类(Keyboard), 也有连接电脑和断开电脑的功能,connet():打印键盘连接了;disconnet():打印键盘断开了;
总结,只要是符合USB设备的东可以连接电脑和断开电脑的功能。
请编写测试类, 测试电脑开机, 电脑使用鼠标, 电脑使用键盘, 电脑关机的功能。
创建USB接口并定义connect和disconnect方法
package lian2;
public interface USB {
public abstract void connect();
public abstract void disconnect();
}
创建鼠标类Mouse,实现接口中的方法
package lian2;
public class Mouse implements USB {
@Override
public void connect() {
System.out.println("鼠标连接了...");
}
@Override
public void disconnect() {
System.out.println("鼠标断开了...");
}
}
创建键盘类Keyboard,实现接口中的方法
package lian2;
public class Keyboard implements USB {
@Override
public void connect() {
System.out.println("键盘连接了...");
}
@Override
public void disconnect() {
System.out.println("键盘断开了...");
}
}
创建电脑类Computer,定义使用USB接口的方法,将USB对象作为形式参数
package lian2;
public class Computer {
public void open(){
System.out.println("电脑开机了...");
}
public void close(){
System.out.println("电脑关机了...");
}
public void useUSBConnect(USB usb){
usb.connect();
}
public void useUSBDisconnect(USB usb){
usb.disconnect();
}
}
创建测试类Test,类中创建电脑、键盘、鼠标对象,并将三个对象作为方法的实际参数
package lian2;
public class Test {
public static void main(String[] args) {
Computer c = new Computer();
Mouse m = new Mouse();
Keyboard k = new Keyboard();
c.open();
c.useUSBConnect(m);
c.useUSBConnect(k);
c.useUSBDisconnect(m);
c.useUSBDisconnect(k);
c.close();
}
}
运行结果: