源码 toString()方法、重点深刨equals()方法详解.

1、JDk类库的根类: object
1.1、这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的。
    任何一个类默认继承bject,就算没有直接继承,最终也会间接继承。

1.2、object类当中有哪些常用的方法?
    我们去哪里找这些方法呢?

    第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)
    第二种方法:去查阅java的类库的帮助文档。

1.3、什么是API? 
    应用程序编程接口。( Application Program Interface )
    整个JDK的类库就是一个javase的API。
    每一个API都会配置一套API帮助文档。
    SUN公司提前写好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)

    目前为止我们只需要知道这几个方法即可:
        protected Object clone()  // 负责对象克隆
        boolean equals(Object obj)  // 判断两个对象是否相等
        int hashCode()   // 获取对象哈希值的一个方法
        String toString()  // 将对象转换成字符串形式
        protected void finalize()  // 垃圾回收器负责调用的方法

 1、关于object类中的toString()方法

关于object类中的tostring()方法
1、源代码长什么样?
public String tostring () {
    return this.getClass ().getName() + "@" + Integer.toHexString(hashCode())} ;
                源代码上tostring()方法的默认实现是:(默认返回结果)
                类名@对象的内存地址转换为十六进制的形式

2、SUN公司设计toString()方法的目的是什么?
tostring()方法的作用是什么?
    tostring()方法的设计目的是:通过调用这个方法可以将一个"java对象"转换成"字符串表示形式"

3、其实SUN公司开发java语言的时候,建议所有的子类都去重写tostring()方法。
tostring()方法应该是一个简洁的、详实的、易阅读的

 1.1 没重写toString()方法的输出结果如下所示:

public class homework01 {
    public static void main(String[] args) {
        Time time =new Time(1970,1,1);
        // 调用object的toString()方法
        String s =time.toString(); // toString()方法返回一个String类型的结果
        // Time类没有重写toString()方法的输出结果
        System.out.println(s); 
        
        // 输出结果:Time@1b6d3586 //类名@对象内存地址形式
        // 然而我们希望拿到的结果是具体的日期 而不是一串内存地址
        // 因此我们需要对object类当中的toString()方法进行重写
                                
    }

}

class Time { // 默认继承object类
    int year;
    int month;
    int day;

    public Time() {
    }

    public Time(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

输出结果: 

 1.2 重写toStrng()方法后的输出结果为:

public class homework01 {
    public static void main(String[] args) {
        Time time =new Time(1970,1,1);
        String s =time.toString();
        System.out.println(s); // 年:1970 月:1 日:1

        // 重点注意:输出引用的时候,会自动调用该引用的toString()方法
        // System.out.println(time); // 年:1970 月:1 日:1
    }
}
class Time { // 默认继承object类
    int year;
    int month;
    int day;

    public Time() {
    }

    public Time(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    // 重写父类object当中的toString()方法
    // 这个toString()方法怎么写呢?
    // 当然是返回的结果越简洁可读性越强越好
    public String toString(){
        return "年:"+this.year+" 月:"+this.month+" 日:"+this.day;
    }
}

输出结果为:

2、Object类的equals方法

关于object类中的equals方法
1、equals方法的源代码:
    public boolean equals(object obj){
    return (this == obj);}
    以上这个方法是object类的默认实现。

2、SUN公司设计equals方法的目的是什么?
    以后编程的过程当中,都要通过equals方法来判断两个对象是否相等。
    equals方法是判断两个对象是否相等的。

3、我们需要研究一下object类给的这个默认的equals方法够不够用! ! ! !
    在Object类中的equals方法当中,默认采用的是"=="判断两个java对象
    是否相等。而"=="判断的是两个java对象的内存地址,我们应该判断
    两个java对象的内容是否相等。所以老祖宗的equals方法不够用,
    需要子类重写equals


4、判断两个java对象(2008,8,8)是否相等,不能使用"==",因为"=="比较的是两个
对象的内存地址。

 当我们默认继承Object类的equals方法时:(不够用 需要我们重写修改)

public class homework01 {
    public static void main(String[] args) {
        // 判断两个基本数据类型的数据是否相等直接使用”==“就行
        int a =100;
        int b =100;
        // 这个”==“是判断a中保存的100和b中保存的100是否相等
        System.out.println(a==b); // true

        // 判断两个java对象是否相等,我们该怎么办呢? 能直接(对象 == 对象) 吗?
        // 创建一个日期对象是:2008年8月8日
        Time time =new Time(2008,8,8); // 这个对象内存地址为0x1234
        // 再创建一个日期对象是:2008年8月8日
        Time time1 =new Time(2008,8,8); // 这个对象内存地址为0x3698

        // 测试以下,比较两个对象是否相等 能不能使用”==“ ?
        // 测试结果:这里的”==“判断的是:time中保存的对象内存地址和time1中保存的对象内存地址是否相等
        // 判断的不是time、time1对象的(2008,8,8)!
        boolean b1 =time.equals(time1);
        System.out.println(b1); // false 输出结果是false表示比较的是对象内存地址
    }
}

class Time { // 默认继承object类
    int year;
    int month;
    int day;

    public Time() {
    }

    public Time(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    // 默认继承父类的equals方法(没修改返回结果前 可以省略不写)
    // Object的equals方法不够用(因为return (this == obj); 比较的还是两个对象的内存地址)
    public boolean equals(Object obj){ // Object obj =new Time(2008,8,8) [内存地址]; 多态
        return (this == obj); // this指向time的对象 因为是time调用equals方法
    }


}

运行结果:

 当我们对父类object的equals()方法进行重写后:(能完成判断两个对象是否相同的需求)

public class homework01 {
    public static void main(String[] args) {
        Time time =new Time(2008,8,8);
        Time time1 =new Time(2008,8,8);
        // time1 =null; 
        boolean b =time.equals(time1); 传进来的为null时
        System.out.println(b);
        
    }
}

class Time{
    private int year;
    private int day;
    private int month;

    public Time() {
    }

    public Time(int year, int day, int month) {
        this.year = year;
        this.day = day;
        this.month = month;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    // 由上次分析可知父类Object的equals方法不够用 我们可以重写以下
    public boolean equals(Object obj){  // Object obj =new Time();
        // 当年相同,月相同,日相同,表示两个日期相同,两个对象相同
        // 获取第一个日期time的年月日
        int year1 =this.year; // time调用的equals方法 所以this指向time对象
        int month1 =this.month;
        int day1 =this.day;

        /*
        // 获取第二个日期time1的年月日
        // 如下操作会报错 因为传进来的time1类型自动转换成了Object类型
        // 而Object类当中没有year、month、day属性 所以需要向下转型
        int year2 =obj.year;
        int month2 =obj.month;
        int day2 =obj.day;
         */

        if (obj instanceof Time){ // 当传进来的为null时 null instanceof Time(不指向Time类 所以直接返回false)
            Time t =(Time)obj;
            int year2 =t.year;
            int month2 =t.month;
            int day2 =t.day;
            if (year1==year2&&month1==month2&&day1==day2){
                return true;
            }
        }
        return false; // 传进来的obj为null时直接返回false不进行上面的转型
    }
   }

运行结果:

结论: 上述代码先取time的年月日再取time1的年月日,体现出了效率过低

由此对equals()方法进行改良:

public class homework01 {
    public static void main(String[] args) {
        Time time =new Time(2008,8,8);
        Time time1 =new Time(2008,8,8);
        boolean b =time.equals(time1);
        System.out.println(b);

    }
}

class Time {
    int year;
    int day;
    int month;

    public Time() {
    }

    public Time(int year, int day, int month) {
        this.year = year;
        this.day = day;
        this.month = month;
    }

    /*
    // 改良equals方法
    public boolean equals(Object obj){
        if (obj == null){
            return false;
        }
        if (!(obj instanceof Time)){
            return false;
        }
        // 如果this和obj保存的内存地址相同,那么没必要比较了,说明传进来的对象和调用该equals方法的对象是一个对象
        if (this == obj){
            return true; // 直接返回true
        }

        // 程序能执行到这里说明什么?
        // 说明obj不是null,obj是Time类型
        // 不用再判断obj是否instanceof Time ,因为上面已经判断指向了,所以这里可以直接转型
        Time time3 =(Time)obj;
        if (this.day==time3.day && this.month ==time3.month && this.year == time3.year){
            return true;
        }
        return false;

       }
       */

    /*
    // 再次改良
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Time)) {
            return false;
        }
        // 如果this和obj保存的内存地址相同,那么没必要比较了,说明传进来的对象和调用该equals方法的对象是一个对象
        if (this == obj) {
            return true; // 直接返回true
        }

        // 程序能执行到这里说明什么?
        // 说明obj不是null,obj是Time类型
        // 不用再判断obj是否instanceof Time ,因为上面已经判断指向了,所以这里可以直接转型
        Time time3 = (Time) obj;
        return this.day == time3.day && this.month == time3.month && this.year == time3.year;
        // 因为此处有运算符&& 所以当day,month,year条件成立时直接返回true 不成立返回false
    }
     */

    // 还可以再次改良
    public boolean equals(Object obj){
        if (obj == null || !(obj instanceof Time)){
            return false;
        }

        // 如果this和obj保存的内存地址相同,那么没必要比较了,说明传进来的对象和调用该equals方法的对象是一个对象
        if (this == obj){
            return true; // 直接返回true
        }

        // 程序能执行到这里说明什么?
        // 说明obj不是null,obj是Time类型
        // 不用再判断obj是否instanceof Time ,因为上面已经判断指向了,所以这里可以直接转型
        Time time3 =(Time)obj;
        if (this.day==time3.day && this.month ==time3.month && this.year == time3.year){
            return true;
        }
        return false;
}}

3、java语言当中的字符串String(String其实也是一个类)有没有重写toString()方法,有没有重写equals方法

总结:
    1、String类已经重写了equals方法,比较两个字符串不能使用==,必须使用equals方法
    equals是通用的
    2、String类已经重写了toString方法

大结论:
    java中基本数据类型比较是否相等时,使用==
    java中所有的引用数据类型统一使用equals方法来判断是否相等

 代码演示:

public class homework01 {
    public static void main(String[] args) {
        // 大部分情况下,采用这样的方式创建字符串对象
        String s1 ="123";
        String s2 ="hello";

        // 实际上String也是一个类,不属于基本数据类型(属于:引用数据类型)
        // 既然String是一个类,那么一定有构造方法(所以我们也可以进行传参的方式进行赋值)
        // 下面的操作和上面其实一样
        String s3 =new String("test1");
        String s4 =new String("test1");
        // new 两次,两个对象的内存地址,s3保存的内存地址和s4保存的内存地址不同
        // == 判断的是内存地址,不是对象内容
        System.out.println(s3 ==s4); //false 说明判断的是对象的内存地址
        // 那么判断内容是否相等我们需要重写equals方法
        // 由下的操作可以得知String已经重写了equals方法 // s3直接调用自己重写的equals方法
        System.out.println(s3.equals(s4)); //true

        // 判断String有没有重写toString方法
        String s5 =new String("junker");
        // 如果String没有重写toString()方法,输出的结果为:类名@对象内存地址形式
        // 由下面的输出结果可知:String重写了toString方法
        System.out.println(s5.toString()); //junker

    }
}

eg: 重写引用数据类型属性的equals方法案例:

public class homework01 {
    public static void main(String[] args) {
        Student student =new Student(1,"bj");
        Student student1 =new Student(1,"bj");
        boolean b =student.equals(student1);
        System.out.println(b);
    }
}

class Student{
    // 学号
    int no; // 基本数据类型(比较的时候用==)
    // 所在学校
    String school;  // 引用数据类型(比较的时候使用equals方法)


    public Student(int no) {
        this.no = no;
    }

    public Student(int no, String school) {
        this.no = no;
        this.school = school;
    }

    // 重写toString方法

    public String toString() {
        return "学号:"+this.no+" 所在学校:"+this.school;
    }

    // 重写equals方法
    // 需求: 当一个学生的学号相等,并且学校相等时,表示同一个学生


    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student s =(Student)o;
        return this.no==s.no && this.school.equals(s.school);
        // this.school 表示的是student对象的school(当前对象)
        // student.equals(student1)传过来的student1 的school 是String[引用数据类型]
        // 又因为school是String类型,String本身也是一个类 引用数据类型比较必须用equals方法进行比较
        // String类当中已经重写了equals和toString方法了 所以可以直接比较两个school是否相同

    }

}

输出结果:

 equals方法深层次刨析:【重点理解】

import java.util.Objects;

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

        // Address address =new Address("北京","第六大街","11111");
        User user =new User("jun",new Address("北京","第六大街","11111"));
        User user1 =new User("jun",new Address("北京","第六大街","11111"));
        System.out.println(user.equals(user1));


    }
}

class User{
    // 用户名
    String name; // 引用数据类型
    // 用户地址
    Address addr; // 引用数据类型

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

    public User(String name, Address addr) {
        this.name = name;
        this.addr = addr;
    }
    // 重写
    // 重写规则:当一个用户的用户名和家庭住址都相同,表示同一个用户
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return this.name.equals(user.name) && this.addr.equals(user.addr);

        // this.addr 指的是 Address类 但下面的Address类当中没有重写equals 所以还是false 所以要对Address类重写方法

        // 这里用this.name.equals(user.name)而不是this.name ==user.name的原因是:name是String类型
        // String类型是引用数据类型 是一个SUN公司写的类, 里面重写好了equals和toString方法
        // 所以this.name当前对象可以直接调用String类中的equals方法进行两个name的判断

        // 但是Address也是引用数据类型,(不过是我们自己写的一个类) 这个类也默认继承了Object类 也默认继承了Object类中的方法
        // 当中的equals方法 但是没有重写 返回的结果还是两个对象的内存地址而不是两个对象的内容 所以结果还是false
        // 所以 && this.addr.equals(user.addr) 为 false
        // 所以我们需要对Address类当中的equals方法进行重写
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, addr);
    }
}

class Address{
    String city;
    String street;
    String zipcode;

    public Address(String city) {
        this.city = city;
    }

    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }

    // 注意:这里并没有重写equals方法
    // 这里的equals方法判断的是:Address对象和Address对象内容是否相等
    // new Address("北京","第六大街","11111")
    
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return this.city.equals(address.city)&&this.street.equals(address.street)&&this.zipcode.equals(address.zipcode);
    }
}

运行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值