JDK源码阅读之路【不断更新】
前言
- 为什么阅读源码?
学习设计模式和思维。总之,知道自己有多菜,在不断学习的过程中发现自身不足并弥补,才能进步。
- 如何阅读
阅读顺序参考:https://blog.csdn.net/qq_21033663/article/details/79571506
结合aip:https://docs.oracle.com/javase/8/docs/api/
1.Object类
类 Object 是类层次结构的根类,每个类都使用 Object 作为超类,所有对象、包括数组都实现了Object类的方法。简而言之,所有类都有Object类中的方法。
//构造函数
Object()
private static native void registerNatives();static {registerNatives();}
Object类中一开始的4行代码,关于registerNatives():
- native:本地方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。native方法在JVM中运行时数据区也和其它方法不一样,它有专门的本地方法栈。native方法主要用于加载文件和动态链接库。由此可知,本地方法的实现是由其他语言编写并保存在动态连接库中,因而在java类中不需要方法实现。
- 静态代码块:用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。
- 代码功能:先定义了registerNatives()方法,然后当该类被加载的时候,调用该方法完成对该类中本地方法的注册。
当包含registerNatives()方法的类被加载的时候,注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法。
一个Java程序要想调用一个本地方法,需要执行两个步骤:第一,通过System.loadLibrary()将包含本地方法实现的动态文件加载进内存;第二,当Java程序需要调用本地方法时,虚拟机在加载的动态文件中定位并链接该本地方法,从而得以执行本地方法。registerNatives()方法的作用就是取代第二步,让程序主动将本地方法链接到调用方,当Java程序需要调用本地方法时就可以直接调用,而不需要虚拟机再去定位并链接。
- 使用registerNatives()方法的三点好处:
- 通过registerNatives方法在类被加载的时候就主动将本地方法链接到调用方,比当方法被使用时再由虚拟机来定位和链接更方便有效;
- 如果本地方法在程序运行中更新了,可以通过调用registerNative方法进行更新;
- Java程序需要调用一个本地应用提供的方法时,因为虚拟机只会检索本地动态库,因而虚拟机是无法定位到本地方法实现的,这个时候就只能使用registerNatives()方法进行主动链接。
- 通过registerNatives()方法,在定义本地方法实现的时候,可以不遵守JNI命名规范。JNI命名规范要求本地方法名由“包名”+“方法名”构成。
Object类中的native方法:
//Returns the runtime class of this Object.
public final native Class<?> getClass();//Returns a hash code value for the object.
public native int hashCode();//Creates and returns a copy of this object.
protected native Object clone() throws CloneNotSupportedException;//Wakes up a single thread that is waiting on this object's monitor.
public final native void notify();//Wakes up all threads that are waiting on this object's monitor.
public final native void notifyAll();//Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
public final native void wait(long timeout) throws InterruptedException;
其他方法:
public boolean equals(Object obj) {return (this == obj);}
- equals()在实际开发中一般都要重写,因为obj1 == obj2只判断了两个对象的地址值,如果该对象的成员变量中包含了引用类型的变量,则需要进一步判断。
public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
toString() demo:
- toString()返回全路径名称+哈希值;一般也需要重写;
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);}
- Object类中wait有三个重载方法,同时必须捕获非运行时异常InterruptedException。
- wait() :需要notify ,notifyAll才能唤醒;
- wait(long timeout) :经过timeout 超时后,若未被唤醒,则自动唤醒;
- wait(timeout, nanos) :经过timeout 超时后,若未被唤醒,则自动唤醒。相对wait(long timeout) 更加精确时间。
方法都必须在synchronized 同步关键字所限定的作用域中调用,否则会报错java.lang.IllegalMonitorStateException ,意思是因为没有同步,所以线程对对象锁的状态是不确定的,不能调用这些方法。
protected void finalize() throws Throwable { }
- finalize()方法是Java中Object类的一个空实现方法。 作用:GC准备回收该对象的内存时,会先调用finalize()。关键字protected是防止在该类之外定义的代码访问finalize()标识符。
2.String类
String类用final关键字修饰,类不能被继承,不能被覆盖,以及final类在执行速度方面比一般类快。
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/*** 成员属性 ***/// 底层利用字符数组实现,final修饰private final char value[];// String对象的hashcode缓存,考虑String不可变特性,hash码频繁使用,因此直接记录下该值也是一种优化。默认值为 0.private int hash;// serialVersionUID 支持序列化和反序列化支持private static final long serialVersionUID = -6849794470754667710L;private static final ObjectStreamField[] serialPersistentFields =new ObjectStreamField[0];// native方法 intern()public native String intern();
}
String类实现三个接口:
- Serializable:可序列化;
- Comparable:类的实例化对象之间可比较;
- CharSequence:此接口对多种不同的对char访问的统一接口;String定义的字符串只能读,CharSequence定义的字符串是可读可写的;
String类的intern()方法:
在用new时,String都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。
String构造函数:
public static void main(String[] args) throws UnsupportedEncodingException {/**String类构造函数demo */// 无参构造String s1 = new String();System.out.println(s1.hashCode()=="".hashCode()); //hash值默认为0; 0==0 trueString s = "123";String s2 = new String(s);System.out.println(s2 == s); // false == 比较的是对象内存地址值,s和s2是堆中的不同对象,所有false;
// System.out.println(s2 == "123"); false 地址值不同,hash值相同
// System.out.println(s == "123"); trueSystem.out.println(s2.hashCode() == s.hashCode()); // true; 比较的是String中的成员对象hash的值 String s3 = "123";System.out.println(s == s3); // true//用字符数组构造String s4 = new String(new char[] {'a', 'b'});String s5 = new String(new char[] {'a', 'b','c'}, 1, 2); //s5 = bc;String s6 = new String(new int[] {65,97,100},0,2); // int数组 用ASCII码构造 AaString s7 = new String(new byte[]{65,2,3}, "GBK"); //指定编码集// StringBuffer、StringBuilder构造String s8 = new String(new StringBuffer());String s9 = new String(new StringBuilder());}
贴几个源码:
public String() {this.value = "".value;}public String(String original) {this.value = original.value;this.hash = original.hash;}public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}public String(StringBuffer buffer) {synchronized(buffer) {this.value = Arrays.copyOf(buffer.getValue(), buffer.length());}}public String(StringBuilder builder) {this.value = Arrays.copyOf(builder.getValue(), builder.length());}
- StringBuffer不是线程安全,要用synchronized关键字;StringBuilder线程安全。
@Overridepublic int length() {return value.length;}@Overridepublic char charAt(int index) {if ((index < 0) || (index >= value.length)) {throw new StringIndexOutOfBoundsException(index);}return value[index];}@Overridepublic CharSequence subSequence(int start, int end) {return this.substring(start, end);}
- 实现CharSequence接口的3个方法;
public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2);char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) {char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {return c1 - c2;}k++;}return len1 - len2;}
- 实现 Comparable接口的compareTo():比较两个字符串的字典顺序。用字符串1跟字符串2作比较,如果字符串1的字典顺序在字符串2前面,则返回一个负数。若在后面,则返回一个正数。若两个字符串的字典顺序相同,则返回0。
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {if (srcBegin < 0) {throw new StringIndexOutOfBoundsException(srcBegin);}if (srcEnd > value.length) {throw new StringIndexOutOfBoundsException(srcEnd);}if (srcBegin > srcEnd) {throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);}System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);}
- 将当前字符串从start到end-1位置上的字符复制到字符数组c中,并从c的offset处开始存放。
Arrays.copyOf()和System.arrayCopy()的区别:
Arrays.copyOf()不仅仅只是拷贝数组中的元素,在拷贝元素时,会创建一个新的数组对象。而System.arrayCopy只拷贝已经存在数组元素。
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;}// 忽略大小写
public boolean equalsIgnoreCase(String anotherString) {return (this == anotherString) ? true: (anotherString != null)&& (anotherString.value.length == value.length)&& regionMatches(true, 0, anotherString, 0, value.length);}
- instanceof:Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
- 首先,判断引用值是否相等,相等即true;否则,判断类型是否匹配,类型相同,长度相等,逐个比较字符是否一样,完全符合,则返回ture。(如果两个对象判断相等,判断类型)
public static final Comparator<String> CASE_INSENSITIVE_ORDER= new CaseInsensitiveComparator();private static class CaseInsensitiveComparatorimplements Comparator<String>, java.io.Serializable {// use serialVersionUID from JDK 1.2.2 for interoperabilityprivate static final long serialVersionUID = 8575799808933029326L;public int compare(String s1, String s2) {int n1 = s1.length();int n2 = s2.length();int min = Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 = s1.charAt(i);char c2 = s2.charAt(i);if (c1 != c2) {c1 = Character.toUpperCase(c1);c2 = Character.toUpperCase(c2);if (c1 != c2) {c1 = Character.toLowerCase(c1);c2 = Character.toLowerCase(c2);if (c1 != c2) {// No overflow because of numeric promotionreturn c1 - c2;}}}}return n1 - n2;}/** Replaces the de-serialized object. */private Object readResolve() { return CASE_INSENSITIVE_ORDER; }}
CaseInsensitiveComparator静态内部类:compare方法实现String对象的大小写不敏感比较。
- 存疑:为什么设计成静态内部类?
public boolean startsWith(String prefix, int toffset) {char ta[] = value;int to = toffset;char pa[] = prefix.value;int po = 0;int pc = prefix.value.length;// Note: toffset might be near -1>>>1.if ((toffset < 0) || (toffset > value.length - pc)) {return false;}while (--pc >= 0) {if (ta[to++] != pa[po++]) {return false;}}return true;}public boolean endsWith(String suffix) {return startsWith(suffix, value.length - suffix.value.length);}
- 子串截取
public String substring(int beginIndex, int endIndex) {if (beginIndex < 0) {throw new StringIndexOutOfBoundsException(beginIndex);}if (endIndex > value.length) {throw new StringIndexOutOfBoundsException(endIndex);}int subLen = endIndex - beginIndex;if (subLen < 0) {throw new StringIndexOutOfBoundsException(subLen);}return ((beginIndex == 0) && (endIndex == value.length)) ? this: new String(value, beginIndex, subLen);}
- 字符替换
- 哪怕多做些循环判断,也要尽量避免new新的对象;
public String replace(char oldChar, char newChar) {if (oldChar != newChar) {int len = value.length;int i = -1;char[] val = value; /* avoid getfield opcode */while (++i < len) {if (val[i] == oldChar) {break;}}if (i < len) {char buf[] = new char[len];for (int j = 0; j < i; j++) {buf[j] = val[j];}while (i < len) {char c = val[i];buf[i] = (c == oldChar) ? newChar : c;i++;}return new String(buf, true);}}return this;}
- 分割字符
public String[] split(String regex, int limit) {/* fastpath if the regex is a(1)one-char String and this character is not one of theRegEx's meta characters ".$|()[{^?*+\\", or(2)two-char String and the first char is the backslash andthe second is not the ascii digit or ascii letter.*/char ch = 0;if (((regex.value.length == 1 &&".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||(regex.length() == 2 &®ex.charAt(0) == '\\' &&(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&((ch-'a')|('z'-ch)) < 0 &&((ch-'A')|('Z'-ch)) < 0)) &&(ch < Character.MIN_HIGH_SURROGATE ||ch > Character.MAX_LOW_SURROGATE)){int off = 0;int next = 0;boolean limited = limit > 0;ArrayList<String> list = new ArrayList<>();while ((next = indexOf(ch, off)) != -1) {if (!limited || list.size() < limit - 1) {list.add(substring(off, next));off = next + 1;} else { // last one//assert (list.size() == limit - 1);list.add(substring(off, value.length));off = value.length;break;}}// If no match was found, return thisif (off == 0)return new String[]{this};// Add remaining segmentif (!limited || list.size() < limit)list.add(substring(off, value.length));// Construct resultint resultSize = list.size();if (limit == 0) {while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {resultSize--;}}String[] result = new String[resultSize];return list.subList(0, resultSize).toArray(result);}return Pattern.compile(regex).split(this, limit);}
public String trim() {int len = value.length;int st = 0;char[] val = value; /* avoid getfield opcode */while ((st < len) && (val[st] <= ' ')) {st++;}while ((st < len) && (val[len - 1] <= ' ')) {len--;}return ((st > 0) || (len < value.length)) ? substring(st, len) : this;}
3.AbstractStringBuilder
这个抽象类是StringBuilder和StringBuffer的直接父类。可以认为AbstractStringBuilder 就是对于可变字符序列的这一概念的描述。
他提供了可变字符序列的一个基本协议约定,也就是基本的功能方法作为一个抽象类, 并且也提供了一部分默认的实现;StringBuffer和StringBuilder都是可变的字符序列,所以他们都实现了AbstractStringBuilder。
类声明:
abstract class AbstractStringBuilder implements Appendable, CharSequence{}
实现了两个接口,其中CharSequence这个字符序列的接口已经很熟悉了:
- 该接口规定了需要实现该字符序列的长度:length();
- 可以取得下标为index的的字符:charAt(int index);
- 可以得到该字符序列的一个子字符序列: subSequence(int start, int end);
- 规定了该字符序列的String版本(重写了父类Object的toString()):toString();
Appendable接口顾名思义,定义添加的’规则’:
- append(CharSequence csq) throws IOException:如何添加一个字符序列
- append(CharSequence csq, int start, int end) throws IOException:如何添加一个字符序列的一部分
- append(char c) throws IOException:如何添加一个字符
成员变量:
/*** The value is used for character storage.*/char[] value;/*** The count is the number of characters used.*/int count;
构造函数:
AbstractStringBuilder() {}/*** Creates an AbstractStringBuilder of the specified capacity.*/AbstractStringBuilder(int capacity) {value = new char[capacity];}
方法:
4.StringBuffer
类声明:
成员变量:
构造函数:
方法:
5.StringBuilder
类声明:
成员变量:
构造函数:
方法:
6.HashMap
public static void main(String[] args) {HashMap map1 = new HashMap<String,String>(8,(float) (0.8));HashMap map2 = new HashMap<String,String>();map1.put("1", "aaa");map1.put("3", "ccc");map2.put("a", "jony");map2.put("1", "kiki");map1.put(null, null);System.out.println(map1.size()); //size=3-HashMap允许存放null键值对System.out.println(map1.put("1", "s")); // put方法-如果key已存在,替换,并返回原value;map2.putAll(map1); //putAll方法-将map1中所有mappings复制到map2中,对于map2之前已有key对应的value将被map1对应的value覆盖,其余map2的键值对不受影响;System.out.println(map2.size());System.out.println(map2.get("1"));System.out.println(map1.get("3"));System.out.println(map1.containsKey("2")); // 是否包含某个keySystem.out.println(map1.containsValue("ccc")); // 是否包含某个valueSet<String> set = map1.keySet(); // 返回所有key构成的集合Collection set2 = map1.values(); //返回所有value构成的集合Set<String> set3 = map1.entrySet(); //返回所有Entry构成的集合System.out.println(map1.replace("1", "ee"));System.out.println(map2.remove("a")); //移除指定key值对应的mapping,如果指定key不存在,返回null}
JDK源码阅读之路【不断更新】相关推荐
- jdk源码阅读-HashMap
前置阅读: jdk源码阅读-Map : http://www.cnblogs.com/ccode/p/4645683.html 在前置阅读的文章里,已经提到HashMap是基于Hash表实现的,所以在 ...
- Hive源码阅读之路
Hive源码阅读(1)阅读环境搭建 前言:让学习成为一种习惯 环境准备 Hive源码下载 Hive源码目录 hive三个最重要的组件: 其他组件 hive辅助组件 编译源码 配置Hive本地调试 配置 ...
- jdk javac运行不了_Intellij IDEA搭建jdk源码阅读环境
一.找到源码位置 直接找到jdk安装的目录,会看到src.zip的压缩包,这里面就是jdk的源码,例如下图: 在这里解压. 第一次尝试建议使用9或更早版本jdk的源码,否则易造成卡死. 二.Intel ...
- 走过的路-java源码阅读之路
源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 一.人生三种境界: 1.昨夜西风凋碧树,独上高楼望尽天涯路. 2.衣带渐宽终不悔,为伊消得人憔悴. ...
- Mac搭建JDK源码阅读环境
点赞再看,养成习惯,微信公众号搜索[虚竹讲程序经],获取更多技术干货! 想要读懂JDK源码,需要在自己电脑上搭建JDK的源码阅读环境,正所谓,工欲善其事,必先利其器.下面演示如何在Mac上结合Idea ...
- jdk源码(jdk源码阅读顺序)
如何在myEclipse中查看JDK源码 myeclipse中查看jdk类库的源码步骤如下: 1.点 "window"-> "Preferences" - ...
- JDK源码阅读环境搭建
内容来源 B站Up主: CodeSheep 视频: https://www.bilibili.com/video/BV1V7411U78L 感谢大佬分享学习心得 Thanks♪(・ω・)ノ~~~ 1. ...
- JDK源码阅读-搭建阅读环境
1.找到源码位置 其实我们安装jdk的时候源码就已经存在,只要找到jdk的安装位置,就能找到源码,如果不知道jdk具体安装位置的话,可以在idea中查看. 打开目录,找到路径下的src.zip,这就是 ...
- JDK源码阅读 String
1.String是如何做到不可变?为什么要将它设计为不可变类? 答:首先String类是被final修饰,不能被继承:它把数据存放在一个数组value中,value同样被final修饰:所 ...
最新文章
- 前端中的this,指的是什么?
- python和R对dataframe的拼接、采样、链式操作:dplyr、tidyr、concat、rbind、cbind、sample、sample_n、set.seed、mutate、filter
- Android开发:关于WebView
- kotlin调用类中的方法_一种轻松的方法来测试Kotlin中令人沮丧的静态方法调用
- Jmeter接口测试使用beanshell断言json返回
- 康轩职教计算机应用基础课件,《Excel中IF函数的应用-机器人任务》计算机应用基础职教课件.ppt...
- 3.空域图像处理的洪荒之力
- Eclipse启动提示“subversive connector discovery”
- 多个安卓设备投屏到电脑_手机投屏软件哪个好,如何将手机屏幕投屏到电脑?...
- java面试英语自我介绍_程序员面试英文自我介绍
- 校园导航系统课程设计,#校园管理系统
- 我们来了!多云架构时代,欢迎加入中国开源网络新势力
- Spring的bean定义 2 : 通用bean定义逻辑 -- AbstractBeanDefinition
- 传图取字:微信小程序自动把图片上的文字提取出来
- mysqladmin命令简介
- js实现粘贴板js插件clipboard.js实现一键复制粘贴功能
- 历时7天,四名学生将《水浒传》搬到线上!
- 获取文件名下载并兼容IE(文件流)
- 那位把每天当做试用期的女孩,升职为总裁助理了
- 乐视android版本怎么升级,乐视手机EUI系统升级教程 乐视手机EUI系统怎么升级