一. Object简介
1. 简介
二. 常用方法
1. clone()方法
1.1 clone方法作用
1.2 clone源码分析
1.3 Java的浅克隆与深克隆
2. hashCode()方法
2.1 简介
2.2 hash值
2.3 案例
3. equals(obj)方法
3.1 equals简介
3.2 使用原则
3.3 基本特性
3.4 案例
4. getClass()方法
4.1 简介
4.2 案例
5. toString()方法
5.1 简介
5.2 案例
6. wait()、wait(long)、wait(long,int)、notify()、notifyAll()方法
7. finalize()方法
7.1 简介
7.2 案例
三. 结语
一. Object简介 1. 简介在了解Object中的常用方法之前,我们先来看看Object类的源码,如下所示:
/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
* @author unascribed
* @see java.lang.Class
* @since JDK1.0
*/
public class Object {
......
从Object类的源码注释可以知道,Object类是Java中所有类的父类,相当于是Java中的”万类之王“,处于最顶层。 所以在Java中,所有的类默认都继承自Object类。同时Java中的所有类对象,包括数组,也都要实现这个类中的方法。
所以,Object是Java中所有类的父类、超类、基类,位于继承树的最顶层。可以说,任何一个没有显式地继承别的父类的类,都会直接继承Object,否则就是间接地继承Object,并且任何一个类也都会享有Object提供的方法。又因为Object是所有类的父类,所以基于多态的特性,该类可以用来代表任何一个类,允许把任何类型的对象赋给 Object类型的变量,也可以作为方法的参数、方法的返回值。
二. 常用方法在Object类中,自带了几个常用的方法,这几个方法任意的子类都会继承,如下图所示:
根据上图,小编把Object类中的常用方法归纳为这么几种:
构造方法;
hashCode()和equals()方法用来判断对象是否相同;
wait()、wait(long)、wait(long,int)、notify()、notifyAll();
toString()和getClass();
clone();
finalize()
接下来小编就给各位介绍Object类中的几个常用方法,分别说一下这些方法的功能作用。
1. clone()方法 1.1 clone方法作用Object中有两个protected修饰的方法,其中一个就是clone()方法,并且该方法还是一个native方法。clone()方法用于创建复制出当前类对象的一个副本,得到一个复制对象。 所谓的复制对象,首先会分配一个和源对象(调用clone方法的对象)同样大小的内存空间,在这个内存空间中会创建出一个新对象;然后再使用源对象中对应的各个成员,填充新对象的成员,填充完成之后,clone方法会创建返回一个新的相同对象供外部引用。
1.2 clone源码分析我们再看看clone()方法源码上的注释,如下图所示:
从这段注释中,我们可以了解到:
1.3 Java的浅克隆与深克隆以x为蓝本创建出的副本,与x对象并不相同,这保证了克隆出的对象拥有单独的内存空间;
源对象和克隆的新对象字节码相同,它们具有相同的类类型,但这并不是强制性的;
源对象和克隆的新对象利用equals()方法比较时是相同的,但这也不是强制性的。
因为每个类的直接或间接父类都是Object,因此它们都含有clone()方法,但因该方法是protected修饰的,所以我们不能在类外访问该方法。但如果我们要对一个对象进行复制,可以对clone方法进行复写,而Java中提供了两种不同的克隆方式,浅克隆(ShallowClone) 和深克隆(DeepClone)。
1.3.1 浅克隆在浅克隆中,如果源对象的成员变量是值类型,则复制一份给克隆对象;如果源对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说源对象和克隆对象的成员变量指向相同的内存地址。
简单说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。我们可以用下图对浅克隆进行展示:
在Java语言中,通过实现Cloneable接口,默认覆盖Object类的clone()方法就可以实现浅克隆。
1.3.2 深克隆在深克隆中,无论源对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,即深克隆将源对象的所有引用对象也复制一份给克隆对象。
简单来说,在深克隆中,除了对象本身被复制外,对象中包含的所有成员变量也将复制。我们可以用下图对深克隆进行展示:
在Java语言中,如果需要实现深克隆,可以通过实现Cloneable接口,自定义覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。 如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。
2. hashCode()方法 2.1 简介hashCode()是Object中的一个native方法,也是所有类都拥有的一个方法,主要是返回每个对象十进制的hash值。hash值是由hash算法根据对象的地址、对象中的字符串、数字等计算出来的。一般情况下,相同的对象应会返回相同的哈希吗值,不同的对象会返回不同的哈希码值。
2.2 hash值哈希值是根据地址值换算出来的一个值,并不是实际的地址值,常用于哈希表中,如HashMap、HashTable、HashSet。关于哈希值,不同的JDK算法其实是不一样的:
2.3 案例Java 6、7 中会默认返回一个随机数;
Java 8 中默认通过和当前线程有关的一个随机数 + 三个确定值,运用Marsaglia’s xorshift scheme的随机数算法得到的一个随机数。
Dog dog01=new Dog("乔治01");
Dog dog02=new Dog("乔治02");
//两个对象的hash值是不同的
System.out.println("dog01的hash值 "+dog01.hashCode());
System.out.println("dog02的hash值 "+dog02.hashCode());
以上两个对象的hash值是不同的,表示这是不同的两个对象。
3. equals(obj)方法 3.1 equals简介Object中的equals方法用于判断this对象和obj本身的值是否相等,即用来判断调用equals方法的对象和形参obj所引用的对象是否是同一对象。 所谓同一对象,就是指两个对象是否指向了内存中的同一块存储单元地址。如果this和obj指向的是同一块内存单元地址,则返回true;如果this和obj指向的不是同一块内存单元地址,则返回false。如果没有指向同一内存单元,即便是内容完全相等,也会返回false。
Object类的equals方法,其作用是比较两个对象是否相同,默认比较的是内存地址,其底层是通过==实现的。如果我们不想比较内存地址,那么就需要重写equals方法。默认的实现源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
我们知道,Java中还有一个==运算符,也可以对两个对象进行比较。如果是基本数据类型,==比较的是它们的值是否相同;如果是引用数据类型,比较的是它们的内存地址是否相同。而equals()方法则是比较两个对象的内容是否相等。
3.2 使用原则我们在使用equals()方法时,需注意下面这些原则:
3.3 基本特性(1).equals()只能处理引用类型变量;
(2).一般情况下,equals()方法比较的是两个引用类型变量的地址值是否相等;
(3).但是String类、基本类型包装类、File类、Date类等,都重写了Object类的equals()方法,比较是两个对象的"具体内容"是否相同。
另外Java语言规范也要求equals方法具有下面的特性:
3.4 案例自反性 : 对于任何非空引用x,x.equals(x)应该返回true;
对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true;
传递性:对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true;
一致性 : 如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果;
对于任何非空引用x,x.equals(null)应该返回false。
/**
* @author 一一哥Sun
* 千锋教育
*/
public class ObjectTest {
public static void main(String[] args) {
Dog dog01=new Dog("乔治01");
Dog dog02=new Dog("乔治02");
System.out.println("dog01对比dog02 "+(dog01==dog02));//false
//equals()方法的底层默认还是利用==实现的
System.out.println("dog01对比dog02 "+(dog01.equals(dog02)));//false
}
}
从上面的案例中,我们也可以证明,equals()方法用于处理引用类型的变量,默认比较的是两个引用类型的变量地址是否相等。
4. getClass()方法 4.1 简介getClass()方法可以用于获取对象运行时的字节码类型,得到该对象的运行时的真实类型。该方法属于Java的反射机制,其返回值是Class类型,例如 Class c = obj.getClass();。
通过对象c,我们可以进一步获取该对象的所有成员方法,每个成员方法都是一个Method对象。我们也可以获取该对象的所有成员变量,每个成员变量都是一个Field对象。同样的,我们也可以获取该对象的构造函数,构造函数则是一个Constructor对象。
/**
* @author 一一哥Sun
* 千锋教育
*/
public class ObjectTest {
public static void main(String[] args) {
//判断运行时d对象和c对象是否是同一个类型
Animal d = new Dog();
Animal c = new Cat();
//方式1:通过instanceof关键字判断
if((d instanceof Dog && c instanceof Dog) ||(d instanceof Cat && c instanceof Cat)) {
System.out.println("是同一个类型");
}else {
System.out.println("不是同一个类型");
}
//方式2:通过getClass方法判断
if(d.getClass() == c.getClass()) {
System.out.println("是同一个类型");
}else {
System.out.println("不是同一个类型");
}
}
}
从上面的代码案例中,我们可以得知,getClass方法用于返回该对象的真实类型(运行时的类型),可以根据对象的字节码来判断两个对象是否是同一个对象。
5. toString()方法 5.1 简介toString()方法可以说是一个进行“自我描述”的方法,可以返回某个对象的字符串,当要输出某个实例对象的信息时,我们可以通过重写该方法来输出自我描述的信息。该方法通常只是为了方便输出本类的描述信息,比如执行System.out.println("xyz")
这样的日志语句。一般情况下,当程序要输出一个对象或者把某个对象和字符串进行连接运算时,系统就会自动调用该对象的toString()方法返回该对象的字符串表示。
Object类的toString()方法会返回“运行时的类名@十六进制哈希码”格式的字符串,但很多类都重写了 Object类的toString()方法,用于返回可以表述该对象信息的字符串。
/**
* @author 一一哥Sun
* 千锋教育
*/
public class Dog implements Animal{
private String name;
public Dog() {}
public Dog(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println("小狗"+this.name+"狗爱吃骨头");
}
//@Override
//public String toString() {
//return "Dog name= " + name;
//}
}
public class ObjectTest {
public static void main(String[] args) {
Dog dog=new Dog("乔治");
System.out.println("dog一号="+dog);
System.out.println("dog二号="+dog.toString());
}
}
上述代码执行结果如下图所示:
从上面程序的运行结果可以发现,默认情况下,对象带不带toString()方法,其最终的输出结果是一样的,即对象输出时一定会调用 Object类中的 toString()方法打印内容,所以我们可以利用此特性来通过 toString()方法取得一些对象的信息。
6. wait()、wait(long)、wait(long,int)、notify()、notifyAll()方法这几个函数体现的是Java的多线程机制,一般是结合synchronize语句使用。
wait()用于让当前线程失去操作权限,当前线程进入等待序列;
notify()用于随机通知一个持有对象的锁的线程获取操作权限;
wait(long) 和wait(long,int)用于设定下一次获取锁的距离当前释放锁的时间间隔;
notifyAll()用于通知所有持有对象的锁的线程获取操作权限。
这几个方法我们后面在分析多线程的面试题时再细说,此处先仅做了解。
7. finalize()方法 7.1 简介finalize()方法在进行垃圾回收的时候会用到,主要是在垃圾回收时,用于作为确认该对象是否确认被回收的一个标记。我们在使用finalize()方法时要注意:
7.2 案例finalize方法不一定会执行,只有在该方法被重写的时候才会执行;
finalize方法只会被执行一次;
对象可以在finalize方法中获得自救,避免自己被垃圾回收,同样的自救也只能进行一次;
不推荐Java程序员手动调用该方法,因为finalize方法代价很大。
为了测试出finalize()方法的作用,小编给大家设计了如下案例:
/**
* @author 一一哥Sun
* 千锋教育
*/
public class Dog implements Animal{
private String name;
public Dog() {}
public Dog(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println("小狗"+this.name+"狗爱吃骨头");
}
//复写finalize方法
@Override
protected void finalize() throws Throwable {
super.finalize();//不要删除这行代码
System.out.println("finalize方法执行了");
}
}
然后我们对Dog对象进行回收测试:
public class ObjectTest {
public static void main(String[] args) {
Dog dog=new Dog("乔治");
//手动将对象标记为垃圾对象
dog = null;
//触发垃圾回收器,回收垃圾对象
System.gc();
}
}
要想确保finalize()方法的执行,我们首先需要在相关对象中重新finalize()方法,然后将待回收的对象手动标记为null,最后再手动调用gc()方法,这样才有可能确保finalize()方法一定执行。
三. 结语至此,就把Object类给大家介绍完毕了,这个类的内容并不是很难,主要是掌握几个常用的方法就可以了,尤其是equals()、hashCode()、toString()、getClass()等方法。
以上就是Java中Object用法详解的详细内容,更多关于Java Object的资料请关注软件开发网其它相关文章!