封装、继承和多态

2023年 7月 31日 75.2k 0

封装 encapsulation

  • 属性私有化
  • 提供一个公共的public set 方法赋值
  • 提供一个公共的public get 方法获取值
  • 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() = 1 && age = 2 && name.length() = 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;
        }
    }
    
    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;来声明继承父类即可。

    继承的示意图

    Untitled.png

    Untitled1.png

    案例

    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);
        }
    }
    
    package com.hspedu.extend_.improve;
    
    public class Pupil extends Student {
    
        public void testing() {
            System.out.println("小学生 " + name + " 正在考小学数学");
        }
    
    }
    
    package com.hspedu.extend_.improve;
    
    public class Graduate extends Student {
    
        public void testing() {
            System.out.println("大学生 " + name + " 正在考大学数学");
        }
    
    }
    
    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

    细节

  • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性不能在子类直接访问,要通过父类的公共的方法去访问
  • 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()...");
        }
    }
    
    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);
        }
    }
    
    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

  • 子类必须调用父类的构造器,完成父类的初始化
  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
  • 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;
        }
    }
    
    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);
        }
    }
    
  • 如果希望指定去调用父类的某个构造器,则显式的调用一下super(参数列表)
  • super在使用时,需要放在构造器第一行(super 只能在构造器里面使用)
  • super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  • public Sub() {
        // this("jack", 100);// 调用本类的有参构造器 报错
        super(10);// 调用父类的有参构造器
        System.out.println("Sub()...");
     }
    
  • java所有类都是Object类的子类,Object是所有类的基类。
  • 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
  • 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。 思考:如何让A类继承B类和C类?(A继承B B继承C)
  • 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
  • 继承的本质

    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"关键字的几种常见用法:

  • 访问父类的属性和方法:子类可以使用"super"关键字访问父类的属性和方法 [前提是父类的属性和方法不能是private]。例如:
  • 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来访问父类的属性。

  • 调用父类的构造函数:子类可以使用"super"关键字调用父类的构造函数来初始化继承的父类属性。例如:
  • 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)来调用父类的构造函数以完成父类属性的初始化。

  • 在多重继承中的使用:当存在多个父类时,"super"关键字可用于按照特定的顺序调用父类的构造函数。它确保每个父类只被调用一次。例如:
  • 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"关键字是在继承关系中用来访问父类的成员的关键字。它可以用来访问父类的属性、方法和构造函数,并提供了灵活的继承机制。

    细节

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

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

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

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

    提示:

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

    super.cal() 直接查找父类

    super与this比较

    Untitled3.png

    方法重写

    简单的说:

  • 方法覆盖(重写)就是子类有一个方法和父类的某个方 法的名称、参数 一样那么我们就说子类的这个方法覆盖了父类的方法
  • 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();//动物叫唤...
    
        }
    }
    
  • 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类比如父类返回类型是Object ,子类方法返回类型是String
  • 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) {
    
        }
    }
    
  • 子类方法不能缩小父类方法的访问权限
  • Untitled4.png

    重载与重写的区别

    Untitled5.png

    多态

    引入问题

    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三大特征,多态是建立在封装和继承基础之上的。

  • 方法的多态 重写 重载
  • 对象的多态
  • 一个对象的编译类型和运行类型可以不一致

    Untitled6.png

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

    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 子类类型(); 特点:编译类型看左边,运行类型看右边。 可以调用父类中的所有成员(需遵守访问权限), 不能调用子类中特有成员; 最终运行效果看子类的具体实现!

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

    向下转型

    语法:子类类型

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

    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

    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的动态绑定机制

    • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
    • 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
    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;
        // }
    }
    

    多态数组

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

    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() + "正在授课...");
        }
    }
    

    多态参数

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

    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

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

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

    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

    返回该对象的哈希码值。

  • 提高具有哈希结构的容器的效率!
  • 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  • 两个引用,如果指向的是不同对象,则哈希值是不一样的
  • 哈希值主要根据地址号来的!,不能完全将哈希值等价于地址。
  • 后面在集合,中hashCode 如果需要的话,也会重写
  • 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)getCLass().getName()类的全类名(包名+类名)
    // (2)Integer.toHexString(hashCode()将对象的hashCode值转成16进制字符串
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
    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

  • 子类往往重写toString方法,用于返回对象的属性信息
  • 当直接输出一个对象时,toString方法会被默认的调用
  • 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()

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

  • 当对象被回收时, 系统自动调用该对象的 finalize 方法。 子类可以重写该方法, 做一些释放资源的操作
  • 什么时候被回收: 当某个对象没有任何引用时, 则 jvm 就认为这个对象是一个垃圾对象, 就会使用垃圾回收机制来 销毁该对象, 在销毁该对象前, 会先调用 finalize 方法。
  • 垃圾回收机制的调用, 是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制。
  • 我们在实际开发中, 几乎不会运用 finalize , 所以更多就是为了应付面试。
  • 相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论