转载

深入分析java中的System类

System是一个类,这个System类主要是一些与系统相关的属性和方法的集合,而且其内部的方法全部是静态的,所以我们直接使用System直接调用就好,比如我们常用的一个System.out.print。这篇文章我们就来分析一下System类。

一、System概述

System就是系统的意思。因此它的主要操作肯定也是和系统信息有关。这个类位于java.lang包。可能我们都有一个疑惑,我们从来没见过System被实例化,这是因为System类内部的构造函数是私有的,在外部不能访问,因此也就不能被实例化了。

他主要有如下功能:

(1)系统信息的访问,如外部属性和环境变量等

(2)垃圾回收相关操作

(3)标准输入输出

(4)比较常用的其他操作,比如数组拷贝

接下来我们就对这些功能进行一个测试与描述:

二、System功能演示

1、获取设置属性方法

也就是说我们的System如何获取系统的属性,或者说是调用哪个方法获取属性。

(1)contains(Object value)、containsKey(Object key):判断给定的参数或属性关键字在属性表中有定义,返回True或者False;

(2)getProperty(String key)、getProperty(String key, String default):根据参数获取属性

(3)list(PrintStream s)、list(PrintWriter w): 在输出流中输出属性表内容;

(4)size():返回当前属性表中定义的属性关键字个数。

我们当然可以设置属性:

(1)put(Object key, Object value) :向属性表中追加属性关键字和关键字的值;

(2)remove(Object key) :从属性表中删除关键字。

2、获取系统属性

上面我们可以直接使用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"));
	}
}
复制代码

当然运行一下我们的控制台就有结果了:

深入分析java中的System类

在这里只是挑选了一部分进行测试,参数已经列出来了,其他的可以自己测。

三、常见操作

1、拷贝数组arraycopy

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] + " ");
	}
}
复制代码

2、获取系统时间

public class SystemTest {
	public static void main(String[] args) {
		System.out.println(System.currentTimeMillis());
        System.out.println(System.nanoTime());
	}
}
//输出:1565841056267(时间戳)
//输出:1130607059454400
复制代码

四、垃圾回收相关操作:System.gc

这句话表明运行了垃圾回收器。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());
	}
}
复制代码

我们看一下运行结果再来分析

深入分析java中的System类

我们可以看到,在进行完垃圾回收之后,再输入User相关信息时由于找不到对象,因此报了空指针异常。

我们进入到System.gc内部看一下,看看内部执行了什么操作,

public static void gc() {
      Runtime.getRuntime().gc();
}
复制代码

在这里我们可以看到其实是执行了Runtime的垃圾回收操作。我们在进入会发现其实垃圾回收就是Runtime做的。

五、源码分析

1、初始化

我们进入到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是如何初始化的,对于每一步,我们可以继续深入下去观察其具体实现,在这里就不赘述了。

2、类属性

类属性其实主要是输入输出流

public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
复制代码

3、类方法

在这里肯定不能所有的方法都讲一遍,在这里列举几个比较重要的方法。

(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类要知道其基本的内部实现以及常用的操作即可。

深入分析java中的System类
原文  https://juejin.im/post/5d550b466fb9a06ad0056890
正文到此结束
Loading...