Object是所有Java类的父类,一个类若没有明确指出父类,Object就默认为此类的父类
Objects是工具类,用于实现基本方法
不同源码的Object可能不同,如安卓原源码里有其他方法,以下按照空格分为:Natives()、getClass()、hashCode()和equals()、clone()、toString()、notify()和wait()、finalize()
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
public final native Class> getClass();
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() + " doesn't implement Cloneable");
}
return internalClone();
}
private native Object internalClone();
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public final native void notify();
public final native void notifyAll();
public final void wait() throws InterruptedException {
wait(0);
}
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final native void wait(long timeout) throws InterruptedException;
protected void finalize() throws Throwable { }
}
Tips:
以下按照空格分为:私有构造、equals()和deepEquals()、hashCode()和hash()、toString()、compare()、requireNonNull()、isNull()和nonNull()
public final class Objects {
private Objects() {
throw new AssertionError("No java.util.Objects instances for you!");
}
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
public static boolean deepEquals(Object a, Object b) {
if (a == b)
return true;
else if (a == null || b == null)
return false;
else
return Arrays.deepEquals0(a, b);
}
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
public static String toString(Object o) {
return String.valueOf(o);
}
public static String toString(Object o, String nullDefault) {
return (o != null) ? o.toString() : nullDefault;
}
public static int compare(T a, T b, Comparator super T> c) {
return (a == b) ? 0 : c.compare(a, b);
}
public static T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
public static T requireNonNull(T obj, Supplier messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier.get());
return obj;
}
public static boolean isNull(Object obj) {
return obj == null;
}
public static boolean nonNull(Object obj) {
return obj != null;
}
Tips:
所有Object类型的变量可以引用除基本数据类型外的各种对象
class Person {
}
但要对其操作还得转为对应的数据类型
Object o = new Person();
Person p = (Person) o;
Object类型变量还可引用所有数组
Object o = new Object();
Person[] people = new Person[10];
o = people;
o = new int[10];
以String中的equals()为例,当两个字符串地址相等时,肯定是同一字符串,当地址不等时,若字符串中每个字符相等也认为是同一字符串,故比较规则由设计者给出(若设计者想在一半相等就返回true也是可以的)
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = length();
if (n == anotherString.length()) {
int i = 0;
while (n-- != 0) {
if (charAt(i) != anotherString.charAt(i))
return false;
i++;
}
return true;
}
}
return false;
}
故Object.equals()经常用来判断两个对象状态是否相等(是否有相同的域)
对于非空的x、y、z,重写Object.equals()应该保证:
在effective java中给出了编写Object.equals()的规则
标准写法如下,(obj == null)判断不是必须的,当obj=null,在instanceof判断时也会返回false
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
/*if (obj == null) {
return false;
}*/
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name);
}
}
为什么是instanceof判断而不是getClass()判断?
如下给出一个使用getClass()判断的反例
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name);
}
}
class Man extends Person {
public Man(String name) {
super(name);
}
}
针对上面的继承结构,若想判断name="song"的人是否存在于List,如下代码输出true和false,显然后者的false并不符合实际要求
List list = new ArrayList<>();
list.add(new Person("song"));
System.out.println(list.contains(new Person("song")));
System.out.println(list.contains(new Man("song")));
此外继承和equals()的性质是相违背的,对于如下继承结构
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name);
}
}
class Man extends Person {
private int age;
public Man(String name, int age) {
super(name);
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Man)) {
return false;
}
Man other = (Man) obj;
return super.equals(obj) && this.age == other.age;
}
}
如下代码输出true、false,表示Person可Man比较,而Man跟Person比较始终为false,明显违背对称性
System.out.println(new Person("song").equals(new Man("song", 18)));
System.out.println(new Man("song", 18).equals(new Person("song")));
通过修改Man的equals进行混合比较可满足上述对称性,如下
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name);
}
}
class Man extends Person {
private int age;
public Man(String name, int age) {
super(name);
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
if (!(obj instanceof Man)) {
return obj.equals(this);
}
Man other = (Man) obj;
return super.equals(obj) && this.age == other.age;
}
}
但是修改后的代码不满足对称性,如下输出true、true、false,因为前两个比较只考虑了name属性,而最后的比较多了age属性,同时上面的代码可能会导致无限递归调用(两个子类比较时都调用对方的equals)
Man m1 = new Man("song", 18);
Person p1=new Person("song");
Man m2 = new Man("song", 19);
System.out.println(m1.equals(p1));
System.out.println(p1.equals(m2));
System.out.println(m1.equals(m2));
故无法在继承时增加比较域又保持equals的性质,而解决办法是把继承改为组合,并在子类中提供一个返回父类的公有视图(修改后将不再允许父类和子类比较)
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name);
}
}
class Man {
private Person person;
private int age;
public Man(String name, int age) {
person = new Person(name);
this.age = age;
}
public Person asPerson() {
return person;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Man)) {
return false;
}
Man other = (Man) obj;
return Objects.equals(this.person, other.person) && this.age == other.age;
}
}
Tips:
重写equals()方法就必须重写hashcode()方法,需要保证
先看不重写hashcode()会怎样,对于如下代码
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name);
}
}
如下将一个实例存储到hashmap再取出,打印为null,原因是
HashMap map = new HashMap<>();
map.put(new Person("song"), 1);
System.out.println(map.get(new Person("song")));
effective java给出的编写规则为:将第一个关键域的hashCode()作为result,其乘以31再累加其他关键域的hashCode(),其他关键域的计算方法如下:
class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return this.name.equals(other.name) && this.age == other.age;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + Integer.hashCode(age);
return result;
}
}
若觉得上面太麻烦,可通过Objects.hash()生成hashcode,但其性能不如上面
class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
return Objects.equals(this.name, other.name) && this.age == other.age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
若一个类经常用于散列,可定义一个域将hashcode缓存起来(可选择创建时初始化或延迟初始化)
赋值 = 对于基本数据类型和不可变对象类型(如String)是拷贝数据,对于可变对象则是拷贝引用
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
对于如下代码,b的改变不会影响a,但p2的改变会影响p1
int a = 1;
int b = a;
b = 2;
Person p1 = new Person("tom", 1);
Person p2 = p1;
p2.setName("john");
要想获得一个与调用者一样的对象实例,则需要clone方法,如下p2的修改不会再影响p1
Person p1 = new Person("tom", 1);
Person p2 = (Person) p1.clone();
p2.setName("john");
上述代码是运行不了的,因为clone()是protect方法,想要调用须实现Cloneable接口并重写clone()
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
但如果类包含可变对象域,super.clone()会让两个实例中对象域的引用相等,如Person新增Clothe域
class Person {
private String name;
private int age;
private Clothe clothe;
public Person(String name, int age, Clothe clothe) {
this.name = name;
this.age = age;
this.clothe = clothe;
}
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;
}
public Clothe getClothe() {
return clothe;
}
public void setClothe(Clothe clothe) {
this.clothe = clothe;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Clothe {
private String Coat;
public Clothe(String coat) {
Coat = coat;
}
public void setCoat(String coat) {
Coat = coat;
}
}
对于以下代码,p1、p2的Clothe域引用同一个对象,对p2的Clothe域修改会影响p1
Person p1 = new Person("tom", 1, new Clothe("裤子"));
Person p2 = (Person) p1.clone();
p2.getClothe().setCoat("裙子");
故还需要对类内部可变对象域进行拷贝,让Clothe也实现Cloneable重写clone()
class Clothe implements Cloneable{
private String Coat;
public Clothe(String coat) {
Coat = coat;
}
public void setCoat(String coat) {
Coat = coat;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
然后在Person的clone()内部完成对可变实例域(Clothe)的拷贝,即递归拷贝
protected Object clone() throws CloneNotSupportedException {
Person tempPerson= (Person) super.clone();
tempPerson.clothe= (Clothe) clothe.clone();
return tempPerson;
}
现在p1、p2的Person引用的对象不一样,其内部Clothe域引用的对象也不一样
Person p1 = new Person("tom", 1, new Clothe("裤子"));
Person p2 = (Person) p1.clone();
p2.getClothe().setCoat("裙子");
Tips:
默认打印类名@十六进制Hashcode,通常重写为对域的遍历打印(会自动生成)
class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
打印结果为
Person{name='null', age=0}