JAVA SE基础《十一》 ---- 面对对象高级二

目录

一、面对对象的三大特征之三:多态

  1、认识多态

  2、使用多态的好处

  3、多态下的类型转换问题

  4、多态中调用成员的特点

二、final

  1、认识final

  2、补充知识:常量详解

三、抽象类

  1、认识抽象类

  2、使用抽象类的好处

  3、抽象类的常见应用场景:模板方法设计模式

四、接口 

  1、接口概述

  2、接口的综合案例

  3、接口的其他细节:JDK8开始,接口中新增的三种方法、

  4、接口的其他细节:接口的多继承、使用接口的注意事项[了解] 

 

一、面对对象的三大特征之三:多态

 

1、认识多态

什么是多态?

  • 多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。

多态的具体代码表现

多态的前提

  • 继承/实现关系;存在父类引用子类对象;存在方法重写

多态的一个注意事项

多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。

 

2、使用多态的好处

  • 在多态形式下,右边对象是解耦合的,更便于扩展和维护。

  • 定义方法时,使用父类类型的形参,可以接受一切子类对象,扩展性更强、更便利。

总结

1、使用多态有什么好处?存在什么问题?

  • 可以解耦合,扩展性更强;使用父类类型的变量作为方法的形参时,可以接收一切子类对象。
  • 多态下不能直接调用子类的独有方法。

 

3、多态下的类型转换问题

类型转换

  • 自动类型转换:父类 变量名 = new 子类();
  • 强制类型转换:子类 变量名 = (子类) 父类变量

强制类型转换的一个注意事项

  • 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
  • 运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException) 的错误出来。

强转前,Java建议:

  • 使用instanceof关键字,判断当前对象的真实类型,再进行强转。

 总结

1、类型转换有几种形式?能解决什么问题?

  • 自动类型转换、强制类型转换。
  • 可以把对象转换成其真正的类型,从而解决了多态下不能调用子类独有方法的问题。

2、强制类型转换需要注意什么?

  • 存在继承/实现时,就可以进行强制类型转换,编译阶段不会报错。
  • 但是,运行时,如果发现对象的真实类型与强转后的类型不同会报错(ClassCastException)。

3、强制类型转换前?Java建议我们做什么事情?

  • 使用instanceof判断当前对象的真实类型:对象 instanceof 类型

 

4、多态中调用成员的特点

  • 变量调用:编译看左边,运行看左边
  • 方法调用:编译看左边,运行看右边
package com.ctgu.pojo;

public class Test {
    public static void main(String[] args) {
        //创建对象,多态方式
        Animal a = new Dog();
        // 调用成员变量:编译看左边,运行看左边
        ////编译看左边: javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败
        // 运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
        System.out.println(a.name); //动物

        // 调用成员方法:编译看左边,运行看右边
        //编译看左边: javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
        // 运行看右边:java运行代码的时候,实际上运行的是子类中的方法。
        a.show(); //Dog----show方法

        // 成员变显:在子类的对象中,会把父类约成员变量也继承下的。父: name 子: name
        // 成员方法;如果子凳对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
    }
}
class Animal{
    String name = "动物";
    public void show(){
        System.out.println("Animal----show方法");
    }
}

class Cat extends Animal{
    String name = "猫";
    public void show(){
        System.out.println("Cat----show方法");
    }
}
class Dog extends Animal{
    String name = "狗";
    public void show(){
        System.out.println("Dog----show方法");
    }
}

 

 

二、final

1、认识final

fianl

  • final关键字是最终的意思,可以修饰(类、方法、变量)
  • 修饰类:该类被称为最终类,特点是不能被继承了。
  • 修饰方法:该方法被称为最终方法,特点是不能被重写了。
  • 修饰变量:改变量只能被赋值一次。

final修饰变量的注意事项

  • final修饰基本类型的变量,变量存储的数据不能被改变。
  • final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。

2、补充知识:常量详解

常量

  • 使用了static final修饰的成员变量就被称为常量
  • 作用:通常用于记录系统的配置信息

注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。

 

使用常量记录系统配置信息的优势、执行原理

  • 代码可读性更好,可维护性也更好。
  • 程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。

 

三、抽象类

1、认识抽象类

什么是抽象类?

  • 在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。
  • abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。

 抽象类的注意事项、特点

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
  • 一个类继承抽象类,必须重写玩抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

总结

1、抽象类、抽象方法是什么样的?

  • 都是用abstract修饰的;抽象方法只有方法签名,不能写方法体。

2、抽象类有哪些注意事项和特点?

  • 抽象类中可以不写抽象方法,但有抽象方法的类一定是抽象类。
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
  • 一个类继承抽象类,必须重写玩抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

 

2、使用抽象类的好处

抽象类的场景和好处

  • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们设计这样的抽象类,就是为了更好的支持多态。

需求

  • 某宠物游戏,需要管理猫、狗的数据。猫的数据有:名字;行为是:喵喵喵的叫~ 。狗的数据有:名字;行为是:汪汪汪的叫~ 。请用面向对象编程设计该程序。

总结

1、抽象类的应用场景和好处是什么?

  • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们抽出这样的抽象类,就是为了更好的支持多态

 

3、抽象类的常见应用场景:模板方法设计模式

模板方法设计模式解决了什么问题?

  • 解决方法中存在重复代码的问题。

模板方法设计模式的写法

1、定义一个抽象类。

2、在里面定义2个方法

  • 一个是模板方法:把相同代码放里面去。
  • 一个是抽象方法:具体实现交给子类完成

多学一招:建议使用final关键字修饰模板方法,为什么?

  • 模板方法是给对象直接使用的,不能被子类重写。
  • 一旦子类重写了模板方法,模板方法就失效了。

总结

1、模板方法设计模式解决了什么问题?

  • 解决方法中存在重复代码的问题。

2、模板方法设计模式应该怎么写?

  • 定义一个抽象类。
  • 在里面定义2个方法,一个是模板方法:放相同的代码里,一个是抽象方法:具体实现交给子类完成。

3、模板方法建议使用什么关键字修饰?为什么?

  • 建议使用final关键字修饰模板方法。

 

四、接口

1、接口概述

 认识接口

  • Java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的结构:接口。

  •  注意:接口不能创建对象;接口是用来被类实现(implements)的,实现接口的类称为实现类

  • 一个类可以实现多个接口(接口可以理解成干爹),实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。

接口的好处(重点)

  • 弥补了类单继承的不足,一个类同时可以实现多个接口。
  • 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。

 

总结

1、使用接口有啥好处,第一个好处是什么?

  • 可以解决类单继承的问题,通过接口,我们可以让一个类有一个亲爹的同时,还可以找多个干爹去扩展自己的功能。

2、为什么我们要通过接口,也就是去找干爹,来扩展自己的功能呢?

  • 因为通过接口去找干爹,别人通过你implements的接口,就可以显性的指导你是谁,从而也就可以放心的把你当作谁来用了。

3、使用接口的第二个好处是什么?

  • 一个类我们说可以实现多个接口,同样,一个接口也可以被多个类实现的。这样做的好处是我们的程序就可以面向接口编程了,这样我们程序员就可以很方便的灵活切换各种业务实现了。

 

2、接口的综合案例

    此案例分为五个类(一个实体类,两个实现类,一个测试类)和一个接口完成。切换业务只需要更改班级管理类ClassManager的实现接口,把StudentOperatorImpl1改为StudentOperatorImpl2即可。

 实体类Student

package com.ctgu.d7_interface_demo;

public class Student {
    private String name;
    private char sex;
    private double score;


    public Student() {
    }

    public Student(String name, char sex, double score) {
        this.name = name;
        this.sex = sex;
        this.score = score;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return sex
     */
    public char getSex() {
        return sex;
    }

    /**
     * 设置
     * @param sex
     */
    public void setSex(char sex) {
        this.sex = sex;
    }

    /**
     * 获取
     * @return score
     */
    public double getScore() {
        return score;
    }

    /**
     * 设置
     * @param score
     */
    public void setScore(double score) {
        this.score = score;
    }
}

班级管理类ClassManager

package com.ctgu.d7_interface_demo;
import java.util.ArrayList;

public class ClassManager {
    private ArrayList<Student> students = new ArrayList<>();
    private StudentOperator studentOperator = new StudentOperatorImpl1();

    public ClassManager(){
        students.add(new Student("迪丽热巴",'女',99));
        students.add(new Student("古力娜扎",'女',100));
        students.add(new Student("马儿扎哈",'男',80));
        students.add(new Student("卡尔扎巴",'男',60));
    }
    // 打印全班全部学生的信息
    public void printInfo(){
        studentOperator.printAllInfo(students);
    }

    //打印全班全部学生的平均分
    public void printScore(){
        studentOperator.printAverageScore(students);
    }
}

接口StudentOperator

package com.ctgu.d7_interface_demo;
import java.util.ArrayList;

public interface StudentOperator {
    void printAllInfo(ArrayList<Student> students);
    void printAverageScore(ArrayList<Student> students);
}

方案一实现类StudentOperatorImpl1

package com.ctgu.d7_interface_demo;


import java.util.ArrayList;

public class StudentOperatorImpl1 implements StudentOperator{

    @Override
    public void printAllInfo(ArrayList<Student> students) {
        System.out.println("-----------全班全部学生信息如下----------");
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println("姓名:" + s.getName() + ",性别:" + s.getSex() + ",成绩:" + s.getScore());
        }
        System.out.println("--------------------------------------");
    }

    @Override
    public void printAverageScore(ArrayList<Student> students) {
        double allScore = 0.0;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            allScore += s.getScore();
        }
        System.out.println("平均分:" + (allScore)/students.size());
    }
}

方案二实现类StudentOperatorImpl2

package com.ctgu.d7_interface_demo;


import java.util.ArrayList;

public class StudentOperatorImpl2 implements StudentOperator {
    @Override
    public void printAllInfo(ArrayList<Student> students) {
        System.out.println("-----------全班全部学生信息如下----------");
        int boy = 0;
        int girl = 0;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println("姓名:" + s.getName() + ",性别:" + s.getSex() + ",成绩:" + s.getScore());
            if(s.getSex() == '男'){
                boy += 1;
            }else{
                girl += 1;
            }
        }
        System.out.println("男生人数是:" + boy + ",女生人数是:" + girl);
        System.out.println("班级总人数是:" + students.size());
        System.out.println("--------------------------------------");
    }

    @Override
    public void printAverageScore(ArrayList<Student> students) {
        double allScore = 0.0;
        double max = students.get(0).getScore();
        double min = students.get(0).getScore();
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            if (s.getScore()>max) max = s.getScore();
            if (s.getScore()<min) min = s.getScore();
            allScore += s.getScore();
        }
        System.out.println("学生的最高分是" + "max");
        System.out.println("学生的最低分是" + "min");
        System.out.println("平均分:" + (allScore-min-max) / (students.size()-2));
    }
}

测试类Test

package com.ctgu.d7_interface_demo;

public class Test {
    public static void main(String[] args) {
        ClassManager clazz = new ClassManager();
        clazz.printInfo();
        clazz.printScore();

    }
}

 

3、接口的其他细节:JDK8开始,接口中新增的三种方法

JDK8开始,接口新增了三种形式的方法:

JDK8开始,接口中为啥要新增加这些方法?

  • 增强了接口的能力,更便于项目的扩展和维护。

总结

1、JDK8开始,接口中新增了哪些方法?

  • 默认方法:使用default修饰,使用实现类的对象调用。
  • 静态方法:static修饰,必须用当前接名调用
  • 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
  • 他们都会默认被public修饰。

2、JDK8开始,接口中为啥要新增这些方法?

  • 增强了接口的能力,更便于项目的扩展和维护。

 

4、接口的其他细节:接口的多继承、使用接口的注意事项【了解】

接口的多继承

一个接口可以同时继承多个接口

接口多继承的作用

  • 便于实现类去实现。

接口其他注意事项(了解)

  • 1、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。

  • 2、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。

  • 3、一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。

  • 4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

posted @ 2023-09-02 16:14  小王子C  阅读(31)  评论(0编辑  收藏  举报