作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)
Object类
相信大家一开始学习Java的时候都知道所有的类都是Object的子类,都默认继承Object,那么对于该类的一些常用方法大家是否有深入的了解呢,今天就给大家详细介绍以下几个常用的方法。
1.clone()
该方法可以在内存空间创建一个新对象,且属性值与原对象一样。
如果使用=把一个对象赋给另一个对象,只是使得两个变量指向同一个内存空间,如果其中一个变量通过set方法修改了属性值,另一个变量访问时,值也会被修改;如果使用clone(),则不会出现上述情况。
public class ObjectDemo implements Cloneable{
private String name;
private Integer age;
public ObjectDemo(String name,Integer age){
this.name = name;
this.age = age;
}
public static void testClone() throws CloneNotSupportedException {
ObjectDemo objectDemo1 = new ObjectDemo("王二",26);
System.out.println("使用普通copy前的age:"+objectDemo1.getAge());
ObjectDemo objectDemo2 = objectDemo1;
objectDemo2.setAge(18);
System.out.println("使用普通copy后的age:"+objectDemo1.getAge());
}
}
只使用=赋值的结果:
使用clone()赋值
public static void testClone() throws CloneNotSupportedException {
ObjectDemo objectDemo1 = new ObjectDemo("王二",26);
System.out.println("使用clone前的age:"+objectDemo1.getAge());
//使用clone()赋值
ObjectDemo objectDemo2 = (ObjectDemo) objectDemo1.clone();
objectDemo2.setAge(18);
System.out.println("使用clone后的age:"+objectDemo1.getAge());
}
结果:通过objectDemo2修改对象的值并不会影响到原始对象的属性值
注意:
1.以上方法适用于成员变量是基本数据类型的,如果是对象类型的,需要重写clone方法。
2.使用clone()方法需要实现Cloneable接口,不然会报CloneNotSupportedException异常。
深拷贝例子
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepProtoType implements Serializable, Cloneable {
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 通过对象的序列化实现 (推荐)
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
2.toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
一般都会重写该方法,返回所有的属性值。一般来讲,该方法用于将对象的属性值打印日志,或者调试时需要知道对象的属性值。在这里也给大家安利一个注解@Data,在类上使用该注解会重写toString方法,且不用手写getter和setter,使得代码更简洁。
3.equals()
public boolean equals(Object obj) {
return (this == obj);
}
比较的是内存地址是否相等,如果当属性值都相等则认为对象相等的话,需要重写equals方法,如String类就重写了equals方法。
注意基本数据类型的包装类,如Integer、Bigdecimal,通过new创建两个不同的对象,值一样时,使用equals的结果为true
public static void testEquals(){
Integer c = new Integer(10);
Integer d = new Integer(10);
System.out.println(c.equals(d));
BigDecimal e = new BigDecimal(0.89);
BigDecimal f = new BigDecimal(0.89);
System.out.println(e.equals(f));
}
结果:
原因是包装类的equals做了处理,比较的是值是否相等而不是内存地址
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
4.hashCode()
hashCode返回一个int整数类型的散列值,该值用于确定对象在“类的散列表”中的位置,散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
hashCode方法的作用也是比较两个对象是否相等,通过比较返回的散列值是否相等来判断,那么既然有了hashCode,为什么还要有equals呢?因为hashCode不是绝对正确的,有可能会出现两个不同对象计算出来的hash值相同,那既然结果不是绝对准确,为啥不直接使用equals,抛弃hash呢?因为hash算法比equals方法简单,效率高,所以在大量且快速的比较运算时,先使用hashCode,若不等,则两个对象肯定不相同了,就不用再equals比较了,如果不等,再使用equals比较。
5.getClass()
返回当前对象的Class对象,通过该对象可以获取所有的类相关的信息。
如getName返回类的全路径信息
getClassLoader返回类加载器等等。