前语

本篇文章会讲解一部分的JDK 源码 从简略下手、逐渐提高难度

比方 String类、ArrayList类、HashMap类 等典型的类的源码的完结~

咱们大概就不讲什么B+树、红黑树啥的了 哈哈 O(∩_∩)O (一方面新手或许没必要了解这些,另一方面作者也是新手 (●’◡’●))

期望咱们翻开 idea 跟从作者一同去一步一步的去阅读源码

String源码

进入到String源码中

咱们首先看到 String 完结了 序列化基础接口(经过序列化,目标的状态能够存储在文件或许网络中传输)

其次 看到 String 完结了 Comparable 接口 该接口界说了一个 compareto() 办法,用于比较两个字符串之间的大小联系。完结该接口的类能够进行排序操作

以及 完结了 CharSequence 接口:该接口界说了一组基本的字符串操作办法,包括获取字符串长度、获取指定方位上的字符、截取子串等操作。完结该接口的类也能够被当做字符串来运用。

在源码中 咱们发现

private final char value[];

也便是说明 String 是不行改变的 由于被final 好处便是愈加安全 当然尽管string自身不行改变,但咱们也能够经过新建String 完结字符串的修正

private int hash; // Default to 0 缓存哈希

咱们都知道hash的效果便是快速判别目标是否持平,假如哈希值不同则目标必不持平。

当然 实践中 Java 中并不是一切的类都适宜运用 hash 值来比较目标是否持平,这取决于详细的事务需求和完结方式。比方,在某些场景下,假如两个目标的特点值相同,则以为它们是持平的,而与其哈希值无关。在这种情况下,应该重写 equals() 办法,并依据目标的实践特点来进行比较。

除此之外呢

public int length() {
    return value.length; //回来长度
}
public boolean isEmpty() {
    return value.length == 0; //是否为空 依据长度 是否为0
}
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
    throw new StringIndexOutOfBoundsException(index); // 索引的校验
}
return value[index];  // 回来索引方位的值
}

其间 还有一些类 结构函数

String():创立一个空的字符串目标。
String(char[] value):运用指定的字符数组来创立一个字符串目标。
String(char[] value, int offset, int count):运用指定的字符数组中的一部分来创立一个字符串目标。
String(String original):运用另一个字符串目标来创立一个新的字符串目标,内容与原始字符串相同。
String(byte[] bytes):运用指定的字节数组来创立一个字符串目标,默许运用 UTF-8 编码方式。
String(byte[] bytes, Charset charset):运用指定的编码方式来将字节数组转换成字符串目标。
String(byte[] bytes, int offset, int length):运用指定的字节数组中的一部分来创立一个字符串目标,默许运用 UTF-8 编码方式。
String(byte[] bytes, int offset, int length, Charset charset):运用指定的编码方式来将字节数组的一部分转换成字符串目标。

除此之外,还有一些特殊的字符串结构函数,比方经过 StringBuffer 或 StringBuilder 目标来结构字符串、经过字符或许 Unicode

能够说是十分全面了~~~(考虑的很全面)

总体来说 string 源码阅读起来并不算太难!(感兴趣小伙伴能够点进去看看 信任都能看懂 、最重要的是走出这一步)

ArrayList源码

看到 import util 它是一个util包下的类

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

看上述代码,首先也是集成一个笼统的List类

咱们进入List类来看看(留意 一定要跟从作者我一同,比方我现在进行了这一步,你也要这样,作者所讲述的真的十分简略)

public abstract class AbstractList extends AbstractCollection implements List

能够看到 它也是承继的 笼统调集类 完结的List接口

笼统调集类 完结的 调集接口 调集接口 同样的 承继的 Iterable 这是一个迭代器…

个人感觉 乱糟糟的有种这样的感觉…

新手想要学习JDK源码,我推荐你从本篇文章开始

不会画图emmm

总而言之

Collection 是一切调集结构类的根接口,它界说了一些通用的操作办法,例如添加元素、删去元素、判别元素是否存在等;

AbstractCollection 是 Collection 接口的笼统完结类,供给了一些默许的完结办法,能够简化承继自 AbstractCollection 的子类的完结进程;

AbstractList 是 List 接口的笼统完结类,同样供给了一些默许的完结办法,能够简化承继自 AbstractList 的子类的完结进程;

ArrayList 是 List 接口的一个详细完结类,底层经过数组来完结,支持动态扩容。

因而,能够将它们的效果总结如下:

Collection 是调集结构中的根接口,界说了调集的基本操作;

AbstractCollection 和 AbstractList 都是笼统类,为承继它们的子类供给了默许完结,能够削减子类的代码量;

ArrayList 是 List 接口的详细完结类,经过数组来完结动态扩容,能够高效地进行随机拜访,但不适宜频繁刺进和删去元素。

那么为什么搞一个笼统类呢? 就像上面简化代码 那么如何做到简化代码的呢?

其实这是由于笼统类能够包括笼统办法和非笼统办法,其间笼统办法是只有声明而没有完结的办法,需求子类去完结。而非笼统办法是现已完结的办法,能够直接在笼统类中调用。

当咱们从一个笼统类承继并完结它时,假如不需求修正默许完结,就能够直接运用父类中的完结,这样能够削减代码量和开发时刻。假如咱们需求修正默许完结,能够覆盖父类中的完结。

举个比方,AbstractList 中供给了默许的 add 和 remove 办法的完结,这些办法能够适用于大多数的List 完结,例如 ArrayList、LinkedList等。当咱们需求界说一种新的List 完结时,咱们能够直接承继 AbstractList 并重写一些特定的办法,这样能够削减咱们的代码量,而且能够避免一些常见的错误。

AbstractList 中供给了默许的 add 和 remove 办法的完结, 这便对错笼统办法!!!

回归正题,咱们持续来看 ArrayList 的源码

首先榜首句话 是实例化版本号

看到第二句话

7/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

没错,这便是大名鼎鼎容量,你们应该听说过什么扩容机制对吧,咱们先不深究就说这儿,默许的容量为10

嘻嘻看不懂了~~ 就先到这儿吧(●’◡’●)

System源码

进入 System 迎面榜首句话

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

这句代码的效果呢便是:

在System类中,静态代码块的效果是在类加载时自动履行registerNatives()办法,从而向JVM注册一些与体系相关的本地办法。这样,当咱们调用System类中的某些办法时,就能够直接运用这些本地办法来完结对应的功用,提高了程序的功率和功能。

System.currentTimeMillis() 获取当时时刻毫秒数(时刻戳)

来看看怎么完结的

public static native long currentTimeMillis(); emm 原来是向jvm注入的本地办法

除此之外针对上面获取时刻戳,咱们好像还能够运用 Date类

那么就让咱们来进入Date类的源码内部看看吧!

咱们看到它的初始化

public Date() {
    this(System.currentTimeMillis());
}

public Date(long date) {

fastTime = date;

}

能够看到无参结构函数内部调用了 System.currentTimeMillis() 办法获取当时时刻的毫秒数,并将其作为参数传入了有参结构函数。在有参结构函数中,它会将传入的毫秒数直接赋值给类成员变量 fastTime,这个成员变量代表了 Date 目标所表明的时刻点的毫秒数。

然后,在 Date 类的其他办法中,比方 toString() 办法,它会依据 fastTime 成员变量的值来计算出对应的年、月、日、时、分、秒等信息,并回来一个字符串方式的日期和时刻表明方式。因而,调用 new Date().toString() 会回来一个字符串,其间包括了当时时刻的年、月、日、时、分、秒等信息。

里面还有很多 获取当时包括了当时时刻的年、月、日、时、分、秒等信息的办法

但是 里面哪些办法 都不好用

比方这儿:

public int getYear() { return normalize().getYear() - 1900; }

这是一个Java代码,它界说了一个公共办法名为“getYear()”。这个办法运用了别的一个界说在该类中的办法“normalize()”,而且从中获取一个日期目标。然后它获取这个日期目标的年份,并将其减去1900,然后回来这个值。

需求留意的是,这个办法实践上不太好用,由于它回来的是一个经过特殊处理的年份值(减去了1900)。假如你想要获取一般年份值,那么你能够运用Java Date类中的getYear()办法,但是这个办法现已被标记为过期了,所以最好运用Calendar类中的getInstance()的get(Calendar.YEAR)办法。

所以咱们运用日历类 Calendar

Calendar源码

System.out.println(Calendar.getInstance().get(Calendar.YEAR));

解析:

当咱们调用Calendar.getInstance()办法时,会回来一个Calendar目标。这个目标是由Calendar类的静态工厂办法创立的,详细完结能够参阅下面的代码:

public static Calendar getInstance() {
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

这个办法会经过默许的时区和言语环境创立一个新的Calendar目标。在这儿,它运用了默许的时区和言语环境,但是你也能够指定自己需求的时区和言语环境来创立Calendar目标。

接下来,咱们能够调用get(Calendar.YEAR)办法来获取Calendar目标所表明的年份。这个办法的完结:

public int get(int field) {
    complete();
    return internalGet(field);
}

这个办法会首先调用complete()办法来保证Calendar目标现已初始化完结,然后再调用internalGet(field)办法来获取指定时刻字段的值。

internalGet(field)办法会依据传入的参数field来获取对应时刻字段的值。在这儿,咱们传入了Calendar.YEAR作为参数,它表明取得年份字段的值。internalGet(field)办法的完结如下:

protected int internalGet(int field) {
    return fields[field];
}

这个办法会直接回来fields数组中相应时刻字段的值,fields数组存储了Calendar目标一切时刻字段的值。因而,Calendar.getInstance().get(Calendar.YEAR)会回来当时时刻的年份。

总结

这篇文章也不能算水吧,不过的确很枯燥无味,我个人也不是很喜欢写,所以我把这些定坐落新手集体,又或许咱们能够把这篇文章当成一篇漫笔来看,由于并没有一些很难的技术讲解,首要还是交给咱们如何去“阅读源码”

✍写在最终,

本人在山东,目前是一名大三的在校生,想暑假开始寻觅一份适宜的实习,假如有大佬看到能够给我一次时机。


好文推荐:

/post/722207… 最适宜新手的设计形式学习

/post/722172… 一文读懂前后端的交互流程