Java基础学习 ---封装和继承

102 阅读23分钟

Java基础学习 ---封装和继承

Java基础学习---点击进入学习

640 (15).jpg

static关键字&方法重写&多态

1.static关键字

1.1 static修饰属性

static修饰属性,表示此属性属于整个类,不属于任何对象,可以被此类的所有对象共享,在内存中

只存在一份。推荐使用类名加点访问,也可以(不推荐)通过对象名加点访问。

对象创建的过程:

1.将类信息文件加载到方法区(加载类的操作),加载类之前会先判断此类是否被加载过

​ 如果已经被加载过,则直接进行第二步

​ 如果没有被加载过,则先加载类

​ 此时静态的信息会在方法区完成初始化,即有空间,有值

2.在堆中开辟空间,此时实例级别的属性将有默认值

3.将堆中的地址赋值给栈中的引用

对象创建的过程.png

描述静态属性实例属性
初始化时机:静态的随着类的加载而初始化实例的随着对象的创建而初始化
存储位置:静态的存储在方法区实例的存储在堆中(对象中)
存储性质:静态的随着类加载而存在,类只加载一次,所以静态的属性只存在一份实例的随着对象的创建而存在,每创建一次对象就存在一份
/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 10:24
 *  学生类
 *      使用代码模拟实现多个学生共享饮水机的操作
 *
 *  static修饰属性,表示此属性属于整个类,不属于任何对象,可以被此类的所有对象共享,在内存中
 *  只存在一份。推荐使用类名加点访问,也可以(不推荐)通过对象名加点访问。
 *
 *  对象创建的过程:
 *      1.将类信息文件加载到方法区(加载类的操作),加载类之前会先判断此类是否被加载过
 *          如果已经被加载过,则直接进行第二步
 *          如果没有被加载过,则先加载类
 *          此时静态的信息会在方法区完成初始化,即有空间,有值
 *      2.在堆中开辟空间,此时实例级别的属性将有默认值
 *      3.将堆中的地址赋值给栈中的引用
 *
 *  实例属性和静态属性的区别?
 *      初始化时机:静态的随着类的加载而初始化 实例的随着对象的创建而初始化
 *      存储位置:静态的存储在方法区  实例的存储在堆中(对象中)
 *      存储性质:
 *          静态的随着类加载而存在,类只加载一次,所以静态的属性只存在一份
 *          实例的随着对象的创建而存在,每创建一次对象就存在一份
 *
 *
 */
public class Student {
    String name;
    static int capacity = 100; // 100升

    public void getWater(){
        if(capacity > 0){
            capacity -= 2;
            System.out.println(name + "接水2L,还剩余:" + capacity + "L");
        }else{
            System.out.println("没水了");
        }
    }

    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.name = "赵四";
        stu1.getWater();

        stu1 = null;



        Student stu2 = new Student();
        stu2.name = "广坤";
        stu2.getWater();

        Student stu3 = new Student();
        stu3.name = "小宝";
        stu3.getWater();

    }
}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 10:35
 *  人类
 */
public class Person {
    String name;
    static String country = "中华人民共和国";


    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "赵四";
        p1.country = "中国";


        System.out.println(Person.country);
        System.out.println(p1.country); // 中国

        Person p2 = new Person();
        p2.name = "大拿";
        System.out.println(p2.country);// 中国

        Person p3 = new Person();
        p3.name = "小宝";
        System.out.println(p3.country);// 中国
    }
}

1.2 static修饰方法

静态方法:

​ 可直接通过类名访问

​ 静态方法中不能使用this和super

​ 不能直接访问所属类的实例变量和实例方法

​ 可直接访问类的静态变量和静态方法

实例方法:

​ 通过实例访问

​ 可直接访问所属类的静态变量、静态方法、实例变量和实例方法

静态方法必须被实现,即静态方法必须有方法体。

静态方法:本类中直接访问,其他类通过类名加点访问

关于静态信息和实例信息互相访问规则

​ 同级别互相直接访问

​ 实例访问实例:直接访问

​ 静态访问静态:直接访问

​ 实例访问静态:直接访问

​ 静态访问实例:不能直接访问 必须先new对象(main方法就是最常用的静态方法)

/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 14:03
 *  静态方法:本类中直接访问,其他类通过类名加点访问
 *
 *  关于静态信息和实例信息互相访问规则
 *      同级别互相直接访问
 *          实例访问实例:直接访问
 *          静态访问静态:直接访问
 *      实例访问静态:直接访问
 *      静态访问实例:不能直接访问 必须先new对象(main方法就是最常用的静态方法)
 *
 */
public class TestStaticMethod {
    String name;
    static int field;
    public static void m1(){
        TestStaticMethod tsm = new TestStaticMethod();
        System.out.println(field);
        System.out.println(tsm.name);
        System.out.println("静态方法m1");
        tsm.m2();
        m4();
    }

    public static void m4(){
        System.out.println("静态方法m4");
    }
    public void m2(){
        m1();
        System.out.println(field);
        System.out.println(name);
        System.out.println(this);
        System.out.println(super.toString());
        m3();
    }
    public void m3(){
        System.out.println("实例方法m3方法");
    }


    public static void main(String[] args) {
        m1();
    }

}




/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 14:04
 */
public class Test {
    public static void main(String[] args) {
        TestStaticMethod.m1();
    }
}

1.3 static修饰代码块

静态代码块:随着JVM加载类而执行,因为类只加载一次,所以静态代码块也只执行一次。

多个静态代码块按照书写顺序执行。

什么时候会使用静态代码块?

初始化数据、做一些前置的操作(连接数据库输入用户名、密码、连接地址、驱动信息等)

什么时候会加载类?

​ 1.创建对象

​ 2.访问类中的静态信息

普通代码块:随着对象的创建而执行



/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 14:20
 *  静态代码块:随着JVM加载类而执行,因为类只加载一次,所以静态代码块也只执行一次。
 *  多个静态代码块按照书写顺序执行。
 *
 *  什么时候会使用静态代码块?
 *      初始化数据、做一些前置的操作(连接数据库输入用户名、密码、连接地址、驱动信息等)
 *
 *  什么时候会加载类?
 *      1.创建对象
 *      2.访问类中的静态信息
 *
 *  普通代码块:随着对象的创建而执行
 *
 */
public class TestStaticCode {
    static int num;

    static{
        System.out.println("静态代码块2");
    }
    static{
        System.out.println("静态代码块1");
    }

    {
        System.out.println("实例/普通代码块1");
    }

    {
        System.out.println("实例/普通代码块2");
    }



    public static void main(String[] args) {
        System.out.println(num);

        TestStaticCode testStaticMethod1 = new TestStaticCode();
        TestStaticCode testStaticMethod2 = new TestStaticCode();

    }


}

2. static练习题

模拟实现选民投票过程:一群选民进行投票,每个选民只允许投一次票,并且当投票总数达到100时,就停止投票


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 14:59
 *  选民类
 *  模拟实现选民投票过程:一群选民进行投票,每个选民只允许投一次票,并且当投票总数达到100时,就停止投票
 */
public class Voter {
    String name; // 选民名称
    static int ticketCount = 100; //投票总数100


    public boolean voteFor(){
        if(ticketCount > 0){
            ticketCount--;
            System.out.println(name + "投出了一票,还剩余:" + ticketCount + "张票");
            return true;
        }else{

            System.out.println("投票结束");
            return false;
        }
    }

    public static void main(String[] args) {
        Voter v1 = new Voter();
        v1.name = "赵四";
        v1.voteFor();

        Voter v2 = new Voter();
        v2.name = "大拿";
        v2.voteFor();


        Voter v3 = new Voter();
        v3.name = "广坤";
        v3.voteFor();

        for(int i = 1;i < 200;i++){
            Voter v = new Voter();
            v.name = i + "号选民";
            if(!v.voteFor()){
                break;
            }
        }


    }

}

3.方法重写

方法重写 override 方法覆盖 (方法重载 overload)

1.存在于子类中(必须有继承)

2.方法名相同

3.参数列表相同

4.返回值相同 (或者是其子类)

5.访问权限不能严于父类(大于等于父类)

6.父类的实例方法不能重写为静态方法

7.父类的静态方法可以被继承 但是不能被重写

8.不能抛出、声明比父类更多的异常

注解:@Override 用于添加在子类重写父类的方法上方 表示此方法为重写方法

如果没有正确重写 将报错 此注解还可以提高代码的阅读性


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:13
 *  宠物类父类
 *  父类中书写共有的属性和方法
 */
public class Pet {
    private String name;
    private int health;
    private int love;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getLove() {
        return love;
    }

    public void setLove(int love) {
        this.love = love;
    }

     void print(){
        System.out.println("宠物的名字是:" + name);
        System.out.println("宠物的健康值是:" + health);
        System.out.println("宠物的亲密值是:" + love);
    }

    public Pet(){
    }

}

/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:07
 *  狗狗类
 *      属性:品种
 *      行为:打印狗狗信息
 *
 */
public class Dog extends Pet {
    private String strain;
    public String getStrain() {
        return strain;
    }
    public void setStrain(String strain) {
        this.strain = strain;
    }
    public Dog(){
    }


    /**
     *  方法重写 override   方法覆盖  (方法重载 overload)
     *  1.存在于子类中(必须有继承)
     *  2.方法名相同
     *  3.参数列表相同
     *  4.返回值相同 (或者是其子类)
     *  5.访问权限不能严于父类(大于等于父类)
     *  6.父类的实例方法不能重写为静态方法
     *  7.父类的静态方法可以被继承 但是不能被重写
     *  8.不能抛出、声明比父类更多的异常
     *
     *  注解:@Override 用于添加在子类重写父类的方法上方 表示此方法为重写方法
     *  如果没有正确重写 将报错 此注解还可以提高代码的阅读性
     */
    @Override
    public void print(){
        super.print(); // super.print()   print()
        System.out.println("狗狗的品种是:" + strain);
    }


}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:10
 *  企鹅类
 *      属性:性别
 *      行为:打印企鹅信息
 *
 *  alt + insert 生成封装的方法
 */
public class Penguin extends Pet {
    private String sex;
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Penguin(){
    }
    public void print(){
        System.out.println("企鹅的性别是:" + sex);
        super.print();
    }

}



/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:16
 */
public class TestPet {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("大黄");
        dog.setHealth(100);
        dog.setLove(100);
        dog.setStrain("金毛");

        dog.print();

    }
}

4. Object类

4.1 重写toString方法

直接输出一个对象,将默认调用此对象的toString方法

1.为什么自定义的类,可以直接调用toString方法?

​ 因为所有的类默认继承自Object类 而toString方法是Object类中的方法

2.toString()方法的作用?

​ 返回当前对象相关的字符串信息 : 全限定名 + "@" + hash值

3.为什么我们要重写toString()方法?

​ 我们在自定义的子类中,可以根据需求重写toString方法,实现我们需要的效果

​ 比如:输出一个对象直接打印此对象对应的属性名和属性值 实际开发中都会这么做

4.为什么必须重写toString / 能不能自定义一个方法实现打印属性名+属性值

​ 因为toString方法作为父类中的方法 任何子类都可以继承 以及 重写此方法

​ 使用toString方法 意味着程序结构的统一


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/9 15:47
 *
 *  直接输出一个对象,将默认调用此对象的toString方法
 *
 *
 *  1.为什么自定义的类,可以直接调用toString方法?
 *      因为所有的类默认继承自Object类 而toString方法是Object类中的方法
 *
 *  2.toString()方法的作用?
 *      返回当前对象相关的字符串信息 : 全限定名 + "@" + hash值
 *
 *  3.为什么我们要重写toString()方法?
 *      我们在自定义的子类中,可以根据需求重写toString方法,实现我们需要的效果
 *      比如:输出一个对象直接打印此对象对应的属性名和属性值 实际开发中都会这么做
 */
public class Student {
    String name;
    int age;
    char sex;
    double height;

    public String toString(){
        return "Student[name = "+ name +",age = "+ age +",sex = "+ sex +",height = "+ height+"]";
    }

    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.name = "赵四";
        stu1.age = 20;
        stu1.sex = '男';
        stu1.height = 188;

        System.out.println(stu1);
        System.out.println(stu1.toString());


    }
}

4.2 重写equals方法

记住:只要是new出来的对象 地址都不同 即使用==比较都为false

1.为什么我们自定义的类可以直接调用equals()方法?

​ 因为equals方法是Object父类中的方法 所有的类都默认继承自此类 自定义的类也同样

​ 所以可以直接调用继承自父类的方法

2.equals()方法的作用?

​ 默认实现为:比较两个对象的地址值是否相同,我们可以重写自定义比较规则,String类

​ 就是对父类的equals方法进行了重写,将原本的只比较地址,重写为了比较地址并且比较内容

3.==和equals()的区别?

​ ==属于比较运算符;equals()属于Object类中的方法

​ ==比较基本数据类型,比较值;==比较引用数据类型,比较地址

​ equals()方法只能比较引用数据类型,默认实现也为比较地址

4.为什么我们要重写equals方法呢?

​ 假如有这样的"两个人",名字身份证号都相同,则我们可以认为这"两个人"就是同一个人

​ 我们应该让这样的两个对象 equals比较为true 通过重写equals方法实现


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 9:14
 *  记住:只要是new出来的对象 地址都不同 即使用==比较都为false
 *
 *  1.为什么我们自定义的类可以直接调用equals()方法?
 *      因为equals方法是Object父类中的方法 所有的类都默认继承自此类 自定义的类也同样
 *      所以可以直接调用继承自父类的方法
 *
 *  2.equals()方法的作用?
 *      默认实现为:比较两个对象的地址值是否相同,我们可以重写自定义比较规则,String类
 *      就是对父类的equals方法进行了重写,将原本的只比较地址,重写为了比较地址并且比较内容
 *
 *  3.==和equals()的区别?
 *      ==属于比较运算符;equals()属于Object类中的方法
 *      ==比较基本数据类型,比较值;==比较引用数据类型,比较地址
 *      equals()方法只能比较引用数据类型,默认实现也为比较地址
 *
 *  4.为什么我们要重写equals方法呢?
 *      假如有这样的"两个人",名字身份证号都相同,则我们可以认为这"两个人"就是同一个人
 *      我们应该让这样的两个对象 equals比较为true 通过重写equals方法实现
 *
 *
 */
public class Person {
    String name;
    String idCard;

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

    public boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        Person p = (Person)obj;
        if(this.name.equals(p.name)  && this.idCard.equals(p.idCard)){
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Person p1 = new Person("赵四", "65243213215652326523325412");

        Person p2 = new Person("赵四", "65243213215652326523325412");

        System.out.println(p1 == p2); // false

        System.out.println(p1.equals(p2)); // false

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

        String str1 = new String("abc");

        String str2 = new String("abc");

        String str3 = str1;

        System.out.println(str1 == str2); // false

        System.out.println(str1.equals(str2)); // true

        // String类对Object类中的equals方法进行了重写 改变了父类中代码的比较规则

    }
}

自定义方法模拟实现String类比较内容


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 9:41
 *  模拟String类编写equals方法
 */
public class TestMyString {

    public static void main(String[] args) {
        System.out.println(myEquals("abc", "acc"));
    }

    public static boolean myEquals(String str1,String str2){
        // 第一步 先比较地址 因为地址如果是相同的 那么就表示属于同一个对象 字符串内容一定是相同的
        if(str1 == str2){
            return true;
        }

        // 获取到str1字符串的长度
        int str1Length = str1.length(); // length()方法用于获取字符串的长度

        // 获取到str2字符串的长度
        int str2Length = str2.length();

        // 比较两个字符串的长度 如果长度不同 则直接返回false  即不需要再继续比较了
        if(str1Length != str2Length){
            return false;
        }

        // 代码能执行到这里 表示长度是相同的
        // 将两个字符串都转换为char数组
        char[] v1 = str1.toCharArray(); // toCharArray()方法用于将字符串转换为char数组
        char[] v2 = str2.toCharArray();
        // 遍历数组 比较相同位置的元素
        for(int i = 0;i < v1.length;i++){
            // 如果相同为位置的元素  内容不同 则直接中断方法 返回false 即不需要再继续比较了
            if(v1[i] != v2[i]){
                return false;
            }
        }

        // 代码可以执行到这里 说明 for循环中的return false没有执行
        // 即表示整个比较结束 所有的位置内容都相同
        return true;
    }
}

4.3 重写hashCode方法

1.为什么我们自定义的类可以直接调用hashCode()方法?

​ 因为所有的类默认都继承自Object 而hashCode()方法是从Object类中继承而来的

2.hashCode()方法的作用?

​ 返回当前对象的hash(哈希)值

3.什么是hash值?

​ hash值是使用杂凑算法 根据当前对象的地址等信息计算出来的一个十进制的int类型的数值

​ 相当于地址的表现形式 不是地址值 Java中的地址值是无法获取到的

4.为什么要重写hashCode()方法?

​ 1: 在默认设定下:两个对象equals比较为true 则hashCode也是相同的 因为hashCode是根据地址计算出来的

​ equals默认就比较地址 总结:equals比较为true的情况 hashCode一定是相同的

​ 综上所述,所以我们要在重写了equals方法以后 继续重写hashCode方法 以维持以上的默认设定

​ 2: 在后续我们所使用的集合中 默认是以两个对象equals比较为true 并且hashCode相同作为去除重复元素的依据

5.应该如何重写hashCode方法/ 重写hashCode方法依据的条件是什么?

​ 我们要保证在equals方法为true的情况 hashCode相等

​ 目前equals方法重写为比较名字和身份证号

​ 所以重写hashCode方法应该继续使用名字和身份证号计算hash值

​ 这样 才可以保证 :在equals方法为true的情况 hashCode相等

6.String类就是重写了equals方法 并且重写了hashCode


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 9:14
 *  1.为什么我们自定义的类可以直接调用hashCode()方法?
 *      因为所有的类默认都继承自Object 而hashCode()方法是从Object类中继承而来的
 *  2.hashCode()方法的作用?
 *      返回当前对象的hash(哈希)值
 *
 *  3.什么是hash值?
 *      hash值是使用杂凑算法 根据当前对象的地址等信息计算出来的一个十进制的int类型的数值
 *      相当于地址的表现形式 不是地址值 Java中的地址值是无法获取到的
 *
 *  4.为什么要重写hashCode()方法?
 *     1: 在默认设定下:两个对象equals比较为true 则hashCode也是相同的 因为hashCode是根据地址计算出来的
 *      equals默认就比较地址 总结:equals比较为true的情况 hashCode一定是相同的
 *      综上所述,所以我们要在重写了equals方法以后 继续重写hashCode方法 以维持以上的默认设定
 *
 *     2: 在后续我们所使用的集合中 默认是以两个对象equals比较为true 并且hashCode相同作为去除重复元素的依据
 *
 *  5.应该如何重写hashCode方法/ 重写hashCode方法依据的条件是什么?
 *      我们要保证在equals方法为true的情况 hashCode相等
 *      目前equals方法重写为比较名字和身份证号
 *      所以重写hashCode方法应该继续使用名字和身份证号计算hash值
 *      这样 才可以保证 :在equals方法为true的情况 hashCode相等
 *
 *  6.String类就是重写了equals方法 并且重写了hashCode
 */
public class Person {
    String name;
    String idCard;

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

    public boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        Person p = (Person)obj;
        if(this.name.equals(p.name)  && this.idCard.equals(p.idCard)){
            return true;
        }
        return false;
    }


    public  int hashCode(){
        int prime = 31; // 权重 重要计算因素
        int result = 0; // 最终返回值

        result = prime * result + (this.name == null ? 0 : this.name.hashCode());

        result = prime * result + (this.idCard == null ? 0 : this.idCard.hashCode());

        return result;
    }

    public static void main(String[] args) {
        Person p1 = new Person("赵1四", "6524321321565232652332541");
        Person p2 = new Person("赵1四", "6524321321565232652332541");
        System.out.println(p1 == p2); // false
        System.out.println(p1.equals(p2)); // true

        System.out.println(p1.hashCode());
        System.out.println(p2.hashCode());


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

        String str1 = new String("abc");
        String str2 = new String("abc");

        System.out.println(str1.equals(str2));
        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode());


    }
}

为什么计算hashCode使用31?

1.因为JDK就使用31

2.因为31是一个特殊的质数 任何数乘以31 等于这个数 左移5位 减去这个数本身

n * 31 = (n << 5) - n

总结:使用31计算hash值效率更高 但是不是非31不可


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 11:02
 *  为什么计算hashCode使用31?
 *      1.因为JDK就使用31
 *      2.因为31是一个特殊的质数 任何数乘以31 等于这个数 左移5位 减去这个数本身
 *      n * 31 = (n << 5) - n
 */
public class TestPrime {
    public static void main(String[] args) {
        int a = 11;
        System.out.println(a * 31);
        System.out.println((a << 5) - a);
    }
}

5. 自定义类型的属性

自定义类型的属性.png

自定义的类,同样可以作为类型使用,属于引用数据类型。


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 14:01
 *  万物皆对象
 *
 *  学生类
 *      属性 : 名字 年龄 性别 地址
 *
 *  ORM Object Relation Mapping 对象关系映射 未来数据库与Java实体类映射的一个操作
 *
 *  一对一关系:一个学生对应一个地址
 *  一对多关系:一个学生对应多门课程
 *  多对一关系:多个爱好属于同一个学生 多个成绩属于同一个学生
 *  多对多的关系:多个学生对应多个课程 多个学生对应多个城市
 *
 */
public class Student {
    private String name;
    private int age;
    private char sex;
    private Address address; // 广东省深圳市  深圳市宝安区   宝安区草围社区  航城街道108号    文化路3005号3楼301

    private Hobby[] hobbies; //

    public void setHobbies(Hobby [] hobbies){
        this.hobbies = hobbies;
    }

    public Hobby [] getHobbies(){
        return hobbies;
    }

    public void setAddress(Address address){
        this.address = address;
    }

    public Address getAddress(){
        return address;
    }


    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 char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", address=" + address +
                '}';
    }
}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 14:08
 *  地址类
 *      属性:省份 城市 区域 街道 邮编
 */
public class Address {
    private String province;
    private String city;
    private String area;
    private String street;
    private String zipCode;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", area='" + area + '\'' +
                ", street='" + street + '\'' +
                ", zipCode='" + zipCode + '\'' +
                '}';
    }
}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 14:32
 *  爱好
 *      属性:爱好类型 爱好名称
 */
public class Hobby {
    private String hobbyType; // 爱好类型
    private String hobbyName; // 爱好名称

    public String getHobbyType() {
        return hobbyType;
    }

    public void setHobbyType(String hobbyType) {
        this.hobbyType = hobbyType;
    }

    public String getHobbyName() {
        return hobbyName;
    }

    public void setHobbyName(String hobbyName) {
        this.hobbyName = hobbyName;
    }

    @Override
    public String toString() {
        return "Hobby{" +
                "hobbyType='" + hobbyType + '\'' +
                ", hobbyName='" + hobbyName + '\'' +
                '}';
    }
}


import java.util.Arrays;

/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 14:21
 */
public class TestStudent {
    public static void main(String[] args) {
        Student stu1 = new Student();

        stu1.setName("赵四");
        stu1.setAge(20);
        stu1.setSex('男');

        Address add1 = new Address();
        add1.setProvince("广东省");
        add1.setCity("深圳市");
        add1.setArea("宝安区");
        add1.setStreet("航城街道");
        add1.setZipCode("567845");

        stu1.setAddress(add1);

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

        System.out.println(stu1);

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

        System.out.println(stu1.getName());
        System.out.println(stu1.getAge());
        System.out.println(stu1.getSex());
        System.out.println(stu1.getAddress());

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

        // 回顾数组的默认值
        // 回顾空指针异常的问题
        // 使用一个指向为null的引用 继续调用属性 或者 方法 就会出现空指针异常
        Hobby [] hobbies = new Hobby[3];


        hobbies[0] = new Hobby();
        hobbies[0].setHobbyType("电子竞技");
        hobbies[0].setHobbyName("LOL");
        System.out.println(hobbies[0]);

        hobbies[1] = new Hobby();
        hobbies[1].setHobbyType("文艺类");
        hobbies[1].setHobbyName("唱");
        System.out.println(hobbies[1]);

        hobbies[2] = new Hobby();
        hobbies[2].setHobbyType("文艺类");
        hobbies[2].setHobbyName("跳");
        System.out.println(hobbies[2]);

        stu1.setHobbies(hobbies);


        System.out.println(stu1.getHobbies()[0].getHobbyType());
        System.out.println(stu1.getHobbies()[0].getHobbyName());

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


        Hobby[] hobbies1 = stu1.getHobbies();

        System.out.println(Arrays.toString(hobbies1));




    }
}

6.多态

多态的前提:

1.必须有继承关系

2.必须有方法重写

生活中的多态:同一个事物 因为环境/条件不同 产生不同的结果

程序中的多态:同一个引用类型 指向不同的实例 (父类引用指向子类对象 )

父类引用指向子类对象:

向上转型,此时通过父类引用可以访问子类重写父类,或者继承父类的方法,不能访问子类独有的方法

向上转型三种表现形式

​ 1.父类作为形参 子类作为实参

​ 2.父类作为声明返回值 实际返回值为子类类型

​ 3.父类类型的数组、集合 其元素为子类类型


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:13
 *  宠物类父类
 *  父类中书写共有的属性和方法
 */
public class Pet {
    private String name;
    private int health;
    private int love;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getLove() {
        return love;
    }

    public void setLove(int love) {
        this.love = love;
    }

     void print(){
        System.out.println("宠物的名字是:" + name);
        System.out.println("宠物的健康值是:" + health);
        System.out.println("宠物的亲密值是:" + love);
    }

    public void cure(){
        System.out.println("宠物看病");
    }




}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:07
 *  狗狗类
 *      属性:品种
 *      行为:打印狗狗信息
 *
 */
public class Dog extends Pet {
    private String strain;
    public String getStrain() {
        return strain;
    }
    public void setStrain(String strain) {
        this.strain = strain;
    }
    public Dog(){
    }

    @Override
    public void print(){
        super.print();
        System.out.println("狗狗的品种是:" + strain);
    }

    public void cure(){
        System.out.println("狗狗看病,吃药,吃骨头,健康值恢复");
        setHealth(100);
    }

}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:10
 *  企鹅类
 *      属性:性别
 *      行为:打印企鹅信息
 */
public class Penguin extends Pet {
    private String sex;
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Penguin(){
    }
    public void print(){
        System.out.println("企鹅的性别是:" + sex);
        super.print();
    }

    public void cure(){
        System.out.println("企鹅看病,打针,吃小鱼,健康值恢复");
        this.setHealth(100);
    }

}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 15:43
 *  主人类
 *      行为
 *          1.带宠物去看病
 */
public class Master {
    public void toHospitalWithDog(Dog dog){
        dog.cure();
    }

    public void toHospitalWithPenguin(Penguin penguin){
        penguin.cure();
    }

    // 以上两个方法分别实现了给狗狗和企鹅看病 但是这种方式不符合开闭原则
    // 因为后续如果有更多的宠物子类 则必须再添加更多的给宠物看病的方法 太啰嗦了
    // 开闭原则:软件设计原则之一 表示程序要对扩展开放 对修改源代码关闭

    // 所以,我们应该编写一个方法 用于实现给所有的宠物(现存的 以及 未来新增的)看病

    public void toHospitalWithPet(Pet pet){ // Pet pet = dog = new Dog();
        pet.cure();
    }

}


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/7 14:16
 */
public class TestPet {
    public static void main(String[] args) {
        Master master = new Master();

        Dog dog = new Dog();
        dog.setName("大黄");
        dog.setLove(100);
        dog.setHealth(50);
        dog.setStrain("金毛");

        master.toHospitalWithDog(dog);

        System.out.println(dog.getHealth());


        Penguin penguin = new Penguin();
        penguin.setName("大白");
        penguin.setSex("雌性");
        penguin.setLove(100);
        penguin.setHealth(20);

        master.toHospitalWithPenguin(penguin);
        System.out.println(penguin.getHealth());


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

        dog.setHealth(10);
        penguin.setHealth(10);

        master.toHospitalWithPet(dog);
        master.toHospitalWithPet(penguin);


        System.out.println(dog.getHealth());
        System.out.println(penguin.getHealth());

        Cat cat = new Cat();
        cat.setHealth(20);

        master.toHospitalWithPet(cat);

    }
}

多态举例:学生携带电脑去学校


/**
 * @author WHD
 * @description TODO
 * @date 2024/1/10 16:08
 */
public class Student {
    public void gotoSchool(Computer computer){
        computer.coding();
    }

    public static void main(String[] args) {
        Student stu1 = new Student();

        NoteBook noteBook = new NoteBook();
        IPad iPad = new IPad();
        DeskComputer deskComputer = new DeskComputer();

        stu1.gotoSchool(noteBook);
        stu1.gotoSchool(iPad);
        stu1.gotoSchool(deskComputer);
    }
}

class Computer{
    public void coding(){
        System.out.println("电脑敲代码");
    }
}
class NoteBook extends Computer{
    @Override
    public void coding() {
        System.out.println("笔记本敲代码,很方便");
    }
}

class IPad extends Computer{
    @Override
    public void coding() {
        System.out.println("平板电脑敲代码,不太方便");
    }
}

class DeskComputer extends Computer{
    @Override
    public void coding() {
        System.out.println("台式机敲代码,视野很广阔");
    }
}