JAVA学习笔记(十):java动态绑定机制、super和this比较、方法重写和重载、Object类(equals、hashCode、toString、finalize)

java动态绑定机制⭐⭐⭐⭐⭐

① 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定;

② 当调用对象方法中的属性时,没有动态绑定机制,哪里声明,哪里使用

③ 当调用类成员变量:全局变量属性时,该**(全局)属性会和编译类型绑定**

public class test{
    public static void main(String[] args){
        A a = new B();//a的编译类型是 A,运行类型是B
        System.out.println(a.i);
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }

}
class A{  //父类
    public int i = 10;

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

    public int sum1(){
        return i + 20;
    }
}
class B extends A{  //子类
    public int i = 20;

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

    public int sum2(){
        return i + 20;
    }
}

此时结果为10(全局属性,与编译类型A绑定,调用的A的属性),30(与运行类型B绑定),30(与运行类型B绑定,B没有,向上查找A)。

super关键字

1、基本介绍

super代表父类的引用,用于访问父类的属性、方法、构造器。

2、基本语法

public class A {
    //父类的四个属性
    public int n1;
    protected int n2;
    int n3;
    private int n4;

    //父类的三个构造器
    public A() {}
    public A(String name) {}
    public A(String name,int age) {}

    //父类的四个方法
    public void test100() {}
    protected void test200() {}
    void test300() {}
    private void test400() {}
}

① 访问父类的属性,但不能访问父类的private属性;

例如:super.属性名;

public class B extends A{
    public void hi(){
        System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
    }
}

② 访问父类的方法,不能访问父类的private方法;

例如:super.方法名(参数列表);

    public void hi2(){
        super.test100();
        super.test200();
        super.test300();
    }

③ 访问父类的构造器;

例如:super(参数列表);

只能放在构造器的第一句,只能出现一句。

public class B extends A{
    private String name;

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

    public B(String name, String name1) {
        super(name);
        this.name = name1;
    }

    public B(String name, int age, String name1) {
        super(name, age);
        this.name = name1;
    }
}

3、super的优点

(1)分工明确,父类属性由父类初始化,子类的属性由子类初始化);

public PC(String cpu, int memory, int disk, String brand) {
    super(cpu, memory, disk);//父类的属性调用父类构造器进行初始化
    this.brand; //子类的特有属性由子类构造器初始化
}

(2)当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super

如果没有重名,使用super、this、直接访问是一样的效果(从子类逐层向上【父类】查找)!

现在有三个类,Son->Father->Grandpa

在Son类中调用hi()方法,顺序是:

1. 先找本类(Son)。如果有,并且可以调用,则调用。如果没有,则找父类。

2. 找父类(Father)。如果有,并且可以调用,则调用。如果没有,则找父类(Grandpa)。

3. ...一直往上找,直到↓
    - 如果找到这个方法,但不能访问,则报错cannot access
    - 如果找完了object都没找到,则提示方法不存在。

(3)super的作用是跳过子类,它的调用不限于访问直接父类

如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个基类(上级类)中都有同名的成员

使用super访问遵循就近原则。A(子类) -> B(父类) -> C(爷爷类)(先找父类,再找爷爷类,若都没有再找子类);

现在有三个类,Son->Father->Grandpa

1. Son中调用super.hi();

2. Father中没有hi();

3. 则看Grandpa,Grandpa中有hi();

4. 则上面的super.hi()调用的是Grandpa类中的hi();


====访问属性也是同样的道理====

4、super和this比较

区别点thissuper
访问属性本类中开始查找父类开始查找
调用方法本类中开始查找父类开始查找
调用构造器this(形参列表)必须放在构造器的首行super(形参列表)必须放在子类构造器的首行
含义表示当前对象子类中访问父类对象

方法重写/覆盖(ovrride)

1、基本介绍

简单来说就是子类的方法和父类的方法一摸一样,那么就说子类的这个方法覆盖了父类的方法。

C->B->A,C中hi()和A中的hi()一样

那么就说C的hi()覆盖了A的hi()

方法覆盖会导致调用的时候,会只调用子类的hi()方法,不会调用父类的hi()方法

2、注意事项和使用细节

(1)子类的方法的形参列表方法名称,要和父类方法的形参列表,方法名称完全一样

(2)子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类

比如:父类返回类型是Object,子类方法返回类型是String

class A { 
    public object getInfo(){}
}
class B extends A{
    public String getInfo(){}
}

(3)子类不能缩小父类方法的访问权限,但可以扩大。
public > protected > 默认 > private

class A { 
    protected void getInfo(){}
}
class B extends A{
    private void getInfo(){} //报错
    public void getInfo(){}  //通过
}

3、方法重写和重载比较

在这里插入图片描述
发生范围:重载在本类或者父子类中,重写必须发生在父子类中
在这里插入图片描述
在这里插入图片描述

方法能够在同一个类中或者在一个子类中被重载。根据实参的类型,判断到底是调用哪一个方法

public class Test {
    public static void main(String[] args) {
        Child c = new Child();
        c.fun(1); //1father
        c.fun(1.0); //1.0child
    }
}



class Child extends Father{
    public Child(){

    }

    public void fun(double n){
        System.out.println(n+"child");
    }
    public void fun1(double n) {
        System.out.println("b");
    }
}
public class Father {
    public Father() {
    }

    public void fun(int n){
        System.out.println(n+"father");
    }
}

Object类详解

Object类中主要的方法有:

  • equals(Object obj)

  • finalize()

  • getClass()

  • hashCode()

  • toString()

1、equals方法

(1)==和equals的对比

==是一个比较运算符:

① ==:既可以判断基本类型,又可以判断引用类型;

  • 如果判断基本类型,判断的是值是否相等。例如:int i = 10; double d = 10.0;true

  • 如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象;

④ equals():是Object类中的方法,只能判断引用类型;

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

(2)equals()练习
① 重写equals方法

public class Test {
    public static void main(String[] args){
        Person a = new Person("jack",20,'男');
        Person b = new Person("jack",20,'男');
        System.out.println(a.equals(b));
    }
}

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

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

    @Override
    public boolean equals(Object obj){
        if(obj == this){
            return true;
        }
        if(obj instanceof Person){
            Person per = (Person) obj;
            return this.name.equals(per.name) && this.age == per.age && this.gender == per.gender;
        }
        return false;
    }
}

② 判断对错

        int it = 65;
        float fl = 65.0f;
        System.out.println(it == fl); //true
        char ch1 = 'A';
        char ch2 = 12;
        System.out.println(it == ch1); //true
        System.out.println(12 == ch2); //true
        
        String str1 = new String("hello"); 
        String str2 = new String("hello");
        System.out.println(str1 == str2); //false
        System.out.println(str1.equals(str2)); //true
        
        Person p = new Person("jack");
        System.out.println("jack" == p); //编译错误

//总结:
//1. 判断基本数据类型时,==只用于判断值是否相等
//2. 判断引用数据类型时
//==用于判断地址,类型不相同时,编译不通过
//equals看是否重写,重写的是判断内容是否相等。默认String和基本类型包装类已经重写
//没有重写的,比如自定义类。则是判断地址
 

2、hashCode方法

hashCode值是jvm用随机数方式分配给各个对象的

  1. 两个引用,如果指向的是同一个对象,哈希值一定相等
  2. 两个引用,如果指向的是不同对象,则哈希值可能相同(概率小,但有可能发生)
  3. 哈希值不等价于地址,是通过地址经过某种转化而来
  4. hashCode在后面的集合中也需要重写

案例:

public class Test {
    public static void main(String[] args){
        AA aa = new AA();
        AA aa2 = new AA();
        AA aa3 = aa;
        System.out.println("aa.hashCode()=" + aa.hashCode());
        System.out.println("aa2.hashCode()=" + aa2.hashCode());
        System.out.println("aa3.hashCode()=" + aa3.hashCode());
    }
}
//输出:
aa.hashCode()=460141958
aa2.hashCode()=1163157884
aa3.hashCode()=460141958

3、toString方法

用于返回对象的属性信息

  1. 默认返回

    全类名 + @ + 哈希值的十六进制

  2. 重写toString

public class Test {
    public static void main(String[] args){
        Monster monster = new Monster("li", "work", 2000);
        System.out.println(monster.toString());
    }
}

class Monster {
    private String name;
    private String job;
    private int age;

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

    @Override
    public String toString(){
        return "name = " + this.name +
                "\njob = " + this.job +
                "\nage = " + this.age;
    }
}
//输出
name = li
job = work
age = 2000
  1. 当直接输出一个对象时,toString方法会被默认的调用
    比如:System.out.println(monster)
    等价于:System.out.println(monster.toString());

4、finalize方法

垃圾回收相关

  1. 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
  2. 什么时候被回收:某个对象没有任何引用,该对象被视为垃圾,会被回收
  3. 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),如果想立即运行垃圾回收器,可以通过System.gc()主动调用垃圾回收器

tips:实际开发中几乎不会用finalize,但作为知识点用来应付面试()

public class Test {
    public static void main(String[] args){
        Car bmw = new Car("宝马");
        //如果程序员不重写finalize,则没什么区别
        bmw = null;
        //这时垃圾回收器视bmw为垃圾,但不一定马上销毁该对象
        System.gc();
        //主动触发垃圾回收
        System.out.println("程序退出");
    }
}
class Car {
    private String name;

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

    @Override
    protected void finalize() throws Throwable {
        System.out.println("我们销毁 汽车" + name);
        System.out.println("释放了某些资源");
    }
}
//输出
程序退出
我们销毁 汽车宝马
释放了某些资源

重写equals方法必须重写hashcode方法

重复元素问题:一个集合中一个地址上,有相同的元素或者不同地址上有相同的元素

hash冲突:存储两个对象时,它们的hash值相同(需要存放到同一个地方)

  • 如果只重写hashcode方法:发生hash冲突时,无法去除重复元素(即使两个对象内容相同,但因为地址不同,也会判断为不同对象,进而存储大量的重复对象)
  • 如果只重写equals方法:那么相同的两个元素会出现在两个不同的地址上

不重写equals方法,则判断的是两个对象的地址是否相同。
重写equals方法,判断两个对象的内容是否相同。

只重写equals方法不重写hashcode方法,则equals判断为相同的两个对象,其hashcode值会不同(因为内容相同但地址不同)
此时这个对象类无法和集合类(hashMap、hashSet)一起工作
(因为hashMap、hashSet的工作机制是:

求取该对象的hashCode值
如果在列表key中没有找到对应的hashCode,那么直接添加该对象

如果找到了该hashCode值,再使用equals比较
如果equals比较内容相同,那么该对象覆盖到上面
如果equals比较内容不相同,那么该对象重新计算hashCode值,并散列到其他地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值