# 缘由

今天是第一天,本来想着每天能够学习一点东西,但是最近一会儿弄这个,一会弄那个,感觉时间很少,或者说是自己比较慵懒吧。

从很早的时候就想着某一天能够把 jdk 里面的所有 java 类搞明白,但是到头来还是没有做到。即便是日常常用的一些 java 类的实现细节可能也不清晰。

要么就是某些时候为了应对面试问题,奋笔疾书,翻看一些常用的集合类,一些常用的并发类等等。

希望能够从今天开始,每天了解一个 java 类,不需要特意背记,只求熟悉。

# 说明

这里统一用 windows 版本的.exe 安装程序,jdk 目前先下载以下几个版本:jdk7, jdk8, jdk9 (主要是看 String 类的变化,也是为了找一个非 LTS 版本参照), jdk11, jdk17,

下载 jdk

  • jdk7:https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html
  • jdk8: https://www.oracle.com/in/java/technologies/javase/javase8-archive-downloads.html
  • jdk9: https://www.oracle.com/java/technologies/javase/javase9-archive-downloads.html (jdk9 下载的时候发现只有 x64 版本,但是有 300M+)
  • jdk10: https://www.oracle.com/java/technologies/java-archive-javase10-downloads.html
  • jdk11: https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html (100M+, 区分 x86 和 x64,可能是因为 LTS 版本,更规范些)
  • jdk12:https://www.oracle.com/java/technologies/javase/jdk12-archive-downloads.html (windows 和 jdk9 一样只有 x64 版本)
  • jdk17: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html

上面所有版本中 7,8,11,17 是 LTS 版本,主要看这些。

有时候我们会遇到 native 修饰的方法,这

# System.java

From JDK1.0
Package:java.lang

  1. System 类被 final 修饰,不可继承

  2. System 类拥有 1 个私有构造方法 private System() {} ,不可实例化

  3. System 类拥有 1 个静态初始化块 static { registerNatives(); } ,类初始化后调用 native 修饰的 registerNatives 方法。

  4. registerNatives 方法是 clinit(注意 init 是实例化过程中对 non-static 变量解析初始化,clinit 是 jvm 进行类加载 - 验证 - 解析 - 初始化过程中的初始化,有本质区别)过程中 jvm 做的第一件事,处于非常早的一个时间点。

  5. 类初始化过程中,静态代码块中的父类的静态变量优先于子类进行赋值操作。

  6. System 类 static 块在 1.7 和 1.8 时调用 registerNatives 本地方法后,在类初始化与线程初始化后,会首先调用 System 类的 initializeSystemClass 私有方法,该方法调用 native initProperties 方法注入系统属性、调用 sun.misc.VM.saveAndRemoveProperties(props) 加载虚拟机参数(如最大直接内存、IntegerCache size 等),并且加载 zip 类库,设置 HUP、TERM、INT 信号处理,初始化系统环境变量,因为主线程不会自动将自己添加到线程组,这里使用 current.getThreadGroup().add(current); 主动将自己添加进去。然后执行 setJavaLangAccess ,最后调用 sun.misc.VM.booted();

  7. 上面的 registerNatives 在 java11 和 java17 中,调用的是 initPhase1 方法,其实 initPhase1 和 initializeSystemClass 方法没太大区别,内部代码执行次序有些变化,调用了 VM.initLevel(1) 设置了虚拟机初始化级别。

  8. 通常我们使用的 System.out 对象,其实是一个 PrintStream 对象,PrintStream 对象里对 println 方法和 print 方法做了重载,支持了各种基本数据类型以及对象、char 数组的输出。

  9. 与 System.out 相对应,对于所有输入(如键盘),可以使用 System.in,in 是一个 InputStream 对象,可以通过 InputStreamReader 读取并通过 BufferedReader 的 readLine 方法获取到对应的输入信息。

public static void main(String[] args) {
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        System.out.println("Please input your name: ");
        try {
            String name = br.readLine();
            System.out.println("Hello " + name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  1. 注意 out 和 in 对象都是静态 final 的。

  2. System 类有一个被 static volatile 修饰的 SecurityManager 对象,其实不仅 System 类有用到,其他比如 Proxy、Thread 等类也用到了该类对象,该对象主要作用监控恶意程序(比如删除系统文件,reboot 系统等),所以对执行的 code 做个权限的管理,防止意外。

  3. System 类还有一个 err 对象,类型和 in 对象一样是 PrintStream,

# 英文单词

determine 决定
performing 表演,展示
possibly 可能
various 各种各样的
potentially 潜在的
invocation 调用
thereby 从而
prevent 防止
permitted 允许
convention 公约,惯例
obtain 获得
in addation to 此外
asterisk 星号
remain 保持
backwards 向后
compatible 兼容的
either 任何一个
restrict 受限制的
comma-separated 逗号分割

# 关于 native 方法

A native method is a Java method whose implementation is provided by non-java code.

除了 System 类,其实 java 里面还有很多类比如 Thread 类、Class 类、ClassLoader 类、Object 类、Unsafe 类等都会在类初始化时通过静态代码块执行 registerNatives 方法,registerNatives 方法(或者说 native 方法)对应的底层实现其实是用 c/c++ 实现的,为了与其他语言通信,java 里面提供了一个可以与其他语言之间通信的 API,名字叫 JNI,也就是 Java Native Interface,实现它可以使 java 调用其他语言的函数。因为本地方法没有方法体,所以更像是定义了一个接口,而具体实现是通过其他语言实现。

native static 修饰的方法不需要实例化这个类,可以直接调用,使用上非常方便,比如:

native static public long Native2() ;

native 方法还可以跟 synchronized 修饰,保证同步调用,比如:

native synchronized private float Native3( Object o ) ;

native 方法可以返回各种类型数据,可以是基本类型,也可以是引用类型,比如 boolean, int 或者是某个对象类型。

native 方法还可以加入异常控制,比如抛出某个异常,同时也可以接收一些 java 类型参数(但是这样很麻烦,毕竟你想想,既然我们决定注入了这么多 java 特性,我们又为何不直接使用 java 语言来实现呢?),比如:

native void Native4( int[] ary ) throws Exception ;
private static native void setIn0 (InputStream in); //System 类
private static native void setOut0 (PrintStream out); //System 类
private static native void setErr0 (PrintStream err); //System 类

native 方法有效地扩展了 jvm 实现,比如 java 并发模块,很多方法是要直接和操作系统交互,比如线程优先级的映射等,多线程的状态,阻塞,锁等。native 方法的实现让 java 和 java 之外的环境交互有了可能,如某些操作系统或某些底层硬件设备。

父类声明的 native 方法,子类可以重写,重写时可以依然声明为 native 方法,也可以移除 native(这块看上去似乎有点奇怪), 例如下面:

Foo.java

public class Foo {
    public native void doSomething();
    public native int doPlus();
}

Son.java

public class Son extends Foo{
    @Override
    public void doSomething() {
        System.out.println("do something");
    }
    @Override
    public native int doPlus();
}

app 运行时,首先会将字节码文件加载到内存,字节码中方法描述符会指向 native 方法的直接实现,通常是 DLL 文件,这其实是通过 java.system.loadLibrary () 实现的,需要注意的是:native 方法在提供可交互的同时会失去 java 的诸多好处,尽可能不要使用本地方法。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Jalen Chu 微信支付

微信支付

Jalen Chu 支付宝

支付宝

Jalen Chu 公众号

公众号