JDK源码:System

mac2024-12-17  16

说起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<String,String> 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

最新回复(0)