Class Object is the root of the class hierarchy.

Object类是所有类的顶级父类,任何一个对象(除了基本类型)都实现了Object类的方法,包括数组。

一、equals

public boolean equals(Object obj)

1、equals与==有啥区别?

我们通常会见到一类问题:==equals有啥区别?每次见到这种问题,都有一种模棱两可的感觉,这次完完整整地总结一波:

从Object类中的equals方法作为切入点,我们看看它的源码:

    public boolean equals(Object obj) {
return (this == obj);
}

事实上,在Object类中,a.equals(b)等价于a==b,即判断两个对象是否具有相同的引用。

==对于基本数据类型,判断数值是否相等,对于引用数据类型,判断地址值是否相等,也就是是否具有相同的引用。

我们发现,如果两个对象具有相同引用,则equals结果相等。但大多数情况下,这样的判断形式没啥意义,在实际情况下,我们往往需要用equals检测对象状态、属性的相等性,往往会在类中重写equals方法。

我们以String来举例,看看String类中的源码:

    //重写注意形参类型必须是Object
public boolean equals(Object anObject) {
//引用相同,必然返回true
if (this == anObject) {
return true;
}
//判断anObject类型是否和String相同
if (anObject instanceof String) {
//anObject向下转型
String anotherString = (String)anObject;
int n = value.length;
//每个位置上字符逐一比较
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

我们可以发现,如果是两个字符串对象,除了判断是否同一引用,还比较字符串值是否相等(至于字符串的地址相关问题,我们之后再做总结)。同样的例子还有Integer等包装类,如果感兴趣,可以查看源码。

2、equals方法的规范

equals方法实现了非空对象引用的等价关系

  1. 自反性(reflexive):对于任何非空引用x,x.equals(x)为true。
  2. 对称性(symmetric):对于任何非空的引用x和y,当且仅当y.equals(x)返回true时,x.equals(y)返回true。
  3. 传递性(transitive):对于任何非空引用x、y和z,如果x.equals(y)返回true,而y.equals(z)返回true,那么x.equals(z)也应该返回true。
  4. 一致性(consistent):如果非空引用x和y的对象没有变化,反复调用x.equals(y)返回相同的结果。
  5. 对于任何非空引用x,x.equals(null)应该返回false。

3、instanceof 和getClass()

如果equals方法判断双方属于同一类,按照上面的规则编写代码其实比较轻松。但是,如果双方不同类,则关于对称性,就需要考虑用哪种方式判定。

我们通过小测试看看instanceofgetClass()的区别:

public class EqualsDemo {
public static void main(String[] args) {
Super aSuper = new Super();
Sub sub = new Sub();
System.out.println(sub.getClass() == aSuper.getClass());//false
System.out.println(sub instanceof Super);//true
}
}
class Super{}
class Sub extends Super{}

可以发现,getClass判断时,子类与父类类型严格不同;而instanceof意味着父类的概念适用于所有子类,相同类。

对于一个要编写equals的类而言:

  • 如果equals的语义在子类中有所改变,则应使用getClass检测。

  • 如果所有的子类都拥有同一的语义,就使用instanceof检测。

4、其他总结

  • 重写equals方法时,注意形参类型一定是Object
  • 数组类型的域可以使用Arrays工具类的静态方法static boolean equals(type[] a,type[] b)判断是否相等。
    int[] a = {1,2,3};
int[] b = {1,2,3};
System.out.println(Arrays.equals(a, b));//true

二、hashCode

public native int hashCode();

Object底层的hashCode方法是native修饰的,不是Java语言编写的,我们要知道:

这个方法将会返回对象的哈希码值,哈希码是整型且没有规律的,也叫做散列码。

我们之前进行过基于JDK1.8的HashMap源码分析,了解到通过哈希函数将键值转化为整型的哈希值,然后通过巧妙的操作,将其映射到数组的各个索引上,利用数组查询快的优势,大大提升了性能。

1、hashCode的规范

  1. 当equals方法被重写时,应该重写hashCode方法,从而保证两个相等的对象拥有相同的哈希码
  2. 程序执行过程中,如果对象的数据没有被修改,则多次调用hashCode方法将返回相同的整数。
  3. 两个不相等的对象可能具有相同的哈希码,但在实现hashCode方法时应避免太多这样的情况出现。

2、String类的hashCode实现

hash函数非常多样,我们以常见的String类的hashCode实现举例:

    public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
  • 字符串底层是由字符数组组成,当我们传入"abc"时,用val[]这个字符数组接收['a','b','c']。
  • 然后将字符转化为int,也就是[97,98,99]。
  • ((97x31)+98)x31+99 = 96354

三、toString

public String toString()

该方法返回对象的字符串表现形式,结果应该是简洁但信息丰富的表示,便于阅读。建议所有子类都重写此方法。

如果不重写的话,Object类中定义的形式会让人很不爽,以下是toString()源码:

    public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

返回字符串 = 对象对应运行时类的名称+“@”+对象哈希码的无符号十六进制表示形式

1、打印对象信息

我们可以试一试:

    public static void main(String[] args) {
Super s = new Super();
System.out.println(s);
}
//输出结果
com.my.objectClass.equals.Super@677327b6

System.out.println(s);意思是标准输出s到打印台上,我们看看具体执行的步骤:

    public void println(Object x) {
//首先调用String类的valueOf方法,获得s的字符串表现形式
String s = String.valueOf(x);
synchronized (this) {
//打印
print(s);
//换行
newLine();
}
}

我们继续看看这个valueOf是怎么一回事:

    public static String valueOf(Object obj) {
//如果为null,则输出"null",非空则调用toString()
return (obj == null) ? "null" : obj.toString();
}

至此,我们可以知道,我们在试图打印对象的时候,都会调用对象的toString方法,我们可以试着重写该方法,则测试的时候能够快速清晰地定位。

2、论优雅打印数组

数组也是一种特殊的类型,我们通过打印可以发现,他的结果比较离谱,看上去比较奇怪,具体规则可以查看官方文档Class类的getName()方法。

    int[] arr = {1,2,3};
System.out.println(arr);//[I@14ae5a5

总之,我们总是希望我们打印出来的是,数组中的数整整齐齐排列着的,对吧。

我们可以利用Arrays工具类的静态方法toString方法,优雅地打印数组:

    public static String toString(int[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]"; StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}

3、自定义toString方法

既然是自定义,那么就没啥严格规定,为了测试数据的时候更加清晰,可以想想适合自己的打印信息的方法。

现在的IDE一般都是支持根据字段,自动生成toString方法的,比如我是用的IDEA,按住AltIns键就可以快速生成。


public class EqualsDemo {
public static void main(String[] args) {
System.out.println(new Person());
}
}
class Person{ String name = "天乔巴夏丶";
int age = 18; @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
} //测试结果
Person{name='天乔巴夏丶', age=18}

五、其他重要方法

至此,已经总结了Object中三个比较重要的方法,其他的诸如

  • 并发编程相关的wait()、notify()、notifyAll()等方法
  • 用于垃圾回收终结的finalize()方法
  • 用于创建对象副本的clone()方法
  • 用于获取对象运行时类信息getClass()的方法

这些方法,我们之后再做总结。


参考资料:《Java核心技术卷Ⅰ》

Java:不得不知的Object类的更多相关文章

  1. Java提高学习之Object类详解(1)

    转自:http://www.importnew.com/10304.html 问:什么是Object类? 答:Object类存储在java.lang包中,是所有java类(Object类除外)的终极父 ...

  2. 代码块和Java的API及Object类

    代码块 局部代码块 特点: 以”{}”划定的代码区域,此时只需要关注作用域的不同即可 方法和类都是以代码块的方式划定边界的 构造代码块 优先于构造方法执行,构造代码块用于执行所有对象均需要的初始化动作 ...

  3. java.lang包【Object类】

    基本描述: (1)Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入: (2)Object类是所有Java类的祖先.每个类都使用 Obje ...

  4. Java 基础 常用API (Object类,String类,StringBuffer类)

    Java API Java 的API(API: Application(应用) Programming(程序) Interface(接口)) Java API就是JDK中提供给我们使用的类,这些类将底 ...

  5. Java基础教程(19)--Object类

      Object类位于类结构树的最顶端,所有的类都是它的直接或间接子类,因此所有的类都继承了Object类的方法,我们可以在需要的时候覆盖这些方法.下面是一些将会在本文中讨论的Object类的方法: ...

  6. [ 转载 ] Java基础10--关于Object类下所有方法的简单解析

    关于Object类下所有方法的简单解析 类Object是类层次结构的根类,是每一个类的父类,所有的对象包括数组,String,Integer等包装类,所以了解Object是很有必要的,话不多说,我们直 ...

  7. java基础学习总结——Object类

    一.Object类介绍

  8. 【代码笔记】Java常识性基础补充(三)——Java的API及Object类、正则表达式、getTime()方法、DateFormat类、Calendar类

    1.0 Java 的API(API: Application(应用) Programming(程序) Interface(接口)) 2.0 Java API就是JDK中提供给我们使用的类,这些类将底层 ...

  9. 1.9(java学习笔记)object类及toString()与equals()方法

    object类 java中objec是所有类公共的父类,一个类只要没有明显的继承某一类,那么它就是继承object类. 例如 class Person {......};和class Person e ...

随机推荐

  1. PAT - A1073

    1073 Scientific Notation (20point(s)) 小数点移动的边界条件 小数点左移 超过数据左边界 小数点右移 未超过数据右边界 超过数据右边界 +0.1E+2 --> ...

  2. 2019.2.21 T2题解

    meet 大概思路就是 , 找出相交的路径 , 判断方向 , 分类讨论.. 假设已经找出了相交路径 ... 若方向相同 , 则找到相交路径上边权的最大值 , 若最大值>出发时间差 , 则可行. ...

  3. git配置多仓库

    git配置多仓库 github , gitee , coding , gitlab , gitlab.company ..... 真TM多 . 真TM多 . 真TM多 . 生成ssh 生成ssh 密钥 ...

  4. yii2 分页

    基于数据提供者 public function actionIndex() { $page = Yii::$app->get('page', 0); $modelClass = $this-&g ...

  5. AcWing 11. 背包问题求方案数

    //g[i,j]表示f[i,j]取最大值的方案数目 //体积最多是j 全部为0,v>=0 //体积恰好为j f[0][0]=0,f[i]=无穷,v>=0 //体积至少是j f[0][0]= ...

  6. 常用UrlEncode编码结果

    空格 ! # $ % + @ : = ? %20 %21 %23 %24 %25 %2B %40 %3A %3D %3F

  7. 从客户端中检测到有潜在危险的request.QueryString

    1. 修改 web.config文件 1.1 <pages validateRequest="false"></pages> (如果存在这个节点 直接加属性 ...

  8. opencv3.2.0+opencv_contrib-3.2.0+vs2015开发配置

    在sift算法实现中,出现了这个问题 出现问题:\vs2015opencv\vs2015\project\mode\mode\sift算法1.cpp(3): fatal error C1083: 无法 ...

  9. HTML的文档设置标记

    1.格式标记 <br/> 强制换行标记 <p> 换段落标记 换段落,由于多个空格和回车在HTML中会被等效为一个空格,所以HTML中要换段落就要用<p>,<p ...

  10. bugku 想蹭网先解开密码

    首先下载文件 然后 创建密码字典:使用命令 crunch 11 11 -t 1391040%%%% -o password.txt 爆破:使用命令 aircrack-ng -a2 所下载文件的地址 - ...