Java常用类(一)Object
这篇开始讲解Java中常见的类,从Object开始,以便补充自己的Java基础。
在以下Java中文 API截图,介绍了Object类各个方法和其基本作用。
该类中主要有以下方法: clone(),equals(),getClass(),finalize(),toString(),其中toString(),getClass(),equals是其中最重要的方法。
注意:
Object类中的getClass(),notify(),notifyAll(),wait()等方法被定义为final类型,因此不能重写。
(一)clone()方法
需要了解的是深复制和浅复制
protected Object clone() throws CloneNotSupportedException
首先对象要实现方法,该类的对象要实现接口 Cloneable,不然会抛出 CloneNotSupportedException。(注意,所有的数组都被视为实现接口 Cloneable。)
1. clone和用"="赋值复制的区别(java语言复制数组的四种方法)
有一个 Person a=new Person() 对象引用,通常我们复制会Person a1=a 这样实现,而这个只是简单的copy,仅仅把a的引用(reference)复制给了a1,a和a1在内存中同时指向同一个地址,而这样带来的不好之处是a或者a1其中一方的操作会影响另一方,比如说起初a.age=20,而此时a1.age=30,改为了30,此时打印a.getAge()方法得到的是修改之后的age的域值30。这并不是我们想要的,我们想要的是复制一模一样的属性,同时互不影响对方,这时就需要clone()来满足我们需求 。Person a1=a.clone(),此时a1就是一个全新的Person对象,并且和a有相同的属性方法。
2.深复制和浅复制(Shallow Clone与Deep Clone)
我们都知道JAVA里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用(reference)传递的。
而此时的clone()也是一样的操作,8种基本类型是值的复制,但令人头疼的是,这里对象的是引用类型变量的复制不就相当于是回到上一个问题的指向同一个内存地址了吗,以A类为例,里面有一个name属性它是String引用类型,经过clone产生新的a1.name的引用,和原始a.name的引用是指向同一个内存地址的,而age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。如下图
如果我们想产生完全不一样的两个对象(具体验证可以用getName()验证,如链接),就要重写clone()方法,实现深复制。
public class Person implements Cloneable{ private int age ; private String name; public Person(int age, String name) { this.age = age; this.name = name; } public Person() {} public int getAge() { return age; } public String getName() { return name; } @Override protected Object clone() throws CloneNotSupportedException { A cloned=(A)super.clone(); cloned.name=(String)name.clone(); return cloned } }
3.clone的保护机制
用protected修饰clone方法,主要是为了让子类去重写它,实现深拷贝,以防在其他任何地方随意调用后修改,以Person类为例,通过声明为protected,就可以保证只有Person类里面才能“克隆”Person对象。
链接:https://blog.csdn.net/liyagangcsdn/article/details/68946795
(二)equals()方法
public boolean equals(Object obj) { return (this == obj); }
从源码我们可以看到,Object的equals方法实质上就是"==",也就是用来比较是否为同一对象,即是否为同一个内存地址。
因此对于一些自定义类,如果没有重写hashcode()方法和equals()方法的话,利用‘==’和equals()方法比较的结果是一样的。
不过像String,Double,Integer,Date,Point等常用的Java类都重写了equals方法,比较的是对象的内容,而不再是对象地址了。
(三)getClass()方法
public final Class<?> getClass()
返回次Object的运行时类类型。
(四)finalize()方法
protected void finalize() throws Throwable
用来释放资源,不过一般不被拿来使用,并且它能做的所有工作,使用try-finally 等方式可以做的更好,更及时。
finalize()的用途:
无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储空间。
不过这种情况一般发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。
(五)toString方法
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、标记符“@”和此对象哈希码的无符号十六进制表示组成。
默认返回对象的名称及引用地址,但一般很多Java类重写。
(六)hashCode()方法
public int hashCode()
返回此对象的一个哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable
提供的哈希表)的查找性能。
为了哈希表减少使用equals次数,重写equals方法一般都要重写hashcode方法(分建议重写和必须重写:详细链接:1,2)
如果两个对象 equals的时候,hashCode必须相等,但hashCode相等,对象不一定equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
如果不重写hashcode(),在HashSet中添加两个equals的对象,会将两个对象都加入进去。
(七)wait方法
public final void wait() throws InterruptedException { wait(0); } public final native void wait(long timeout) throws InterruptedException; 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); }
方法中的异常:
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,
如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
八、notify()方法
public final void notify()
九、notifyAll方法
public final void notifyAll()