面向对象进阶

面向对象进阶

static

static: 表示静态,是 Java 中的一个修饰符,可以修饰成员方法成员变量

静态的,意味着可以共享

static 修饰成员变量

被 static 修饰的成员变量,叫做静态变量

特点:

  • 被该类所有对象共享
  • 跟对象无关,随着类的加载而加载,优先于对象存在

调用方式:

  • 类名调用( 推荐 )
  • 对象名调用

静态变量内存图

静态变量是随着类的加载而加载的,优先于对象出现的

在这里插入图片描述

static 修饰成员方法

被 static 修饰的成员方法,叫做静态方法

特点:

  • 多用在测试类工具类
  • Javabean类中很少会用

调用方式:

  • 类名调用( 推荐 )
  • 对象名调用

工具类

帮助我们做一些事情的,但是不描述任何事情的类,叫做工具类

工具类书写规则:

  • 类名见名知意

  • 私有化构造方法

    public class ArrUtil{
        //私有构造方法
        private ArrUtil(){}
    }
    

    构造方法一旦私有,在外界就无法创建这个类的对象。

    由于测试类不描述任何事物,所以创建其对象也毫无意义。

  • 方法定义为静态

    public class ArrUtil{
        //私有构造方法
        private ArrUtil(){}
        
        //方法定义为静态
        public static int getMax(...){...}
        public static int getMin(...){...}
        public static int getSum(...){...}
        public static int getAvg(...){...}
    }
    

工具类中的方法在测试类调用格式:

  • 工具类类名.工具类中的方法

练习:定义数组工具类

需求:在实际开发中,经常会遇到一些数组使用的工具类。

请按照如下要求编写一个数组的工具类:ArrayUtil

  • 提供一个工具类方法printArr , 用于返回整数数组的内容。

    返回的字符串格式如:[10,20,50,34,100] (只考虑整数数组,且只考虑一维数组)

  • 提供这样一个工具方法 getAerage ,用于返回平均分。(只考虑浮点型数组,且只考虑一维数组)

  • 定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果。

package Just.a02staticdemo2;

public class ArrayUtil {
    //私有化构造方法
    //目的:不让外界创建他的对象
    private ArrayUtil(){}

    //定义printArr,用于返回整数数组的内容
    public static String printArr(int[] arr){
        //定义一个 StringBuilder 类的对象,用来拼接数组
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        //for 循环遍历数组
        for (int i = 0; i < arr.length; i++) {
            //i 索引  arr[i] 数组元素
            if(i == arr.length - 1){
                sb.append(arr[i]);
            }else{
                sb.append(arr[i]).append(",");
            }
        }
        sb.append("]");
        //把 StringBuilder 类型转换成 String 类型并返回
        return sb.toString();
    }

    //定义getAerage函数,用于返回平均值
    public static double getAerage(double[] arr){
        //定义变量,用来获取数组元素的总和
        double sum = 0;
        //for循环遍历数组
        for (int i = 0; i < arr.length; i++) {
            sum = sum + arr[i];
        }
        //定义变量,用来接收平均值
        double average = sum/arr.length;
        //返回平均值
        return average;
    }
}
===========================================================================================
package Just.a02staticdemo2;

public class TestDemo {
    public static void main(String[] args) {
        //测试工具类中的两个方法是否正确
        int[] arr1 = {1,2,3,4,5};
        String str = ArrayUtil.printArr(arr1);
        System.out.println(str);
        
        double[] arr2 = {1.5,3.7,4.9,5.8,6.6};
        double avg = ArrayUtil.getAerage(arr2);
        System.out.println(avg);
    }
}
============================================================================================
运行结果:
[1,2,3,4,5]
4.5

练习:定义学生工具类

需求:定义一个集合,用于存储3个学生对象。

学生类的属性为:name 、age、gender

定义一个工具类,用于获取集合中最大学生的年龄

package Just.a03staticdemo3;

public class Student {
    //定义私有化成员变量 姓名、年龄、性别
    private String name;
    private int age;
    private String gender;

    //无参构造函数
    public Student() {
    }

    //有参构造函数
    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    
     //获取
    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 String getGender() {
        return gender;
    }
    
    //设置
    public void setGender(String gender) {
        this.gender = gender;
    }
}
=======================================================================================================
package Just.a03staticdemo3;

import java.util.ArrayList;

public class StudentTest {
    public static void main(String[] args) {
        //定义集合用来存储3个学生对象
        ArrayList<Student> list = new ArrayList<>();

        //创建三个学生对象
        Student stu1 = new Student("鑫鑫",21,"女");
        Student stu2 = new Student("肉墩",22,"女");
        Student stu3 = new Student("几坨",23,"男");

        //把创建的学生对象添加到集合中
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);

        //调用工具类中的函数,用来返回集合中最大学生的年龄
        int maxAge = StudentUtil.getMaxAge(list);
        System.out.println("输出集合中最大学生的年龄:");
        System.out.println(maxAge);
    }
}
=============================================================================================================
package Just.a03staticdemo3;

import java.util.ArrayList;

public class StudentUtil {
    //定义私有化构造函数
    private StudentUtil(){}

    //定义方法,获取集合中最大学生的年龄
    public static int getMaxAge(ArrayList<Student> list){
        //定义变量,用来记录学生中的最大年龄
        int maxAge = list.get(0).getAge();
        //for循环遍历集合中的对象及对象的年龄
        for (int i = 0; i < list.size(); i++) {
            //获取集合中的对象
            Student stu = list.get(i);
            //获取对象的年龄
            int age = stu.getAge();
            //把学生中最大学生的年龄赋值给 maxAge
            if(maxAge < age){
                maxAge = age;
            }
        }
        return maxAge;
    }

}
=======================================================================================================
运行结果:
输出集合中最大学生的年龄:
23

static 的注意事项

static 的注意事项:

  • 静态方法只能访问静态变量和静态方法
  • 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
  • 静态方法中没有this关键字

静态:随着类的加载而加载

非静态:跟对象有关
在这里插入图片描述

从新认识main方法

在这里插入图片描述

  • public: 被 JVM 调用,访问权限足够大

  • static: 被 JVM 调用,不用创建对象,直接类名访问

    ​ 因为 main 方法是静态的,所以测试类中其他方法也需要是静态的

  • void: 被 JVM 调用,不需要给 JVM 返回值

  • main: 一个通用的名称,虽然不是关键字,但是被 JVM 识别

  • String[] args: 以前用于接收键盘录入数据的,现在没用

继承

在这里插入图片描述

继承概述

继承定义

继承是面向对象三大特征之一,可以让类跟子类之间产生子父的关系。

继承的格式
  • Java 中提供一个关键字 extends ,用这个关键字,我们可以让一个类和另一个类建立起继承关系。

    public class Student extends Person { }

  • Student 称为子类 ( 派生类 ) ,Person 称为父类 ( 基类或超类 )

使用继承的好处:
  • 可以把多个子类中重复的代码抽取到父类中提高代码的复用性
  • 子类可以得到父类的属性和行为并进行使用
  • 子类可以在父类的基础上,增加其他功能,使子类更强大。
什么时候用继承?

当类与类之间,存在相同 (共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码。

继承的特点

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

单继承:一个子类只能继承一个父类

不支持多继承:子类不能同时继承多个父类

多层继承:子类 A 继承父类 B ,父类 B 可以继承父类 C

每一个类都直接或间接的继承于 Object

注意:子类只能访问父类中非私有的成员

继承体系的设计

设计技巧

画图法:从下往上画

下面:子类

上面:父类

需要把子类中的共性内容抽取到父类中

核心:

1、共性内容抽取

2、子类是父类中的一种

书写代码:

从上往下
在这里插入图片描述

练习:继承的练习

( 自己设计一个继承体系 )

现在有四种动物:布偶猫、中国狸花猫、哈士奇、泰迪

暂时不考虑属性,只要考虑行为。

请按照继承的思想特点进行继承体系的设计。

四种动物分别有以下的行为:

  • 布偶猫:吃法、喝水、抓老鼠
  • 中国狸花猫:吃饭、喝水、抓老鼠
  • 哈士奇:吃饭、喝水、看家、拆家
  • 泰迪:吃饭、喝水、看家、蹭一蹭
package Just.a01oopextendsdemo1;

public class Animal {
    //注意事项:
    //子类只能访问父类中非私有的成员

    //吃饭
    public void eat(){
        System.out.println("吃东西");
    }

    //喝水
    public void drink(){
        System.out.println("喝水");
    }
}
=================================================
package Just.a01oopextendsdemo1;

public class Cat extends Animal{
    //抓老鼠
    public void catMouse(){
        System.out.println("抓老鼠");
    }
}
==================================================
package Just.a01oopextendsdemo1;

public class Dog extends Animal{
    //看家
    public void lookHome(){
        System.out.println("狗看家");
    }
}
===================================================
package Just.a01oopextendsdemo1;

public class Ragdoll extends Cat{

}
==================================================
package Just.a01oopextendsdemo1;

public class LiHua extends Cat{

}
====================================================
package Just.a01oopextendsdemo1;

public class Husky extends Dog{
    public void breakHome(){
        System.out.println("哈士奇在拆家");
    }
}
=====================================================
package Just.a01oopextendsdemo1;

public class Teddy extends Dog{
    public void touch(){
        System.out.println("泰迪又在曾我的腿~");
    }
}
======================================================
package Just.a01oopextendsdemo1;

public class Test {
    public static void main(String[] args) {
       //创建对象并调用方法

       //1、创建布偶猫的对象
       Ragdoll rd = new Ragdoll();
       rd.eat();
       rd.drink();
       rd.catMouse();

       System.out.println("---------------------");

        //2、创建哈士奇的对象
        Husky h = new Husky();
        h.eat();
        h.drink();
        h.lookHome();
        h.breakHome();
    }
}
========================================================
运行结果:
吃东西
喝水
抓老鼠
---------------------
吃东西
喝水
狗看家
哈士奇在拆家

子类继承父类的内容

误区:
  • 误区1:父类私有的东西,子类就无法继承
  • 误区2:父类中非私有的成员,就被类继承下来
子类的继承情况:
父类中的内容父类中的权限子类继承情况父类中的权限子类继承情况说明
构造方法非私有不能继承私有不能继承父类的构造方法不能被子类继承
成员变量非私有能继承私有能继承子类虽然可以继承父类私有成员变量,但是不能调用
成员方法非私有能继承私有不能继承只有父类中的虚方法,才能被子类继承

虚方法表:

  • JAVA 底层会在最顶级的父类设计一个 虚方法表
  • 把这个类中最经常使用的方法,单独的抽取出来,放到虚方法表当中,
  • 满足经常使用方法条件:不能被,private 、static 、和 final 修饰
  • 在继承的时候,父类会把虚方法表交给子类,子类会相应添加自己的虚方法,构成新的虚方法表,向下继承,依次类推

继承中:成员变量访问特点

继承中成员变量访问特点:就近原则,谁离我近,我就用谁

先在局部位置找 ——> 本类成员位置找 ——> 父类成员位置找 ——> 逐级往上。
在这里插入图片描述

如果遇到重名:

System.out.println(name); <——> 从局部位置开始往上找

System.out.println(name); <——> 从本类成员位置开始往上找

System.out.println(name); <——> 从父类成员位置开始往上找

package Just.a05oopextendsdemo5;

//测试类
public class Test {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.ziShow();
    }
}

//父类
class Fu{
    String name = "Fu";
}
//子类
class Zi extends Fu{
    String name = "Zi";
        public void ziShow(){
            String name = "ziShow";
            System.out.println(name);
            System.out.println(this.name);
            System.out.println(super.name);
        }
}
=======================================================================
运行结果:
ziShow
Zi
Fu

继承中:成员方法访问特点

成员方法访问特点

直接调用满足就近原则:谁离我近,我就用谁

this调用:就近原则

super调用,直接访问父类

方法的重写

应用场景:

​ 当父类的方法不能满足子类现在的需求时,需要进行方法重写

书写格式:

​ 在继承体系中,子类出现了和父类一模一样的方法声明,我们就称子类这个方法是重写的方法。

@Override重写注释:

​ 1、@Override 是放在重写后的方法上,校验子类重写时语法是否正确。

​ 2、加上注释后如果有红色波浪线,表示语法错误。

​ 3、建议重写方法都加@Override注解,代码安全,优雅!

package Just.a06oopextendsdemo6;

public class Test {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.lunch();
    }

}

//父类
class Person{
    public void eat(){
        System.out.println("吃米饭、吃菜");
    }

    public void drink(){
        System.out.println("喝开水");
    }
}

//子类
class Student extends Person{

    @Override
    public void eat(){
        System.out.println("吃意大利面");
    }

    @Override
    public void drink(){
        System.out.println("喝凉水");
    }

    public void lunch(){
        this.eat();
        this.drink();

        super.eat();
        super.drink();
    }
}
==================================================================
运行结果:
吃意大利面
喝凉水
吃米饭、吃菜
喝开水
方法重写的本质

子类覆盖了从父类继承下来的虚方法表中的方法
在这里插入图片描述

方法从写注意事项和要求

1、重写方法的名称、形参列表必须与父类中的一致

2、子类重写父类方法时,访问权限子类必须大于等与父类( 暂时了解 :空着不写 < protected < public )

3、子类重写父类方法时,返回值类型子类必须小于等于父类

4、建议:重写的方法尽量和父类保持一致

5、只有被添加到虚方法列表中的方法才能被重写

练习:利用方法的重写设计继承结构

现在有三种动物:哈士奇、沙皮狗、中华田园犬

暂时不考虑属性,只考虑行为。

请按照继承的思想特点进行继承体系的设计。

三种动物分别有以下的行为:

  • 哈士奇: 吃饭(吃狗粮) 喝水、看家、 拆家
  • 沙皮狗: 吃饭(吃狗粮、吃骨头) 喝水、看家
  • 中华田园犬: 吃饭(吃剩饭) 喝水、看家
package Just.a07oopextendsdemo7;

public class Dog {
    //吃饭
    public void eat(){
        System.out.println("吃狗粮");
    }

    //喝水
    public void water(){
        System.out.println("喝水");
    }

    //看家
    public void seeHome(){
        System.out.println("看家");
    }
}
=============================================================
package Just.a07oopextendsdemo7;

public class Husky extends Dog{
    //拆家
    public void breakHome(){
        System.out.println("拆家");
    }
}
=============================================================
package Just.a07oopextendsdemo7;

public class SharPei extends Dog{
    //父类中的方法不能满足我们的需求了,所以需要进行重写
    @Override
    public void eat(){
        //吃狗粮直接可以调用父类中的方法
        super.eat();
        System.out.println("吃骨头");
    }
}
===============================================================
package Just.a07oopextendsdemo7;

public class ChineseRuralDog extends Dog{
    //父类中的方法不能满足我们的需求了,所以需要进行重写
    @Override
    public void eat(){
        System.out.println("吃剩饭");
    }
}
===============================================================
package Just.a07oopextendsdemo7;

public class DogTest {
    public static void main(String[] args) {
        //创建对象并调用

        Husky h = new Husky();
        h.eat();
        h.water();
        h.seeHome();
        h.breakHome();

        ChineseRuralDog cd = new ChineseRuralDog();
        cd.eat();
        cd.water();
        cd.seeHome();
    }
}
================================================================
运行结果:
吃狗粮
喝水
看家
拆家
吃剩饭
喝水
看家

继承中:构造方法的特点

  • 父类中的构造方法不会被子类继承
  • 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
    • 为什么呢?
      • 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
      • 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
    • 怎么调用父类构造方法的?
      • 子类构造方法的第一行语句默认都是:super(),不写也存在,若写且必须在第一行
      • 如果想调用父类有参构造,必须手动写 super 进行调用。
package Just.a08oopextendsdemo08;

public class Person {
    String name;
    int age;

    //无参构造
    public Person() {
        System.out.println("父类的无参构造");
    }

    //有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

}
=======================================================================
package Just.a08oopextendsdemo08;

public class Student extends Person{

    public Student(){
        //子类构造方法中隐藏的 super() 去访问父类的无参构造
        super();//==此super方法可以省略==
        System.out.println("子类的无参构造");
    }

    //调用父类中的有参构造
    public Student(String name,int age){
        super(name,age);
    }
}
========================================================================
package Just.a08oopextendsdemo08;

public class Test {
    public static void main(String[] args) {
        //创建学生对象
        Student stu1 = new Student();

        Student stu2 = new Student("zhangsan",23);
        System.out.println(stu2.name + "、" +stu2.age);
    }
}
=========================================================================
父类的无参构造
子类的无参构造
zhangsan、23

继承中构造方法的访问特点是什么?

  • 子类不能继承父类中的构造方法但是可以通过 super 调用
  • 子类构造方法的第一行,有一个默认的 super( );
  • 默认先访问父类中无参构造的构造方法,再执行自己
  • 如果想要访问父类有参构造,必须手动书写

this、super 使用内容

  • **this **:理解为一个变量,表示当前方法调用者的地址;
  • super : 代表父类存储空间
关键字访问成员变量访问成员方法访问构造方法
thisthis. 成员变量
访问本类成员变量
this. 成员方法(…)
访问本类成员方法
this(…)
访问本类其他构造方法
supersuper. 成员变量
访问父类成员变量
super. 成员方法(…)
访问父类成员方法
super(…)
访问父类构造方法

**this(…) 访问其他构造方法举例 **

package Just.a09oopextendsdemo09;

public class Student {
    String name;
    int age;
    String school;

    /*
    需求:默认大学名为 “传智大学”
    利用:this 调用本类中的其他构造
     */

    public Student() {
        /*
        this() 调用本类其他构造方法
        细节:虚拟机就不会再添加super();
        其他语句写在this()下面
        */
        this(null,0,"传智大学");
    }

    public Student(String name, int age, String school) {
        this.name = name;
        this.age = age;
        this.school = school;
    }
}
======================================================================
package Just.a09oopextendsdemo09;

public class Test {
    public static void main(String[] args) {
        Student stu = new Student();
        System.out.println(stu.school);
    }
}
======================================================================
传智大学

练习

带有继承结构的标准 Javabean 类

1、经理

成员变量:工号、姓名、工资、管理奖金

成员方法:工作(管理其他人)、吃饭(吃米饭)

2、厨师

成员变量:工号、姓名、工资

成员方法:工作(炒菜)、吃饭(吃米饭)

package Just.a10oopextendsdemo10;

public class Employee {
    //工号
    private String id;
    //姓名
    private String name;
    //工资
    private double salary;

    //无参构造
    public Employee() {
    }

    //有参构造
    public Employee(String id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public double getSalary() {
        return salary;
    }
    
    public void setSalary(double salary) {
        this.salary = salary;
    }

    //工作
    public void work(){
        System.out.println("员工在工作");
    }

    //吃饭
    public void eat(){
        System.out.println("吃米饭");
    }
}
======================================================================================
package Just.a10oopextendsdemo10;

public class Manager extends Employee{
    //管理奖金
    private double bonus;

    //空参构造
    public Manager(double bonus) {

    }

    //带全部参数的构造
    //父类 + 子类
    public Manager(String id, String name, double salary, double bonus) {
        //父类有参构造
        super(id, name, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    @Override
    public void work() {
        System.out.println("管理其他人");
    }
}
========================================================================================
package Just.a10oopextendsdemo10;

public class Cook extends Employee{
    public Cook() {
    }

    public Cook(String id, String name, double salary) {
        //父类有参构造
        super(id, name, salary);
    }

    @Override
    public void work() {
        System.out.println("厨师正在炒菜");
    }
}
=======================================================================================
package Just.a10oopextendsdemo10;

public class Test {
    public static void main(String[] args) {
        //创建对象并赋值调用
        Manager m = new Manager("001","张三",15000,8000);
        System.out.println(m.getId() + "," + m.getName() + "," + m.getSalary() + "," + m.getBonus());
        m.work();
        m.eat();

        System.out.println();

        Cook c = new Cook();
        c.setId("002");
        c.setName("李四");
        c.setSalary(10000);
        System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());
        c.work();
        c.eat();
    }
}
=========================================================================================
001,张三,15000.0,8000.0
管理其他人
吃米饭

002,李四,10000.0
厨师正在炒菜
吃米饭

多态

面向对象三大特征之一

认识多态

什么是多态?

​ 同类型的对象,表现出的不同形态。

多态的表现形式:

父类类型 对象名称 = 子类对象 ;

多态的前提:

  • 有继承 / 实现关系;
  • 有父类引用指向子类对象 **例子:Fu m = new Zi () ; **
  • 有方法重写

多态的好处?

使用父类型作为参数,可以接收所有子类对象,

体现多态的扩展性与便利

package Just.a01polymorphismdemo1;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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 void show(){
        System.out.println(name + "," + age);
    }
}
====================================================================
package Just.a01polymorphismdemo1;

public class Student extends Person{
    @Override
    public void show() {
        System.out.println("学生的信息为:" + getName() + "," +getAge());
    }
}
====================================================================
package Just.a01polymorphismdemo1;

public class Teacher extends Person{
    @Override
    public void show() {
        System.out.println("老师的信息为:" + getName() + "," +getAge());
    }
}
====================================================================
package Just.a01polymorphismdemo1;

public class Administrator extends Person{
    @Override
    public void show() {
        System.out.println("管理员的信息为:" + getName() + "," +getAge());
    }
}
====================================================================
package Just.a01polymorphismdemo1;

public class Test {
    public static void main(String[] args) {
        //创建三个对象,并调用register方法

        Student stu = new Student();
        stu.setName("张三");
        stu.setAge(18);

        Teacher t = new Teacher();
        t.setName("王建国");
        t.setAge(30);

        Administrator admin = new Administrator();
        admin.setName("管理员");
        admin.setAge(35);

        //调用register方法
        register(stu);
        register(t);
        register(admin);

    }

    //这个方法既能接收老师、又能接收学生,还能接收管理员
    //只能把参数写成这三个类型的父亲
    //多态的运用
    public static void register(Person p){
        p.show();
    }

}
====================================================================
学生的信息为:张三,18
老师的信息为:王建国,30
管理员的信息为:管理员,35

多态中调用成员的特点

  • 调用成员变量:编译看左边,运行也看左边

    Animal a = new Dog();

    /*
    调用成员变量:编译看左边,运行也看左边(看左边:定义对象时等号左边的类)
    编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
    运行也看左边:java运行代码的时候,实际获取的就是左边父类中的成员变量的值
    */

  • 调用成员方法:编译看左边,运行看右边

    Animal a = new Dog();
    /*
    编译成员方法:编译看左边,运行看右边(看左边:定义对象时等号左边的类。看右边:定义对象时等号右边的类)
    编译看左边:javac编译代码的时候,会看左边父类中有没有这个方法,如果有,编译成功,如果没有编译失败
    **运行看右边:**java运行代码的时候,实际上运行的是子类中的方法
    */

  • 理解:

    Animal a = new Dog();

    /*

    现在用 a 去调用变量和方法

    而 a 是 Animal 类型的,所以默认都会从 Animal 这个类中去找

    成员变量:在子类的对象中,会把父类的成员变量也继承下来

    成员方法:如果子类对方法进行了重写,那么在虚方法表中时会把父类的方法进行覆盖的。

    */

  • 多态调用成员的内存图解
    在这里插入图片描述

package Just.a02polymorphismdemo2;

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


        /*
        编译成员方法:编译看左边,运行看右边(看左边:定义对象时等号左边的类。看右边:定义对象时等号右边的类)
        编译看左边:javac编译代码的时候,会看左边父类中有没有这个方法,如果有,编译成功,如果没有编译失败
        运行看右边:java运行代码的时候,实际上运行的是子类中的方法
         */
        a.show();//结果:运行 Dog 类中的show()方法,输出————Dog --- show方法
    }
}

class Animal{
    String name = "动物";

    public void show(){
        System.out.println("Animal --- show方法");
    }
}

class Dog extends Animal{
    String name = "狗";

    @Override
    public void show() {
        System.out.println("Dog --- show方法");
    }
}

class Cat extends Animal{
    String name = "猫";

    @Override
    public void show() {
        System.out.println("Cat --- show方法");
    }
}

多态的优势和弊端

多态的优势

  • 在多态形势下,右边对象可以实现解耦合,便于扩展和维护。

    /*

    Person p = new Student () ;

    p.work( ); //业务逻辑发生改变时,后续代码无需修改

    例如:过了几天,不想让学生去工作了,而是想让老师去工作,此时,在代码当中,只需要把创建对象的 new Student();部分修改成new Teacher();

    其他地方都不用修改,再调用 work 方法的时候,它实际上运行的是修改之后的 Teacher 类里面的代码。

    */

  • 定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利

public class Test {
    public static void main(String[] args) {
        //调用register方法
        register(stu);
        register(t);
        register(admin);
}

    //这个方法既能接收老师、又能接收学生,还能接收管理员
    //只能把参数写成这三个类型的父亲
    //多态的运用
    public static void register(Person p){
        p.show();
    }
    
}

多态的弊端

  • 不能调用子类的特有的功能(子类中有 而父类中没有 的函数)

    原因:当调用成员方法的时候,编译看左边,运行看右边

    ​ javac编译代码的时候,检查父类中没有这个方法,直接报错

引用数据类型的类型转换

  • 自动类型转换

    Person p = new Student ( ) ; 把子类对象,赋值给父类类型的变量 由小变大

    ​ 父类 <—— 子类

  • 强制类型转换

    Student s = (Student) p; 把父类类型的变量,强转成子类类型的变量 由大变小

    ​ 子类 <—— 父类

强制类型转换能解决什么问题?

  • 可以转换成真正的子类类型,从而调用子类独有的功能

  • 转换类型与真实对象类型不一致会报错

  • 转换的时候用 instanceof 关键字进行判断

    变量名 instanceof 类名

    例如:a instanceof Dog 判断 a 是不是 Dog 类 如果是进行强转 Dog d = (Dog)a; 转换之后变量名为 d 。如果不是,为 false 继续向下判断

    if(a instanceof Dog){
        Dog d = (Dog)a;
        d.lookHome();
    }else if(a instanceof Cat){
        Cat c = (Cat)a;
        c.catchMouse();
    }else {
        System.out.println("没有这个类型,无法转换");
    }
    

    新特性:

    先判断 a 是否为 Dog 类型,如果是,则强转成 Dog 类型,转换之后变量名为 d

    如果不是,则强转成,结果直接是false

    if(a instanceof Dog d){
        d.lookHome();
        }else if(a instanceof Cat c){
        c.catchMouse();
        }else {
        System.out.println("没有这个类型,无法转换");
         }
    
package Just.a03polymorphismdemo3;

public class Test {
    public static void main(String[] args) {
        //创建对象   (多态继承)
        Animal a = new Dog();
        Animal f = new Cat();
        //编译看左边,运行看右边
        a.eat();
        f.eat();

        //多态弊端
        //不能调用子类的特有的功能
        //报错原因?
        //当调用成员方法的时候,编译看左边,运行看右边
        //那么在编译的时候会先检查左边的父类中没有这个方法,直接报错
        //a.lookHome();

        //解决方法:
        //变回子类类型就可以了
        //细节:转换的时候不能瞎转,如果转成其他类型,就会报错。错误例子:Cat c = (Cat) a;因为上面是new Dog();

        //变换成子类型 方法一
        Dog d = (Dog)a;
        d.lookHome();

        Cat c = (Cat)f;
        c.catchMouse();
        //变换成子类型 方法二
        //if(a instanceof Dog){
            //Dog d = (Dog)a;
            //d.lookHome();
        //}else if(a instanceof Cat){
            //Cat c = (Cat)a;
            //c.catchMouse();
        //}else {
            //System.out.println("没有这个类型,无法转换");
        //}
    }
}

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

class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }

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

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

    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}

练习

根据要求完成代码:

1、定义狗类

​ 属性: 年龄,颜色

​ 行为: eat( String something ) (something 表示吃的东西)

​ 看家lookHome方法(无参数)

2、定义猫类

​ 属性: 年龄,颜色

​ 行为: eat( String something ) (something 表示吃的东西)

​ 逮老鼠catchMouse方法(无参数)

3、定义Person类//饲养员

​ 属性: 姓名,年龄

​ 行为: keepPet(Dog dog , String something)

​ 功能:喂养宠物狗,something表示喂养的东西

​ 行为: keepPet(Cat cat , String something)

​ 功能:喂养宠物猫,something表示喂养的东西

​ 生成空参有参构造,set 和 get

4、定义测试类(完成以下打印效果):

​ keepPet(Dog dog , String something)方法打印的内容如下:

​ 年龄为30岁的老王养了一只黑色的2岁的宠物

​ 2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃

​ keepPet(Cat cat , String something)方法打印的内容如下:

​ 年龄为25岁的老李养了一只灰颜色的3岁的宠物

​ 3岁的灰颜色的猫眯着眼睛侧着鱼头吃鱼

5、思考:

​ 1、Dog 和 Cat 都是 Animal 的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?

​ 2、Dog 和 Cat 都是 Animal 的子类,但是都有其特有的方法,能否想办法在keepPet中调用特有方法?

package Just.a04polymorphismdemo4;

public class Animal {
    private int age;
    private String color;

    //无参构造
    public Animal() {
    }

    //有参构造
    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }

    //set 和 get 方法


    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    //eat方法
    public void eat(String something){
        System.out.println("动物在吃" + something);
    }
}
==========================================================================
package Just.a04polymorphismdemo4;

public class Dog extends Animal{

    //空参构造
    public Dog() {
    }

    //有参构造
    public Dog(int age, String color) {
        super(age, color);
    }

    //从写eat方法
    @Override
    public void eat(String something) {
        System.out.println(getAge() + "岁的"+ getColor() + "颜色的狗两只前腿死死的抱住" + something + "猛吃");
    }

    //lookHome方法
    public void lookHome(){
        System.out.println("狗在看家");
    }
}
==========================================================================
package Just.a04polymorphismdemo4;

public class Cat extends Animal{

    //无参构造
    public Cat() {
    }

    //有参构造
    public Cat(int age, String color) {
        super(age, color);
    }

    //从写eat方法
    @Override
    public void eat(String something) {
        System.out.println(getAge() + "岁的" + getColor() + "颜色的猫眯着眼睛侧着头吃" + something);
    }

    //catchMouse方法
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
==========================================================================
package Just.a04polymorphismdemo4;

public class Person {
    private String name;
    private int age;

    //无参构造
    public Person() {
    }

    //有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //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 void keepPet(Dog dog ,String something){
        System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗");
        dog.eat(something);
    }

    //饲养猫
    public void keepPet(Cat cat ,String something){
        System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的狗");
        cat.eat(something);
    }
     */

    //想要一个方法,能接收所有的动物,包括猫,包括狗
    //方法的形参:可以写这些类的父亲  Animal
    //饲养动物
    public void keepPet(Animal a ,String something){
        if(a instanceof Dog){
            Dog d = (Dog)a;
            System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的狗");
            a.eat(something);
        }else if(a instanceof Cat){
            Cat c = (Cat)a;
            System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的猫");
            a.eat(something);
        }
    }
}
==========================================================================
package Just.a04polymorphismdemo4;

public class Test {
    public static void main(String[] args) {
        //创建对象并调用方法
        Person p1 = new Person("老王",30);
        Dog d = new Dog(3,"黑");
        p1.keepPet(d,"骨头");
        d.lookHome();

        System.out.println();

        Person p2 = new Person("老李",25);
        Cat c = new Cat(2,"灰");
        p2.keepPet(c,"鱼");
        c.catchMouse();
    }
}
==========================================================================
年龄为30岁的老王养了一只黑颜色的3岁的狗
3岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
狗在看家

年龄为25岁的老李养了一只灰颜色的2岁的猫
2岁的灰颜色的猫眯着眼睛侧着头吃鱼
猫抓老鼠

包、final

什么是包?

包就是文件夹。用来管理各种不同功能的 Java 类,方便后期代码维护。

  • **包名的规则:**公司域名反写 + 包的作用,需要全部英文小写,见名知意。
    在这里插入图片描述

  • 什么是全类名?

    包名 + 类名

使用其他类的规则:

  • 使用同一个包中的类时,不需要导包。
  • 使用 java.lang 包中的类时( 例如:String ),不需要导包。
  • 其他情况都需要导包
  • 如果同时使用两个包中的同名类,需要用全类名。
package Just.test;

import Just.domain.Teacher;

public class Test {
    public static void main(String[] args) {
        //创建对象

        //1、使用同一个包中的类时,不需要导包
        Student stu = new Student();
        stu.setName("王勇");
        stu.setAge(21);
        System.out.println(stu.getName() + "、" + stu.getAge());

        //2、使用 java.lang 包中的类时,不需要导包
        String s = "韩炜";
        System.out.println(s);

        //3、其他情况都需要导包
        Teacher t = new Teacher();
        
        //4、如果同时使用两个包中的同名类时,需要用全类名
        Just.Dog1.Dog d1 = new Just.Dog1.Dog();
        Just.Dog2.Dog d2 = new Just.Dog2.Dog();
    }
}

final

最终的 ——> 不可被改变的

final修饰的成分解释使用场景final使用举例
方法表明该方法是最终方法,不能被重写定义一些规则,不希望被修改public final void show()
表明该类是最终类,不能被继承final class Fu
变量叫做 常量,只能被赋值一次定义一个变量,不想再让他进行改变了final int num = 21;
final Student s tu = new Student(“鑫鑫”,21)

常量

实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性

常量的命名规范:

  • 单个单词:全部大写
  • 多个单词:全部大写,单词之间用下划线隔开

细节:

  • final 修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变。

  • final 修饰的变量是引用数据类型:那么变量存储的地址值不能发生改变,对象内部的可以改变

    核心:常量记录的数据是不能发生改变的。

  • 知识回顾:字符串是不能改变的

    ​ 原因:字符串的底层是被 private 和 final 修饰的

package Just.test1;

public class Test {
    public static void main(String[] args) {
        /*
        final 修饰的变量是**基本数据类型**:那么变量存储的==数据值==不能发生改变。
        final 修饰的变量是**引用数据类型**:那么变量存储的==地址值==不能发生改变,**对象内部的可以改变**。

       */
        final double PI = 3.14;

        //创建对象
        final Student STU = new Student("鑫鑫",21);
        System.out.println(STU.getName() + "、" + STU.getAge());
        STU.setName("肉墩");
        STU.setAge(20);
        System.out.println(STU.getName() + "、" + STU.getAge());

        //数组
        final int[] ARR = {1,2,3,4,5};
        //遍历数组
        for (int i = 0; i < ARR.length; i++) {
            System.out.print(ARR[i] + "、");
        }
        ARR[0] = 90;
        ARR[1] = 98;
        //遍历数组
        for (int i = 0; i < ARR.length; i++) {
            System.out.print(ARR[i] + "、");
        }
    }
}
===========================================================================================
运行结果:
鑫鑫、21
肉墩、20
1、2、3、4、5、90、98、3、4、5、
Process finished with exit code 0

权限修饰符、代码块

权限修饰符

  • **权限修饰符:**是用来控制一个成员能够被访问的范围的。
  • 可以修饰成员变量,方法,构造方法,和内部类。

权限修饰符的分类:

​ 有四种 , 作用范围由小到大:private < 空着不写(缺省/默认)< protected < public

修饰符(作用范围)同一个类中同一个包中其他类不同包下的子类不同包下的无关类
private
空着不写
protected
public

权限修饰符的使用规则

​ 实际开发中,一般只用 private 和 public

  • 成员变量 私有
  • 方法公开

特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有。

代码块

代码块种类位置解释
局部代码块方法里面写在方法里面的一对大括号
构造代码块方法外,类里面
静态代码块static 修饰的静态代码块static + 构造代码块

局部代码块:

  • 提前结束变量的生命周期

构造代码块:

  • 写在成员位置的代码块
  • 作用:可以把多个构造方法重复的代码抽取出来
  • 执行时期:我们在创建本类对象的时候会先执行构造代码块再执行构造方法(每执行一次构造方法,便会执行一次构造代码块)
public class Student {
    private String name;
    private int age;

    //构造代码块
    {
        System.out.println("开始创建对象了");
    }

    //无参构造
    public Student() {
        System.out.println("空参构造");
    }

    public Student(String name, int age) {
        System.out.println("有参构造");
        this.name = name;
        this.age = age;
    }

静态代码块

格式:static{}

特点:需要通过static 关键字修饰,随着类的加载而加载,并且自动触发、只执行一次

使用场景:在类加载的时候,做一些数据初始化的时候使用

public class Student {
    private String name;
    private int age;

    //静态代码块
    static {
        System.out.println("静态代码块执行了");
    }

    public Student() {
        System.out.println("空参构造");
    }

    public Student(String name, int age) {
        System.out.println("有参构造");
        this.name = name;
        this.age = age;
    }
}
========================================================
public class Test {
    //静态代码块
    static {
        System.out.println("测试静态代码块");
    }

    public static void main(String[] args) {
        //创建对象
        Student s1 = new Student();

        Student s2 = new Student("鑫鑫",21);
    }
}
========================================================
运行结果:
测试静态代码块
静态代码块执行了
空参构造
有参构造

抽象类和抽象方法

抽象方法和抽象类

  • **抽象方法:**将共性的行为(方法)抽取到父类之后。

    ​ 由于每一个子类执行的内容是不一样的,

    ​ 所以,在父类中不能确定具体的方法体

    ​ 该方法就可以定义为抽象方法。

  • **抽象类:**如果一个类中存在抽象方法,那么该类就必须声明为抽象类

定义格式

  • 抽象方法定义格式:

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

    注意:定义抽象方法,省略 {}

  • 抽象类的定义格式:

    public abstract class 类名 {}

  • 子类继承抽象类之后,如何重写抽象方法

抽象类和抽象方法的注意事项

  • 抽象类不能实例化(即不能创建对象)

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

  • 可以有构造方法

    抽象类中构造方法的作用:当创建子类对象的时,给属性进行赋值

  • 抽象类的子类

    ​ 要么重写抽象类中的所有方法

    ​ 要么是抽象类

练习:编写带有抽象类的标准 Javabean 类

青蛙frog 属性:名字,年龄 行为:吃虫子,喝水

狗Dog 属性:名字,年龄 行为:吃骨头,喝水

山羊Sheep 属性

package Just.a02abstractdemo2;

public abstract class Animal {
    private String name;
    private int age;


    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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 void drink(){
       System.out.println(getName() + "在喝水");
   }

   public abstract void eat();
}
===============================================================================
package Just.a02abstractdemo2;

public class Frog extends Animal{
    public Frog() {
    }

    public Frog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "在吃虫子");
    }
}
===============================================================================
package Just.a02abstractdemo2;

public class Dog extends Animal{
    public Dog() {
    }

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "在吃骨头");
    }
}
===============================================================================
package Just.a02abstractdemo2;

public class Sheep extends Animal{
    public Sheep() {
    }

    public Sheep(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "在吃草");
    }
}
===============================================================================
package Just.a02abstractdemo2;

public class Test {
    public static void main(String[] args) {
        //创建对象
        Frog f = new Frog("青蛙小绿",2);
        System.out.println(f.getName() + "、" + f.getAge());
        f.drink();
        f.eat();
        System.out.println();

        Dog d = new Dog("狗",5);
        System.out.println(d.getName() + "、" + d.getAge());
        d.drink();
        d.eat();
        System.out.println();

        Sheep s = new Sheep("山羊",7);
        System.out.println(s.getName() + "、" + s.getAge());
        s.drink();
        s.eat();
        System.out.println();
    }
}
===============================================================================
青蛙小绿、2
青蛙小绿在喝水
青蛙小绿在吃虫子

狗、5
狗在喝水
狗在吃骨头

山羊、7
山羊在喝水
山羊在吃草

接口

接口就是一种规则,是对行为的抽象。

接口的定义和使用

  • 接口用关键字 interface定义

    public interface 接口名 { }

  • 接口不能实例化(即接口不能创建对象)

  • 接口和类之间是实现关系,通过 implements 关键字表示

    public class 类名 implements 接口名 { }

  • 接口的子类(实现类)

    ​ 要么重写接口中的所有抽象方法

    ​ 要么是抽象类

注意

  • 接口和类的实现关系,可以单实现,也可以多实现

    public class 类名 implements 接口名1 , 接口名2 { }

  • 实现类还可以在继承一个类的同时实现多个接口

    public class 类名 extends 父类 implements 接口名1 , 接口名2 { }

练习:带有接口和抽象类的标准 Javabean类

青蛙frog 属性:名字,年龄 行为:吃虫子,蛙泳

狗Dog 属性:名字,年龄 行为:吃骨头,狗刨

兔子 属性:名字,年龄 行为:吃胡萝卜
在这里插入图片描述

package Just.a01interfacedemo1;

public abstract class Animal {
    private String name;
    private int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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 abstract void eat();
}
========================================================================
package Just.a01interfacedemo1;

public interface Swim {
    public abstract void swim();
}
========================================================================
package Just.a01interfacedemo1;

public class Frog extends Animal implements Swim{
    public Frog() {
    }

    public Frog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "吃虫子");
    }

    @Override
    public void swim() {
        System.out.println("游泳方式:蛙泳");
    }
}
========================================================================
package Just.a01interfacedemo1;

public class Dog extends Animal implements Swim{
    public Dog() {
    }

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "吃骨头");
    }

    @Override
    public void swim() {
        System.out.println("游泳方式:狗刨");
    }
}
========================================================================
package Just.a01interfacedemo1;

public class Rabbit extends Animal{
    public Rabbit() {
    }

    public Rabbit(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "吃胡萝卜");
    }
}
========================================================================
package Just.a01interfacedemo1;

public class Test {
    public static void main(String[] args) {
        //创建对象
        Frog f = new Frog("小绿",1);
        System.out.println(f.getName() + "、" + f.getAge() + "岁");
        f.eat();
        f.swim();

        Dog d = new Dog("阿黄",5);
        System.out.println(d.getName() + "、" + d.getAge() + "岁");
        d.eat();
        d.swim();

        Rabbit r = new Rabbit("大白",3);
        System.out.println(r.getName() + "、" + r.getAge() + "岁");
        r.eat();
    }

}
========================================================================
小绿、1岁
小绿吃虫子
游泳方式:蛙泳

阿黄、5岁
阿黄吃骨头
游泳方式:狗刨

大白、3岁
大白吃胡萝卜

接口中成员的特点

  • 成员变量

    ​ 只能是常量

    ​ 默认修饰符:public static final 在接口中定义变量的时候,可以不用写

    ​ 接口名可以调用接口中的成员变量

  • 构造方法

    ​ 没有

  • 成员方法

    ​ 只能是抽象方法

    ​ 默认修饰符:public abstract 在接口中定义方法的时候,可以不用写

  • **JDK7 以前:**接口中只能定义抽象方法。

  • JDK8 的新特性接口中可以定义有方法体的方法

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

接口和类之间的关系

  • 类和类的关系

    ​ 继承关系,只能单继承,不能多继承,但是可以多层继承

  • 类和接口的关系

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

    ​ 当接口中有重复的方法时,类中只重写一遍

    package Just.a02interfacedemo2;
    
    public interface Inter1 {
        public abstract void method1();
        public abstract void method2();
        public abstract void method3();
    }
    ========================================================================
    package Just.a02interfacedemo2;
    
    public interface Inter2 {
        public abstract void method2();
        public abstract void method3();
        public abstract void method4();
    }
    ========================================================================
    package Just.a02interfacedemo2;
    
    public class InterImpl implements Inter1,Inter2{
    
        @Override
        public void method1() {
    
        }
    
        @Override
        public void method2() {
    
        }
    
        @Override
        public void method3() {
    
        }
    
        @Override
        public void method4() {
    
        }
    }
    
  • 接口和接口的关系

    ​ 继承关系,可以单继承,也可以多继承

    ​ 细节:如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法

package Just.a03interfacedemo3;

public interface Inter1 {
    public abstract void method1();
}
========================================================================
package Just.a03interfacedemo3;

public interface Inter2 {
    public abstract void method2();
}
========================================================================
package Just.a03interfacedemo3;

public interface Inter3 extends Inter1,Inter2{
    public abstract void method3();
}
========================================================================
package Just.a03interfacedemo3;

public class InterImpl implements Inter3{

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }
}

上面的例子中,Inter3 继承了 Inter1 和 Inter2 ,所以当类InterImpl 使用接口Inter3 时,重写了Inter3 中的抽象方法,还重写了Inter3 重 Inter1 和 Inter2 中继承的方法。

有接口和抽象类的标准 Javabean 类

练习:编写带有接口和抽象类的标准 Javabean 类

​ 我们现有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。

​ 为了出国交流,跟乒乓球相关人员都需要学习英语。

​ 请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?

乒乓球运动员:姓名,年龄,学打乒乓球,说英语

乒乓球教练:姓名,年龄,教打乒乓球,说英语

篮球运动员:姓名,年龄,学打篮球

篮球教练:姓名,年龄,教打篮球
在这里插入图片描述

package Just.a04interfacedemo4;

public abstract class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }
}
========================================================================
package Just.a04interfacedemo4;

public abstract class Athlete extends Person{
    public Athlete() {
    }

    public Athlete(String name, int age) {
        super(name, age);
    }

    public abstract void study();
}
========================================================================
package Just.a04interfacedemo4;

public abstract class Coach extends Person{
    public Coach() {
    }

    public Coach(String name, int age) {
        super(name, age);
    }

    public abstract void teach();
}
========================================================================
package Just.a04interfacedemo4;

public interface Speak {
    public abstract void speak();
}
========================================================================
package Just.a04interfacedemo4;

public class TableTennisPlayer extends Athlete implements Speak{
    public TableTennisPlayer() {
    }

    public TableTennisPlayer(String name, int age) {
        super(name, age);
    }

    @Override
    public void study() {
        System.out.println("乒乓球运动员在学习打乒乓球");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球运动员在说英语");
    }
}
========================================================================
package Just.a04interfacedemo4;

public class BasketballPlayer extends Athlete{
    public BasketballPlayer() {
    }

    public BasketballPlayer(String name, int age) {
        super(name, age);
    }

    @Override
    public void study() {
        System.out.println("篮球运动员在学习学打篮球");
    }
}
========================================================================
package Just.a04interfacedemo4;

public class TableTennisCoach extends Coach implements Speak{
    public TableTennisCoach() {
    }

    public TableTennisCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("乒乓球教练在教如何打乒乓球");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球教练在说英语");
    }
}
========================================================================
package Just.a04interfacedemo4;

public class BasketballCoach extends Coach{
    public BasketballCoach() {
    }

    public BasketballCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("篮球教练在教如何打篮球");
    }
}
========================================================================
鑫鑫、21
乒乓球运动员在学习打乒乓球
乒乓球运动员在说英语

肉墩、35
篮球教练在教如何打篮球

JDK8开始接口中新增的方法

1、JDK7以前接口中只能定义抽象方法

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

默认方法

  • 允许在接口中定义默认方法,需要使用关键字 default 修饰

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

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

  • 格式:Public default 返回值类型 方法名 ( 参数列表 ) { }
  • 范例:public default void show( ){ }

接口中默认方法的注意事项

  • 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉 default 关键字;如果默认方法在类中重写,创建此类的对象,调用默认方法,调用的是重写后的方法
  • public 可以省略,default 不能省略
  • 如果实现了多个接口,多个接口中存在相同名字的默认方法,**子类(实现类)**就必须对该方法进行重写
public interface InterA {
    
    public abstract void method();
    
    public default void show(){
        System.out.println("InterA接口中的默认方法 --- show");
    }
}
========================================================================
public interface InterB {
    public default void show(){
        System.out.println("InterB接口中的默认方法 --- show");
    }
}
========================================================================
public class InterImpl implements InterA,InterB {
    @Override
    public void method() {
        System.out.println("实现类重写的抽象方法");
    }

    //由于接口 InterA和 InterB 中存在相同名字的默认方法,子类(实现类)就必须对该方法进行重写
    @Override
    public void show() {
        System.out.println("重写接口中的默认方法");
    }
}
========================================================================
public class Test {
    public static void main(String[] args) {
        //创建对象
        InterImpl ii = new InterImpl();
        //调用重写的method方法
        ii.method();
        //调用默认方法show
        ii.show();
    }
}
========================================================================
运行结果:
实现类重写的抽象方法
重写接口中的默认方法

静态方法

  • 允许在接口中定义静态方法,需要用 **static **修饰

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

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

接口中静态方法的注意事项

  • 静态方法不能被重写

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

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

public interface Inter {

    public abstract void method();

    public static void show(){
        System.out.println("Inter 接口中的静态方法");
    }
}
========================================================================
public class InterImpl implements Inter{

    @Override
    public void method() {
        System.out.println("InterImpl 重写的抽象方法");
    }

    //不叫重写,只是InterImpl 中的方法与 接口 Inter里面的方法重名
    public static void show(){
        System.out.println("InterImpl 类中的静态方法");
    }
}
========================================================================
public class Test {
    public static void main(String[] args) {
        //调用接口中的静态方法
        Inter.show();

        //调用实现类中的静态方法
        InterImpl.show();
        InterImpl ii = new InterImpl();
        ii.method();
    }
}
========================================================================
运行结果:
Inter 接口中的静态方法
InterImpl 类中的静态方法
InterImpl 重写的抽象方法

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

接口中私有方法的定义格式:

为默认方法服务:

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

为静态方法服务

  • 格式2:private static 返回值类型 方法名( 参数列表){ }
  • 范例2: public static void method ( ) { }
public interface InterA {

    public default void show1(){
        System.out.println("show1 默认方法开始执行了");
        show3();
    }

    public default void show2(){
        System.out.println("show1 默认方法开始执行了");
        show3();
    }
    //普通的私有方法,给默认方法服务的
    private void show3(){
        System.out.println("记录程序在运行过程的各种细节,这里有100行代码");
    }
}
========================================================================
public interface InterB {

    public static void show1(){
        System.out.println("show1 静态方法开始执行了");
        show3();
    }

    public static void show2(){
        System.out.println("show1 静态方法开始执行了");
        show3();
    }

    //静态的私有方法,给静态的方法服务的
    private static void show3(){
        System.out.println("记录程序在运行过程的各种细节,这里有100行代码");
    }
}
========================================================================
public class InterImpl implements InterA,InterB{

}
========================================================================
public class Test {
    public static void main(String[] args) {
        //创建对象
        InterImpl ii = new InterImpl();
        //调用默认方法
        ii.show1();
        ii.show2();

        System.out.println();
        //调用静态方法
        InterB.show1();
        InterB.show2();
    }
}
========================================================================
运行结果:
show1 默认方法开始执行了
记录程序在运行过程的各种细节,这里有100行代码
show2 默认方法开始执行了
记录程序在运行过程的各种细节,这里有100行代码

show1 静态方法开始执行了
记录程序在运行过程的各种细节,这里有100行代码
show2 静态方法开始执行了
记录程序在运行过程的各种细节,这里有100行代码

接口的应用

1、接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。

2、当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态

适配器设计模式

  • 设计模式( Design pattern ) 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验总结。

    使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

    简单理解:设计模式就是各种套路

  • 适配器设计模式: 解决接口与接口实现类之间的矛盾问题。

总结:

  • 当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以采用适配器设计模式

  • 书写步骤:

    • 编写中间类XXXAdapter ,实现对应的接口
    • 让接口中的抽象方法进行空实现
    • 让真正的实现类继承中间类 ,并重写需要用的方法
    • 为了避免其他类创建适配器的对象,中间的适配器类abstract 进行修饰
  • **注意:**如果真正实现类还继承其他类,由于类不能多继承,所以用下面解决方案:

    让中间类继承真正实现类本要继承的类,再让真正实现类,继承中间类,通过间接继承,让真正实现类继承其本要继承的类

public interface Inter {
    public abstract void method1();
    public abstract void method2();
    public abstract void method3();
    public abstract void method4();
    public abstract void method5();
    public abstract void method6();
    public abstract void method7();
    public abstract void method8();
    public abstract void method9();
    public abstract void method10();
}
========================================================================
public abstract class InterAdapter implements Inter{
    //所有重写方法进行空实现
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    ...............
}
========================================================================
public class InterImpl extends InterAdapter{
    //需要用到哪个方法,就重写哪个方法

    @Override
    public void method5() {
        System.out.println("只要用第五个方法");
    }
}

内部类

内部类基本概念

1、什么是内部类?

  • 写在一个类里面的类就叫作内部类

​ 举例:在 A 类的内部定义 B 类,B 类就被称为内部类
在这里插入图片描述

2、什么时候用到内部类?

​ B 类表示的事物是 A 类的一部分,且 B 单独存在没有意义。

​ 比如:汽车的发动机,ArrayList 的迭代器,人的心脏等等

3、内类的详情

  • 内部类表示的事物是外部类的一部分

  • 内部类单独出现没有任何意义

4、内部类的访问特点:

  • 内部类可以直接访问外部类,包括私有
  • 外部类访问内部类的成员,必须创建对象

练习:

​ 需求:写一个 Javabean 类描述汽车。

​ 属性:汽车的品牌,车龄,颜色,发动机的品牌,使用年限。

public class Car {
    String carName;
    int carAge;
    String carColor;

    public void show(){
        System.out.println(carName);
        //System.out.println(engineName);
        //外部类要访问内部类的成员,必须创建对象
        Engine e = new Engine();
        System.out.println(e.engineName);
    }

    class Engine{
        String engineName;
        int engineAge;

        public void show(){
            System.out.println(engineName);
            //内部类可以直接访问外部类的成员,包括私有
            System.out.println(carName);
        }
    }
}

成员内部类(了解)

成员内部类代码书写
  • 写在成员位置的,属于外部类的成员类内,方法外

  • 成员内部类可以被一些修饰符所修饰,比如:private 、默认、protected、public、static 等

    • 注意:成员内部类定义为 private 的时候,在外界不能直接创建其对象。
  • 在成员内部类里面,JDK16 之前不能定义静态变量,JDK16 开始才可以定义静态变量。

    public class Car { 外部类

    String carName ;

    int carAge ;

    int carColor ;

    class Engine { 成员内部类

    String engineName ;

    int engineAge ;

    }

    }

创建成员内部类对象

方式一:

在外部类中编写方法,对外提供内部类的对象。

​ 使用场景:当用 private 权限修饰符修饰内部类的时候

public class Outer {
    //内部类
    private class Inner{
    }
    //格式一:外部类编写方法,对外提供内部类对象
    public Inner getInstance(){
        return new Inner();
    }
}
调用方法:
//外部类的对象
Outer o = new Outer();
方法一:用变量接收外部类的方法  
Object inner = o.getInstance();
System.out.println(inner);
方法二:        
System.out.println(o.getInstance());

方式二:

​ 直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

​ 范例:Outer.Inner oi = new Outer().new Inter() ;

​ 使用场景:非使用 private 权限修饰符

成员内部类获取外部类成员变量

外部类成员变量和内部类成员变量重名时,在内部类如何访问?

System.out.println(Outer.this.变量名

内部类的内存图
package Just.ao3innerclassdemo3;

public class Outer {
    private int a = 10;

    class Inner{
        private int a = 20;
        public void show(){
            int a = 30;
            System.out.println(a);  //30
            System.out.println(this.a);  //20
            //Outer.this  获取了外部类对象的地址值
            System.out.println(Outer.this.a);  //10
        }
    }
}
========================================================================
package Just.ao3innerclassdemo3;

public class Test {
    public static void main(String[] args) {
        //创建内部类对象对象,并调用show方法
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}
========================================================================
运行结果:
30
20
10

静态内部类(了解)

静态内部类也是成员内部类的一种

静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象

public class Car { 外部类

String carName ;

int carAge ;

int carColor ;

static class Engine { 成员内部类

String engineName ;

int engineAge ;

}

}

==创建静态内部类对象的格式:==外部类名.内部类名 对象名 = new 外部类名.内部类名( ) ;

​ 范例:Outer.Inter oi = new Outer.Inter();

==调用非静态方法的格式:==先创建对象,用对象调用

​ 范例:Outer.Inter oi = new Outer.Inter();

oi.show1();

==调用静态方法的格式:==外部类名.内部类名.方法名();

​ 范例:Outer.Inter.show2();

package Just.ao4innerclassdemo4;

public class Outer {

    int a = 10;
    static int b = 20;
    //静态内部类
    static class Inter{
        //非静态方法
        public void show1(){
            System.out.println("非静态的方法被调用了");
            //静态内部类访问外部类中的非静态变量和静态方法,需要创建对象
            Outer o = new Outer();
            System.out.println(o.a);
            //静态内部类直接访问外部类中的静态变量和静态方法
            System.out.println(b);
        }
        //静态方法
        public static void show2(){
            System.out.println("静态的方法被调用了");
            //静态内部类访问外部类中的非静态变量和静态方法,需要创建对象
            Outer o = new Outer();
            System.out.println(o.a);
            //静态内部类直接访问外部类中的静态变量和静态方法
            System.out.println(b);
        }

    }
}
========================================================================
package Just.ao4innerclassdemo4;

public class Test {
    public static void main(String[] args) {
        //创建静态内部类的对象
        //只要是静态的东西,都可以用类名.直接获取
        Outer.Inter oi = new Outer.Inter();
        //调用非静态方法
        oi.show1();
        //调用静态方法
        Outer.Inter.show2();
    }
}
========================================================================
运行结果:
非静态的方法被调用了
10
20
静态的方法被调用了
10
20

局部内部类(了解)

1、将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量

2、外界无法直接使用,需要在方法内部创建对象并使用

3、该类可以直接访问外部类的成员,也可以访问方法内的局部变量

package Just.ao5innerclassdemo5;

public class Outer {
    //外部类的成员
    int b = 20;
    public void show(){
        //方法内的局部变量
        int a = 10;
        //局部内部类
        class Inner{
            String name;
            int age;

            public void method1(){
                //内部类可以直接访问外部类的成员,也可以访问方法内的局部变量
                System.out.println(a);
                System.out.println(b);
                System.out.println("局部内部类中的methodl方法");
            }

            public static void method2(){
                System.out.println();
            }
        }
        //外界是无法直接使用局部内部类,需要在方法内部创建对象并使用
        //创建局部内部类的对象
        Inner i = new Inner();
        System.out.println(i.name);
        System.out.println(i.age);
        i.method1();
        Inner.method2();
    }

}
========================================================================
package Just.ao5innerclassdemo5;

public class Test {
    public static void main(String[] args) {

        //调用show方法,让代码执行
        Outer o = new Outer();
        o.show();
    }
}

匿名内部类(掌握)

匿名内部类:隐藏了名字的内部类,可以写在成员位置、局部位置

格式:

new (父)类名或者接口名 () { 继承 \ 实现

重写方法; 方法重写

}; 创建对象

格式细节

​ 包含了继承或实现,方法重写,创建对象。

​ 整体就是一个类的子类对象或者接口的实现类对象

解释:new (父)类名 或者 接口名();是 new 出来的 没有名字的类的对象

​ **真正没有名字的是 { } **

{ } 与 前面的对象,要么是继承关系,要么是实现关系

在 { } 里面重写抽象方法

总结匿名内部类是集:{ } 匿名类 ——> 继承 或 接口 ——> 方法重写 ——> 创建匿名类对象 于一体的存在

整体,呈现出来的是 匿名类的对象

使用场景

​ 当方法的参数是接口时或者类时,

​ 以接口为例,可以传递这个接口的实现类对象,

​ 如果实现类只要使用一次,就可以用匿名内部类简化代码。

举例:

package Just.ao6innerclassdemo6;

public interface Swim {
    public abstract void swim();

}
========================================================================
package Just.ao6innerclassdemo6;

public abstract class Animal {
    public abstract void eat();
}
========================================================================
package Just.ao6innerclassdemo6;

public class Test {
    public static void main(String[] args) {

        //编写匿名内部类的代码
        new Swim(){
            @Override
            public void swim() {
                System.out.println("重写了游泳的方法");
            }
        };
        /*
        new Animal(){
            @Override
            public void eat() {
                System.out.println("重写了eat方法");
            }
        };
        上面注释的代码表示:创建了 Animal 子类的对象,其中包括,匿名类继承父类——> 匿名类重写父类的抽方法——> 创建匿名类的对象
        */
        //在测试类中调用下面的 method 方法?
        method(
                //把 Animal 子类(匿名类)的对象,作为参数,调用的 method 方法
                new Animal() {
                    @Override
                    public void eat() {
                        System.out.println("狗吃骨头");
                    }
                }
        );
    }

    public static void method(Animal a){//Animal a = 子类对象;符合:标准多态
        a.eat();//编译看左边,运行看右边
    }
}
========================================================================
运行结果:
狗吃骨头

补充:匿名内部类的其他使用

package Just.ao6innerclassdemo6;

public class Test2 {
    public static void main(String[] args) {

        /*下面的整体,可以理解为 Swim接口的 实现类对象
           我们可以把此对象,赋值给一个 Swim接口的类型的 变量 ————> 进而实现 接口的多态
         */
        Swim s = new Swim(){

            @Override
            public void swim() {
                System.out.println("重写游泳后的方法1");
            }
        };
        //用对象 s 调用实现类的重写方法
        //编译看左边,运行看右边
        s.swim();


        /*下面的整体,可以理解为 Swim接口的 实现类对象
           既然是实现类的对象,那可以调用类的方法
           首先调用实现类中的方法
         */
        new Swim(){

            @Override
            public void swim() {
                System.out.println("重写游泳后的方法2");
            }
        }.swim();
    }
}
========================================================================
运行结果:
重写游泳后的方法1
重写游泳后的方法2
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值