关于Object和Objects
第1章 Object类
1.1概述
java.lang.Object
类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。
如果一个类没有特别指定父类, 那么默认则继承自Object类。例如:
public class MyClass /*extends Object*/ {
// ...
}
根据JDK源代码及Object类的API文档,Object类当中包含的方法有11个。今天我们主要学习其中的2个:
public String toString()
:返回该对象的字符串表示。public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。
1.2 toString方法
方法摘要
public String toString()
:返回该对象的字符串表示。
toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
覆盖重写
如果不希望使用toString方法的默认行为,则可以对它进行覆盖重写。
例如自定义的Person类:
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 省略构造器与Getter Setter
}
在IntelliJ IDEA中,可以点击Code
菜单中的Generate...
,也可以使用快捷键alt+insert
,点击toString()
选项。选择需要包含的成员变量并确定。如下图所示:
小贴士: 在我们直接使用输出语句输出对象名的时候,其实通过该对象调用了其toString()方法。
1.3 equals方法
方法摘要
public boolean equals(Object obj)
:指示其他某个对象是否与此对象“相等”。
调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的。这里的“相同”有默认和自定义两种方式。
默认地址比较
如果没有覆盖重写equals方法,那么Object类中默认进行==
运算符的对象地址比较,只要不是同一个对象,结果必然为false。
对象内容比较
如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。例如:
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 如果对象地址一样,则认为相同
if (this == o)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
if (o == null || getClass() != o.getClass())
return false;
// 转换为当前类型
Person person = (Person) o;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
}
这段代码充分考虑了对象为空、类型一致等问题,但方法内容并不唯一。大多数IDE都可以自动生成equals方法的代码内容。在IntelliJ IDEA中,可以使用Code
菜单中的Generate…
选项,也可以使用快捷键alt+insert
,并选择equals() and hashCode()
进行自动代码生成。如下图所示:
tips:Object类当中的其他方法,今后学习。
案例:
/**
* 学生系统中,添加学生信息,并要求同名且同年龄的则为同一人,不允许添加至系统中。
* 定义一个添加方法,可以将学生添加至容器中。
*/
package com.igeek.javase.code.ch06.object;
import java.util.Objects;
/**
* @Version 1
* @Description TODO
* @Athuor zhangquan
* @Date 2021/9/8 9:45
*/
public class Student extends Object {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
//重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.igeek.javase.code.ch06.object;
import java.util.Arrays;
import java.util.Objects;
/**
* @Version 1
* @Description TODO
* @Athuor zhangquan
* @Date 2021/9/8 13:32
*/
public class StudentTest {
private Student[] stus=new Student[3];
private int index;
public Student[] getStus() {
return stus;
}
/**
* 添加学生信息到该容器里面,当该容器里面有该学生的信息时(name,age都是一样)就添加失败
* @param stu 需要添加的学生对象
* @return 添加是否成功的结果
*/
public boolean add(Student stu){
//判断是否需要数组扩容
if(index >= stus.length){
stus=Arrays.copyOf(stus,stus.length*3/2+1);
}
//只能存放不同的学生
boolean flag=true;
for (int i = 0; i < index; i++) {
/*if(stus[i] != null){
if(stus[i].equals(stu)){
flag = false;
}
}*/
//等于上面这种写法,Objects 类里面的方法对空指针安全(容忍空指针)
if(Objects.equals(stus[i],stu)){
flag = false;
}
}
if(flag) {
stus[index++]=stu;
System.out.println("添加"+stu.getName()+"成功");
}
return flag;
}
@Override
public String toString() {
return "StudentTest{" +
"stus=" + Arrays.toString(stus) +
", index=" + index +
'}';
}
public static void main(String[] args) {
StudentTest ST = new StudentTest();
Student stu1 = new Student("张三",18);
Student stu2 = new Student("张三",19);
Student stu3 = new Student("张三",18);
Student stu5 = new Student("李四",18);
Student stu6 = new Student("王五",18);
Student stu4 = stu1;
//添加学生
ST.add(stu1);
ST.add(stu2);
ST.add(stu3);
ST.add(stu4);
ST.add(stu5);
ST.add(stu6);
System.out.println(ST.toString());
}
}
1.4 clone方法
源码:
@HotSpotIntrinsicCandidateprotected
native Object clone() throws CloneNotSupportedException;
克隆一个具有该类同样信息的对象,地址不同,属性值相同!
克隆 "拷贝"
-
若一个类的对象要使用clone(),则当前类必须实现Cloneable接口否则抛出异CloneNotSupportedException
-
Object中clone()是protected权限,只支持本类中调用自身的clone()
-
若想调用其他类中的clone(),则必须要重写clone(),必须实现Cloneable接口
-
浅克隆 和 深克隆
案例
public class CloneDemo1 implements Cloneable{
//重写方法时,允许将protected权限 --> public权限
//可以将返回值由 Object --> CloneDemo1 避免之后使用每次都要强转
@Override
protected CloneDemo1 clone() throws CloneNotSupportedException {
return (CloneDemo1)(super.clone());
}
}
public class CloneDemo2 /*extends Object*/ implements Cloneable{
public static void main(String[] args) throws CloneNotSupportedException {
CloneDemo2 demo1 = new CloneDemo2();
//Object.clone()返回的是一个Object类的对象
CloneDemo2 demo2 = (CloneDemo2)demo1.clone();
System.out.println("demo1 = "+demo1);
System.out.println("demo2 = "+demo2);
System.out.println("---------------------------");
CloneDemo1 demo3 = new CloneDemo1();
CloneDemo1 demo4 = demo3.clone();
System.out.println("demo3 = "+demo3);
System.out.println("demo4 = "+demo4);
System.out.println("---------------------------");
//Object obj1 = new Object();
//Object obj2 = obj1.clone();
}
}
浅克隆 和 深克隆
发生在类和类之间有关联关系存在时
浅克隆只克隆外层类,关联类被克隆的是地址
深克隆是将关联类也进行克隆
package com.igeek.javase.ch06.object.clone;
public class Person implements Cloneable{
private String name;
private String gender;
private int age;
//一对一关联
private Phone phone;
//重写克隆方法 Ctrl+O
@Override
protected Person clone() throws CloneNotSupportedException {
//浅克隆
//Person person = (Person)super.clone();
//return person;
//深克隆
Person person = (Person)super.clone();
Phone phone = this.phone.clone();
person.setPhone(phone);
return person;
}
public Person() {
}
public Person(String name, String gender, int age, Phone phone) {
this.name = name;
this.gender = gender;
this.age = age;
this.phone = phone;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}
/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return phone
*/
public Phone getPhone() {
return phone;
}
/**
* 设置
* @param phone
*/
public void setPhone(Phone phone) {
this.phone = phone;
}
public String toString() {
return "Person{name = " + name + ", gender = " + gender + ", age = " + age + ", phone = " + phone + "}";
}
}
package com.igeek.javase.ch06.object.clone;
public class Phone implements Cloneable {
private String label;
private double price;
//重写克隆方法
@Override
protected Phone clone() throws CloneNotSupportedException {
return (Phone)super.clone();
}
public Phone() {
}
public Phone(String label, double price) {
this.label = label;
this.price = price;
}
/**
* 获取
* @return label
*/
public String getLabel() {
return label;
}
/**
* 设置
* @param label
*/
public void setLabel(String label) {
this.label = label;
}
/**
* 获取
* @return price
*/
public double getPrice() {
return price;
}
/**
* 设置
* @param price
*/
public void setPrice(double price) {
this.price = price;
}
public String toString() {
return "Phone{label = " + label + ", price = " + price + "}";
}
}
package com.igeek.javase.ch06.object.clone;
public class PersonTest {
public static void main(String[] args) throws CloneNotSupportedException {
Phone phone = new Phone("华为",6000.0);
Person p1 = new Person("张三","男",20, phone);
Person p2 = p1.clone();
System.out.println("---------------浅克隆----------------");
System.out.println("----------克隆后,修改前--------");
System.out.println("p1 = "+p1); //张三 男 20 华为 6000.0
System.out.println("p2 = "+p2); //张三 男 20 华为 6000.0
System.out.println("----------克隆后,修改后--------");
p1.setGender("女");
p1.getPhone().setLabel("苹果");
System.out.println("p1 = "+p1); //张三 女 20 苹果 6000.0
System.out.println("p2 = "+p2); //张三 男 20 苹果 6000.0
System.out.println("---------------深克隆----------------");
Phone phone2 = new Phone("华为",6000.0);
Person p3 = new Person("张三","男",20, phone2);
Person p4 = p3.clone();
System.out.println("----------克隆后,修改前--------");
System.out.println("p3 = "+p3); //张三 男 20 华为 6000.0
System.out.println("p4 = "+p4); //张三 男 20 华为 6000.0
System.out.println("----------克隆后,修改后--------");
p3.setGender("女");
p3.getPhone().setLabel("苹果");
System.out.println("p3 = "+p3); //张三 女 20 苹果 6000.0
System.out.println("p4 = "+p4); //张三 男 20 华为 6000.0
}
}
第2章 Objects类
Objects类是对象工具类,它里面的的方法都是用来操作对象的。
2.1 equals方法
在刚才IDEA自动重写equals代码中,使用到了java.util.Objects
类,那么这个类是什么呢?
在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法如下:
public static boolean equals(Object a, Object b)
:判断两个对象是否相等。
我们可以查看一下源码,学习一下:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
2.2 isNull
static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true。
Student s1 = null;
Student s2 = new Student("蔡徐坤", 22);
// static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true
System.out.println(Objects.isNull(s1)); // true
System.out.println(Objects.isNull(s2)); // false
第3章:补充 equals方法和hashCode方法
源码:
@HotSpotIntrinsicCandidate
public native int hashCode();
ps : native关键字是 java 语言和其他语言的交互的信号,即该功能使用了其他语言实现
hashCode()是一种算法,根据hash值将相同hash值得元素划分在一块区域,类似下图的哈希桶,
当两个对象的equals(Object中未被子类重写的)结果相同时,他们的哈希值一定相同,但是hash值相同,equals
不一定相同,可以是一个桶中的不同元素
在开发中我们经常会重写equals()方法,建议这边也重写一下hashCode()方法
具体参考:
(PS:哈希算法是为了提高查查找效率)