封装、继承和多态

封装 encapsulation

  1. 属性私有化
  2. 提供一个公共的public set 方法赋值
  3. 提供一个公共的public get 方法获取值
csharp 复制代码
package com.hspedu.encapsulation;

public class encapsulation01 {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setAge(30);
        p1.setName("jack");
        p1.setSalary(3000);

        System.out.println(p1.info());
    }
}

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

    // 快捷键:alt + insert 快速生成getter/setter
    public String getName() {
        return name;
    }

    public void setName(String name) {
        // 加入对数据的校验
        if (name.length() > 2 && name.length() < 6) {
            this.name = name;
        } else {
            System.out.println("名字不符合要求,需要在2-6之间");
            this.name = "无名";
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 1 && age <= 130) {
            this.age = age;
        } else {
            System.out.println("年龄不合法,需要在1-130之间");
            this.age = 18;
        }
    }

    public double getSalary() {
        // 可以增加当前类的业务逻辑
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    //     写一个方法,返回属性信息
    public String info() {
        return "信息为 name=" + name + " age=" + age + " salary=" + salary;
    }
}

案例2

csharp 复制代码
package com.hspedu.encapsulation;

public class Account {
    private String name;
    private double balance;
    private String pwd;

    public Account() {
    }

    public Account(String name, double balance, String pwd) {
        this.setName(name);
        this.setBalance(balance);
        this.setPwd(pwd);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 4) {
            this.name = name;
        } else {
            System.out.println("名字不符合要求,需要在2-4之间 默认为无名");
            this.name = "无名";
        }
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        if (balance >= 20) {
            this.balance = balance;
        } else {
            System.out.println("余额不足,默认为0");
            this.balance = 0;
        }
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        if (pwd.length() == 6) {
            this.pwd = pwd;
        } else {
            System.out.println("密码不符合要求,必须是6位,默认为000000");
            this.pwd = "000000";
        }
    }

    // 显示账号信息
    public String info() {
        return "账号名:" + name + "\n余额:" + balance + "\n密码:" + pwd;
    }
}
java 复制代码
package com.hspedu.encapsulation;

public class AccountTest {
    public static void main(String[] args) {
        Account account = new Account();
        account.setName("jack");
        account.setBalance(1000);
        account.setPwd("123456");
        System.out.println(account.info());
    }
}

输出

名字不符合要求,需要在2-4之间 默认为无名 账号名:无名 余额:1000.0 密码:123456

继承 extend

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends;来声明继承父类即可。

继承的示意图

案例

csharp 复制代码
package com.hspedu.extend_.improve;

// pupil graduate的父类
public class Student {
    public String name;
    public int age;
    private double score;

    public void setScore(double score) {
        this.score = score;
    }

    public void showInfo() {
        System.out.println("学生名 " + name + " 年龄 " + age + " 成绩 " + score);
    }
}
scala 复制代码
package com.hspedu.extend_.improve;

public class Pupil extends Student {

    public void testing() {
        System.out.println("小学生 " + name + " 正在考小学数学");
    }

}
scala 复制代码
package com.hspedu.extend_.improve;

public class Graduate extends Student {

    public void testing() {
        System.out.println("大学生 " + name + " 正在考大学数学");
    }

}
ini 复制代码
package com.hspedu.extend_.improve;

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

        Pupil pupil = new Pupil();
        pupil.name = "jack~";
        pupil.age = 10;
        pupil.testing();
        pupil.setScore(99);
        pupil.showInfo();

        Graduate graduate = new Graduate();
        graduate.name = "tom~";
        graduate.age = 22;
        graduate.testing();
        graduate.setScore(88);
        graduate.showInfo();

    }
}

输出

小学生 jack~ 正在考小学数学 学生名 jack~ 年龄 10 成绩 99.0 大学生 tom~ 正在考大学数学 学生名 tom~ 年龄 22 成绩 88.0

细节

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性不能在子类直接访问,要通过父类的公共的方法去访问
csharp 复制代码
package com.hspedu.extend_;

public class Base {
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

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

    public void test100() {
        System.out.println("test100()...");
    }

    protected void test200() {
        System.out.println("test200()...");
    }

    void test300() {
        System.out.println("test300()...");
    }

    private void test400() {
        System.out.println("test400()...");
    }
}
kotlin 复制代码
package com.hspedu.extend_;

public class Sub extends Base {

    public Sub() {
        System.out.println("Sub()...");
    }
    public  void  sayOk(){
        //非私有的属性和方法可以在子类直接访问
        System.out.println(this.n1 + " " + this.n2 + " " + this.n3);
        this.test100();
        this.test200();
        this.test300();

        // this.n4;//编译错误
        //this.test400();//编译错误

        this.callTest400();
        int res = this.getN4();
        System.out.println("res=" + res);
    }
}
vbnet 复制代码
package com.hspedu.extend_;

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.sayOk();
    }
}

输出

Base()... Sub()... 100 200 300 test100()... test200()... test300()... test400()... res=400

  1. 子类必须调用父类的构造器,完成父类的初始化
  2. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
csharp 复制代码
package com.hspedu.extend_;

public class Base {
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    // 无参构造器
/*
    public Base() {
        System.out.println("Base()...");
    }
*/

    public Base(int n1) {
        this.n1 = n1;
    }

    public void test100() {
        System.out.println("test100()...");
    }

    protected void test200() {
        System.out.println("test200()...");
    }

    void test300() {
        System.out.println("test300()...");
    }

    private void test400() {
        System.out.println("test400()...");
    }

    //     父类提供一个公共的方法,这个方法中调用了父类的私有方法
    public void callTest400() {
        test400();
    }

    public int getN4() {
        return this.n4;
    }
}
kotlin 复制代码
package com.hspedu.extend_;

public class Sub extends Base {

    public Sub() {
        super(10);// 调用父类的有参构造器
        System.out.println("Sub()...");
    }

    public void sayOk() {
        // 非私有的属性和方法可以在子类直接访问
        System.out.println(this.n1 + " " + this.n2 + " " + this.n3);
        this.test100();
        this.test200();
        this.test300();

        // this.n4;//编译错误
        // this.test400();//编译错误

        this.callTest400();
        int res = this.getN4();
        System.out.println("res=" + res);
    }
}
  1. 如果希望指定去调用父类的某个构造器,则显式的调用一下super(参数列表)
  2. super在使用时,需要放在构造器第一行(super 只能在构造器里面使用)
  3. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
csharp 复制代码
public Sub() {
    // this("jack", 100);// 调用本类的有参构造器 报错
    super(10);// 调用父类的有参构造器
    System.out.println("Sub()...");
 }
  1. java所有类都是Object类的子类,Object是所有类的基类。
  2. 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
  3. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。 思考:如何让A类继承B类和C类?(A继承B B继承C)
  4. 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系

继承的本质

scala 复制代码
package com.hspedu.extend_;

public class ExtendsTheroy {
    public static void main(String[] args) {
        Son son = new Son();
    }
}

class Grandpa {
    String name = "大头爷爷";
    String hobby = "旅游";

    public Grandpa() {
        System.out.println("Grandpa()...");
    }
}

class Father extends Grandpa {
    String name = "大头爸爸";
    int age = 40;

    public Father() {
        System.out.println("Father()...");
    }
}

class Son extends Father {
    String name = "大头儿子";
    int age = 10;

    public Son() {
        System.out.println("Son()...");
    }
}

super关键字

在Java中,"super"关键字主要用于访问父类的成员(属性、方法和构造函数)。它可以用于继承关系中,用来引用父类的成员。

以下是在Java中使用"super"关键字的几种常见用法:

  1. 访问父类的属性和方法:子类可以使用"super"关键字访问父类的属性和方法 [前提是父类的属性和方法不能是private]。例如:
scala 复制代码
class Parent {
    protected int num;

    public void display() {
        System.out.println("Parent class");
    }
}

class Child extends Parent {
    public void display() {
        super.display();  // 调用父类的方法
        System.out.println("Child class");
        super.num = 10;  // 访问父类的属性
    }
}

在上面的例子中,子类Child继承了父类Parent,并在重写的display方法中使用super.display()来调用父类的方法,同时使用super.num来访问父类的属性。

  1. 调用父类的构造函数:子类可以使用"super"关键字调用父类的构造函数来初始化继承的父类属性。例如:
scala 复制代码
class Parent {
    protected int num;

    public Parent(int num) {
        this.num = num;
    }
}

class Child extends Parent {
    public Child(int num) {
        super(num);  // 调用父类的构造函数
    }
}

在上面的例子中,子类Child继承了父类Parent,并在自己的构造函数中使用super(num)来调用父类的构造函数以完成父类属性的初始化。

  1. 在多重继承中的使用:当存在多个父类时,"super"关键字可用于按照特定的顺序调用父类的构造函数。它确保每个父类只被调用一次。例如:
scala 复制代码
class Parent1 {
    public Parent1() {
        System.out.println("Parent1 class");
    }
}

class Parent2 extends Parent1 {
    public Parent2() {
        System.out.println("Parent2 class");
    }
}

class Child extends Parent2 {
    public Child() {
        super();  // 按照指定顺序调用父类的构造函数
    }
}

在上面的例子中,子类Child继承了父类Parent1Parent2,并在自己的构造函数中使用super()来按照指定顺序调用父类的构造函数。

输出

Parent1 class Parent2 class

总之,"super"关键字是在继承关系中用来访问父类的成员的关键字。它可以用来访问父类的属性、方法和构造函数,并提供了灵活的继承机制。

细节

  1. 调用父类的构造器的好处(分工明确, 父类属性由父类初始化,子类的属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、 直接访问是一样的效果!
  3. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循 就近原则。A->B->C [前提是父类的属性和方法不能是private]

cal() , this.cal()

找方法时,顺序是: (1)先找本类,如果有,则调用

(2)如果没有,则找父类(如果有,并可以调用,则调用)

(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到0bject类

提示:

如果查找方法的过程中,找到了,但是不能访问,则报错 如果查找方法的过程中,没有找到,则提示方法不存在

super.cal() 直接查找父类

super与this比较

方法重写

简单的说:

  1. 方法覆盖(重写)就是子类有一个方法和父类的某个方 法的名称、参数 一样那么我们就说子类的这个方法覆盖了父类的方法
csharp 复制代码
package com.hspedu.override_;

class  Animal {
    public void cry() {
        System.out.println("动物叫唤...");
    }
}

class  Dog extends Animal {
    // 重写了父类的 cry
    public void cry() {
        System.out.println("小狗汪汪叫...");
    }
}

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

            //创建Dog 对象,然后让其 cry
            Dog dog = new Dog();
            dog.cry();//小狗汪汪叫...

            //创建Animal 对象,然后让其 cry
            Animal animal = new Animal();
            animal.cry();//动物叫唤...

    }
}
  1. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类比如父类返回类型是Object ,子类方法返回类型是String
typescript 复制代码
package com.hspedu.override_;

class  Animal {

    public  Object m1() {
        return null;
    }

    public  String m2() {
        return null;
    }
}

class  Dog extends Animal {

    //重写了父类的 m1
    public String m1() {
        return null;
    }
    // Object 不是 String 的子类,所以不能重写
    // public  Object m2() {
    //     return null;
    // }
}

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

    }
}
  1. 子类方法不能缩小父类方法的访问权限

重载与重写的区别

多态

引入问题

scala 复制代码
package com.hspedu.poly;

import com.hspedu.super_.A;

class Food {
    private String name;

    public Food(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

class Fish extends Food {
    public Fish(String name) {
        super(name);
    }
}

class Bone extends Food {
    public Bone(String name) {
        super(name);
    }
}

class Animal {
    private String name;

    public String getName() {
        return name;
    }

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

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

class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}

class Master {
    private String name;

    public String getName() {
        return name;
    }

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

    public Master(String name) {
        this.name = name;
    }

    // 主人给小狗喂食物 骨头
    public void feed(Dog dog, Bone bone) {
        System.out.println("主人" + name + "给小狗" + dog.getName() + "喂" + bone.getName());
    }

    // 主人给小猫喂食物 鱼
    public void feed(Cat cat, Fish fish) {
        System.out.println("主人" + name + "给小猫" + cat.getName() + "喂" + fish.getName());
    }

    // 主人给小狗喂食物 鱼
    public void feed(Dog dog, Fish fish) {
        System.out.println("主人" + name + "给小狗" + dog.getName() + "喂" + fish.getName());
    }

//  如果动物很多,食物也很多,那么方法就会很多,代码冗余

}

public class Poly01 {

    public static void main(String[] args) {
        Master jack = new Master("jack");
        Dog dog = new Dog("小白");
        Bone bone = new Bone("骨头");
        jack.feed(dog, bone);

        // 主人给小猫喂食物 鱼
        Cat cat = new Cat("小花");
        Fish fish = new Fish("鱼");
        jack.feed(cat, fish);

        // 主人给小狗喂食物 鱼
        jack.feed(dog, fish);

    }
}

方法或对象具有多种形态。是面向对象的第3三大特征,多态是建立在封装和继承基础之上的。

  1. 方法的多态 重写 重载
  2. 对象的多态

一个对象的编译类型和运行类型可以不一致

编译类型在定义对象时,就确定了,不能改变 运行类型是可以变化的 编译类型看定义时=号的左边,运行类型看=号的右边

scala 复制代码
package com.hspedu.poly;

import com.hspedu.super_.A;

class Food {
    private String name;

    public Food(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

class Fish extends Food {
    public Fish(String name) {
        super(name);
    }
}

class Bone extends Food {
    public Bone(String name) {
        super(name);
    }
}

class Animal {
    private String name;

    public String getName() {
        return name;
    }

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

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

class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}

class Master {
    private String name;

    public String getName() {
        return name;
    }

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

    public Master(String name) {
        this.name = name;
    }

    // // 主人给小狗喂食物 骨头
    // public void feed(Dog dog, Bone bone) {
    //     System.out.println("主人" + name + "给小狗" + dog.getName() + "喂" + bone.getName());
    // }
    //
    // // 主人给小猫喂食物 鱼
    // public void feed(Cat cat, Fish fish) {
    //     System.out.println("主人" + name + "给小猫" + cat.getName() + "喂" + fish.getName());
    // }
    //
    // // 主人给小狗喂食物 鱼
    // public void feed(Dog dog, Fish fish) {
    //     System.out.println("主人" + name + "给小狗" + dog.getName() + "喂" + fish.getName());
    // }

    //  如果动物很多,食物也很多,那么方法就会很多,代码冗余
    // 解决
    // 使用多态的方式,只需要一个方法即可
    // 1. 使用父类作为形参,这样就可以接收更多的子类对象
    // 2. 使用父类作为返回值,这样就可以返回更多的子类对象
    // Animal 是父类,可以接收Dog,Cat等子类对象
    // Food 是父类,可以接收Bone,Fish等子类对象
    public void feed(Animal animal, Food food) {
        System.out.println("主人" + name + "给" + animal.getName() + "喂" + food.getName());
    }
}

public class Poly01 {

    public static void main(String[] args) {
        Master jack = new Master("jack");
        Dog dog = new Dog("小白");
        Bone bone = new Bone("骨头");
        jack.feed(dog, bone);

        // 主人给小猫喂食物 鱼
        Cat cat = new Cat("小花");
        Fish fish = new Fish("鱼");
        jack.feed(cat, fish);

        // 主人给小狗喂食物 鱼
        jack.feed(dog, fish);

    }
}

细节

向上转型

本质:父类的引用指向了子类的对象 语法:

语法 父类类型 引用名 = new 子类类型(); 特点:编译类型看左边,运行类型看右边。 可以调用父类中的所有成员(需遵守访问权限), 不能调用子类中特有成员; 最终运行效果看子类的具体实现!

java 复制代码
package com.hspedu.poly.detail;

public class PolyDetail {
    public static void main(String[] args) {
        // 向上转型();
        // 1. 父类的引用指向子类的对象
        // 语法 父类类型 引用名 = new 子类类型();
        Animal animal = new Cat();
        Object obj = new Cat();

        // 可以调用父类的方法,不能调用子类 特有的方法 【编译类型决定能不能调用方法】
        // cat.catchMouse(); //错误

        // 最终执行的是子类的eat方法 【运行类型决定执行哪个方法】
        // 规则和方法调用一样
        animal.eat();
    }
}

向下转型

语法:子类类型

引用名= (子类类型) 父类引用: 只能强转父类的引用,不能强转父类的对象 要求父类的引用必须指向的是当前目标类型的对象 当向下转型后,可以调用子类类型中所有的成员

java 复制代码
package com.hspedu.poly.detail;

public class PolyDetail {
    public static void main(String[] args) {
        // 向上转型();
        // 1. 父类的引用指向子类的对象
        // 语法 父类类型 引用名 = new 子类类型();
        Animal animal = new Cat();
        Object obj = new Cat();

        // 可以调用父类的方法,不能调用子类特有的方法 【编译类型决定能不能调用方法】
        // cat.catchMouse(); //错误

        // 最终执行的是子类的eat方法 【运行类型决定执行哪个方法】
        // 规则和方法调用一样
        animal.eat();

        System.out.println("---------------------");
        // 向下转型();
        // 调用 cat.catchMouse();
        // (1) 语法:子类类型 引用名 = (子类类型) 父类引用;

        // 编译类型 Cat 运行类型 Cat
        Cat cat = (Cat) animal;
        cat.catchMouse();
        // (2) 要求父类引用必须指向的是当前目标类型的对象
        // 比如: Animal animal = new Cat(); 本身指向的就是Cat对象
        // Dog dog = (Dog) animal;//编译通过,运行错误

    }
}

属性没有重写的说法 直接看编译类型

instanceof

判断对象的类型是否为XX的类型或XX类型的子类型

语法:引用 instanceof 类型 返回boolean

typescript 复制代码
package com.hspedu.poly.detail;

public class PolyDetail02 {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb instanceof AA);// true
        System.out.println(bb instanceof BB);// true

        System.out.println("--------------");
        // 编译类型 AA 运行类型 BB
        AA aa = new BB();
        System.out.println(aa instanceof AA);// true
        System.out.println(aa instanceof BB);// true

        System.out.println("--------------");
        Object obj = new Object();
        System.out.println(obj instanceof Object);// true
        System.out.println(obj instanceof AA);// false

        System.out.println("--------------");
        String str = "hello";
        System.out.println(str instanceof Object);// true
    }
}

class AA {
}

class BB extends AA {

}

java的动态绑定机制

  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
csharp 复制代码
package com.hspedu.poly.dynamic;

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

        // 编译类型是A,运行类型是B
        A a = new B();
        System.out.println(a.sum()); // 210
        System.out.println(a.sum1()); // 40
    }

}

class A {
    public int i = 20;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    public int sum() {
        return getI() + 10;
    }
    public int sum1() {
        return i + 20;
    }
}

class B extends A {
    public int i = 200;

    public int getI() {
        return i;
    }

    // public int sum() {
    //     return getI() + 20;
    // }

    // public int sum1() {
    //     return i + 20;
    // }
}

多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

csharp 复制代码
package com.hspedu.poly.polyarr;

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

        // 多态数组的使用
        Person[] person = new Person[5];
        person[0] = new Person("jack", 20);
        person[1] = new Student("tom", 18, 100);
        person[2] = new Student("smith", 19, 50);
        person[3] = new Teacher("mary", 30, 5000);
        person[4] = new Teacher("scott", 33, 8000);

        // 遍历数组,调用每个对象的say方法
        for (int i = 0; i < person.length; i++) {
            // 编译类型是Person,运行类型是根据情况而定
            System.out.println(person[i].say());

            // 判断person[i]的运行类型是什么
            if (person[i] instanceof Student) {
                // 向下转型,调用子类特有的方法
                Student student = (Student) person[i];
                student.study();
            } else if (person[i] instanceof Teacher) {
                ((Teacher) person[i]).teach();
            } else {
                System.out.println("不是学生也不是老师");
            }
        }
    }
}

class Person {
    private String name;
    private int age;

    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 String say() {
        return "姓名=" + name + " 年龄=" + age;
    }
}

class Student extends Person {
    private double score;

    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;
    }

    @Override
    public String say() {
        return super.say() + " 成绩=" + score;
    }

    public void study() {
        System.out.println("学生" + getName() + "正在学习...");
    }
}

class Teacher extends Person {
    private double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String say() {
        return super.say() + " 工资=" + salary;
    }

    public void teach() {
        System.out.println("老师" + getName() + "正在授课...");
    }
}

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

java 复制代码
package com.hspedu.poly.polyparam;

public class Test {
    public static void main(String[] args) {
        Worker worker = new Worker("ade", 200);
        Manager manager = new Manager("tom", 200, 10);

        Test test = new Test();
        test.showEmpAnnual(worker);
        test.showEmpAnnual(manager);

        test.testWork(worker);
        test.testWork(manager);
    }

    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());
    }

    public void testWork(Employee e) {
        if (e instanceof Worker) {
            ((Worker) e).work(); //向下转型
        } else if (e instanceof Manager) {
            ((Manager) e).manage(); //向下转型
        }else{
            System.out.println("不做处理");
        }

    }
}

Object类常见API

equals

==是一个比较运算符

1.既可以判断基本类型,又可以判断引用类型 2.如果判断基本类型,判断的是值是否相等。示例: int i= 10; double d=10.0; 3.如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

eauals

指示其他某个对象是否与此对象"相等"。

默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。

typescript 复制代码
package com.hspedu.object_;

public class EqualsExercise01 {
    public static void main(String[] args) {
        Person person1 = new Person("jack", 10, '男');
        Person person2 = new Person("jack", 10, '男');

        // 默认比较地址 重写后比较每一个属性
        System.out.println(person1.equals(person2));
    }
}

class Person {
    private String name;
    private int age;
    private char gender;

    public boolean equals(Object obj) {
        // 判断同一个

        if (this == obj) return true;
        // 类型的判断是否为 Person
        if (obj instanceof Person) {
            //     将类型转换,向下转型
            Person p = (Person) obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }

        return false;
    }

    public Person(String name, int age, char 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 char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }
}

输出

true

hashCode

返回该对象的哈希码值。

  1. 提高具有哈希结构的容器的效率!
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的!,不能完全将哈希值等价于地址。
  5. 后面在集合,中hashCode 如果需要的话,也会重写
java 复制代码
package com.hspedu.object_;

public class HashCode_ {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
        A a3 = a1;
        System.out.println("a1 " + a1.hashCode());
        System.out.println("a3 " + a3.hashCode());
        System.out.println("a2 " + a2.hashCode());

    }
}

class A {

}

toString

  1. 默认返回:全类名+ @ +哈希值的十六进制。

全类名:包名 + 类名

typescript 复制代码
// 源代码
// (1)getCLass().getName()类的全类名(包名+类名)
// (2)Integer.toHexString(hashCode()将对象的hashCode值转成16进制字符串
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
typescript 复制代码
package com.hspedu.object_;

public class ToString_ {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "code", 100);

        System.out.println(monster.toString());
    }
}

class Monster {
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }

    public String getName() {
        return name;
    }

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

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }
}

输出

com.hspedu.object_.Monster@1b6d3586

  1. 子类往往重写toString方法,用于返回对象的属性信息
  2. 当直接输出一个对象时,toString方法会被默认的调用
typescript 复制代码
package com.hspedu.object_;

public class ToString_ {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "code", 100);

        System.out.println(monster.toString());
    }
}

class Monster {
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }

    public String getName() {
        return name;
    }

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

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    @Override
    public String toString() { //一般输出对象的属性,也可以自己定制
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", sal=" + sal +
                '}';
    }
}

输出

Monster{name='小妖怪', job='code', sal=100.0}

finalize()

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  1. 当对象被回收时, 系统自动调用该对象的 finalize 方法。 子类可以重写该方法, 做一些释放资源的操作
  2. 什么时候被回收: 当某个对象没有任何引用时, 则 jvm 就认为这个对象是一个垃圾对象, 就会使用垃圾回收机制来 销毁该对象, 在销毁该对象前, 会先调用 finalize 方法。
  3. 垃圾回收机制的调用, 是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制。
  4. 我们在实际开发中, 几乎不会运用 finalize , 所以更多就是为了应付面试。
相关推荐
小灰灰要减肥14 分钟前
装饰者模式
java
张铁铁是个小胖子25 分钟前
MyBatis学习
java·学习·mybatis
Yan.love1 小时前
开发场景中Java 集合的最佳选择
java·数据结构·链表
椰椰椰耶1 小时前
【文档搜索引擎】搜索模块的完整实现
java·搜索引擎
大G哥1 小时前
java提高正则处理效率
java·开发语言
智慧老师2 小时前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm2 小时前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
V+zmm101342 小时前
基于微信小程序的乡村政务服务系统springboot+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
Oneforlove_twoforjob3 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
xmh-sxh-13143 小时前
常用的缓存技术都有哪些
java