目录

1.类构造器public Object();

2.private static native void registerNatives();

3.protected native Object clone() throws CloneNotSupportedException;

4.public final native Class getClass();

5.public boolean equals(Object obj);

6.public native int hashCode();

7.public String toString();

8. wait(...) / notify() / notifyAll()

13. protected void finalize();


Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入。Object类没有定义属性,一共有13个方法,具体的方法如下图:

1.类构造器public Object();

大部分情况下,Java中通过形如 new A(args..)形式创建一个属于该类型的对象。其中A即是类名,A(args..)即此类定义中相对应的构造函数。通过此种形式创建的对象都是通过类中的构造函数完成。为体现此特性,Java中规定:在类定义过程中,对于未定义构造函数的类,默认会有一个无参数的构造函数,作为所有类的基类,Object类自然要反映出此特性,在源码中,未给出Object类构造函数定义,但实际上,此构造函数是存在的。

当然,并不是所有的类都是通过此种方式去构建,也自然的,并不是所有的类构造函数都是public。

2.private static native void registerNatives();

registerNatives函数前面有native关键字修饰,Java中,用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成,并被编译成了.dll,由Java去调用。方法的具体实现体在dll文件中,对于不同平台,其具体实现应该有所不同。用native修饰,即表示操作系统,需要提供此方法,Java本身需要使用。具体到registerNatives()方法本身,其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。

既然如此,可能有人会问,registerNatives()修饰符为private,且并没有执行,作用何以达到?其实,在Java源码中,此方法的声明后有紧接着一段静态代码块:

private static native void registerNatives();static {registerNatives();}

3.protected native Object clone() throws CloneNotSupportedException;

看,clone()方法又是一个被声明为native的方法,因此,我们知道了clone()方法并不是Java的原生方法,具体的实现是有C/C++完成的。clone英文翻译为"克隆",其目的是创建并返回此对象的一个副本。Java术语表述为:clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。

clone()的正确调用是需要实现Cloneable接口,如果没有实现Cloneable接口,并且子类直接调用Object类的clone()方法,则会抛出CloneNotSupportedException异常。

4.public final native Class<?> getClass();

getClass()也是一个native方法,返回的是此Object对象的类对象/运行时类对象Class<?>。效果与Object.class相同。

首先解释下"类对象"的概念:在Java中,类是是对具有一组相同特征或行为的实例的抽象并进行描述,对象则是此类所描述的特征或行为的具体实例。作为概念层次的类,其本身也具有某些共同的特性,如都具有类名称、由类加载器去加载,都具有包,具有父类,属性和方法等。于是,Java中有专门定义了一个类,Class,去描述其他类所具有的这些特性,因此,从此角度去看,类本身也都是属于Class类的对象。为与经常意义上的对象相区分,在此称之为"类对象"。

5.public boolean equals(Object obj);

==与equals在Java中经常被使用,大家也都知道==与equals的区别:

==表示的是变量值完成相同(对于基础类型,地址中存储的是值,引用类型则存储指向实际对象的地址);

equals表示的是对象的内容完全相同,此处的内容多指对象的特征/属性。

实际上,上面说法是不严谨的,更多的只是常见于String类中。首先看一下Object类中关于equals()方法的定义:

    public boolean equals(Object obj) {return (this == obj);}

如上,在object类中,此标尺即为==。当然,这个标尺不是固定的,其他类中可以按照实际的需要对此标尺含义进行重定义。如String类中则是依据字符串内容是否相等来重定义了此标尺含义。如此可以增加类的功能型和实际编码的灵活性。当然了,如果自定义的类没有重写equals()方法来重新定义此标尺,那么默认的将是其父类的equals(),直到object基类。

String类中的equals实现:

    public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}

6.public native int hashCode();

hashCode()方法返回一个整形数值,表示该对象的哈希码值。

hashCode()具有如下约定:

1).在Java应用程序程序执行期间,对于同一对象多次调用hashCode()方法时,其返回的哈希码是相同的,前提是将对象进行equals比较时所用的标尺信息未做修改。在Java应用程序的一次执行到另外一次执行,同一对象的hashCode()返回的哈希码无须保持一致;

2).如果两个对象相等(依据:调用equals()方法),那么这两个对象调用hashCode()返回的哈希码也必须相等;

3).反之,两个对象调用hasCode()返回的哈希码相等,这两个对象不一定相等。

即严格的数学逻辑表示为: 两个对象相等 <=>  equals()相等  => hashCode()相等。因此,重写equlas()方法必须重写hashCode()方法,以保证此逻辑严格成立,同时可以推理出:hasCode()不相等 => equals()不相等 <=> 两个对象不相等。

可能有人在此产生疑问:既然比较两个对象是否相等的唯一条件(也是冲要条件)是equals,那么为什么还要弄出一个hashCode(),并且进行如此约定,弄得这么麻烦?

其实,这主要体现在hashCode()方法的作用上,其主要用于增强哈希表的性能。

以集合类中,以Set为例,当新加一个对象时,需要判断现有集合中是否已经存在与此对象相等的对象,如果没有hashCode()方法,需要将Set进行一次遍历,并逐一用equals()方法判断两个对象是否相等,此种算法时间复杂度为o(n)。通过借助于hasCode方法,先计算出即将新加入对象的哈希码,然后根据哈希算法计算出此对象的位置,直接判断此位置上是否已有对象即可。(注:Set的底层用的是Map的原理实现)

在此需要纠正一个理解上的误区:对象的hashCode()返回的不是对象所在的物理内存地址。甚至也不一定是对象的逻辑地址,hashCode()相同的两个对象,不一定相等,换言之,不相等的两个对象,hashCode()返回的哈希码可能相同。

因此,在上述代码中,重写了equals()方法后,需要重写hashCode()方法。

String类中的hashcode实现(默认值是0):

    public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}

上述hashCode()的重写中出现了h*31,是因为h*31 = (h<<5) - h。之所以选择31,是因为左移运算和减运算计算效率远大于乘法运算。当然,也可以选择其他数字。

7.public String toString();

Object 类中的实现:

    public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}

getClass()返回对象的类对象,getClassName()以String形式返回类对象的名称(含包名)。Integer.toHexString(hashCode())则是以对象的哈希码为实参,以16进制无符号整数形式返回此哈希码的字符串表示形式。

toString()是由对象的类型和其哈希码唯一确定,同一类型但不相等的两个对象分别调用toString()方法返回的结果可能相同。

8. wait(...) / notify() / notifyAll()

这几个方法主要用于java多线程之间的协作。先具体看下这几个方法的主要含义:

wait():调用此方法所在的当前线程等待,直到在其他线程上调用此方法的主调(某一对象)的notify()/notifyAll()方法。

wait(long timeout)/wait(long timeout, int nanos):调用此方法所在的当前线程等待,直到在其他线程上调用此方法的主调(某一对象)的notisfy()/notisfyAll()方法,或超过指定的超时时间量。

notify()/notifyAll():唤醒在此对象监视器上等待的单个线程/所有线程。

wait(...) / notify() | notifyAll()一般情况下都是配套使用。

看一个简单的例子:

package com.qqyumidi;public class ThreadTest {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubMyRunnable r = new MyRunnable();Thread t = new Thread(r);t.start();synchronized (r) {try {System.out.println("main thread 等待t线程执行完");r.wait();System.out.println("被notity唤醒,得以继续执行");} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println("main thread 本想等待,但被意外打断了");}System.out.println("线程t执行相加结果" + r.getTotal());}}
}class MyRunnable implements Runnable {private int total;@Overridepublic void run() {// TODO Auto-generated method stubsynchronized (this) {System.out.println("Thread name is:" + Thread.currentThread().getName());for (int i = 0; i < 10; i++) {total += i;}notify();System.out.println("执行notif后同步代码块中依然可以继续执行直至完毕");}System.out.println("执行notif后且同步代码块外的代码执行时机取决于线程调度");}public int getTotal() {return total;}
}

输出结果:

main thread 等待t线程执行完
Thread name is:Thread-0
执行notif后同步代码块中依然可以继续执行直至完毕
执行notif后且同步代码块外的代码执行时机取决于线程调度  //此行输出位置有具体的JVM线程调度决定,有可能最后执行
被notity唤醒,得以继续执行
线程t执行相加结果45

从上述例子的输出结果中可以得出如下结论:

1、wait(...)方法调用后当前线程将立即阻塞,且适当其所持有的同步代码块中的锁,直到被唤醒或超时或打断后且重新获取到锁后才能继续执行;

2、notify()/notifyAll()方法调用后,其所在线程不会立即释放所持有的锁,直到其所在同步代码块中的代码执行完毕,此时释放锁,因此,如果其同步代码块后还有代码,其执行则依赖于JVM的线程调度。

在Java源码中,可以看到wait()具体定义如下:

wait(long timeout, int nanos)方法定义内部实质上也是通过调用wait(long timeout)完成。而wait(long timeout)是一个native方法。因此,wait(...)方法本质上都是native方式实现。

notify()/notifyAll()方法也都是native方法。

Java中线程具有较多的知识点,是一块比较大且重要的知识点。

13. protected void finalize();

Object类中的定义:

protected void finalize() throws Throwable { }

Object中定义finalize方法表明Java中每一个对象都将具有finalize这种行为,其具体调用时机在:JVM准备对此对形象所占用的内存空间进行垃圾回收前,将被调用。由此可以看出,此方法并不是由我们主动去调用的。通常我们知道对象创建完,要做清理操作,当然GC会清理哪些由new分配的内存,但是如果不是通过new分配的内存清理的话,就需要调用finalize方法.
工作原理:一旦垃圾回收器准备释放对象占用的内存空间,将首先调用finalize方法,并且在下一次垃圾回收动作发生,才会真正的回收该对象占用的内存,也就是finalize方法会在垃圾回收器真正回收对象之前调用

  • finalize方法通常是用于清理一些非本地方法(native),和一些对象安全释放校验的操作
  • 垃圾回收内存是JVM的一个不确定操作,通常会在系统濒临内存溢出可能会回收,所以你不要指望垃圾回收来控制finalize方法的调用

Java Object 类 深入分析相关推荐

  1. 重写Java Object类中的equals()方法

    Java中的Object类中的equals方法 Object类的equals方法 重写Object类的equals()方法 Person类默认继承了Object类,所以可以使用Object类的equa ...

  2. Java Object类的各个方法

    Java中所有的类都继承自java.lang.Object类,Object类中一共有11个方法: public final native Class<?> getClass();publi ...

  3. Java Object类详解

    Object 是 Java 类库中的一个特殊类,也是所有类的父类.也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量.当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 O ...

  4. Java Object类中toString方法的重写

    Object类中的tostring方法的: 当我们输出一个对象时,实际是输出的是这个类中的tostring方法,是一个地址值,而不是类中的属性. 一:子类没有重写Object类中的toStrinng方 ...

  5. java object类_快速上手 Kotlin 开发系列之与 Java 互调 (1)

    学习完基础语法之后,我们来学习下 Java 和 Kotlin 互相调用的问题 本节将介绍 Kotlin 与 Java 之间的语法变化 Kotlin 文件中的函数 首先让大家感到非常不适应的一点是,Ko ...

  6. Java: Object类,System类,StringBuilder类

    Object 类 java.lang.Object类是Java语言中的根类(万类之源),即所有类的父类(超类).在对象实例化的时候,最终找的父类就是Object. 如果一个类没有特别指定父类, 那么默 ...

  7. java——Object类、Date类、DateFormat类、Calendar类、包装类

    Object类 java.lang.Object类是Java语言中的根类,即所有类的父类.它中描述的所有方法子类都可以使用.在对象实例化的时候,最终找的父类就是Object. 如果一个类没有特别指定父 ...

  8. java Object类及其常用方法

    一:类概述 Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法. 在不明确给出超类的情况下,Java会自动把Object作为要定义类的超 ...

  9. java Object类中的各个方法及其作用

    原文链接 一.简介Object类 Object类是所有类的父类,即每个类都直接或简介继承自该类.所以一个Object类型的变量可以引用任何对象,不论是类实例还是数组. 在不明确给出父类的情况下,Jav ...

最新文章

  1. hdu5014 构造b数列使得t最大(小想法)
  2. python前端开发之准备开发环境(建议收藏)
  3. 梯度下降法快速教程 | 第三章:学习率衰减因子(decay)的原理与Python实现
  4. python 加快计算速度_python怎么提高计算速度
  5. 微信小程序 数组索引 data-“”解释
  6. Kaldi语音识别快速入门
  7. Python访问MySQL数据库速度慢解决方法
  8. wordprss只显示一个当前主题 问题的解决
  9. c lua mysql 扩展_lua安装及luarocks安装mysql扩展
  10. ROS下使用摄像头进行机器人巡线功能(附带功能包链接)
  11. 关于 Spring 中 getBean 的全流程源码解析
  12. 手机如何卸载android,怎样删除手机自带软件(安卓手机自带软件怎么卸载)
  13. 2018计算机二级html,2020年全国计算机二级Web程序设计考试大纲(2018年版)
  14. 2022最新简约好用的夏雨图床系统源码+UI超好看
  15. MyCat启动报错,运行./mycat console报错 Unrecognized VM option 'AggressiveOpts'
  16. 三次样条插值matlab实现
  17. hive数据文件备份
  18. iMeta期刊部分文章被PubMed收录
  19. mac xcode 开发C++
  20. 体育这事,除了抢IP、赞助,就真不能干点其他啥了?

热门文章

  1. matlab 如何读数据文件,详解如何在python中读写和存储matlab的数据文件(*.mat)
  2. luoguP1742 最小圆覆盖
  3. Anaconda安装新模块
  4. Rhino学习教程——1.5
  5. 俩层判断,判断button是否可以点击
  6. Codeforces Round #196 (Div. 1 + Div. 2)
  7. CentOS6.4x64_安装Qt5
  8. C++基于TCP和UDP的socket通信
  9. laxcus的新功能:支持表跨数据库操作
  10. 以二进制的形式保存在数据库中