System是一个类,这个System类主要是一些与系统相关的属性和方法的集合,而且其内部的方法全部是静态的,所以我们直接使用System直接调用就好,比如我们常用的一个System.out.print。这篇文章我们就来分析一下System类。
System就是系统的意思。因此它的主要操作肯定也是和系统信息有关。这个类位于java.lang包。可能我们都有一个疑惑,我们从来没见过System被实例化,这是因为System类内部的构造函数是私有的,在外部不能访问,因此也就不能被实例化了。
他主要有如下功能:
(1)系统信息的访问,如外部属性和环境变量等
(2)垃圾回收相关操作
(3)标准输入输出
(4)比较常用的其他操作,比如数组拷贝
接下来我们就对这些功能进行一个测试与描述:
也就是说我们的System如何获取系统的属性,或者说是调用哪个方法获取属性。
(1)contains(Object value)、containsKey(Object key):判断给定的参数或属性关键字在属性表中有定义,返回True或者False;
(3)list(PrintStream s)、list(PrintWriter w): 在输出流中输出属性表内容;
(4)size():返回当前属性表中定义的属性关键字个数。
我们当然可以设置属性:
(1)put(Object key, Object value) :向属性表中追加属性关键字和关键字的值;
(2)remove(Object key) :从属性表中删除关键字。
上面我们可以直接使用System.contains等方法来调用,下面我们可以输入以下参数来获取系统信息。
功能 | 描述 |
---|---|
java.version | Java 运行时环境版本 |
java.vendor | Java 运行时环境供应商 |
java.vendor.url | Java 供应商的 URL |
java.home | Java 安装目录 |
java.vm.specification.version | Java 虚拟机规范版本 |
java.vm.specification.vendor | Java 虚拟机规范供应商 |
java.vm.specification.name | Java 虚拟机规范名称 |
java.vm.version | Java 虚拟机实现版本 |
java.vm.vendor | Java 虚拟机实现供应商 |
java.vm.name | Java 虚拟机实现名称 |
java.specification.version | Java 运行时环境规范版本 |
java.specification.vendor | Java 运行时环境规范供应商 |
java.specification.name | Java 运行时环境规范名称 |
java.class.version | Java 类格式版本号 |
java.class.path | Java 类路径 |
java.library.path | 加载库时搜索的路径列表 |
java.io.tmpdir | 默认的临时文件路径 |
java.compiler | 要使用的 JIT 编译器的名称 |
java.ext.dirs | 一个或多个扩展目录的路径 |
os.name | 操作系统的名称 |
os.arch | 操作系统的架构 |
os.version | 操作系统的版本 |
file.separator | 文件分隔符(在 UNIX 系统中是“/”) |
path.separator | 路径分隔符(在 UNIX 系统中是“:”) |
line.separator | 行分隔符(在 UNIX 系统中是“/n”) |
user.name | 用户的账户名称 |
user.home | 用户的主目录 |
user.dir | 用户的当前工作目录 |
然后使用代码测试一下几个比较典型的吧:
public class SystemTest { public static void main(String[] args) { System.out.println("Java 运行时环境版本 :" + System.getProperty("java.version")); System.out.println("Java 运行时环境供应商 :" + System.getProperty("java.vendor")); System.out.println("Java 运行时环境规范版本 :" + System.getProperty("java.specification.version")); System.out.println("Java 运行时环境规范供应商:" + System.getProperty("java.specification.vendor")); System.out.println("Java 运行时环境规范名称 :" + System.getProperty("java.specification.name")); System.out.println("操作系统的名称:" + System.getProperty("os.name")); System.out.println("操作系统的架构:" + System.getProperty("os.arch")); System.out.println("操作系统的版本:" + System.getProperty("os.version")); System.out.println("用户的账户名称 :" + System.getProperty("user.name")); System.out.println("用户的主目录 :" + System.getProperty("user.home")); System.out.println("用户的当前工作目录 : " + System.getProperty("user.dir")); } } 复制代码
当然运行一下我们的控制台就有结果了:
在这里只是挑选了一部分进行测试,参数已经列出来了,其他的可以自己测。
public class SystemTest { public static void main(String[] args) { int[] arr1 = {1,2,3,4,5 }; int[] arr2 = { 6,7,8,9,10}; /* * 第一个参数arr1:被复制的数组 * 第二个参数1:arr1中要复制的起始位置 * 第三个参数arr2:目标数组 * 第四个参数0:目标数组的复制起始位置 * 第五个参数3:目标数组的复制结束位置 */ System.arraycopy(arr1, 1, arr2, 0, 3); for (int i = 0; i < 5; i++) System.out.print(arr2[i] + " "); } } 复制代码
public class SystemTest { public static void main(String[] args) { System.out.println(System.currentTimeMillis()); System.out.println(System.nanoTime()); } } //输出:1565841056267(时间戳) //输出:1130607059454400 复制代码
这句话表明运行了垃圾回收器。java虚拟机会回收一下系统垃圾,比如说没有使用的对象。
public class SystemTest { public static void main(String[] args) { User user = new User();//新建一个对象 System.out.println(user.toString()); user=null;//将引用置为空 System.gc();//垃圾回收 System.out.println(user.toString()); } } 复制代码
我们看一下运行结果再来分析
我们可以看到,在进行完垃圾回收之后,再输入User相关信息时由于找不到对象,因此报了空指针异常。
我们进入到System.gc内部看一下,看看内部执行了什么操作,
public static void gc() { Runtime.getRuntime().gc(); } 复制代码
在这里我们可以看到其实是执行了Runtime的垃圾回收操作。我们在进入会发现其实垃圾回收就是Runtime做的。
我们进入到System的源码中,可以看到首先由这样的描述:
/* register the natives via the static initializer. * VM will invoke the initializeSystemClass method to complete * the initialization for this class separated from clinit. * Note that to use properties set by the VM, see the constraints * described in the initializeSystemClass method. */ private static native void registerNatives(); static { registerNatives(); } /** Don't let anyone instantiate this class */ private System() {} 复制代码
上面是什么意思呢?
首先:registerNatives()方法是一个入口方法,注册成了natives,也就是说该方法会令vm通过调用initializeSystemClass方法来完成初始化工作。
然后:构造函数被设置成private,说明我们不能实例化这个类,注释也已经说明了。
既然System初始化的操作是通过initializeSystemClass,我们不如进入到这个类中去看看。
private static void initializeSystemClass() { //第一步:初始化props props = new Properties(); initProperties(props); // initialized by the VM //第二步: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(); } 复制代码
通过initializeSystemClass,我们已经能够明白System是如何初始化的,对于每一步,我们可以继续深入下去观察其具体实现,在这里就不赘述了。
类属性其实主要是输入输出流
public final static InputStream in = null; public final static PrintStream out = null; public final static PrintStream err = null; 复制代码
在这里肯定不能所有的方法都讲一遍,在这里列举几个比较重要的方法。
(1)getProperty:获取系统属性
public static String getProperty(String key) { //校验key的值 checkKey(key); //检查参数是否安全 SecurityManager sm = getSecurityManager(); if (sm != null) { sm.checkPropertyAccess(key); } //获取系统属性 return props.getProperty(key); } 复制代码
我们在这里发现,其实获取属性的操作最关键的就是最后一句props.getProperty(key)。我们进入到这个方法看看:
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; } 复制代码
也就是说其实是 一直是回调defaults.getProperty(key),让父类一直不停的去调用。最后返回一个String。
(2)checkKey:校验key
private static void checkKey(String key) { if (key == null) { throw new NullPointerException("key can't be null"); } if (key.equals("")) { throw new IllegalArgumentException("key can't be empty"); } } 复制代码
里面很简单就是看看是否为空。
(3)setProperties:设置系统属性
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; } 复制代码
最核心的就是最后一行,但是前面首先检验了是否是系统安全的属性,而且也根据这个属性初始化了一次。我们进入initProperties。
private static native Properties initProperties(Properties props); 复制代码
这是一个native方法。
(4)exit():退出当前的jvm
public static void exit(int status) { Runtime.getRuntime().exit(status); } 复制代码
其实调用的也是runtime的退出方法。
(5)其他方法
public static native long currentTimeMillis(); public static native long nanoTime(); public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); public static native int identityHashCode(Object x); 复制代码
我们会发现经常操作的这些方法其实也是native的。
(6)安全管理机制
与之相关的方法有三个
public static void setSecurityManager(final SecurityManager s) { try { s.checkPackageAccess("java.lang"); } catch (Exception e) { } setSecurityManager0(s); } 复制代码
第二个:
private static synchronized void setSecurityManager0(final SecurityManager s) { SecurityManager sm = getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission ("setSecurityManager")); } if ((s != null) && (s.getClass().getClassLoader() != null)) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { s.getClass().getProtectionDomain().implies(SecurityConstants.ALL_PERMISSION); return null; } }); } security = s; InetAddressCachePolicy.setIfNotSet(InetAddressCachePolicy.FOREVER); } 复制代码
还有最后一个
public static SecurityManager getSecurityManager() { return security; } 复制代码
OK。源码分析也就先说到这里,对于System类要知道其基本的内部实现以及常用的操作即可。