开篇寄语
哈喽,大家好,我是尘心,一个每天都在电脑前酣战淋漓的男人。说起System类,想必大家都不陌生,从一个hello world程序开始就在使用:System.out.print("hello world!")。日常开发中,也经常会用到System类,但是我们都没有去探究它,那么我们今天就研究一下System类的源码。
本文使用Java 8 作为研究版本。
Native关键字
native关键字在jdk源码层面是比较常见的,那么我们就非常有必要搞清楚,native的作用。native方法我们一般称为本地方法,在java源程序中以关键字 “native”声明,并不提供函数体。既然有声明则必定有实现。native方法的实现一般使用C/C++语言在另外的文件中编写,编写的规则遵循Java本地接口的规范(也就是我们说的JNI)。简而言之,就是Java中声明的可调用使用C/C++实现的方法。C/C++语言是可以直接操作计算机系统内存的。
源码剖析System类
System 位于 java.lang包下,被final修饰。不允许我们自己创建System对象。
开头有一段静态代码块,用于注册Natives(本地方法)。根据注释可以看出,VM将调用 initializeSystemClass方法完成这个类的初始化。registerNatives() 方法被native修饰,native表示该方法为本地方法,调用底层方法完成注册。
/* register the natives via the static initializer.
*
* VM will invoke the initializeSystemClass method to complete
* the initialization for this class separated from clinit.
* Note that to use properties set by the VM, see the constraints
* described in the initializeSystemClass method.
*/
private static native void registerNatives();
static {
registerNatives();
}
System类初始化
System类初始化是使用initializeSystemClass方法初始化,源码如下(已去除多余注释)
/*
* Initialize the system class. Called after thread initialization.
*/
private static void initializeSystemClass() {
props = new Properties();
initProperties(props); // initialized by the VM
sun.misc.VM.saveAndRemoveProperties(props);
lineSeparator = props.getProperty("line.separator");
sun.misc.Version.init();
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));
loadLibrary("zip");
Terminator.setup();
sun.misc.VM.initializeOSEnvironment();
Thread current = Thread.currentThread();
current.getThreadGroup().add(current);
setJavaLangAccess();
sun.misc.VM.booted();
}
初始化中做了以下操作:
- 创建一个Properties配置对象,调用
initProperties(props);方法进行初始化。initProperties方法也是本地方法,底层使用C/C++实现Java运行环境配置的初始化操作。我已经制作成表格,所有可以取的属性都在表格中。当然,运行中还会有其他属性设置,用户也可添加属性。
| Key | Description of Associated Value |
|---|---|
java.version | Java Runtime Environment version |
java.vendor | Java Runtime Environment vendor |
java.vendor.url | Java vendor URL |
java.home | Java installation directory |
java.vm.specification.version | Java Virtual Machine specification version |
java.vm.specification.vendor | Java Virtual Machine specification vendor |
java.vm.specification.name | Java Virtual Machine specification name |
java.vm.version | Java Virtual Machine implementation version |
java.vm.vendor | Java Virtual Machine implementation vendor |
java.vm.name | Java Virtual Machine implementation name |
java.specification.version | Java Runtime Environment specification version |
java.specification.vendor | Java Runtime Environment specification vendor |
java.specification.name | Java Runtime Environment specification name |
java.class.version | Java class format version number |
java.class.path | Java class path |
java.library.path | List of paths to search when loading libraries |
java.io.tmpdir | Default temp file path |
java.compiler | Name of JIT compiler to use |
java.ext.dirs | Path of extension directory or directories Deprecated. This property, and the mechanism which implements it, may be removed in a future release. |
os.name | Operating system name |
os.arch | Operating system architecture |
os.version | Operating system version |
file.separator | File separator ("/" on UNIX) |
path.separator | Path separator (":" on UNIX) |
line.separator | Line separator ("\n" on UNIX) |
user.name | User's account name |
user.home | User's home directory |
user.dir | User's current working directory |
看到这里大家就大概明白,为什么获取用户信息的时候,可以直接从System类中获取。例如:
// 获取用户home 目录
System.getProperty("user.home");
// 获取用户当前工作目录
System.getProperty("user.dir");
-
调用
sun.misc.VM.saveAndRemoveProperties(props);方法保存系统配置 -
调用
sun.misc.Version.init();初始化版本信息 -
初始化一些输入,标准输出,错误输出流,编码从系统属性中取。
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));
setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));
成员属性中定义了out,err等输出流,在初始化时会被赋值(也就是上面那段代码),这就是我们经常使用的 System.out.print()了
public final static PrintStream out = null;
public final static PrintStream err = null;
我们看一下PrintStream的println()方法,发现其底层还是加了synchronized 关键字,保障线程安全问题。
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
- 加载jdk相关资源
loadLibrary("zip"); // 其中还有很多资源通过这种方加载, 例如:awt,jni等等
- 安装Java信号处理器: HUP,TERM,INT
// Setup Java signal handlers for HUP, TERM, and INT (where available).
Terminator.setup();
- 初始化一些操作系统环境
sun.misc.VM.initializeOSEnvironment();
- 将
main线程加入到它自己的线程组
// The main thread is not added to its thread group in the same
// way as other threads; we must do it ourselves here.
Thread current = Thread.currentThread();
current.getThreadGroup().add(current);
// 注释大致意思:主线程不会像其他线程一样加入到它的线程组中;我们必须在这里自己做。
- 注册共享密钥. 主要是注册一些共享信息,如常量池等
// register shared secrets
setJavaLangAccess();
- 在初始化期间调用的子系统可以调用 sun.misc.VM.isBooted() 以避免做应该等到应用程序类加载器设置完成的事情。
// Subsystems that are invoked during initialization can invoke
// sun.misc.VM.isBooted() in order to avoid doing things that should
// wait until the application class loader has been set up.
// IMPORTANT: Ensure that this remains the last initialization action!
sun.misc.VM.booted();
System常用API
这里我们总结以下System的一些API,方便大家在开发过程中使用:
| 方法 | 说明 |
|---|---|
| public static void setIn(InputStream in) | 设置System打印的输入流 |
| public static void setOut(PrintStream out) | 设置System打印的输出流 |
| public static void setErr(PrintStream err) | 设置System打印的错误输出流 |
| public static native long currentTimeMillis(); | 获取当前毫秒值 |
| public static native long nanoTime(); | 获取当前纳秒值 |
| public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length); | 数组拷贝 |
| public static native int identityHashCode(Object x); | 计算对象的hashcode值 |
| public static Properties getProperties() | 获取JVM属性对象 |
| public static String lineSeparator() | 获取系统行分割符 |
| public static String setProperty(String key, String value) | 设置属性 |
| public static String getProperty(String key, String def) | 获取属性,没有则取默认值def |
| public static String getProperty(String key) | 获取key对应属性 |
| public static String clearProperty(String key) | 删除某个属性 |
| public static Map<String,String> getenv() | 获取运行环境,计算机名称,JDK路径等等,Maven路径 |
| public static void exit(int status) | 退出 |
| public static void gc() | 调用GC |
输入输出方法就不写上去了,毕竟天天用。
总结
System类是我们常用的一个工具类,用于获取JVM运行时系统的一些属性,调用一些较为底层的操作,如gc();,exit();等等。其底层很多使用natvie修饰的方法,可以直接操作计算机系统内存。经常使用的东西,我们往往会忽视它的存在,了解他或许会让你更加如鱼得水。我是尘心,如果觉得文章对你有帮助,可以点赞给更多的人看到。公众号:(GitHub严选),分享那些有深度的内容。