DAY06_面向对象高级(3)&接口&多态

1. 黑马信息管理系统改进

1.1 黑马信息管理系统集合改进

  • 使用数组容器的弊端
    • 容器长度是固定的,不能根据添加功能自动增长
    • 没有提供用于赠删改查的方法
  • 优化步骤
    • 创建新的StudentDao类,OtherStudentDao
    • 创建ArrayList集合容器对象
    • OtherStudentDao中的方法声明,需要跟StudentDao保持一致
      • 注意:如果不一致,StudentService中的代码就需要进行修改
    • 完善方法(添加、删除、修改、查看)
    • 替换StudentService中的Dao对象

1.1.1 修改OtherStudentDao类

import com.itheima.edu.info.manager.domain.Student;

import java.util.ArrayList;

public class OtherStudentDao extends BaseStudentDao {
    // 集合容器
    private static ArrayList<Student> stus = new ArrayList<>();

    static {
        Student stu1 = new Student("heima001", "张三", "23", "1999-11-11");
        Student stu2 = new Student("heima001", "李四", "24", "2000-11-11");
        stus.add(stu1);
        stus.add(stu2);
    }

    // 添加学生方法
    @Override
    public boolean addStudent(Student stu) {
        stus.add(stu);
        return true;
    }

    // 查看学生方法
    @Override
    public Student[] findAllStudent() {
        Student[] students = new Student[stus.size()];
        for (int i = 0; i < students.length; i++) {
            students[i] = stus.get(i);
        }
        return students;
    }

    //删除学生方法
    @Override
    public void deleteStudentById(String delId) {
        // 1. 查找id在容器中所在的索引位置
        int index = getIndex(delId);
        stus.remove(index);
    }

    //根据id找索引方法
    @Override
    public int getIndex(String id) {
        int index = -1;
        for (int i = 0; i < stus.size(); i++) {
            Student stu = stus.get(i);
            if (stu != null && stu.getId().equals(id)) {
                index = i;
                break;
            }
        }
        return index;
    }

    //学生修改方法
    @Override
    public void updateStudent(String updateId, Student newStu) {
        // 1. 查找updateId, 在容器中的索引位置
        int index = getIndex(updateId);
        stus.set(index, newStu);
    }
}

1.1.2 修改StudentService类

import com.itheima.edu.info.manager.dao.OtherStudentDao;
import com.itheima.edu.info.manager.domain.Student;

public class StudentService {
    // 创建StudentDao (库管)
    private OtherStudentDao studentDao = new OtherStudentDao();

    // 添加学生方法
    public boolean addStudent(Student stu) {
        // 2. 将学生对象, 传递给StudentDao 库管中的addStudent方法
        // 3. 将返回的boolean类型结果, 返还给StudentController
        return studentDao.addStudent(stu);
    }

    // 判断学号是否存在方法
    public boolean isExists(String id) {
        Student[] stus = studentDao.findAllStudent();
        // 假设id在数组中不存在
        boolean exists = false;
        // 遍历数组, 获取每一个学生对象, 准备进行判断
        for (int i = 0; i < stus.length; i++) {
            Student student = stus[i];
            if (student != null && student.getId().equals(id)) {
                exists = true;
                break;
            }
        }

        return exists;
    }

    // 查看学生方法
    public Student[] findAllStudent() {
        // 1. 调用库管对象的findAllStudent获取学生对象数组
        Student[] allStudent = studentDao.findAllStudent();
        // 2. 判断数组中是否有学生信息 (有: 返回地址,  没有: 返回null)
        // 思路: 数组中只要存在一个不是null的元素, 那就代表有学生信息
        boolean flag = false;
        for (int i = 0; i < allStudent.length; i++) {
            Student stu = allStudent[i];
            if (stu != null) {
                flag = true;
                break;
            }
        }

        if (flag) {
            // 有信息
            return allStudent;
        } else {
            // 没有信息
            return null;
        }

    }

    public void deleteStudentById(String delId) {
        studentDao.deleteStudentById(delId);
    }

    public void updateStudent(String updateId, Student newStu) {
        studentDao.updateStudent(updateId, newStu);
    }
}

1.2黑马信息管理系统抽取Dao

  • 优化步骤
    • 将方法向上抽取,抽取出一个父类 ( BaseStudentDao )
    • 方法的功能实现在父类中无法给出具体明确,定义为抽象方法
    • 让两个类分别继承 BaseStudentDao ,重写内部抽象方法

1.2.1 修改BaseStudentDao类

import com.itheima.edu.info.manager.domain.Student;

public abstract class BaseStudentDao {
    // 添加学生方法
    public abstract boolean addStudent(Student stu);
    // 查看学生方法
    public abstract Student[] findAllStudent();
    public abstract void deleteStudentById(String delId);
    public abstract int getIndex(String id);
    public abstract void updateStudent(String updateId, Student newStu);
}

1.2.2 修改StudentDao类

import com.itheima.edu.info.manager.domain.Student;

public class StudentDao extends BaseStudentDao {
    // 创建学生对象数组
    private static Student[] stus = new Student[5];

    static {
        Student stu1 = new Student("heima001", "张三", "23", "1999-11-11");
        Student stu2 = new Student("heima001", "李四", "24", "2000-11-11");
        stus[0] = stu1;
        stus[1] = stu2;
    }

    // 添加学生方法
    @Override
    public boolean addStudent(Student stu) {

        // 2. 添加学生到数组
        //2.1 定义变量index为-1,假设数组已经全部存满,没有null的元素
        int index = -1;
        //2.2 遍历数组取出每一个元素,判断是否是null
        for (int i = 0; i < stus.length; i++) {
            Student student = stus[i];
            if (student == null) {
                index = i;
                //2.3 如果为null,让index变量记录当前索引位置,并使用break结束循环遍历
                break;
            }
        }

        // 3. 返回是否添加成功的boolean类型状态
        if (index == -1) {
            // 装满了
            return false;
        } else {
            // 没有装满, 正常添加, 返回true
            stus[index] = stu;
            return true;
        }
    }

    // 查看学生方法
    @Override
    public Student[] findAllStudent() {
        return stus;
    }

    @Override
    public void deleteStudentById(String delId) {
        // 1. 查找id在容器中所在的索引位置
        int index = getIndex(delId);
        // 2. 将该索引位置,使用null元素进行覆盖
        stus[index] = null;
    }

    @Override
    public int getIndex(String id) {
        int index = -1;
        for (int i = 0; i < stus.length; i++) {
            Student stu = stus[i];
            if (stu != null && stu.getId().equals(id)) {
                index = i;
                break;
            }
        }
        return index;
    }

    @Override
    public void updateStudent(String updateId, Student newStu) {
        // 1. 查找updateId, 在容器中的索引位置
        int index = getIndex(updateId);
        // 2. 将该索引位置, 使用新的学生对象替换
        stus[index] = newStu;
    }
}

1.2.3 修改OtherStudentDao类

import com.itheima.edu.info.manager.domain.Student;

import java.util.ArrayList;

public class OtherStudentDao extends BaseStudentDao {
    // 集合容器
    private static ArrayList<Student> stus = new ArrayList<>();

    static {
        Student stu1 = new Student("heima001", "张三", "23", "1999-11-11");
        Student stu2 = new Student("heima001", "李四", "24", "2000-11-11");
        stus.add(stu1);
        stus.add(stu2);
    }

    // 添加学生方法
    @Override
    public boolean addStudent(Student stu) {
        stus.add(stu);
        return true;
    }

    // 查看学生方法
    @Override
    public Student[] findAllStudent() {
        Student[] students = new Student[stus.size()];
        for (int i = 0; i < students.length; i++) {
            students[i] = stus.get(i);
        }
        return students;
    }

    //删除学生方法
    @Override
    public void deleteStudentById(String delId) {
        // 1. 查找id在容器中所在的索引位置
        int index = getIndex(delId);
        stus.remove(index);
    }

    //根据id找索引方法
    @Override
    public int getIndex(String id) {
        int index = -1;
        for (int i = 0; i < stus.size(); i++) {
            Student stu = stus.get(i);
            if (stu != null && stu.getId().equals(id)) {
                index = i;
                break;
            }
        }
        return index;
    }

    //学生修改方法
    @Override
    public void updateStudent(String updateId, Student newStu) {
        // 1. 查找updateId, 在容器中的索引位置
        int index = getIndex(updateId);
        stus.set(index, newStu);
    }
}

2 接口

2.1 接口的概述

  • 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
  • 当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义为接口,接口也是一种引用数据类型,它比抽象类还要抽象
  • java中接口存在的两个意义
    • 用来定义规范
    • 用来做功能的拓展

2.2 接口的定义和特点

  • 接口用关键字interface来定义
    • public interface 接口名 {}
  • 接口不能实例化
  • 接口和类之间是实现关系,通过implements关键字表示
    • public class 类名 implements 接口名 {}
  • 接口的子类(实现类)
    • 要么重写接口中的所有抽象方法
    • 要么是抽象类
  • 注意:接口和类的实现关系,可以单实现,也可以多实现。
    • public class 类名 implements 接口名1 , 接口名2 {}


  • Inter接口
public interface Inter {
    public abstract void study();
}
  • InterA接口
public interface InterA {
    public abstract void print1();
    public abstract void print2();
    public abstract void study();
}
  • InterImpl实现类
public class InterImpl implements Inter,InterA {
    @Override
    public void study() {
        System.out.println("我是实现类中的study方法");
    }

    @Override
    public void print1() {

    }

    @Override
    public void print2() {

    }
}
  • Test1Interface测试类
public class Test1Interface {
    /*
        接口的定义格式:
            public interface 接口名 {}
        类实现接口的格式:
            public class 类名 implements 接口名 {}
     */
    public static void main(String[] args) {
        // Inter i = new Inter();//不允许实例化
        InterImpl ii = new InterImpl();
        ii.study();
    }
}

2.3 接口中成员的特点

  • 成员变量
    • 只能是常量
    • 默认修饰符:public static final
  • 构造方法
    • 没有,因为接口主要是扩展功能的,而没有具体存在
  • 成员方法
    • 只能是抽象方法
    • 默认修饰符:public abstract
    • 关于接口中的方法,JDK8和JDK9中有一些新特性
public interface Inter {
    public static final int NUM = 10;

    // public Inter(){}

    public abstract void show();
}
public class TestInterface {
    /*
        成员变量: 只能是常量 系统会默认加入三个关键字
                    public static final
        构造方法: 没有
        成员方法: 只能是抽象方法, 系统会默认加入两个关键字
                    public abstract
     */
    public static void main(String[] args) {
        System.out.println(Inter.NUM);
    }
}

class InterImpl extends Object implements Inter{
    public InterImpl(){
        super();
    }

    public void method(){
        // NUM = 20;
        System.out.println(NUM);
    }

    public void show(){

    }
}

2.4 JDK8版中接口成员的特点——默认方法

  • JDK8版本后
    • 允许在接口中定义非抽象方法,但是需要使用关键字 default 修饰,这些方法就是默认方法
      • 作用:解决接口升级的问题
    • 接口中允许定义static静态方法
  • 接口中默认方法的定义格式:
    • 格式:public default 返回值类型 方法名(参数列表) { }
    • 范例:public default void show() { }
  • 接口中默认方法的注意事项:
    • 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
    • public可以省略,default不能省略
    • 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
public interface InterA {
    public default void show() {
        System.out.println("我是A接口中的show方法");
    }
}
public interface InterB {
    public default void show() {
        System.out.println("B....show方法");
    }
}
public class TestInterface {
    public static void main(String[] args) {
        InterAImpl ia = new InterAImpl();
        ia.show();
    }
}

class InterAImpl implements InterA, InterB {

    @Override
    public void show() {

    }
}

2.5 JDK8版中接口成员的特点——静态方法

  • JDK8版本后
    • 接口中允许定义static静态方法
  • 接口中静态方法的定义格式:
    • 格式:public static 返回值类型 方法名(参数列表) { }
    • 范例:public static void show() { }
  • 接口中静态方法的注意事项:
    • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
    • public可以省略,static不能省略
public interface InterA {
    public static void show(){
        System.out.println("InterA...show");
    }
}
public interface InterB {
    public static void show(){
        System.out.println("InterB...show");
    }
}
public class TestInterface {
    public static void main(String[] args) {
        InterAImpl ia = new InterAImpl();
        // ia.show();
        InterA.show();
        InterB.show();
        // InterAImpl.show();
    }
}

class InterAImpl implements InterA , InterB {

}

2.6 JDK9版中接口成员的特点——私有方法

  • 接口中私有方法的定义格式:
    • 格式1:private 返回值类型 方法名(参数列表) { }
      • 范例1:private void show() { }
    • 格式2:private static 返回值类型 方法名(参数列表) { }
      • 范例2:private static void method() { }
  • 接口中私有方法的注意事项
    • 默认方法可以调用私有的静态方法和非静态方法
    • 静态方法只能调用私有的静态方法
public interface Inter {
    public default void start() {
        System.out.println("start方法执行了...");
        log();
    }

    public default void end() {
        System.out.println("end方法执行了...");
        log();
    }

    private void log(){
        System.out.println("日志记录 ( 模拟 )");
    }

    private static void check(){
        System.out.println("权限校验 ( 模拟 )");
    }

    public static void open() {
        check();
        System.out.println("open方法执行了");
    }

    public static void close(){
        check();
        System.out.println("close方法执行了");
    }
}
public class TestInterface {
    public static void main(String[] args) {
        InterImpl ii = new InterImpl();
        ii.start();
        ii.end();

        Inter.open();
        Inter.close();
    }
}

class InterImpl implements Inter {

}

2.7 总结:接口的使用思路

  • 如果发现一个类中所有的方法都是抽象方法,那么就可以将该类,改进为一个接口
  • 涉及到了接口大面积更新方法,而不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法
  • 希望默认方法调用的更加简洁,可以考虑设计为static静态方法。(需要去掉default关键字)
  • 默认方法中出现了重复的代码,可以考虑抽取出一个私有方法。(需要去掉default关键字)

2.8 类和接口的关系

  • 类和类的关系
    • 继承关系,只能单继承,但是可以多层继承
  • 类和接口的关系
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
public class Fu {
    public void show(){
        System.out.println("Fu...show");
    }
}
public interface Inter {
    public default void show(){
        System.out.println("Inter....show");
    }
}
public class TestInterface {
    public static void main(String[] args) {
        InterImpl ii = new InterImpl();
        ii.show();
        // 如果直接父类, 和接口中出现了相同的方法声明, 但是代码逻辑不一样
        // 优先使用直接父类的代码逻辑.
    }
}

class InterImpl extends Fu implements Inter {

}
  • 接口和接口的关系
    • 继承关系,可以单继承,也可以多继承
public interface InterA {
    public abstract void showA();

    public default void method(){
        System.out.println("InterA...method方法");
    }
}
public interface InterB {
    public abstract void showB();

    public default void method(){
        System.out.println("InterB...method方法");
    }
}
public interface InterC extends InterA , InterB {
    @Override
    public default void method() {
        System.out.println("InterC接口,解决代码逻辑冲突问题, 重写method方法");
    }
}
/*
    接口实现类
 */
public class InterImpl implements InterC {

    @Override
    public void showA() {

    }

    @Override
    public void showB() {

    }
}
public class TestInterface {
    public static void main(String[] args) {
        InterImpl ii = new InterImpl();
        ii.method();//InterC接口,解决代码逻辑冲突问题, 重写method方法
    }
}

3 黑马信息管理系统使用接口改进

  • 实现步骤:
    • 将 BaseStudentDao抽象类改进为一个接口
    • 让 StudentDao 和 OtherStudentDao 去实现这个接口

3.1 修改BaseStudentDao接口

import com.itheima.edu.info.manager.domain.Student;

public interface BaseStudentDao {
    // 添加学生方法
    public abstract boolean addStudent(Student stu);
    // 查看学生方法
    public abstract Student[] findAllStudent();
    public abstract void deleteStudentById(String delId);
    public abstract int getIndex(String id);
    public abstract void updateStudent(String updateId, Student newStu);
}

3.2 修改StudentDao类

public class StudentDao implements BaseStudentDao {
	//内容都没有变
}

3.3 修改OtherStudentDao类

public class OtherStudentDao implements BaseStudentDao {
	//内容都没有变
}

4 多态

4.1 多态概述

  • 什么是多态
    • 同一个对象,在不同时刻表现出来的不同形态
      • 举例:猫
        • 我们可以说猫是猫:猫 cat = new 猫();
        • 我们也可以说猫是动物:动物 animal = new 猫();
        • 这里猫在不同的时刻表现出来了不同的形态,这就是多态
  • 多态的前提和体现
    • 有继承/实现关系
    • 有方法重写
    • 有父类引用指向子类对象
public class Test1Polymorphic {
    /*
        多态的前提:
            1. 要有(继承 \ 实现)关系
            2. 要有方法重写
            3. 要有父类引用, 指向子类对象
     */
    public static void main(String[] args) {
        // 当前事物, 是一只猫
        Cat c = new Cat();
        // 当前事物, 是一只动物
        Animal a = new Cat();
        a.eat();
    }
}

class Animal {
    public void eat(){
        System.out.println("动物吃饭");
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

4.2 多态中的成员访问特点

  • 构造方法:
    • 同继承一样,子类会通过 super 访问父类构造方法
  • 成员变量:
    • 编译看左边(父类),执行看左边(父类)
  • 成员方法:
    • 编译看左边(父类),执行看右边(子类)

  • 为什么成员变量和成员方法的访问不一样呢?
    • 因为成员方法有重写,而成员变量没有
public class Test2Polymorpic {
    /*
         多态的成员访问特点:
                成员变量: 编译看左边 (父类), 运行看左边 (父类)
                成员方法: 编译看左边 (父类), 运行看右边 (子类)
     */
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);
        f.method();
    }
}

class Fu {
    int num = 10;
    public void method(){
        System.out.println("Fu.. method");
    }
}

class Zi extends Fu {
    int num = 20;
    public void method(){
        System.out.println("Zi.. method");
    }
}

4.3 多态的好处和弊端

  • 多态的好处:
    • 提高了程序的扩展性
      • 具体体现:定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的任意子类对象
  • 多态的弊端:
    • 不能使用子类的特有功能
public class Test3Polymorpic {
    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 {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void watchHome(){
        System.out.println("看家");
    }
}

class Cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

4.4 多态中的转型

  • 向上转型
    • 从子到父
    • 父类引用指向子类对象
  • 向下转型
    • 从父到子
    • 父类引用转为子类对象
public class Test4Polymorpic {
    public static void main(String[] args) {
        // 1. 向上转型 : 父类引用指向子类对象
        Fu f = new Zi();
        f.show();
        // 多态的弊端: 不能调用子类特有的成员
        // f.method();

        // A: 直接创建子类对象
        // B: 向下转型

        // 2. 向下转型 : 从父类类型, 转换回子类类型
        Zi z = (Zi) f;
        z.method();
    }
}

class Fu {
    public void show(){
        System.out.println("Fu..show...");
    }
}

class Zi extends Fu {
    @Override
    public void show() {
        System.out.println("Zi..show...");
    }

    public void method(){
        System.out.println("我是子类特有的方法, method");
    }
}

4.5 多态中转型存在的风险和解决方案

  • 风险
    • 如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
  • 解决方案
    • 关键字
      • instanceof
    • 使用格式
      • 变量名 instanceof 类型
      • 通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
public class Test3Polymorpic {
    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();//不能调用子类特有的功能

        // 判断a变量记录的类型, 是否是Dog
        if(a instanceof Dog){
            Dog dog = (Dog) a;
            dog.watchHome();
        }

          //向下转型
//        Dog dog = (Dog) a;
//        dog.watchHome();  // ClassCastException  实际类型和目标类型不是同一种类型会导致类型转换异常
    }
}

abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void watchHome(){
        System.out.println("看家");
    }
}

class Cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

5 黑马信息管理系统使用多态改进

  • 实现步骤
    • StudentDaoFactory类中方法的返回值定义成父类类型BaseStudentDao
    • StudentService中接收方法返回值的类型定义成父类类型BaseStudentDao

5.1 修改StudentDaoFactory类

import com.itheima.edu.info.manager.dao.BaseStudentDao;
import com.itheima.edu.info.manager.dao.OtherStudentDao;

public class StudentDaoFactory {
    public static BaseStudentDao getStudentDao() {
        return new OtherStudentDao();
    }
}

5.2 修改StudentService类

public class StudentService {
    // 创建StudentDao (库管)
    //private OtherStudentDao studentDao = new OtherStudentDao();

    //通过学生库管工厂类获取库管对象
    private BaseStudentDao studentDao = StudentDaoFactory.getStudentDao();

	//余下代码无变动
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值