概述
本文主要从源码角度学习
System.java
的内部组织结构和实现
具体结构可查看下图
域和相关类
in
InputStream
- 标准输入流
- 用于键盘输入或其它由主机环境或用户指定的输入源
out
PrintStream
- 标准输出流
- 用于显示输出或其它由主机环境或用户指定的输出目的
err
PrintStream
- 标准错误输出流
- 用于显示错误信息或其它立即提示用户的信息
SecurityManager
- 描述
- 允许应用实现安全策略的安全管理器
- 允许应用在执行不安全或敏感操作之前确定这个操作是什么以及此操作是否在允许被执行的安全环境里
- 有一系列以
check
开头的方法,用于在执行某些潜在敏感操作之前 - 若操作不被允许,会抛出
SecurityException
异常
public void checkPermission(Permission perm):基于当前的安全策略,若提供的权限不被允许,会抛出
SecurityException
异常;内部实现是通过AccessController.checkPermission(Permission)
来完成的123public void checkPermission(Permission perm) {java.security.AccessController.checkPermission(perm);}public Object getSecurityContext():创建一个封装当前执行环境的对象;返回的结果是
AccessControlContext
对象(封装有当前执行环境的充足信息用于执行安全检查)123public Object getSecurityContext() {return AccessController.getContext();}protected native Class[] getClassContext():
native
方法,返回当前的执行栈到Class
数组里;数组的长度是执行栈的方法数,其中指数为0
的元素是当前执行方法的类,指数为1
的元素是方法调用者的类1protected native Class[] getClassContext();
Console
- 描述
- 用于访问基于字符的终端设备,若有的话,该终端设备关联当前的Java虚拟机
- 一个虚拟机是否拥有一个终端,依赖于基础平台和虚拟机被调用的方式。若虚拟机通过交互式命令行启动,并没有重定向标准输入/输出流,那么它的终端会存在并且通常连接到启动虚拟机的键盘和显示;若果虚拟机自动启动,例如通过一个后台工作调度器,那么它通常没有终端
- 如果虚拟机有终端,那么可以通过调用
System.console()
方法可以获取唯一实例;如果没有终端设备,该方法会返回null
- 读写操作是同步的,为了保证重要操作的原子级的完成
- 当终端输入流读到最后时,终端的
read()
方法返回null
,如在UNIX
中输入Ctrl+D
或Windows
中输入Ctrl+Z
- 如果应用需要读取密码或其它安全数据,应该使用
readPassword()
等方法,并且在最小化敏感数据在内存的生命期后手动置空返回的字符数组
- private char[] readline(boolean zeroOut) throws IOException:待补充
- private Console():待补充
Properties
- 描述
- 表示一系列属性,可通过流完成存取操作;属性列表里的键和键对应的值都是字符串
- 一个属性列表可以包含有另一个属性列表作为“
defaults
”,如果属性的键在原始的属性列表里没有找到,会去第二个属性列表里查找 - 线程安全,多线程可以分享同一个
Properties
实例
public synchronized Object setProperty(String key, String value):内部调用
HashTable
的put()
方法;返回的结果是调用HashTable
的put()
方法后的结果(若提供的键在属性列表里存在,返回之前该键所在的值,否则返回null
)123public synchronized Object setProperty(String key, String value) {return put(key, value);}public String getProperty(String key):在属性列表里根据提供的键搜索对应的属性;若属性列表这个键没有找到,会去检查默认的属性列表
12345public String getProperty(String key) {Object oval = super.get(key);String sval = (oval instanceof String) ? (String)oval : null;return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;}
方法
标准输入/输出
非native方法
public static void setIn(InputStream in):重分配标准输入流
1234public static void setIn(InputStream in) {checkIO(); // 通过SecurityManager.checkPermission()检查是否有“setIO”运行时权限setIn0(in); // 调用native方法}public static void setOut(PrintStream out):重分配标准输出流
1234public static void setOut(PrintStream out) {checkIO(); // 通过SecurityManager.checkPermission()检查是否有“setIO”运行时权限setOut0(out); // 调用native方法}public static void setErr(PrintStream err):重分配标准错误流
1234public static void setErr(PrintStream err) {checkIO(); // 通过SecurityManager.checkPermission()检查是否有“setIO”运行时权限setErr0(err); // 调用native方法}
native方法
- private static native void setIn0(InputStream in):设置标准输入流
- private static native void setOut0(PrintStream out):设置标准输出流
- private static native void setErr0(PrintStream err):设置标准错误流
访问属性/环境变量
用于获取系统的相关属性、环境变量
非native方法
public static Properties getProperties():获取系统属性
12345678public static Properties getProperties() {SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPropertiesAccess(); // 首先判断是否有属性访问权限}return props; // 由initializeSystemClass()创建或setProperties()方法提供}public static String getProperty(String key):根据给定的键获取对应的系统属性
123456789public static String getProperty(String key) {checkKey(key); // 检查提供的键是否有效(即不能为null或“”)SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPropertyAccess(key); // 检查是否有属性访问权限}return props.getProperty(key); // 根据对应的键获取对应的值}public static void setProperties(Properties props):将系统属性设置为给定的属性;注意如果给定的参数为
null
,新建一个属性设置为系统属性1234567891011public static void setProperties(Properties props) {SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPropertiesAccess(); // 检查是否有属性访问权限}if (props == null) { // 给定的参数为null,新建一个系统属性,和initializeSystemClass()创建一样props = new Properties();initProperties(props);}System.props = props; // 将给定或新建的参数设置为系统属性}public static String setProperty(String key, String value):根据给定的键和值设置对应的系统属性;返回值为系统属性之前的值,若不存在则返回
null
12345678910public static String setProperty(String key, String value) {checkKey(key); // 检查提供的键是否有效(即不能为null或“”)SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPermission(new PropertyPermission(key,SecurityConstants.PROPERTY_WRITE_ACTION)); // 检查是否有写属性操作权限}return (String) props.setProperty(key, value); // 返回对应键的系统属性之前的值}public static String clearProperty(String key):根据给定的键移除对应的系统属性;返回值为系统属性之前的值,若不存在则返回
null
123456789public static String clearProperty(String key) {checkKey(key); // 检查提供的键是否有效(即不能为null或“”)SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPermission(new PropertyPermission(key, "write")); // 检查是否有写属性操作权限}return (String) props.remove(key); // 返回对应键的系统属性之前的值}public static java.util.Map
getenv() :返回当前系统变量未修改的map
视图;如果系统不支持环境变量,会返回一个空的map
;返回的map
不会含有为null
的键和值,否则查询时会抛出NullPointerException
;返回的map
的键和值都是String
类型,否则查询时会抛出ClassCastException
;返回的map
在所有平台上是大小写敏感12345678public static java.util.Map<String,String> getenv() {SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("getenv.*")); // 检查是否有“getenv*”运行时访问权限}return ProcessEnvironment.getenv(); // Java源码实现(?)}public static String getenv(String name):根据提供的环境变量名获取对应的值
12345678public static String getenv(String name) {SecurityManager sm = getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("getenv."+name));}return ProcessEnvironment.getenv(name); // Java源码实现(?)}
native方法
- private static native Properties initProperties(Properties props):完成
Properties
的初始化
加载文件/库
常用于以文件的形式加载本地库
public static void load(String filename):根据给定的文件名加载本地库;文件名必须是绝对路径名(必须带有平台指定的前缀、路径、扩展名等);通过
Runtime
完成文件的加载1234public static void load(String filename) {Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);}public static void loadLibrary(String libname):根据给定的库名称加载本地库;文件名并不必须带有平台指定的前缀、路径、扩展名等;通过
Runtime
完成文件的加载1234public static void loadLibrary(String libname) {Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);}
数组拷贝
使用
native
的方式实现数组的拷贝
- public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):从提供的源数组拷贝
[srcPos, srcPos+len)
区间的内容到目的数组的[destPos, destPos+len)
区间处
执行命令
主要执行
垃圾回收
、对象结束
、进程退出
等工作
public static void gc():运行垃圾回收器;使Java虚拟机回收未再使用的对象,让它们占用的内存可以快速复用;通过
Runtime
完成123public static void gc() {Runtime.getRuntime().gc();}public static void runFinalization():运行待结束的对象的结束方法;使Java虚拟机运行对象的
finalize()
方法,这些对象要求是被丢弃,但是finalize()
方法还未运行;通过Runtime
完成123public static void runFinalization() {Runtime.getRuntime().runFinalization();}public static void exit(int status):终结当前运行着的Java虚拟机;若给定的参数非0,表示异常终结;通过
Runtime
完成123public static void exit(int status) {Runtime.getRuntime().exit(status);}
其它
非native方法
public static Console console():如果有的话,获取当前Java虚拟机关联的
Console
单例12345678public static Console console() {if (cons == null) {synchronized (System.class) {cons = sun.misc.SharedSecrets.getJavaIOAccess().console();}}return cons;}private static void initializeSystemClass():用于初始化
System
类,在线程初始化后被调用12345678910111213141516171819private static void initializeSystemClass() {// 初始化系统属性props = new Properties();initProperties(props); // initialized by the VM// 设置文件输入/输出/错误流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")));// 加载zip库,用于之后使用java.util.zip.ZipFile加载该库loadLibrary("zip");// register shared secretssetJavaLangAccess();}
native方法
- private static native void registerNatives():通过静态初始块注册
natives
,虚拟机会调用initializeSystemClass()
方法完成初始化过程 - public static native long currentTimeMillis():获取当前毫秒级的时间;注意当返回的时间值的单位是毫秒时,粒度由基本的操作系统决定,也可能更大,如许多操作系统是几十毫秒级的单位
- public static native long nanoTime():返回运行着的Java虚拟机高解析时间源的纳秒级时间值;只用于逝去的时间,并不关联系统或挂钟时间
- public static native int identityHashCode(Object x):返回给定对象
hashCode()
方法的哈希码,无论给定对象是否重写该方法 - public static native String mapLibraryName(String libname):将给定的库名映射到指定平台的本地库名,返回指定平台的本地库名;若库名为
null
,将抛出NullPointerException