JDK源码:System
发布日期:2021-06-30 21:30:18 浏览次数:2 分类:技术文章

本文共 8966 字,大约阅读时间需要 29 分钟。

说起System类,对我们来说既熟悉又陌生,熟悉的是我们刚接触java时就会用到这个类,甚至每天写代码也会用到这个类(System.out.println());陌生的是我们对这个类一知半解,它是如何实现的,为什么这样写就能在屏幕上输出东西。今天就让我们通过阅读它的源码揭开它那神秘的面纱。

基础知识:

深拷贝与浅拷贝:

浅拷贝:只是简单的对对象的引用进行复制。

深拷贝:拷贝对象的值和对象的内容。

类定义:

public final class System {}

System类是被final修饰的,不能被继承。

成员变量:

//标准输入流,一般接收键盘输入    public final static InputStream in = null;    //标准输出流,向控制台输出正确信息    public final static PrintStream out = null    //标准输出流,向控制台输出错误信息    public final static PrintStream err = null;    //安全管理器,用volatile关键字修饰,表示对象在多个线程间可见    private static volatile SecurityManager security = null;    //JAVA控制台对象与JAVA虚拟机相联,接收控制台输入,或向控制台输出信息。只能在控制台运行代码,否则获得对象为null。    private static volatile Console cons = null;    //用于承装系统属性,如os.name os.version等    private static Properties props;    //换行符widows返回"\r\n",UNIX系统返回"\n"    private static String lineSeparator;

看到这些成员变量,有些是公有的,有些是私有的。这些对象被赋值为null,而且有的被final修饰不能被重新赋值。找遍整个类也没看到在哪里初始化这些对象。但我们使用System.out.println();的时候并没有报错啊。能够使用,就说明对象在谋个时刻被创建了,那到底这些对象是什么时候被创建的呢。怀着无比好奇的心理,在类中查找,忽然我看到了如下代码。

static {        registerNatives();        private static native void registerNatives();

静态代码块,类被加载的时候执行,会不会这些对象是在registerNatives方法中创建的呢。再一看这个方法是本地方法。看下注释,也看不懂,但隐约能看到VM啊,initializer啊,initializeSystemClass method之类的,找了一下,发现类中有一个initializeSystemClass方法,大概能猜到,是VM会执行这个方法。

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();    }

在这个方法中可以看到刚才那些对象都是在这里被创建的。

lineSeparator = props.getProperty("line.separator");从系统中获取换行符。
setIn0(new BufferedInputStream(fdIn));设置标准输入流,也就是创建in对象,追溯到源头也就是创建了FileDescriptor.in对象,这个对象用于操作标准输入流,再往深了就到了虚拟机内部实现细节了,这里就有展开了,有兴趣的同学可以自行研究。
其它的对象像out、err创建过程同in类似。到这里我们终于明白了为什么执行System.out.println()时会向控制台输出东西了。

构造函数:

private System() {    }

可以看到System类就一个构造函数,而且被私有化了,不允许我们创建对象。如果要使用这个类,只能使用类中的一些静态成员。这也是java中单例模式的雏形,把构造私有化,然后对外提供一个内部创建的唯一的对象,就形成单例了。

其它函数:

setIn:

public static void setIn(InputStream in) {        checkIO();        setIn0(in);    }    //对setIo进行安全检查    private static void checkIO() {        SecurityManager sm = getSecurityManager();        if (sm != null) {            sm.checkPermission(new RuntimePermission("setIO"));        }    }    //给System.in重新赋值,内部实现。    private static native void setIn0(InputStream in);

给System.in对象重新赋值。即当调用了些方法后,我们再使用System.in的时候接收的信息不再是从键盘录入了,而是我们指定的文件或设备。点开函数可以看到,第一个函数是进行安全检查的,第二个函数是调用的本地方法。有人疑惑System.in变量不是被final修饰了吗,为什么还可以被修改。额...这个...它调用的是本地方法,由虚拟机内部实现,他是老大能不能改变他说了算。

setOut:

public static void setOut(PrintStream out) {        checkIO();        setOut0(out);    }

给System.out重新赋值,原理同setIn。比如我们想让System.out.println()把内容输出到文件,就可以调用这个方法,指定输出文件。

setErr:

public static void setErr(PrintStream err) {        checkIO();        setErr0(err);    }

给System.err重新赋值,原理同setOut。

console:

public static Console console() {         if (cons == null) {             synchronized (System.class) {                 cons = sun.misc.SharedSecrets.getJavaIOAccess().console();             }         }         return cons;     }

返回唯一的Console对象,这个对象是跟Java虚拟机相关联的。可以看到这里使用了synchronized代码块,避免多线程调用的时候返回多个Console对象。

inheritedChannel:

public static Channel inheritedChannel() throws IOException {        return SelectorProvider.provider().inheritedChannel();    }

返回从创建此 java 虚拟机的实体中继承的通道。如果有则返回,没有返回null。

SecurityManager设置与获取:

public static void setSecurityManager(final SecurityManager s) {        try {            s.checkPackageAccess("java.lang");        } catch (Exception e) {            // no-op        }        setSecurityManager0(s);    }    public static SecurityManager getSecurityManager() {        return security;    }

设置和获取java的安全管理器,当运行未知的Java程序的时候,该程序可能有操作如删除系统文件、重启系统等,为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。

currentTimeMillis:

public static native long currentTimeMillis();

获取当前系统时间的毫秒数。1秒=1000毫秒(ms);

nanoTime:

public static native long nanoTime();

获取当前系统时间的纳秒数。1秒=1,000,000,000 纳秒(ns);

arraycopy:

public static native void arraycopy(                                    Object src,//源数组                                    int  srcPos, //源数组要复制的起始位置                                    Object dest, //目标数组                                    int destPos, //目标数组的起始位置                                    int length//复制的长度                                    );

复制数组,从一个明确的数组中复制数组元素到另一个数组中。System.arraycopy采用的是浅复制,使用的时候注意一下。

identityHashCode:

public static native int identityHashCode(Object x);

获取对象的hashCode,一般可通过对象的hashCode()方法获取对象的hashCode,但有些对象重写了hashCode()方法,那么想要获取对象的hashCode时就要使用本方法了。

props:

private static Properties props;    private static native Properties initProperties(Properties 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;    }    public static Properties getProperties() {        SecurityManager sm = getSecurityManager();        if (sm != null) {            sm.checkPropertiesAccess();        }        return props;    }

props是用于承装java的系统变量的,initProperties是初始化props对象,本地方法实现。setProperties()重新设置系统的Properties对象。getProperties()方法用于获取Properties对象。不论是设置还是获取都先会进行安全检查,看是否对系统属性有安全限制。获取Properties对象后可重新设值取值。

public static String setProperty(String key, String value) {        checkKey(key);        SecurityManager sm = getSecurityManager();        if (sm != null) {            sm.checkPermission(new PropertyPermission(key,                SecurityConstants.PROPERTY_WRITE_ACTION));        }        return (String) props.setProperty(key, value);    }

设置系统属性到Properties对象中,如果已经存在则覆盖原有属性,并返回原有属性。该对象是全局的,在程序中到处都可以调用。

getProperty:

public static String getProperty(String key) {        checkKey(key);        SecurityManager sm = getSecurityManager();        if (sm != null) {            sm.checkPropertyAccess(key);        }        return props.getProperty(key);    }

从Properties中获取系统属性,如果有则返回,如果没有返回null。

getProperty(String key, String def):

public static String getProperty(String key, String def) {        checkKey(key);        SecurityManager sm = getSecurityManager();        if (sm != null) {            sm.checkPropertyAccess(key);        }        return props.getProperty(key, def);    }

从Properties中获取系统属性,如果有则返回,如果没有返回默认值,第二个参数为默认值。

clearProperty:

public static String clearProperty(String key) {        checkKey(key);        SecurityManager sm = getSecurityManager();        if (sm != null) {            sm.checkPermission(new PropertyPermission(key, "write"));        }        return (String) props.remove(key);    }

用于清除系统属性,并返回原属性值。

lineSeparator:

public static String lineSeparator() {        return lineSeparator;    }

获取系统的换行符widows系统返回"\r\n",UNIX系统返回"\n"。

getenv:

public static java.util.Map
getenv() { SecurityManager sm = getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("getenv.*")); } return ProcessEnvironment.getenv(); }

获取java程序环境信息,如LOCALAPPDATA,JAVA_HOME等。返回的是一个Map对象。

exit:

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

结束当前正在运行中的java虚拟机。参数0代表正常退出,非0代表异常退出。

gc:

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

该方法用于告知虚拟机进行垃圾回收。垃圾收集器将运行以回收未使用的内存空间。注意这一步只是通知虚拟机要进行垃圾回收操作,虚拟机并不一定立即进行回收操作。

runFinalization:

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

我们知道Object对象不有一个finalize方法,当进行垃圾回收时,对象被回收前会执行finalize方法。调用runFinalization方法运行处于挂起终止状态的所有对象的终止方法。Java虚拟机已经尽最大努力去完成所有未执行的终止方法。

load:

public static void load(String filename) {        Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);    }

用于装载指定名称filename的系统库文件。filename必需是绝对路径。

loadLibrary:

public static void loadLibrary(String libname) {        Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);    }

加载系统中的本地库,如果是Windows系统加载的是xxx.dll,如果是unix系统加载的是xxx.so,注意该方法不能包含文件的扩展名,但该文件必需在JVM属性java.library.path所指向的路径中。

mapLibraryName:

public static native String mapLibraryName(String libname);

将一个库名称映射到特定于平台的、表示本机库的字符串中。有点抽象,比如你想映射mapLibraryName这个库名,映射到Windows平台就是mapLibraryName.dll映射到Unix平台就是mapLibraryName.so

转载地址:https://lizz6.blog.csdn.net/article/details/102849133 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Spring版本线:新特性
下一篇:回归主线

发表评论

最新留言

很好
[***.229.124.182]2024年04月20日 01时29分19秒