继承、接口和多态

目录

static关键字

1.介绍

2.特点

3.static修饰成员变量内存图解

4.使用场景

5.static修饰成员方法——工具类——注意事项

继承

1.介绍

2.格式

3.使用场景

4.继承中成员变量和成员方法的访问特点

①成员变量

②成员方法

5.Java中继承的特点

6.继承中构造方法访问特点

7.继承的案例

8.继承中构造方法执行流程的内存图解

9.综合案例

10.this和super

final关键字

1.介绍

2.特点

3.final修饰成员变量的注意事项

4.final修饰变量的命名规范

面向对象高级

一、包

1.介绍

2.导包

二、抽象类

1.介绍

2.格式

3.注意事项

4.abstract关键字的冲突

三、接口

1.介绍

2.定义格式

3.注意事项

4、接口中的成员特点

5.类和接口之间的各种关系

四、多态

1.介绍

2.多态的前提

3.多态的成员访问特点和好处

4.多态的转型

五、接口新特性

JDK8

JDK9

六、代码块

1.介绍

2.分类


static关键字

1.介绍

修饰符,是静态的意思,可以修饰成员变量,也可以修饰成员方法

2.特点

  • 被其修饰的成员,被该类的所有对象所共享

  • 多了一种调用方式,可以通过类名调用(推荐)

  • 随着类的加载而加载,优先于对象存在

package com.itheima.mstatic;
 
public class Student {
    String name;
    int age;
    
    //"传智专修学院"被所有的对象共享
    static String school;
}
package com.itheima.mstatic;
 
public class StaticDemo1 {
​
    public static void main(String[] args) {
 
        //可以通过类名调用(推荐使用)
        Student.school = "传智专修学院";
 
        Student stu1 = new Student();
        stu1.name = "张三";
        stu1.age = 23;
        System.out.println(stu1.name + "---" + stu1.age + "---" + Student.school);
 
 
        System.out.println("---------------------");
 
        Student stu2 = new Student();
        stu2.name = "李四";
        stu2.age = 24;
 
        System.out.println(stu2.name + "---" + stu2.age + "---" + Student.school);
 
    }
}

运行结果

3.static修饰成员变量内存图解

4.使用场景

  • static成员变量:共享数据

  • static成员方法:常用于工具类

5.static修饰成员方法——工具类——注意事项

  • 成员方法什么时候加入 static --> 常用于制作工具类

  • 工具类: 不是描述事物的, 而是帮我们完成一些事情 (打工)

  • 如果发现一个类中, 所有的方法, 全都是 static 所修饰——私有该类的构造方法 (目的: 为了不让其他类, 再创建对象)

package com.itheima.tools;
 
              //工具类,为调用者提供服务
public class ArrayTools {
 
    //私有这个工具类的构造方法(不让别人再去创建对象:static是静态的,用类名可以直接调用)
    private ArrayTools(){}
 
    public static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }
 
    public static int getMin(int[] arr) {
        int min = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] < min) {
                min = arr[i];
            }
        }
        return min;
    }
 
    public static void printArray(int[] arr) {
        System.out.print("[");
        for (int i = 0; i < arr.length - 1; i++) {
            System.out.print(arr[i] + ", ");
        }
        System.out.println(arr[arr.length - 1] + "]");
    }
 
}
package com.itheima.tools;
 
public class Test {
​
    public static void main(String[] args) {
        int[] arr = {11,22,33};
 
                           //**类名.方法名(因为getMax和getMin有静态变量static修饰)
        //不需要创建对象:ArrayTools tools = new ArrayTools();
        System.out.println(ArrayTools.getMax(arr));
        System.out.println(ArrayTools.getMin(arr));
        ArrayTools.printArray(arr);
 
        //终止当前Java虚拟机正在运行的任务
        System.exit(0);
    }
}
  • static方法中,只能访问静态成员(直接访问)(非静态需要把对象new出来,要不然在内存中还没有)

  • static中不允许使用this关键字

package com.itheima.mstatic;
 
public class StaticDemo2 {
 
    static int num1 = 10;
 
    int num2 = 20;
 
    public static void method(){
        System.out.println("static...method");
    }
 
    public void print(){
        System.out.println("print...");
    }
 
    public static void main(String[] args) {
        // 在静态方法中, 只能访问静态成员(直接访问)
        System.out.println(num1);
        method();
 
        //解决办法:创建对象后能够使用
        StaticDemo2 sd = new StaticDemo2();
        System.out.println(sd.num2);
        sd.print();
    }
}

ps:重新认识main方法

继承

1.介绍

类与类之间产生关系(子父类关系),子类可以直接使用父类非私有的成员

2.格式

public class 子类名 extends 父类名 { }

范例:public class Zi extends Fu { }

Fu:是父类,也被称为基类、超类

Zi:是子类,也被称为派生类

创建类的细节:一个java文件中可以编写多个class(不建议使用)

  • 保证类与类之间是平级关系

  • 只有一个能被public修饰

package com.itheima.mextends;
 
public class ExtendsDemo1 {
    /*
        继承: 类与类之间产生关系(子父类关系), 子类就可以直接使用父类中的非私有的成员
     */
    public static void main(String[] args) {
        Coder c = new Coder();
        c.setName("张三");
        c.setAge(23);
        c.setSalary(12000);
 
        System.out.println(c.getName() + "---" + c.getAge() + "---" + c.getSalary());
    }
}
 
class Employee {
    private String name;
    private int age;
    private double salary;
 
    
    //虽然父类中的私有了,但set和get方法中是公有的,子类可以继承使用
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public double getSalary() {
        return salary;
    }
 
    public void setSalary(double salary) {
        this.salary = salary;
    }
}
 
class Coder extends Employee {
 
}
 
class Manager extends Employee {
 
}

3.使用场景

好处:提高代码的复用性

使用场景:当类与类之间,存在相同(共性)的内容,并且产生了谁is谁的a一种的关系,就可以考虑使用继承,来优化代码。

4.继承中成员变量和成员方法的访问特点

①成员变量

子父类中,如果出现了重名的成员变量,根据就近原则,优先使用子类的

使用super关键字来进行解决

this:调用本类成员

super:调用父类成员

public class Fu {
    int num = 10;
}
class Zi extends Fu {
    int num = 20;
    
    public void method(){
        int num = 30;
        System.out.println(num);// 30
        System.out.println(this.num);// 20
        System.out.println(super.num);// 10
    }
}

②成员方法

在继承体系中,子类可以继承到父类的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写,方法重写又称方法覆盖

方法重载(Overload) : 在同一个类中, 方法名相同, 参数不同, 与返回值无关 ---> 参数不同: 类型不同, 个数不同, 顺序不同

方法重写(Override) : 在子父类当中, 出现了方法声明一模一样的方法 (方法名, 参数, 返回值)

  • 目标1: 能够独立识别出, 方法是不是重写的方法 (在子类的方法上面加注解:@Override,如果不报错则为方法重写)

  • 目标2: 方法重写的使用场景 ---> 当子类需要父类的方法, 但是觉得父类的方法逻辑不好 (修改 | 增强) 就可以对父类的方法进行重写

package com.itheima.mextends;
 
public class ExtendsDemo3 {
​
    public static void main(String[] args) {
        Son s = new Son();
        s.love();
    }
}
 
class Father {
 
    public void love() {
        System.out.println("送花");
        System.out.println("送肉");
        System.out.println("唱歌");
    }
 
}
 
class Son extends Father {
 
    @Override
    public void love() {
        System.out.println("送酱肉包");
        System.out.println("送口红");
        System.out.println("我喜欢你, 一起去爬山吗~");
    }
}

运行结果

送酱肉包

送口红

我喜欢你, 一起去爬山吗~

注意事项:

  • 父类中私有方法不能被重写

  • 子类重写父类方法时,访问权限必须大于等于父类

修饰符类本身同一个包的类不同包的子类任何类
private
default(默认)
protected
public

5.Java中继承的特点

  • Java只支持单继承,不支持多继承,但支持多层继承

package com.itheima.mextends;
 
public class ExtendsDemo4 {
    public static void main(String[] args) {
        C c = new C();
        c.methodC();
        c.methodB();
        c.methodA();
    }
}
 
class A {
    public void methodA() {
        System.out.println("A...");
    }
}
 
class B extends A {
    public void methodB() {
        System.out.println("B...");
    }
}
 
class C extends B {
    public void methodC() {
        System.out.println("C...");
    }
}

6.继承中构造方法访问特点

  • 父类中的构造方法能否被继承?

不能,子类需要自己手动编写构造方法

  • 子类在初始化之前,是否需要先完成父类的初始化?

需要,因为子类可能会调用到父类的数据

  • 如何完成父类的初始化?

构造方法用于对象初始化,子类只要有办法调用到父类的构造方法即可

  • 如何调用父类的构造方法?

除了Object类,在所有的构造方法的第一行代码中,都默认隐藏了一句话super();通过这句代码,来访问父类的空参构造方法,从而完成父类的初始化

细节: Java当中所有的类, 都直接或者间接的继承到了 Object 类(父类也默认继承了Object类)

package com.itheima.mextends.constructor;
 
public class Test {
​
    public static void main(String[] args) {
        Zi z1 = new Zi();
        Zi z2 = new Zi(10);
 
    }
}
 
class Demo {}
 
 
class Fu {
    public Fu() {
        
        //父类也默认继承了Object类
        super();
        System.out.println("Fu类的空参构造方法");
    }
 
    public Fu(int num) {
        System.out.println("Fu类的带参构造方法...");
    }
}
 
class Zi extends Fu {
 
    public Zi() {
        super();
        System.out.println("Zi类的空参构造方法");
    }
 
    public Zi(int num) {
        super();
        System.out.println("Zi类的带参构造方法...");
    }
 
}

运行结果

Fu类的空参构造方法

Zi类的空参构造方法

Fu类的空参构造方法

Zi类的带参构造方法...

7.继承的案例

需求: 人类: (Person) 成员变量: 姓名, 年龄

老师类: ​ 成员变量: 姓名, 年龄 ​ 成员方法: teach

姓名为张三, 年龄为30岁的老师正在讲课

学生类: ​ 成员变量: 姓名, 年龄, 成绩 ​ 成员方法: study

姓名为李四, 年龄为20岁, 成绩为100分的学生, 正在学习

父类:Person

package com.itheima.test;
​
public class Person {
    private String name;
    private int age;
​
    public Person() {
    }
​
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
​
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
​
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
​
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
}

子类:Teacher、Student

package com.itheima.test;
​
public class Teacher extends Person{
​
    public Teacher() {
    }
​
    public Teacher(String name, int age) {
        super(name, age);
    }
​
    public void teach() {
        System.out.println("姓名为" + super.getName() +",年龄为" + super.getAge() + "的老师正在讲课");
    }
}
package com.itheima.test;
​
public class Student extends Person{
​
    private double score;
​
    public Student() {
    }
​
    public Student(String name, int age, double score) {
        super(name, age);
        //父类没有,需要自己初始化
        this.score = score;
    }
​
    public double getScore() {
        return score;
    }
​
    public void setScore(double score) {
        this.score = score;
    }
​
    public void study() {
                                                                                         //成绩没有必要调用方法,score成员变量虽然被私有化,但它的使用环境就在本类当中
        System.out.println("姓名为" + super.getName() + ",年龄为" + super.getAge() + "岁,成绩为" + score + "分的学生,正在学习");
    }
}

测试类

package com.itheima.test;
 
public class Test1 {
​
    public static void main(String[] args) {
        Teacher t = new Teacher("张三", 30);
        t.teach();
 
        Student stu = new Student("李四", 20, 100);
        stu.study();
    }
}

运行结果

姓名为张三,年龄为30的老师正在讲课 姓名为李四,年龄为24岁,成绩为100.0分的学生,正在学习

8.继承中构造方法执行流程的内存图解

注意:父类中私有的变量子类可以继承,但由于权限的不足,无法直接使用

 

9.综合案例

父类 --> Employee

package com.itheima.test;
​
public class Employee {
    private String name;
    private int age;
    private double salary;
​
    public Employee() {
    }
​
    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
​
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
​
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
​
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
​
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
​
    /**
     * 获取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }
​
    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }
​
    public void work() {
        System.out.println("员工工作...");
    }
}

子类 --> Coder、Manager

package com.itheima.test;
​
public class Coder extends Employee{
​
    public Coder() {
    }
​
    public Coder(String name, int age, double salary) {
        super(name, age, salary);
    }
​
    //重写父类方法
    @Override
    public void work() {
                                //子类没有get...的方法,所以super.可以省略
        System.out.println("姓名为" + super.getName()
                + ",年龄为" + super.getAge() + ",工资为"
                + super.getSalary() + "的程序员正在编写代码");
    }
}
package com.itheima.test;
​
public class Manager extends Employee{
​
    private double bonus;
​
    public Manager() {
    }
​
    public Manager(String name, int age, double salary, double bonus) {
        super(name, age, salary);
        //父类没有的自己初始化
        this.bonus = bonus;
    }
​
    public double getBonus() {
        return bonus;
    }
​
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
​
    @Override
    public void work() {
                                //子类没有get...的方法,所以super.可以省略
        System.out.println("姓名为" + super.getName()
                + ",年龄为" + super.getAge() + ",工资为"
                + super.getSalary() + ",奖金为" + bonus
                + "的项目经理正在分配任务");
    }
}

测试类

package com.itheima.test;
​
public class Test2 {
    public static void main(String[] args) {
        Coder c = new Coder("张三",23,15000);
        c.work();
​
        Manager m = new Manager("李四",24,18000,5000);
        m.work();
    }
}

10.this和super

this:代表本类对象的引用

super:代表父类存储空间的标识

关键字访问成员变量访问成员方法访问构造方法
thisthis.本类成员变量;this.本类成员方法();this(); this(…);本类构造方法
supersuper.父类成员变量;super.父类成员方法();super(); super(…);父类构造方法

被调用的变量和方法,如果在子类中不存在,那么"super."可以直接省略

注意:this()和super()都在争夺构造方法第一行的位置,所以二者不能共存

开闭原则:对功能扩展做开放,对修改代码做关闭(尽量不要修改已经编写好的代码,如果有新功能,重新编写)

final关键字

1.介绍

是最终的意思,可以修饰(方法,类,变量)

2.特点

  • 修饰方法:表明该方法是最终方法不能被重写

  • 修饰类:表明该类是最终类不能被继承

  • 修饰变量:表明该变量是常量不能再次被赋值

基本数据类型:数据值不可改变

引用数据类型:地址值不可改变,内容可以改变

3.final修饰成员变量的注意事项

  • final修饰成员变量时,不允许修饰默认值

  • final修饰成员变量的初始化时机:

    ①在定义的时候直接赋值

class Student{
​
    final int num = 10;
    
}

②在构造方法中完成赋值(不能在普通成员方法中赋值 public void study(){num = 2;})

class Student{
​
    final int num = 10;
    
    public Student() {
        num = 20;
        
    }
}

4.final修饰变量的命名规范

如果变量名是一个单词,所有字母大写 max -->MAX

如果变量名是多个单词,所有字母大写,中间使用下划线(_)分割 maxValue --> MAX_VALUE

面向对象高级

一、包

1.介绍

包本质来说就是文件夹,用来管理类文件的

  • 建包的语法格式:package公司域名倒写.技术名称。包名建议全部英文小写,且具备意义

    package com.itheima.domain;
    ​
    public class Student {
    ​
    }

  • 建包语句必须在第一行,一般IDEA工具会帮助创建

2.导包

  • 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!

  • 导包格式:import包名.类名;

  • 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。--> 使用全类名创建对象:包名 + 类名

a包

package com.itheima.a;
​
public class Student {
    public void eat() {
        System.out.println("学生吃饭");
    }
}

b包

package com.itheima.b;
​
public class Student {
    public void sleep() {
        System.out.println("学生睡觉");
    }
}

测试类

package com.itheima.c;
​
import com.itheima.a.Student;
​
public class Test {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.eat();
​
        //创建b包下的Student对象时报错:com.itheima.b.Student,系统不会自动导入b包代码
        //使用全类名创建对象:包名 + 类名
        com.itheima.b.Student stu2 = new com.itheima.b.Student();
        stu2.sleep();
    }
}

二、抽象类

1.介绍

抽象类是一种特殊的父类,内部可以编写抽象方法

当我们将共性的方法, 抽取到父类之后, 发现这个方法的实现逻辑在父类中无法给出具体明确(描述不清了),而且这个方法, 还是子类必须要有的方法, 就可以设计为抽象方法.

package com.itheima.mabstract;
​
public class AbstractTest1 {
    public static void main(String[] args) {
​
    }
}
/**
 * 父类描述不清子类必须要有的东西
class Animal {
    public void eat() {
        System.out.println("????");
    }
}
 */
​
//抽象方法要放到抽象类中,因此class前也要加 abstract
abstract class Animal {
    public abstract void eat();
}
​
​
//抽象类的子类必须要重写方法
class Cat extends Animal {
    
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
​
class Dog extends Animal {
​
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}

2.格式

①抽象方法的定义格式

public abstract 返回值类型 方法名(参数列表);

②抽象类的定义格式

public abstract class 类名 { }

3.注意事项

  • 抽象类不能实例化(如果抽象类允许创建对象,就可以调用内部没有方法体的抽象方法了)

  • 抽象类存在构造方法(存在意义:交给子类,通过super进行访问)

  • 抽象类中可以存在普通方法(存在意义:可以让子类继承到继续使用)

  • 抽象类的子类:要么重写抽象类中的所有抽象方法,要么变成抽象类

4.abstract关键字的冲突

  • final:被abstract修饰的方法,强制要求子类重写,被final修饰的方法子类不能重写

  • private:被abstract修饰的方法,强制要求子类重写,被private修饰的方法子类不能重写

  • static:被static修饰的方法可以类名调用,类名调用抽象方法没有意义

 

三、接口

1.介绍

接口体现的思想是对规则的声明,Java中的接口更多体现的是对行为的抽象(抽象方法)。

思路:如果发现一个类,所有的组成都是抽象方法,没有成员变量,没有普通方法,这种类,我们通常会设计为java中的接口(因为这个类唯一存在的价值就是声明规则了)

2.定义格式

public interface 接口名{ }

3.注意事项

①接口不允许实例化(new对象):Inter i = new Inter();

②接口和类之间是实现关系,通过implements关键字完成

  • 格式:class 类名 implements 接口名 { }

③实现类(接口的子类)

  • 重写所有的抽象方法

  • 将实现类变成抽象类(abstract)

package com.itheima.minterface;
​
import javax.swing.*;
​
public class InterfaceTest1 {
    public static void main(String[] args) {
        //接口不允许实例化(new对象):Inter i = new Inter();
        /**
         * 接口和类之间是实现关系,通过implements关键字完成
         * 格式:class 类名 implements 接口名 { }
         */
​
​
        //创建实现类对象
        InterImpl ii = new InterImpl();
        ii.method();
        ii.show();
    }
}
​
//接口是声明规则的,里面只能编写抽象方法
interface Inter {
    public abstract void show();
    public abstract void method();
}
​
​
//作为实现类(接口的子类),要重写接口里的抽象方法
class InterImpl implements Inter{
​
    @Override
    public void show() {
        System.out.println("show...");
    }
​
    @Override
    public void method() {
        System.out.println("method...");
    }
}

4、接口中的成员特点

  • 成员变量 : 只能定义常量, 因为系统会默认加入三个关键字:public(允许跨包访问) static(通过类名.变量进行访问)final(只能赋值一次,不能更改了,因此变量名要大写)[这三个关键字没有顺序关系]

  • 成员方法 : 只能是抽象方法, 因为系统会默认加入两个关键字:public abstract

  • 构造方法 : 没有

package com.itheima.minterface;
 
import com.itheima.a.Inter;
 
public class InterfaceTest2 {
    public static void main(String[] args) {
        System.out.println(Inter.num);
    }
}
 
interface MyInter {
    public static final int NUM = 10;
 
    public abstract void show();
    
    void method();
}
package com.itheima.a;
​
public interface Inter {
    static final int num = 10;
}

5.类和接口之间的各种关系

①关系

  • 类和类的关系:继承关系,只能单继承,但是可以多层继承

  • 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口和接口的关系:继承关系,可以单继承,也可以多继承

②抽象类和接口的对比

成员变量: 抽象类:可以定义变量,也可以定义常量

接口:只能定义常量

成员方法: 抽象类:可以是定义具体方法,也可以定义抽象方法

接口:只能定义抽象方法

构造方法: 抽象类:有

接口:没有

③抽象类的应用场景

 

④接口的应用场景

接口可以为程序制定规则,代码更加规范

1.创建接口

 

2.实现类重写内部抽象方法

⑤总结

抽象类:对事物做抽象(描述事物)

接口:对行为抽象(制定规则)

四、多态

1.介绍

同一个行为(方法)具有多个不同表现形式或形态的能力

OrderService ---> 接口

package com.itheima.order;
 
/**
 * 订单业务接口
 */
public interface OrderService {
    /**
     * 查询单个订单
     */
    void create();
 
    /**
     * 查询单个订单
     */
    void findOne();
 
    /**
     * 查询订单列表
     */
    void findList();
 
    /**
     * 取消订单
     */
    void cancel();
 
    /**
     * 完结订单
     */
    void finish();
 
    /**
     * 支付订单
     */
    void paid();
}

OrderServiceImpl ---> 国内订单实现类

package com.itheima.order;
 
public class OrderServiceImpl implements OrderService {
    @Override
    public void create() {
        System.out.println("创建订单");
    }
 
    @Override
    public void findOne() {
        System.out.println("查询单个订单");
    }
 
    @Override
    public void findList() {
        System.out.println("查询订单列表");
    }
 
    @Override
    public void cancel() {
        System.out.println("取消订单");
    }
 
    @Override
    public void finish() {
        System.out.println("完结订单");
    }
 
    @Override
    public void paid() {
        System.out.println("支付订单");
    }
}

OverseasServiceImpl ---> 国外订单实现类

package com.itheima.order;
 
public class OverseasServiceImpl implements OrderService {
 
    @Override
    public void create() {
        System.out.println("国外业务 --- 创建订单");
    }
 
    @Override
    public void findOne() {
        System.out.println("国外业务 --- 查询单个订单");
    }
 
    @Override
    public void findList() {
        System.out.println("国外业务 --- 查询订单列表");
    }
 
    @Override
    public void cancel() {
        System.out.println("国外业务 --- 取消订单");
    }
 
    @Override
    public void finish() {
        System.out.println("国外业务 --- 完结订单");
    }
 
    @Override
    public void paid() {
        System.out.println("国外业务 --- 支付订单");
    }
}

测试类

package com.itheima.order;
 
import java.util.Scanner;
 
public class Test {
​
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入:  1. 国内订单   2. 国外订单");
 
        OrderService orderService = null;
 
        int choice = sc.nextInt();
        switch (choice) {
            case 1:
                // 创建国内订单的业务类
                orderService = new OrderServiceImpl();
                break;
            case 2:
                // 创建国外订单的业务类
                orderService = new OverseasServiceImpl();
                break;
        }
 
        orderService.findOne();
        orderService.findList();
        orderService.cancel();
        orderService.finish();
        orderService.paid();
    }
}

 

 

创建对象的时候,左右两边的类型可以不一致,是所有对象都可以这样创建吗?

为什么方法是同一套,但是却有不同的表现形式?(3.②)

2.多态的前提

  • 有继承/实现关系

  • 有方法重写

  • 有父类引用指向子类对象

package polymorphism;
​
public class PolymorphismTest1 {
​
    /*
                1.有继承/实现关系
                2.有方法重写
                3.有父类引用指向子类对象
​
                Dog d = new Dog(); ---> 子类引用指向子类对象
             对象多态:父类引用指向子类对象:
                    Animal a1 = new Dog();
                    Animal a2 = new Cat();
​
                    好处:方法的形参可以定义为父类类型,这个方法就可以接收到该父类的任意子类对象了
             行为多态:
                    好处:同一个方法,具有多种不同表现形式或形态的能力
         */
​
    public static void main(String[] args) {
        //对象多态
        useAnimal(new Dog());
        useAnimal(new Cat());
        /*
        useDog(new Dog());
        useCat(new Cat());
         */
    }
    /*
    优化 useDog 和 useCat
    public static void useDog(Dog d) {
​
    }
​
    public static void useCat(Cat d) {
​
    }
     */
​
                                //对象多态:
    public static void useAnimal(Animal a) {
​
        //往这个方法中传入不同的对象,就会有不同的表现形式 ---> 行为多态
        a.eat();
​
    }
}
​
abstract class Animal {
    public abstract void eat();
}
​
class Dog extends Animal {
​
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
​
class Cat extends Animal {
​
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

3.多态的成员访问特点和好处

①访问特点

package com.itheima.polymorphism;
 
public class PolymorphismTest2 {
    /*
        多态的成员访问特点:
 
              1. 成员变量 : 编译看左边(父类), 运行看左边(父类)
 
              2. 成员方法 : 编译看左边(父类), 运行看右边(子类)
 
                                在编译的时候, 会检查父类中有没有这个方法
                                        没有 : 编译出错
                                        有 : 编译通过, 但是运行的时候, 一定会执行子类的方法逻辑
 
                                原因: 担心你调用的方法, 在父类中是一个抽象方法
 
                 ---------------------------------------------------------------------
 
            多态创建对象, 调用静态成员 :
 
                    静态的成员, 推荐类名进行调用
                    细节: 静态的成员, 可以使用对象名调用, 但这是一种假象
                                    - 生成字节码文件后, 会自动将对象名调用, 改成类名调用
     */
    public static void main(String[] args) {
        //左边  右边   
        Fu f = new Zi();
        System.out.println(f.num);
        f.show();
        f.print();          // Fu.print();
 
        System.out.println("-----------------");
 
        Inter i = new InterImpl();
        i.method();
    }
 
}
 
interface Inter {
    void method();
}
 
class InterImpl implements Inter {
 
    @Override
    public void method() {
        System.out.println("method...");
    }
}
 
class Fu {
    int num = 10;
 
    public void show() {
        System.out.println("Fu...show");
    }
 
    public static void print(){
        System.out.println("Fu...print");
    }
}
 
class Zi extends Fu {
    int num = 20;
 
    @Override
    public void show() {
        System.out.println("Zi...show");
    }
 
    public static void print(){
        System.out.println("Zi...print");
    }
}

②疑问解答

创建对象的时候,左右两边的类型可以不一致,是所有对象都可以这样创建吗?

  • 不是,必须是父类引用指向子类对象或者接口类引用指向实现类对象

为什么方法是同一套,但是却有不同的表现形式?

  • 在用多态创建完对象调用成员方法的时候,编译检查父类,但运行的时候一定会走子类的代码逻辑,所以不同的子类对象走的是不同的代码逻辑,所以有不同的表现形式

③多态的好处与弊端

多态的好处:提高了程序的扩展性

  • 对象多态 :将方法的形参定义为父类类型,这个方法可以接收该父类的任意子类对象

  • 行为多态:同一个行为具有多个不同表现形式或形态的能力 

 多态的弊端:不能使用子类的特有成员(可以使用多态的转型技术)

  • 因为在编译的时候, 会检查父类中有没有这个方法,而子类特有的方法父类是一定没有的,会编译出错

4.多态的转型

向下转型需要强制转型

①四、多态-1.--案例补充

OrderService ---> 接口

package com.itheima.order;
 
/**
 * 订单业务接口
 */
public interface OrderService {
    /**
     * 查询单个订单
     */
    void create();
 
    /**
     * 查询单个订单
     */
    void findOne();
 
    /**
     * 查询订单列表
     */
    void findList();
 
    /**
     * 取消订单
     */
    void cancel();
 
    /**
     * 完结订单
     */
    void finish();
 
    /**
     * 支付订单
     */
    void paid();
}

OrderServiceImpl ---> 国内订单实现类

package com.itheima.order;
 
public class OrderServiceImpl implements OrderService {
    @Override
    public void create() {
        System.out.println("创建订单");
    }
 
    @Override
    public void findOne() {
        System.out.println("查询单个订单");
    }
 
    @Override
    public void findList() {
        System.out.println("查询订单列表");
    }
 
    @Override
    public void cancel() {
        System.out.println("取消订单");
    }
 
    @Override
    public void finish() {
        System.out.println("完结订单");
    }
 
    @Override
    public void paid() {
        System.out.println("支付订单");
    }
}

OverseasServiceImpl ---> 国外订单实现类

package com.itheima.order;
 
public class OverseasServiceImpl implements OrderService {
 
    public void check() {
        System.out.println("IP地址检测");
    }
 
    @Override
    public void create() {
        System.out.println("国外业务 --- 创建订单");
    }
 
    @Override
    public void findOne() {
        System.out.println("国外业务 --- 查询单个订单");
    }
 
    @Override
    public void findList() {
        System.out.println("国外业务 --- 查询订单列表");
    }
 
    @Override
    public void cancel() {
        System.out.println("国外业务 --- 取消订单");
    }
 
    @Override
    public void finish() {
        System.out.println("国外业务 --- 完结订单");
    }
 
    @Override
    public void paid() {
        System.out.println("国外业务 --- 支付订单");
    }
}

测试类

package com.itheima.order;
 
import java.util.Scanner;
 
public class Test {
    /*
        ClassCastException : 类型转换异常
 
                在引用数据类型的强转中, [实际类型]和[目标类型]不匹配, 就会出现此异常
     */
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入:  1. 国内订单   2. 国外订单");
 
        OrderService orderService = null;
 
        int choice = sc.nextInt();
        switch (choice) {
            case 1:
                // 创建国内订单的业务类
                orderService = new OrderServiceImpl();
                break;
            case 2:
                // 创建国外订单的业务类
                orderService = new OverseasServiceImpl();
                break;
        }
 
        // instanceof : 判断左边的引用, 是否是右边的数据类型
        if(orderService instanceof OverseasServiceImpl){
            OverseasServiceImpl osi = (OverseasServiceImpl) orderService;
            osi.check();
        }
 
        orderService.create();
        orderService.findOne();
        orderService.findList();
        orderService.cancel();
        orderService.finish();
        orderService.paid();
    }
}

②多态中的转型问题

概述:如果被转的引用类型变量,对应的实际类型目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException

解决方法:

③关键字:instanceof

使用格式:对象名 instanceof 类型

判断一个对象是否是一个类的实例

通俗的理解:判断关键字左边的对象,是否是右边的类型,返回boolean类型结果

④案例

接口

package com.itheima.test;
 
public interface Payment {
    void pay(double money);
}

平台支付

package com.itheima.test;
 
public class PlatformPaymentImpl implements Payment {
    @Override
    public void pay(double money) {
        System.out.println("通过支付平台支付了:" + money + "元!");
    }
}

银行卡支付

package com.itheima.test;
 
public class BankcardPaymentImpl implements Payment{
    @Override
    public void pay(double money) {
        System.out.println("通过银行卡网银支付了:" + money + "元!");
    }
}

信用卡支付

package com.itheima.test;
 
public class CreditCardPaymentImpl implements Payment {
    @Override
    public void pay(double money) {
        System.out.println("通过信用卡快捷支付:" + money + "元!");
    }
}

测试类

package com.itheima.test;
 
import java.util.Scanner;
 
public class Test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请选择支付方式:  1. 支付平台支付   2. 银行卡网银支付  3. 信用卡快捷支付");
        int choice = sc.nextInt();
 
        Payment payment = null;
 
        switch (choice) {
            case 1 :
                payment = new PlatformPaymentImpl();
                break;
            case 2:
                payment = new BankcardPaymentImpl();
                break;
            case 3:
                payment = new CreditCardPaymentImpl();
                break;
        }
 
        System.out.println("请输入您的支付金额: ");
        double money = sc.nextDouble();
        payment.pay(money);
    }
}

五、接口新特性

JDK8

JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)

JDK9的新特性:接口中可以定义私有方法。

1.介绍

允许在接口中定义非抽象方法,但是需要使用关键字default修饰,这些方法就是默认方法

作用:解决接口升级的问题

接口中允许定义static静态方法

2.接口中默认方法的定义格式:

格式:public default 返回值类型 方法名(参数列表) {}

范例:public default void show() {}

public可以省略,default不可以

3.注意事项

  • 默认方法不是抽象方法,所以不强制被重写(但是可以被重写,重写的时候去掉default关键字)

  • public可以省略,default不能省略

  • 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写

4.接口中静态方法的定义格式:

格式:public static 返回值类型 方法名(参数列表) {}

范例:public static void show() {}

5.注意事项

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

  • public可以省略,static不能省略

package com.itheima.minterface.jdk8;
 
public class InterfaceTest {
​
    public static void main(String[] args) {
        AInterImpl a = new AInterImpl();
        a.method();
 
        //静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
        A.function();
    }
}
 
interface A {
    default void method(){
        System.out.println("A...method");
    }
 
     static void function(){
        System.out.println("A...static...function");
    }
}
 
interface Inter {
    void show();
    void print();
 
    //加入新业务时,为了不改动全局,用default
    //public可以省略,default不能省略
    default void method(){
        System.out.println("Inter...method");
    }
 
}
 
                                           //Inter和A中方法不一样,强制重写
class AInterImpl extends Object implements Inter , A {
 
 
    @Override
    public void show() {
        System.out.println("AInterImpl...show");
    }
 
    @Override
    public void print() {
        System.out.println("AInterImpl...print");
    }
 
    
    //默认方法不是抽象方法,所以不强制被重写(但是可以被重写,重写的时候去掉default关键字)
    @Override
    public void method() {
        A.super.method();
        Inter.super.method();
    }
 
}
 
class BInterImpl implements Inter {
 
    @Override
    public void show() {
        System.out.println("BInterImpl...show");
    }
 
    @Override
    public void print() {
        System.out.println("BInterImpl...print");
    }
}

JDK9

1.介绍

接口中允许定义private私有方法(提升复用性,减少冗余代码)

2.接口中静态方法的定义格式

  • 格式1:private返回值类型 方法名(参数列表) {}

范例1:private void show() {}

  • 格式2:private static返回值类型 方法名(参数列表) {}

范例2:private static void method() {}

package com.itheima.minterface.jdk9;
 
public class InterfaceTest {
    
    public static void main(String[] args) {
 
    }
}
 
 
interface Inter {
    void show();
    void print();
 
    public static void start(){
        System.out.println("start方法执行...");
        log();
    }
 
    public static void end(){
        System.out.println("end方法执行...");
        log();
    }
 
    private static void log(){
        
        //重复的逻辑  只为start和end方法提供服务,且不需要暴露给其他方法使用,所以用private
        System.out.println("日志记录");
    }
}

六、代码块

1.介绍

代码块 : 使用 { } 括起来的代码被称为代码块

2.分类

①局部代码块(了解)

  • 位置:方法中的一对大括号

  • 作用: 限定变量的生命周期, 提早的释放内存

②构造代码块(了解)

  • 位置: 类中方法外的一对大括号

  • 特点: 在创建对象, 每次执行构造方法的时候, 就会执行构造代码块 (优先于构造方法执行)

  • 作用: 将多个构造方法中, 重复的代码, 抽取到构造代码块中, 从而提升代码的复用性

③静态代码块(掌握)

  • 位置: 类中方法外的一对大括号, 需要加入static关键字

  • 特点: 随着类的加载而执行, 因为类只加载一次, 所以也就只执行一次

  • 作用: 对数据进行初始化,如果初始化的数据是一个对象且这个对象的初始化很复杂,就可以借助于静态代码块;加载配置文件的时候也会用到

package com.itheima.block;
 
import java.net.Socket;
 
public class BlockTest {
​
    public static void main(String[] args) {
        Student stu1 = new Student();
        Student stu2 = new Student(10);
    }
}
 
class Student {
 
    static String school;
 
    static {
        
        //静态代码块,对数据进行初始化,
        school = "黑马程序员";
        
        System.out.println("static...Student类的静态代码块");
 
        // Socket
    }
 
    
    //构造代码块,将重复的代码, 抽取到构造代码块中
    {
        System.out.println("Student类的构造代码块");
        System.out.println("好好学习");
    }
 
    public Student(){
 
        System.out.println("空参构造方法...");
    }
 
    public Student(int num){
        System.out.println("带参构造方法...");
    }
 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值