Java对象比较

发布时间:2026/7/9 10:49:06
Java对象比较 在进行对象比较之前我们先来了解两个基础概念1.继承子类继承父类子类可以拿到父类所有非私有属性、方法子类可以重写父类方法修改原有逻辑、扩展功能。下面我们来举一个例子以动物为父类猫为子类父类class Animal(){ String name; int age; public void eat(){ System.out.println(name正在吃饭”); } }子类class Cat extends Animal(){ //子类独有的方法 public void meow(){ SYstem.out,println(name喵喵叫) } //子类重写父类方法 public void eat(){ System.out.println(name在吃鱼); } }2.Object类Java 中所有类直接 / 间接继承 Object任何对象都自带 Object 的 11 个方法equals()、hashCode()、toString()、getClass()、clone()、finalize()等我们来区分一下getClass()与getName()getClass() 是Object 的方法所有对象都能调用返回Class 字节码对象是一个对象不是字符串getName() 是Class 类的方法必须先拿到 Class 对象才能调用返回String 字符串类的完整包名 类名项目getClass()getName()归属哪个类Object 类所有对象自带Class 字节码对象专属方法调用者实例对象this、user、obj 等Class 类型变量getClass () 的返回结果返回值类型Class 对象字节码对象存一整个类的全部信息String 字符串只是一串文字核心作用获取对象运行时真实的类型模板把 Class 对象翻译成文字类名能否直接用 比较可以c1 c2判断是不是同一个类模板只能用.equals()比较字符串示例返回结果Class45678内存地址标识代表 User 类模板com07.User我们用代码看一下差异User user new User(); // 第一步调用getClass()拿到Class字节码对象 Class clazz user.getClass(); System.out.println(clazz); // 输出类似class com07.User // 第二步调用Class对象的getName()得到字符串类名 String className clazz.getName(); System.out.println(className); // 输出com07.UsergetClass () 能干什么Class 对象能力很多拿到 Class 后除了 getName还能做很多反射操作getClass() 拿到的是完整类信息模板。Class c user.getClass(); c.getSimpleName(); // 获取简单类名 User c.getDeclaredFields(); // 获取类所有成员变量name、age c.getDeclaredMethods(); // 获取所有方法equals、hashCode c.newInstance(); // 反射创建对象了解完以上两个基础知识下面我们正式进入对象比较环节1.字符串拼接与字符串比较public class User { String name; int age; public static void main(String[] args) { String str1 hello; String str2 world; String str0 helloworld; String str str1 str2; // 字符串拼接底层会new新String对象存堆不在常量池 System.out.println(str str0);输出false普通数据类型加变量名定义的字符串存在字符串常量池中而字符串拼接得到的新的字符串编译器无法确定常量底层 new String堆内存新对象str 指向堆str0 指向常量池地址不同 → 输出 false2.new String () 对比常量池字符串String str3 new String(hello); String str4 hello; System.out.println(str4 str3);输出falsestr4hello常量池地址new String(hello)强制在堆内存新建对象一个常量池地址、一个堆地址地址不同 → 输出 false。3.intern () 方法System.out.println(str3.intern() str4);我们来了解一下intern的作用去字符串常量池查找和当前字符串内容一样的对象找到返回常量池对象地址没找到把当前字符串存入常量池返回池内地址。输出为true常量池早就有 hellostr4 创建时放进去的str3.intern() → 返回常量池 hello 的地址和 str4 地址完全一样str3.intern() str4 → true4.下面我们以实际User对象来对比User user new User(); user.name 张三; user.age 18; User user1 new User(); user1.name 张三; user1.age 18; System.out.println(user user1); System.out.println(user.equals(user1));输出falsenew User() 两次堆中创建两个完全独立对象地址不同user user1比较地址 → false没有重写 equals使用 Object 原生 equals等价于 → 也是false5.同一个引用指向同一个对象User user2 user; System.out.println(user user2);输出trueuser2 user 只是复制地址两个引用指向堆中同一个 User 对象地址相同 → truehashCode 哈希值打印System.out.println(user.hashCode()); System.out.println(user1.hashCode());未重写 hashCode 时默认根据对象内存地址生成哈希值两个不同对象地址不同哈希值数字不一样。6.接着我们来看一下重写equals与不重写的区别若不重写equals方法默认继承 Object 原生方法只比较地址与相同一般用在只做地址判断不关心内部属性而若两个对象属性完全相同就认为是同一个对象则需要重写equals只对比属性而不是只看内存地址。我们来看一下具体怎样重写equals方法Override public boolean equals(Object obj) { // 如果地址相等证明 两个对象变量名指向的同一个对象 if (this obj) return true; // 将传进来的对象 进行类型的判断 if (this.getClass().getName().equals(obj.getClass().getName())) { // 将传进来的对象变量转成本类类型 向下转型 User otUser (User) obj; // 姓名、年龄全部相同才判定相等 if (this.name.equals(otUser.name) this.age otUser.age) { return true; // 属性相同 } else { return false;// 属性值不一样 } } return false;// 类型不一样 }基本逻辑如下1.if (this obj) return true;快速判断两个引用地址一样必然是同一个对象直接返回 true不用对比属性。2.this.getClass().getName().equals(obj.getClass().getName())判断两个对象是不是同一个类防止父子类之间乱比较类型不同直接返回 false。3.User otUser (User) obj;向下转型Object 无法访问 name/age转成 User 才能拿属性。4.this.name.equals(otUser.name) this.age otUser.age自定义相等规则姓名完全相同、年龄完全相同才算两个用户相等。任意条件不满足返回 false。7.最后我们来看重写hashCode配套方法Override public int hashCode() { return Objects.hash(name, age); }格式Objects.hash(属性1,属性2,...)支持传入多个任意属性自动组合算出哈希值自动处理空值、基础类型不用自己手动移位运算。那么为什么一定要重写hashCode方法呢因为规则a.equals(b) true 时a.hashCode() b.hashCode() 必须成立。若 equals 判断用 nameagehash 却只写 name两个 age 不同、name 相同的对象哈希值一样但 equals 判定不相等破坏契约HashMap/HashSet 逻辑错乱。