java.lang.System源码分析

1.final修饰,不能被继承。构造函数使用private修饰,不能被其他类实例化

    public final class System {
        ...
        private System() {
        }
        ...
    }

2.成员变量:final修饰,不能被重新赋值    

    public final static InputStream in = null;// 标准输入流,默认已经打开,一般用来响应键盘或者其他用户指定的输入
    public final static PrintStream out = null;// 标准输出流,默认已经打开,一般用来展示输出
    public final static PrintStream err = null;// 标准错误输出流,默认已经打开,按照约定,用来输出错误信息
    private static native void setIn0(InputStream in);// 初始化这些流的工作由本地native方法实现,由系统连接的共享库去实现
    private static native void setOut0(PrintStream out);
    private static native void setErr0(PrintStream err);
    private static volatile SecurityManager security = null;// 系统安全管理器,使用volatile修饰该变量。在初始化IO流时,需要使用安全器校验权限
    private static volatile Console cons = null;// 标准输出控制台

3.SecurityManager,安全管理器,用来做访问权限校验

    public static void setSecurityManager(final SecurityManager s) {
        try {
            s.checkPackageAccess("java.lang");
        } catch (Exception e) {
            // no-op
        }
        setSecurityManager0(s);
    }
    private static synchronized void setSecurityManager0(final SecurityManager s) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            // ask the currently installed security manager if we
            // can replace it.
            sm.checkPermission(new RuntimePermission("setSecurityManager"));
        }
        if ((s != null) && (s.getClass().getClassLoader() != null)) {
            // New security manager class is not on bootstrap classpath.
            // Cause policy to get initialized before we install the new
            // security manager, in order to prevent infinite loops when
            // trying to initialize the policy (which usually involves
            // accessing some security and/or system properties, which in turn
            // calls the installed security manager's checkPermission method
            // which will loop infinitely if there is a non-system class
            // (in this case: the new security manager class) on the stack).
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    s.getClass().getProtectionDomain().implies
                        (SecurityConstants.ALL_PERMISSION);
                    return null;
                }
            });
        }
        security = s;
    }
    private static void checkIO() {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setIO"));
        }
    }
    public void checkPermission(Permission perm) {
        java.security.AccessController.checkPermission(perm);
    }

4.Console 只能用在标准输入、输出流未被重定向的原始控制台中使用

    public static Console console() {
         if (cons == null) {
             synchronized (System.class) {
                 cons = sun.misc.SharedSecrets.getJavaIOAccess().console();// console是通过sun.misc.SharedSecrets 类获取得到的(作用是从JVM里面获取实例对象)
             }
         }
         return cons;
     }

5.获取系统时间,都是通过本地native方法实现

    public static native long currentTimeMillis();// 获取毫秒级的时间戳(1970年1月1日0时起的毫秒数)
    public static native long nanoTime();// 获取纳秒,返回的可能是任意时间(主要用于衡量时间段)

6.数组拷贝

public static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

    此方法在Collection的实现类里面扩容的时候经常被遇到,例如 ArrayList.add(int, E)。

    还有另外一个比较常用的数组拷贝的方法:Arrays.copyOf,通过看源码会发现,最后实际调用的也是 Syetem.arraycopy 。    

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

7.获取对象的地址,本地native方法

public static native int identityHashCode(Object x);

    当Object的hashCode()被重写的时候,如何获取到对象的地址呢?通过 System.identityHashCode(Object) 可以获取到对象的地址。

public class OverrideHashCode {
       @Override
       public int hashCode() {
             //return super.hashCode();
             return 0;
       }
}

OverrideHashCode overrideHash = new OverrideHashCode();
System.out.println("覆盖之后,hashCode(): " + overrideHash.hashCode());
System.out.println("通过System.identityHashCode()重新获取对象地址: " + System.identityHashCode(overrideHash));

    测试结果:

覆盖之后,hashCode(): 2018699554 // 未被重写
通过System.identityHashCode()重新获取对象地址: 2018699554
覆盖之后,hashCode(): 0 // 重写后的值
通过System.identityHashCode()重新获取对象地址: 2018699554

8.系统变量

    private static Properties props;
    private static native Properties initProperties(Properties props);// 初始化是通过本地方法实现的
    public static Properties getProperties() {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {// 获取系统变量的时候,需要做权限校验。如果没有权限,会抛出 AccessControlException 
            sm.checkPropertiesAccess();
        }
        return props;
    }
    public static void setProperties(Properties props) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertiesAccess();
        }
        if (props == null) {
            props = new Properties();
            initProperties(props);
        }
        System.props = props;
    }

9.获取操作系统环境变量,用户自己配置的系统变量,也做了权限校验

    public static String getenv(String name) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("getenv."+name));
        }
        return ProcessEnvironment.getenv(name);
    }

    测试使用:

System.out.println(System.getenv("ROCKETMQ_HOME"));

    能够获取,之前在Windows中设置的环境变量的值。

10.程序退出,接收一个参数status,0表示正常退出,非零参数表示非正常退出。不管status为何值都会退出程序。和return 相比,return是回到上一层,而System.exit(status)是回到最上层

    public static void exit(int status) {
        Runtime.getRuntime().exit(status);// 实际上通过获取Runtime运行时来退出的
    }

11.手动调用gc-垃圾回收器,注意调用后不会马上发生消息回收,JVM会在适合的时候触发GC

    public static void gc() {
        Runtime.getRuntime().gc();
    }

12.加载动态库(Windows下面是dll文件),用来装载库文件,不论是JNI库文件还是非JNI库文件

    @CallerSensitive
    public static void load(String filename) {// fileName一定要是绝对路径,否则会抛出 UnsatisfiedLinkError
        Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);
    }
    @CallerSensitive
    public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
    }
    好了,Syetem的代码就这么多~
上一篇:故障案例-ESXI6.5主机无法发生重启,并有发生网卡无故UP DOWN的事件


下一篇:IBM投资2亿美金在德国慕尼黑建立Watson物联网总部