Java基础知识之Object类

143 阅读10分钟
package java.lang;
public class Object {
public Object() { /* compiled code */ }
private static native void registerNatives();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object o) { /* compiled code */ }
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public java.lang.String toString() { /* compiled code */ }
public final native void notify();
public final native void notifyAll();
public final native void wait(long l) throws java.lang.InterruptedException;
public final void wait(long l, int i) throws java.lang.InterruptedException { /* compiled code */ }
public final void wait() throws java.lang.InterruptedException { /* compiled code */ }
protected void finalize() throws java.lang.Throwable { /* compiled code */ }
}
Object
java
所有类的终极类
,
我们常见的自定义
class
但是并没有继承
Object(Java
编译器自动引入
,
如果手动继承
Object,
也是没有问题的
,java
单继承 有一定的局限
)
public static class User {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
return super.hashCode();
}
}
Object
12
个方法
,
下面一一解释这些方法的作用
1. getClass()
public final Class<?> getClass() {
return shadow$_klass_;
}
返回此
Object
的运行时类
实际结果的类型是
Class<? extends |X|>
其中
|X|
是静态类型上其表达的擦除
getClass
被调用。 例如,在此代码片段中不需要转换:
Number n = 0;
Class<? extends Number> c = n.getClass();
2. hashCode()
/* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public int hashCode() {
return identityHashCode(this);
}
返回对象的
hash
,
支持这种方法是为了散列表
,
hashmap
hashcode
的特点是
:
1.
只要在执行
Java
应用程序时多次在同一个对象上调用该方法
,hashcode()
始终返回相同的整数
(
前提是该对象的信息没有发生改变
)
2.
相对于两个对象来说
,
如果使用了
equals
方法比较返回
true,
那么这两个对象的
hashcode
值也是相同的
3.
对于两个对象来说
,
如果使用
equals
方法比较为
false,
那么这两个对象的
hash
值不一定要求不同
,
可以相同也可以不同
,
然而如果不同
,
则可以提高性能
4.
对于
Object
类来说
,
不同的
Object
对象的
hashcode
是不同的
(Object
hashcode
表示对象的存储地址
,
但是如果重写了
hashcode
就不一定表示存储地址了
)
3. Clone()
protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() +
" doesn't implement Cloneable");
}
return internalClone();
}
克隆方法
:
创建并返回此对象的副本
;
对于任何对象
x x.clone()!=x
而且
x.clone().getClass()==x.getClass()
成立 虽然对象的基类都支持
clone
但是
object
本身并未实现
cloneable,
所以自定义的类需要实现
cloneable
接口
,
否则将抛出异常
4. toString()
返回对象的字符串表示形式
,
一般说来
,
这个方法返回一个固定的模版
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
这个在实际开发中
,
并不好体现
,
所以各大编译器都支持 重新生成
toString(),
:
public static class User {
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
5. finalize()
/* @throws Throwable the {@code Exception} raised by this method
* @see java.lang.ref.WeakReference
* @see java.lang.ref.PhantomReference
* @jls 12.6 Finalization of Class Instances
*/
protected void finalize() throws Throwable { }
finalize()
方法可以被子类对象所覆盖,然后作为一个终结者,当
GC
被调用的时候完成最后的清理工作(例如释放系统资源之类)。这就是终止。默认的
finalize()
方法什么也不做,当被调用时直接返回。
对于任何一个对象,它的
finalize()
方法都不会被
JVM
执行两次。如果你想让一个对象能够被再次调用的话(例如,分配它的引用给一个静态变量),注意当这个对象已经被
GC
回收的时候,
finalize()
方法不会被调用第二次。测试
:
package asange.javastudy.java.lang;
/**
* @author youxuan E-mail:xuanyouwu@163.com
* @version 2.3.1
* @Description
* @date createTime
2018/1/20
*/
public class ObjectTest {
public static class User {
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected void finalize() throws Throwable {
System.out.println("finalize");
super.finalize();
}
}
public static void main(String[] args) throws Exception {
User o = new User();
o = null;
System.gc();
System.gc();
}
}
运行结果
:
asange.javastudy.java.lang.ObjectTest
finalize
Process finished with exit code 0
可以看到结果
:
两次
gc finalize
只执行了一次
6. wait() notify() notifyAll()
这三个方法是有关线程阻塞与线程唤醒
1. wait(),notify()
notifyAll()
方法是本地方法
,
并且是
final
类型
,
无法重写
2.
调用某个对象的
wait()
方法能让当前线程阻塞
,
并且当前线程必须拥有此对象的
monitor
(即锁)
3.
调用某个对象的
notify()
方法能唤醒一个正在等待这个对象的
monitor
的线程
,
如果有多个线程在等待这个
monitor,
唤醒其中一个线程
;
4.
调用
notifyAll()
方法能唤醒所有正在等待这个对象的
monitor
为何这三个不是
Thread
类声明中的方法,而是
Object
类中声明的方法(当然由于
Thread
类继承了
Object
类,所以
Thread
也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有
monitor
(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
  上面已经提到,如果调用某个对象的
wait()
方法,当前线程必须拥有这个对象的
monitor
(即锁),因此调用
wait()
方法必须在同步块或者同步方法中进行(
synchronized
块或者
synchronized
方法)。
  调用某个对象的
wait()
方法,相当于让当前线程交出此对象的
monitor
,然后进入等待状态,等待后续再次获得此对象的锁(
Thread
类中的
sleep
方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
  
notify()
方法能够唤醒一个正在等待该对象的
monitor
的线程,当有多个线程都在等待该对象的
monitor
的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
  同样地,调用某个对象的
notify()
方法,当前线程也必须拥有这个对象的
monitor
,因此调用
notify()
方法必须在同步块或者同步方法中进行(
synchronized
块或者
synchronized
方法)。  
nofityAll()
方法能够唤醒所有正在等待该对象的
monitor
的线程,这一点与
notify()
方法是不同的。
  这里要注意一点:
notify()
notifyAll()
方法只是唤醒等待该对象的
monitor
的线程,并不决定哪个线程能够获取到
monitor
  举个简单的例子:假如有三个线程
Thread1
Thread2
Thread3
都在等待对象
objectA
monitor
,此时
Thread4
拥有对象
objectA
monitor
,当在
Thread4
中调用
objectA.notify()
方法之后,
Thread1
Thread2
Thread3
只有一个能被唤醒。注意,被唤醒不等于立刻就获取了
objectA
monitor
。假若在
Thread4
中调用
objectA.notifyAll()
方法,则
Thread1
Thread2
Thread3
三个线程都会被唤醒,至于哪个线程接下来能够获取到
objectA
monitor
就具体依赖于操作系统的调度了。
  上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的
monitor
,只有等调用完
notify()
或者
notifyAll()
并退出
synchronized
块,释放对象锁后,其余线程才可获得锁执行。
public class ObjectTest {
public static Object obj = new Object();
public static void main(String[] args) throws Exception {
Object o2 = new Object();
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
thread2.start();
}
static class Thread1 extends Thread {
@Override
public void run() {
super.run();
System.out.println("
线程
" + Thread.currentThread().getName() + "
开始
");
synchronized (obj) {
try {
System.out.println("
线程
" + Thread.currentThread().getName() + "
调用了
object.wait()");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("
线程
" + Thread.currentThread().getName() + "
获取到了锁
");
}
}
}
static class Thread2 extends Thread {
@Override
public void run() {
super.run();
System.out.println("
线程
" + Thread.currentThread().getName() + "
开始
");
synchronized (obj) {
obj.notify();
System.out.println("
线程
" + Thread.currentThread().getName() + "
调用了
object.notify()");
}
System.out.println("
线程
" + Thread.currentThread().getName() + "
释放了锁
");
}
}
}
运行结果
:
线程
Thread-0
开始
线程
Thread-1
开始
线程
Thread-0
调用了
object.wait()
线程
Thread-1
调用了
object.notify()
线程
Thread-1
释放了锁
线程
Thread-0
获取到了锁
Process finished with exit code 0
1
Object
类的七大
native
方法:
registerNatives()
getClass()
hashCode()
clone()
notify()
notifyAll()
wait(long)native
关键字修饰的方法称为本地方法,这些方法并不是用
java
实现的,考虑到实现的性能问题,大多是由
C/C++
编写的程序,编译成
dll
文件,再由
java
去加载这些
dll
文件,就可以通过
java
来调用
dll
中的函数了
1
**registerNatives()
方法:
**
主要是将用
C/C++
语言写的一些方法,如
hashCode
wait
notify
等方法加载到
jvm
中,感兴趣的可以去
OpenJDK
中查看相关
C
的代码如下
static JNINativeMethod methods[] = {
  
{
hashCode
,
()I
, (void *)&JVM_IHashCode},
  
{
wait
,
(J)V
, (void *)&JVM_MonitorWait},
  
{
notify
,
()V
, (void *)&JVM_MonitorNotify},
  
{
notifyAll
,
()V
, (void *)&JVM_MonitorNotifyAll},
  
{
clone
,
()Ljava/lang/Object;
, (void *)&JVM_Clone},};JNIEXPORT void JNICALLJava_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
  
(*env)->RegisterNatives(env, cls,methods, sizeof(methods)/sizeof(methods[0]));
}
2
**getClass()
方法:
**
我们可以看到这个方法是由
final
修饰的,因此不能被重写的,对
final
关键字不太了解的,可以看一下我的另一篇文章:
Java
源码解析之
final
关键字的使用详解
//class
是一个类的属性,能获取该类编译时的类对象
System.out.println(Bird.class);//class com.somta.test.commonuseobj.objectobj.Bird//getClass()
是一个类的方法,它是获取该类运行时的类对象
System.out.println(bird.getClass());//class com.somta.test.commonuseobj.objectobj.Bird
3
hashCode()
方法:返回一个整数的
HashCode
值,该值依赖于内部表示堆的对象的指针,一般情况下,该方法都会被重写。关于
hashCode
equals
的关系,我们只需要记住下面四句话(前提:需要重写
equals
hashCode
方法,
String
Integer
Boolean
Double
等都重写了这两个方法,不重写
HashCode
方法,任何对象的
hashCode
都是不相等的,并且
equals
方法也会使用
Object
中的
equals
方法,此时
equals
和外汇返佣
http://www.fx61.com/
==
是等价的,
equals
比较的是栈中的引用,而非对象的值)
1
、如果两个对象相等,则它们的
hashCode
值一定相等
2
、如果两个对象不相等,则它们的
hashCode
值不一定都不相等
3
、如果两个对象的
hashCode
值相等,两个对象不一定相等
4
、如果两个对象的
hashCode
值不相等,两个对象一定不相等
4
clone()
方法: 将一个对象克隆出一个新对象,要实现
clone
的对象一般需要先实现
Cloneable
接口才能达到克隆的目的,克隆其实也分“浅克隆”,“深克隆” ,
Object
类的克隆属于“浅克隆”,关于克隆的知识,后续在写文章说明,此处就不展开了
5
notify()
notifyAll()
wait(long)
方法: 这些方法后续在多线程中在具体讲解
2
Object
类的常用方法:
equals()
toString()
1)
equals()
方法我们可能在以前的面试中被问到过关于
==
equals
的区别,可能大多人的回答是:“
==
运算符通常是用来比较基本类型的值是否相等或者比较对象的引用是否相等;
equals
比较的是两个对象是否相等”,这种说法其实并不太正确,不妨我们先看
Object
中关于
equals()
方法的源码
public boolean equals(Object obj) {
return (this == obj);
}
Object
类是所有类的子类,如果一个类没有重写
equals()
方法,那它就会使用父类的的
equals()
方法,通过上面的代码可以看出其实在
Object
类中,
==
运算符和
equals()
方法是等价的,其实上面的说法只适用于那些重写了
Object
equals
方法的类而言是正确的,比如
String
等。注意:重写
equals()
方法的时候,必须要重写
hashCode
方法,以维护
hashCode
的约束,确保
equals()
方法声明相等的对象具有相同的
hashCode
2)
toString()
方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
getClass().getName()
返回了该类的全类名,
Integer.toHexString(hashCode())
返回了以
16
进制无符号整数的形式返回了此
hashCode
的字符串,这个是根类
Object
的实现,子类中一般都会重写该方法

更多技术资讯可关注:itheimaGZ获取