阅读JDK源码也是一件非常重要的事情,尤其是使用频率最高的一些类,通过源码可以清晰的清楚其内部机制。

如何阅读jdk源码(基于java8)?

首先找到本地电脑中的jdk安装路径,例如我的就是E:\jdk,

src.zip中就包含了jdk的所有源码,并将相关源码导入到ideal、eclipse等等,如图:

第一步就来介绍所有类的基类Object,先看下此类的架构图:

 一、registerNatives

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

在Object被导入的时候,就会调用此方法,该方法是使用native关键字修饰,也就是说调用的是本地方法。

native解释:

Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI(Java Native Interface)接口调用其他语言(例如C、C++等等)来实现对底层的访问。 JNI是Java本机接口,是一个本机编程接口,它是JDK的一部分。JNI允许Java代码

使用以其他语言编写的代码和代码库。InvocationAPI(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。

registerNatives:

在类初始化时调用registerNatives()方法进行本地方法的注册,也就是初始化java原生方法(native修饰)映射到对应的其他语言描述方法,比如c语言的方法。我们可以发现用native修饰的方法都没有具体的方法体(类似于抽象方法),

因为不需要java来实现,是使用其他语言实现的,直接调用即可。

说白了,就是将程序中所有native修饰的方法与其他语言描述的方法对应上。

二、getClass()

public final native Class<?> getClass();

作用:获取某个对象运行时的类对象(class对象)

三、hashCode()、equals()、clone()、toString()

1、hashCode()

一般hashCode()不单独使用,功能是在比较俩个对象是否相等的时候提高比较效率,比如在一些hashSet、hashTable等数据结构中,不允许存在相同的元素,所以每次插入元素都要和已经存在的元素依次进行比较,如果依次对每个元素调用equals(Object obj),

对于大量数据来说太慢了,hashCode()便是在哈希表结构中提高比较速率的利器,如果俩个元素的hashCode()不同,则equals(Object obj)就不必比较了,因为肯定不同;如果俩个元素的hashCode()相同,则再比较equals(Object obj)于是我们得出了一个结论:hashCode()相同,equals(Object obj)不一定相同;hashCode()不同,equals(Object obj)一定不同;equals(Object obj)相同,hashCode()一点相同;

但是有一个前提,必须同时重写equals(Object obj)和hashCode()。

原则:

  • 1. 在java程序运行期间,若用于equals方法的信息或者数据没有修改,name同一个对象多次调用此方法,返回的哈希码是相同的。而在两次独立的运行java程序时,对于同一对象,不需要返回的哈希码相同
  • 2. 如果根据equals方法,两个对象相同,则这两个对象的哈希码一定相同
  • 3. 假如两个对象通过equals方法比较不相同,那么这两个对象调用hashCode也不是要一定不同,相同也是可以的。但是使用者应该知道对不同的对象产生不同的hashCode是可以提高hash tables的性能的。

2、equals()

此方法比较的是两个对象的内存地址是否一致。如果不重写equals()方法,equals()与==的意义一致。

public native int hashCode();

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

3、clone()

protected native Object clone() throws CloneNotSupportedException;

作用:返回一个克隆对象,满足以下原则:

返回一个此类的克隆对象,二者具有相同的属性,二者拥有独立的属性存储空间,分别位于堆内存中的俩快空间中,不相互影响,但是也不是绝对的,因为存在浅克隆和深克隆的区别

前提:如果一个类没有实现Cloneable接口(这个接口里面没有任何方法的声明,是一个标记接口),那么对此类的对象进行复制时,在运行时会出现CloneNotSupportedException异常。

clone涉及到一个很经典的问题就是深拷贝与浅拷贝,请看如下示例

浅拷贝:

class House {
private String addr;
public House(String addr) {
this.setAddr(addr);
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
} }
class Person implements Cloneable {
private String name;
private House house;
public Person(String name, House house) {
this.setName(name);
this.setHouse(house);
}
@Override
public Object clone() {
//浅拷贝
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public House getHouse() {
return house;
}
public void setHouse(House house) {
this.house = house;
} }
public class ShallowClone {
public static void main(String[] args) {
House house = new House("地址");
Person person = new Person("名字", house);
Person personClone = (Person) person.clone();
//这里我改变person对象中的house,可以看到personClone中的house也进行了变化
person.getHouse().setAddr("新地址");
System.out.println(personClone.getHouse().getAddr());
} }

深拷贝:

//对象具有拷贝功能必须先实现Cloneable接口,否则报错
class House implements Cloneable {
private String addr;
public House(String addr) {
this.setAddr(addr);
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
} }
class Person implements Cloneable {
private String name;
private House house;
public Person(String name, House house) {
this.setName(name);
this.setHouse(house);
}
@Override
public Object clone() {
Person p = null;
try {
p = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
     //同时将成员变量house也拷贝一份,以实现深拷贝
p.house = (House) p.house.clone();
return p;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public House getHouse() {
return house;
}
public void setHouse(House house) {
this.house = house;
} }
public class DeepClone { public static void main(String[] args) throws CloneNotSupportedException {
House house = new House("地址");
Person person = new Person("名字", house);
Person personClone = (Person) person.clone();
//这里我改变person对象中的house,可以看到personClone中的house没有变化
person.getHouse().setAddr("新地址");
System.out.println(personClone.getHouse().getAddr());
} }
  • 浅拷贝: 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝: 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

4、toString()

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

返回类的名称(权限定名称)加上@,然后 加上此类的哈希码的16进制表示,例如:com.ll.client.House@2a139a55

四、notify()、notifyAll()、wait()

接触过多线程的应该对这几个方法非常熟悉了。

1、notify()

public final native void notify();

通知可能等待该对象的对象锁的其他线程。由JVM(与优先级无关)随机挑选一个处于wait状态的线程。

  • - 在调用notify()之前,线程必须获得该对象的对象级别锁
  • - 执行完notify()方法后,不会马上释放锁,要直到退出synchronized代码块,当前线程才会释放锁
  • - notify()一次只随机通知一个线程进行唤醒

2、notifyAll()

public final native void notifyAll();

和notify()差不多,只不过是使所有正在等待池中等待同一共享资源的全部线程从等待状态退出,进入可运行状态
让它们竞争对象的锁,只有获得锁的线程才能进入就绪状态
每个锁对象有两个队列:就绪队列和阻塞队列

  • - 就绪队列:存储将要获得锁的线程
  • - 阻塞队列:存储被阻塞的线程

3、wait()

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);
} public final void wait() throws InterruptedException {
  wait(0);
}

wait()和wait(long timeout, int nanos)都在在内部调用了wait(long timeout)方法,所以重点研究wait(long timeout)方法即可。

wait方法会引起当前线程阻塞,直到另外一个线程在对应的对象上调用notify或者notifyAll()方法,或者达到了方法参数中指定的时间。 调用wait方法的当前线程一定要拥有对象的监视器锁。

wait方法会把当前线程T放置在对应的object上的等待队列中,此时这个对象上的所有同步请求都不会得到该线程的响应。

在以下四件事发生之前,线程T一直处于休眠状态(线程T是在其代码中调用wait方法的那个线程)

  • 当其他的线程在对应的对象上调用notify方法,而在此对象的对应的等待队列中将会任意选择一个线程进行唤醒。
  • 其他的线程在此对象上调用了notifyAll方法
  • 其他的线程调用了interrupt方法来中断线程T
  • 等待的时间已经超过了wait中指定的时间。如果参数timeout的值为0,不是指真实的等待时间是0,而是线程等待直到被另外一个线程唤醒。

走进JDK(一)------Object的更多相关文章

  1. 调试过程中发现按f5无法走进jdk源码

    debug 模式 ,在fis=new FileInputStream(file); 行打断点 调试过程中发现按f5无法走进jdk源码 package com.lzl.spring.test; impo ...

  2. 走进JDK(十)------HashMap

    有人说HashMap是jdk中最难的类,重要性不用多说了,敲过代码的应该都懂,那么一起啃下这个硬骨头吧!一.哈希表在了解HashMap之前,先看看啥是哈希表,首先回顾下数组以及链表数组:采用一段连续的 ...

  3. 走进JDK(八)------AbstractSet

    说完了list,再说说colletion另外一个重要的子集set,set里不允许有重复数据,但是不是无序的.先看下set的整个架构吧: 一.类定义 public abstract class Abst ...

  4. 走进JDK(十二)------TreeMap

    一.类定义 TreeMap的类结构: public class TreeMap<K,V> extends AbstractMap<K,V> implements Navigab ...

  5. 走进JDK(十一)------LinkedHashMap

    概述LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,LinkedHas ...

  6. 走进JDK(二)------String

    本文基于java8. 基本概念: Jvm 内存中 String 的表示是采用 unicode 编码 UTF-8 是 Unicode 的实现方式之一 一.String定义 public final cl ...

  7. 走进JDK(九)------AbstractMap

    map其实就是键值对,要想学习好map,得先从AbstractMap开始. 一.类定义.构造函数.成员变量 public abstract class AbstractMap<K,V> i ...

  8. 走进JDK(七)------LinkedList

    要学习LinkedList,首先得了解链表结构.上篇介绍ArrayList的文章中介绍了底层是数组结构,查询快的问题,但是删除时,需要将删除位置后面的元素全部左移,因此效率比较低. 链表则是这种机制: ...

  9. 走进JDK(六)------ArrayList

    对于广大java程序员来说,ArrayList的使用是非常广泛的,但是发现很多工作了好几年的程序员不知道底层是啥...这我觉得对于以后的发展是非常不利的,因为java中的每种数据结构的设计都是非常完善 ...

随机推荐

  1. Fibonacci again and again

    Fibonacci again and again http://acm.hdu.edu.cn/showproblem.php?pid=1848 Time Limit: 1000/1000 MS (J ...

  2. vue 返回上一页后,上一页由参数渲染的内容无法显示

    思路1:将参数传递给第二个页面后,返回上一页时,再讲参数传回第一页(此方法适用于层级少的)(亲测有效) 思路2:将参数放到全局变量中(还未尝试过)

  3. f5会话保持导致站点访问故障

    1.环境: vs Service Port为443和80,vip为10.160.100.66,pool为10.160.45.42/43的8080.80的vs重新到443. 2.问题: 单独访问源站都没 ...

  4. Python: PySide(Qt)异步获取网页源码

    学习PyQt UI编程笔记.相对PyQt来说,PySide资料为少. 此篇记录异步获取代码后,同步显示于界面窗体中,涉及线程网步,此为知识点. 直录代码: # encoding: utf-8 from ...

  5. swift - 本地通知

    1. AppDelegate  注册 class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? fun ...

  6. ABAP开发需要养成的习惯—程序修改数据库表

    ①此外将内表数据写入数据库,推荐用Modify而不是insert,因为会有些key一样的报dump loop at it_record. *          报错 *          insert ...

  7. wget: command not found

    支持yum yum -y install wget 不支持yum a.查看当前linux版本 cat /etc/issue && uname -a b.根据版本下载相应的rpm htt ...

  8. ALV界面显示

    PERFORM ADD.   IS_LAYOUT_LVC-CWIDTH_OPT = 'X'.   IS_LAYOUT_LVC-SEL_MODE = 'A'. CALL FUNCTION 'REUSE_ ...

  9. css让内层div自动撑开外层div

    .clear{clear:both;height:0px;font-size: 1px;line-height: 0px;} <div class="audi_items"& ...

  10. 安装mysql时启动服务出错问题

    mysql安装最后一步 无法启动服务错误 博客分类: IDE问题解析     今天安装mysql程序时候,在安装到最后一步时候,在最后一步却发现无法启动服务,出现这样的提示“cannot create ...