System.java 源码学习

概述

本文主要从源码角度学习System.java的内部组织结构和实现

具体结构可查看下图

域和相关类

in

  • InputStream
  • 标准输入流
  • 用于键盘输入或其它由主机环境或用户指定的输入源

out

  • PrintStream
  • 标准输出流
  • 用于显示输出或其它由主机环境或用户指定的输出目的

err

  • PrintStream
  • 标准错误输出流
  • 用于显示错误信息或其它立即提示用户的信息

SecurityManager

  1. 描述
    • 允许应用实现安全策略的安全管理器
    • 允许应用在执行不安全或敏感操作之前确定这个操作是什么以及此操作是否在允许被执行的安全环境里
    • 有一系列以check开头的方法,用于在执行某些潜在敏感操作之前
    • 若操作不被允许,会抛出SecurityException异常
  2. public void checkPermission(Permission perm):基于当前的安全策略,若提供的权限不被允许,会抛出SecurityException异常;内部实现是通过AccessController.checkPermission(Permission)来完成的

    1
    2
    3
    public void checkPermission(Permission perm) {
    java.security.AccessController.checkPermission(perm);
    }
  3. public Object getSecurityContext():创建一个封装当前执行环境的对象;返回的结果是AccessControlContext对象(封装有当前执行环境的充足信息用于执行安全检查)

    1
    2
    3
    public Object getSecurityContext() {
    return AccessController.getContext();
    }
  4. protected native Class[] getClassContext()native方法,返回当前的执行栈到Class数组里;数组的长度是执行栈的方法数,其中指数为0的元素是当前执行方法的类,指数为1的元素是方法调用者的类

    1
    protected native Class[] getClassContext();

Console

  1. 描述
    • 用于访问基于字符的终端设备,若有的话,该终端设备关联当前的Java虚拟机
    • 一个虚拟机是否拥有一个终端,依赖于基础平台和虚拟机被调用的方式。若虚拟机通过交互式命令行启动,并没有重定向标准输入/输出流,那么它的终端会存在并且通常连接到启动虚拟机的键盘和显示;若果虚拟机自动启动,例如通过一个后台工作调度器,那么它通常没有终端
    • 如果虚拟机有终端,那么可以通过调用System.console()方法可以获取唯一实例;如果没有终端设备,该方法会返回null
    • 读写操作是同步的,为了保证重要操作的原子级的完成
    • 当终端输入流读到最后时,终端的read()方法返回null,如在UNIX中输入Ctrl+DWindows中输入Ctrl+Z
    • 如果应用需要读取密码或其它安全数据,应该使用readPassword()等方法,并且在最小化敏感数据在内存的生命期后手动置空返回的字符数组
  2. private char[] readline(boolean zeroOut) throws IOException待补充
  3. private Console()待补充

Properties

  1. 描述
    • 表示一系列属性,可通过流完成存取操作;属性列表里的键和键对应的值都是字符串
    • 一个属性列表可以包含有另一个属性列表作为“defaults”,如果属性的键在原始的属性列表里没有找到,会去第二个属性列表里查找
    • 线程安全,多线程可以分享同一个Properties实例
  2. public synchronized Object setProperty(String key, String value):内部调用HashTableput()方法;返回的结果是调用HashTableput()方法后的结果(若提供的键在属性列表里存在,返回之前该键所在的值,否则返回null

    1
    2
    3
    public synchronized Object setProperty(String key, String value) {
    return put(key, value);
    }
  3. public String getProperty(String key):在属性列表里根据提供的键搜索对应的属性;若属性列表这个键没有找到,会去检查默认的属性列表

    1
    2
    3
    4
    5
    public 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;
    }

方法

标准输入/输出

主要是关于大家熟悉的inouterr等标准流

非native方法

  1. public static void setIn(InputStream in):重分配标准输入流

    1
    2
    3
    4
    public static void setIn(InputStream in) {
    checkIO(); // 通过SecurityManager.checkPermission()检查是否有“setIO”运行时权限
    setIn0(in); // 调用native方法
    }
  2. public static void setOut(PrintStream out):重分配标准输出流

    1
    2
    3
    4
    public static void setOut(PrintStream out) {
    checkIO(); // 通过SecurityManager.checkPermission()检查是否有“setIO”运行时权限
    setOut0(out); // 调用native方法
    }
  3. public static void setErr(PrintStream err):重分配标准错误流

    1
    2
    3
    4
    public static void setErr(PrintStream err) {
    checkIO(); // 通过SecurityManager.checkPermission()检查是否有“setIO”运行时权限
    setErr0(err); // 调用native方法
    }

native方法

  1. private static native void setIn0(InputStream in):设置标准输入流
  2. private static native void setOut0(PrintStream out):设置标准输出流
  3. private static native void setErr0(PrintStream err):设置标准错误流

访问属性/环境变量

用于获取系统的相关属性、环境变量

非native方法

  1. public static Properties getProperties():获取系统属性

    1
    2
    3
    4
    5
    6
    7
    8
    public static Properties getProperties() {
    SecurityManager sm = getSecurityManager();
    if (sm != null) {
    sm.checkPropertiesAccess(); // 首先判断是否有属性访问权限
    }
    return props; // 由initializeSystemClass()创建或setProperties()方法提供
    }
  2. public static String getProperty(String key):根据给定的键获取对应的系统属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static String getProperty(String key) {
    checkKey(key); // 检查提供的键是否有效(即不能为null或“”)
    SecurityManager sm = getSecurityManager();
    if (sm != null) {
    sm.checkPropertyAccess(key); // 检查是否有属性访问权限
    }
    return props.getProperty(key); // 根据对应的键获取对应的值
    }
  3. public static void setProperties(Properties props):将系统属性设置为给定的属性;注意如果给定的参数为null,新建一个属性设置为系统属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public 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; // 将给定或新建的参数设置为系统属性
    }
  4. public static String setProperty(String key, String value):根据给定的键和值设置对应的系统属性;返回值为系统属性之前的值,若不存在则返回null

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public 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); // 返回对应键的系统属性之前的值
    }
  5. public static String clearProperty(String key):根据给定的键移除对应的系统属性;返回值为系统属性之前的值,若不存在则返回null

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public 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); // 返回对应键的系统属性之前的值
    }
  6. public static java.util.Map getenv():返回当前系统变量未修改的map视图;如果系统不支持环境变量,会返回一个空的map;返回的map不会含有为null的键和值,否则查询时会抛出NullPointerException;返回的map的键和值都是String类型,否则查询时会抛出ClassCastException;返回的map在所有平台上是大小写敏感

    1
    2
    3
    4
    5
    6
    7
    8
    public static java.util.Map<String,String> getenv() {
    SecurityManager sm = getSecurityManager();
    if (sm != null) {
    sm.checkPermission(new RuntimePermission("getenv.*")); // 检查是否有“getenv*”运行时访问权限
    }
    return ProcessEnvironment.getenv(); // Java源码实现(?)
    }
  7. public static String getenv(String name):根据提供的环境变量名获取对应的值

    1
    2
    3
    4
    5
    6
    7
    8
    public static String getenv(String name) {
    SecurityManager sm = getSecurityManager();
    if (sm != null) {
    sm.checkPermission(new RuntimePermission("getenv."+name));
    }
    return ProcessEnvironment.getenv(name); // Java源码实现(?)
    }

native方法

  1. private static native Properties initProperties(Properties props):完成Properties的初始化

加载文件/库

常用于以文件的形式加载本地库

  1. public static void load(String filename):根据给定的文件名加载本地库;文件名必须是绝对路径名(必须带有平台指定的前缀、路径、扩展名等);通过Runtime完成文件的加载

    1
    2
    3
    4
    @CallerSensitive
    public static void load(String filename) {
    Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);
    }
  2. public static void loadLibrary(String libname):根据给定的库名称加载本地库;文件名并不必须带有平台指定的前缀、路径、扩展名等;通过Runtime完成文件的加载

    1
    2
    3
    4
    @CallerSensitive
    public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
    }

数组拷贝

使用native的方式实现数组的拷贝

  1. public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):从提供的源数组拷贝[srcPos, srcPos+len)区间的内容到目的数组的[destPos, destPos+len)区间处

执行命令

主要执行垃圾回收对象结束进程退出等工作

  1. public static void gc():运行垃圾回收器;使Java虚拟机回收未再使用的对象,让它们占用的内存可以快速复用;通过Runtime完成

    1
    2
    3
    public static void gc() {
    Runtime.getRuntime().gc();
    }
  2. public static void runFinalization():运行待结束的对象的结束方法;使Java虚拟机运行对象的finalize()方法,这些对象要求是被丢弃,但是finalize()方法还未运行;通过Runtime完成

    1
    2
    3
    public static void runFinalization() {
    Runtime.getRuntime().runFinalization();
    }
  3. public static void exit(int status):终结当前运行着的Java虚拟机;若给定的参数非0,表示异常终结;通过Runtime完成

    1
    2
    3
    public static void exit(int status) {
    Runtime.getRuntime().exit(status);
    }

其它

非native方法

  1. public static Console console():如果有的话,获取当前Java虚拟机关联的Console单例

    1
    2
    3
    4
    5
    6
    7
    8
    public static Console console() {
    if (cons == null) {
    synchronized (System.class) {
    cons = sun.misc.SharedSecrets.getJavaIOAccess().console();
    }
    }
    return cons;
    }
  2. private static void initializeSystemClass():用于初始化System类,在线程初始化后被调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    private 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 secrets
    setJavaLangAccess();
    }

native方法

  1. private static native void registerNatives():通过静态初始块注册natives,虚拟机会调用initializeSystemClass()方法完成初始化过程
  2. public static native long currentTimeMillis():获取当前毫秒级的时间;注意当返回的时间值的单位是毫秒时,粒度由基本的操作系统决定,也可能更大,如许多操作系统是几十毫秒级的单位
  3. public static native long nanoTime():返回运行着的Java虚拟机高解析时间源的纳秒级时间值;只用于逝去的时间,并不关联系统或挂钟时间
  4. public static native int identityHashCode(Object x):返回给定对象hashCode()方法的哈希码,无论给定对象是否重写该方法
  5. public static native String mapLibraryName(String libname):将给定的库名映射到指定平台的本地库名,返回指定平台的本地库名;若库名为null,将抛出NullPointerException

参考方案

  1. java.lang.System Class
  2. java.lang.System in Java