toString() 和 equals() 都是java面试中经常问到的地方.

特别是1条经典问题:  equals 和 "==" 的区别...

本文就从简单介绍下这两个object中的函数.

一. toString()函数

我们首先看1个简单的代码例子:

package Object_kng.Object_baseMethod;class Ob_1{}public class Ob_tostring_1{public static void f(){Ob_1 a = new Ob_1();System.out.printf("%s\n", a.toString());//System.out.printf("%s\n", a); //ok same as below//System.out.println(a); //ok same as below//System.out.printf("%s\n", a.getClass().getName());//System.out.printf("%s\n", a.getClass().toString());}
}

上面的例子定义了1个空的类 Ob_1. 在下面的启动类中首先实例化类Ob_1 的一个对象.  然后调用了a.toString()函数.

上面代码是能正确编译和执行的.

问题是既然Ob_1是1个空类, 里面并没有定义toString()这个函数, 为何还能调用呢.

1.1 java中所有的类都默认继承基类Object.

原因很简单, 因为Ob_1的toString()方法是继承自类Object的.

Java中所有类都是基类Object的派生子孙类.

如果1个类A在定义中没有指明继承哪个类, 那么类A就继承了类Object!

如果类A extends B呢,  因为B肯定也是Object类的子孙类, 所以无论如何类A肯定是基类Object的派生类or子孙类.

1.2 toString()是类Object内定义的一个方法.

打开jdk api文档, 我们可以查到toString()函数是如下介绍的:


toString

public String toString()
返回该对象的字符串表示。通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。
Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:

getClass().getName() + '@' + Integer.toHexString(hashCode())
 
返回:
该对象的字符串表示形式。

根据上面的的介绍, 我们简单地得出如下的信息:

1. toString()函数返回1个String对象.

2. toString()函数可以被重写, 而且jdk介绍中建议我们为所有类重写这个方法.

3. 不重写这个方法的话, 返回值是 getClass().getName() + '@' + Integer.toHexString(hashCode())

4. toString()不是静态方法, 必须实例化对象才能调用.

1.3 不重写 toString()方法, 其返回值解释

例如上面的代码是没有重写toString()方法的.

执行结果如下:

[java] Object_kng.Object_baseMethod.Ob_1@19c8f4

Object_kng.Object_baseMethod.Ob_1@19c8f45 就是a.toString()的返回值.

根据上面的解析, 我们得知

Object_kng.Object_baseMethod.Ob_1 是由  a.getClass().getName() 来的.  这里实际上是类Ob_1的完整类名(包括所在包名).

而19c8f45 是 Integer.toHexString(a.hashCode()) 获得的.

1.3.1 getClass()方法

这里顺便解析一下getClass(),  getClass()方法也是属于基类Object的1个方法, 它返回的是1个对象所属的运行时类.

至于什么是运行时呢, 就是那个对象引用当前状态指向的对象的所属类.  这个跟多态有关.

本人语言表达能力有限, 还是用例子说明的吧:

package Object_kng.Object_baseMethod;class Ob_2{}class Ob_3 extends Ob_2{}public class Ob_getclass_1{public static void f(){Ob_2 a = new Ob_2();System.out.printf("%s\n", a.getClass());a = new Ob_3();System.out.printf("%s\n", a.getClass());}
}

本例子有两个空类, 其中Ob_3 继承自 Ob_2

下面首先实力化1个Ob_2 的对象a, 然后输出a.getClass() (实际上输出a.getClass().toString())

然后把引用a 指向 Ob_3 的另1个对象(多态), 然后再输出1次a.getClass().

执行结果:

[java] class Object_kng.Object_baseMethod.Ob_2
[java] class Object_kng.Object_baseMethod.Ob_3

可以见到两次输出是不同的, 第一次是Ob_2, 第二次是Ob_3.

也就是说运行时类不是指 引用a是属于哪个类, 而是指a 内存指向的对象属于哪个类, 这个需要多态的知识.

至于getClass().getName()就是获取当前运行时类的完整类名了.

1.3.2 Integer.toHexString(a.hashCode())

那么"@"后面那串字符是什么呢, 实际上它是该对象的hash码的16进制表示.

java中,每1个对象都有1个唯一的hashcode, 它跟这个对象的内存地址有关.

1.4 重写方法的1个简单例子:

package Object_kng.Object_baseMethod;class Point_1{private int x;private int y;public Point_1(int x, int y){this.x = x;this.y = y;}public String toString(){return "[" + x +"," + y +"]" ;}
}public class Ob_tostring_2{public static void f(){Point_1 a = new Point_1(2,4);System.out.printf("%s\n", a.toString());System.out.printf("%s\n", a); //ok same as belowSystem.out.println(a); //ok same as below}
}

上面定义了1个关于点的类, 有x, y的两个int类型成员(坐标)

然后重写了toString()方法. 不在输出类名和hashcode, 改成输出x和y的值, 并左一定的格式化.

输出如下:

[java] [2,4]
[java] [2,4]
[java] [2,4]

1.5 重写方法toString()方法的好处:

1. 方便自定义输出对象的信息.

而原来的运行时类名和hashcode有可能不是我们需要的.

2. 由上面例子中, System.out.prinft(), 和 System.out.println()里面的参数只放对象名a的话, 实质上会自动调用a.toString().

这样在coding当中就可以利用这两个函数方便调试.

3. 实际上肯定java的自带的类都重写了toString()方法, 例如java.util.Date 日期这个类

1.6 toString()方法的一些小结.

通过上面的例子, 我相信读者最起码清楚如下1点:

toString()是定义在类Object的1个非静态方法.

这意味这个方法必须需要1个已实例化对象才能调用.

也就是为什么我们定义1个整形变量 int x时, 不能利用x.toString()方法把x转化为字符串.

因为x只是1个基本变量, 而不是1个对象啊.

二.equals()函数

2.1 "==" 符号比较的是指向对象的内存地址

下面开始讲另1个函数equals(), 在这之前我们看一下,另1个判断是否相等的符号"=="

接下来又是1个例子:

package Object_kng.Object_baseMethod;class Ob_4{private int id;private String name;public Ob_4(int id, String name){this.id = id;this.name = name;}
}public class Ob_equal_1{public static void f(){Ob_4 a = new Ob_4(1,"Kent");Ob_4 a2 = new Ob_4(1,"Kent");System.out.println((a == a2));a2 = a;System.out.println((a == a2));}
}

输出结果:

[java] false
[java] true

上例子中定义了类Ob_4, 它有两个成员id 和 name.

下面启动类中,实例化了类Ob_4的两个对象a 和 a2, a和a2拥有相同的id和name成员.

但是用 "==" 来比较a 和 a2的话, 它们两者不相等的, 返回了false

接下来执行a2 = a; 这个语句意思就是把引用a2 指向a所指向的对象, 这样的话a2 和 a就指向Heap区的同一块地址了.

再用"==" 比较a 和 a2的话,就返回了true.

由此可见, 用"=="来比较两个对象的话, 实际是比较对象是否hashcode(), 只有两个对象"完全"相同, 才会返回true.

2.2 equals默认比较的是指向对象的内存地址

实际上equals()也是基类Object的一个方法.

public boolean equals(Object obj)

由此可见:

1. 非静态方法, 必须利用已实例化对象调用.

2. 返回boolean类型

3. 需要1个对象(Object)参数,  由于Object是java里所有其他类的超类, 这里也是运用了多态.

4. 冇final关键字, equals方法可以被重写.

我们将上面的代码改一下, 将" == " 改成 equals:

package Object_kng.Object_baseMethod;class Ob_5{private int id;private String name;public Ob_5(int id, String name){this.id = id;this.name = name;}
}public class Ob_equals_2{public static void f(){Ob_5 a = new Ob_5(1,"Kent");Ob_5 a2 = new Ob_5(1,"Kent");System.out.println((a.equals(a2)));a2 = a;System.out.println((a.equals(a2)));}
}

输出:

[java] false
[java] true

见到输出结果跟上面例子是一样的, 所以基类Object的equals方法也是比较内存的地址.

实际上equals()方法在基类Object的源代码如下:

public boolean equals(Objectobj){

return (this == obj)

}

也就是equals调用了 "==", 基本上是等价的.

2.3 equals 和 " == " 并非比较对象的hashCode().

因为hashCode()跟内存地址有关, 所以也有人讲equals 和 "=="比较的是对象的hashCode().

但这种说法是错误的, 因为hashCode()也是基类Object的方法, 也可以重写.

我们来尝试重写hashCode()方法, 来证明观点:

package Object_kng.Object_baseMethod;class Ob_6{private int id;private String name;public Ob_6(int id, String name){this.id = id;this.name = name;}    public int hashCode(){return 1;}
}public class Ob_equals_3{public static void f(){Ob_6 a = new Ob_6(1,"Kent");Ob_6 a2 = new Ob_6(2,"David");[System.out.println(a);System.out.println(a2);System.out.println((a.equals(a2)));System.out.println((a == a2));a2 = a;System.out.println((a.equals(a2)));System.out.println((a == a2));}
}

输出:

     [java] Object_kng.Object_baseMethod.Ob_6@1[java] Object_kng.Object_baseMethod.Ob_6@1[java] false[java] false[java] true[java] true

上面例子中, 我们在类Ob_6里重写了hashCode的方法, 都返回1.

下面实例化两个对象a 和 a2, 而且两个对象的 id, name成员都不一样.

但是输出对象信息(toString()) 方法, 见到@后面的数字都是1, 也就是它们的hashCode已经相等.

但是后面的比较还是跟之前的例子一样.

也就证明了 equals默认情况下 和 "=="都是比较对象的内存地址, 而非hashCode().

2.4 重写equals()方法.

但是实际生产中, 我需要比较两个不同的对象, 如果两者的所有成员相等, 我们就认为两者相等.

这是我们就可以重写equals()方法, 改变它的机制以适用我们的业务需要.

最常见的改写方法:

package Object_kng.Object_baseMethod;class Ob_7{private int id;private String name;public Ob_7(int id, String name){this.id = id;this.name = name;}    public boolean equals(Object obj){Ob_7 ob;try{ob = (Ob_7)obj;}catch(ClassCastException e){System.out.printf("warning: the object is not belonged to Class Ob_7!!\n");return false;}catch(Exception e){e.printStackTrace();return false;}if (this.id == ob.id && this.name == ob.name){return true;}return false;}
}public class Ob_equals_4{public static void f(){Ob_7 a = new Ob_7(1,"Kent");Ob_7 a2 = new Ob_7(1,"Kent");Ob_7 a3 = new Ob_7(2,"David");System.out.println((a.equals(a2)));System.out.println((a.equals(a3)));System.out.println((a.equals(new Ob_6(1,"kent"))));a2 = a;System.out.println((a.equals(a2)));}
}

这个例子在类Ob_7 中重写了equals方法.

注意,重写这个方法涉及了多态的知识, 必须将形参强制转化为对应的类, 才可以比较成员.

为防止传错了其他类的对象的异常, 最好加上try{}语句来捕捉.

输出结果:

符合我们的需要了, 只要id和name相等, 我们就认为这两个对象相等:

     [java] hello ant, it's the my meeting with ant![java] true[java] false[java] warning: the object is not belonged to Class Ob_7!![java] false[java] true

2.5 Java里有一些自带的类也重写了equals()方法.

最明显的例子就是String类. 下面例子:

package Object_kng.Object_baseMethod;public class Ob_equals_5{public static void f(){String s1 = "abc";String s2 = new String("abc");System.out.println((s1.equals(s2)));System.out.println((s1 == s2));}
}

输出:

     [java] true[java] false

上面定义了两个字符串对象, 值都是"abc"

用equals比较两者是相等的.

用 "==" 则不等.

由此可见String类改写了equals方法, 当然更可能是String的其中1个超类改写了equals方法.

2.5 Java里equals 和 "==" 的区别

以后大家可以这样回答面试官:

1. "==" 可以比较基本类型(如 int 和 boolean)或对象.

2. equals是对象的非静态方法, 只能用于比较对象, 不能比较基本类型.

3. "==" 用于对象时, 比较的是对象内存地址, equals() 通常情况下也是会比较内存地址.

4. 一些类如String() 改写了equals()方法, 比较的是String对象的值, 而不是内存地址.

5. 我们可以改写equals()方法, 根据需要来比较两个对象.

Java 的toString() 和 equals()函数简单介绍相关推荐

  1. Java中Synchronized的用法(简单介绍)

    简单介绍 synchronized是Java中的关键字,是一种同步锁.它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调 ...

  2. Java里的字符串, String类简单介绍.

    String类在java面试中也是1个常见的问题点. 所以也是写在这里方便以后查阅了. 大家都知道c语言里是没有String 字符串这个数据类型的. 只能用字符数组的1个特殊形式来表示一个字符串, 就 ...

  3. java 字符串掐头去尾_Java 8 Stream 简单介绍

    前言 其实,Stream很简单,如果你感到困难的话,不妨换个角度去理解它.任何新东西都不是凭空产生的,而是缘于某种旧东西的升华和改造,不妨把Stream当做高级版的 Iterator,那么将大幅拉近S ...

  4. c++自定义函数简单介绍

    大家好, 今天给大家介绍一下自定义函数. 如有错误请在评论区指出 正文: 1.简单介绍: 函数是一组一起执行一个任务的语句.每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序 ...

  5. 【修真院Java小课堂】Tiles框架简单介绍

    大家好,我是IT修真院上海分院第6期的学员,一枚正直纯洁善良的程序员 今天给大家分享一下,Tiles框架简单介绍 Tiles框架简单介绍 背景介绍 什么是Tiles Tiles 是一种JSP布局框架, ...

  6. JAVA中调用C语言函数简单教程

    背景知识 本地代码 在JAVA中使用其他语言的代码(如C/C++)称为本地代码. 历史原因 JAVA的早期阶段,很多人认为使用C和C++来加速JAVA应用中的关键部分是个好主意,但是实际上,虽然JAV ...

  7. Java里的接口的interface 简单介绍.

    这是写给我自己和我这种初学者看的. Java作为1个强面向对象语言,  基本上所有东西(成员和方法)都是写在class(类)里面的. 但是也存在一种与class平行的东西, 它就是interface ...

  8. java 继承 冒号_java继承(extends)简单介绍

    继承相信很多人都有听说过,继承是面向对象的三个基本特征之一,下面的话就一起通过简单的文章来对java继承进行一下了解吧. 继承和现实生活中的"继承"的相似之处是保留一些父辈的特性, ...

  9. Java基础篇:字符串的简单介绍

    你可能注意到了,在前面关于数据类型和数组的讨论中没有提到字符串或字符串数据类型.这不是因为Java不支持这样一种类型,它支持.只是因为Java的字符串类型,叫做字符串(String),它不是一种简单的 ...

最新文章

  1. 做产品16年,我有9条心得--百度贴吧前负责人
  2. 使用Canvas进行验证码识别
  3. VTK:绘图之ParallelCoordinates
  4. 深度学总结:CNN Decoder, Upsampling的处理
  5. 全球服务器内存芯片市场规模,2020年全球存储芯片行业市场现状分析,中国是全球最主要的消费国「图」...
  6. C#读写config配置文件
  7. 如何用四个简单的步骤加速 LibreOffice
  8. vs2015 中无法链接strcasecmp 和 strncasecmp的解决办法
  9. 重装系统都杀不掉的十大病毒
  10. opencv图像处理学习(五十七)——峰值信噪比和结构相似性
  11. HTTP长连接和短连接
  12. 100+个NLP数据集
  13. 使用aspose.words将Word转为PDF
  14. mysql归档模式_数据库归档模式设置步骤
  15. 概率与数理统计学习总结四---连续型随机变量及其概率密度
  16. MapServer 之 发布网络地图服务(WMS-Web Map Service)
  17. 暑期实训CPU设计(四)
  18. 如何组建和管理测试团队
  19. java自学笔记(day14)归纳总结自:B站狂神说java
  20. 陶哲轩实分析-第13章 度量空间上的连续函数

热门文章

  1. 【Flask项目】项目准备之-创建项目的APP对象
  2. 【Flask】Jinja2之模板中使用url_for
  3. MySQL—相关子查询
  4. [register]-ARMV8系统中通用寄存器和系统寄存器的介绍和总结
  5. 2021-06-29
  6. Mysql的事务事务的特征事务的隔离级别
  7. 2017报计算机热不热,2017年五月份热吗?2017年五月天气热不热?
  8. (60)逆向分析 KiSwapThread —— 找就绪线程和空闲线程
  9. 恶意代码实战Lab13-01分析
  10. 【PHP】关于IPv4、IPv6 的操作函数