java.lang.System类的源码分析
API简介:
System 类包含一些有用的类字段和方法。它不能被实例化。
在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。
类声明:
public final class System
可以得出这个类不能被继承
静态代码块和registerNatives()方法
/* First thing---register the natives */
private static native void registerNatives();
static {
registerNatives();
}
说明:和Object类一样,registerNatives()方法会被静态代码块首先指向,而registerNatives又是native方法,目前水平有限,只该方法与JNI有关。
构造器
/** Don't let anyone instantiate this class */
private System() {
}
说明:API中说System类不可被实例化,原因就在这了,唯一的一个构造器被声明为private方法。所以new对象的时候对象没有权限调用该构造器,也就不能实例化
成员变量:
- 输入流(in):
public final static InputStream in = nullInputStream();
说明:
- 这是给类对象,所以可以通过System.in直接调用。
- 而类型是InputStream 这个输入字节流抽象类(这个类是所有输入字节流类的超类。)
- nullInputStream()方法是System的一个类方法。但是翻看源码时,却发现并没有返回任何一个’正常’的输入流对象,由此猜想可能是registerNatives()已经给初始化了.
(具体代码在【函数】部分的第11节)
题外话:
其实仔细想想也能想通。既然in是所有java输入的第一个’出生’的对象,而在这之前java没有任何与输入输出有关的类/对象,所以这个in肯定是底层实现的,而java本身因为’跨平台的’优势,那么也就牺牲掉了对操作系统的底层操作的实现,所以只能通过c/c++来获得IO流
- 需要记住的是:in成员的的确确是InputStream类的对象,而不是System的内部类的对象(面试有可能问到)
简单应用:(不过咱们从来不会这么用,毕竟in是InputStream类,方法太少,而且得到的AscII码,但是效率是最高的)
package com.system;
import java.io.IOException;
public class test1 {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
int i = System.in.read(); //返回读入的ASCII码 如:2 ->50
System.out.print(i);
}
}
- 输出流(out):
public final static PrintStream out = nullPrintStream();
说明
- 和in类似,也是类对象,System.out可以直接调用(这个可用的就多了)
- 类型是 PrintStream 这个和in是不一样的,(毕竟如果in对应的话类型应该是OutputStream,PrintStream 是其子类的子类),所以方法多,我们也就用的多
- nullPrintStream():和nullInputStream()差不多(这个是真的差不多,而不同之处都用native实现了)
- 需要记住的是:out也是PrintStream类对象,而不是System的内部类
3.错误流(err):
public final static PrintStream err = nullPrintStream();
说明:
- 和out很相似,仅通过这个是辨别不出err和out的区别(毕竟具体实现都是native),所以只能通过Sun给的解释了:
/**
* The "standard" error output stream. This stream is already
* open and ready to accept output data.
* <p>
* Typically this stream corresponds to display output or another
* output destination specified by the host environment or user. By
* convention, this output stream is used to display error messages
* or other information that should come to the immediate attention
* of a user even if the principal output stream, the value of the
* variable <code>out</code>, has been redirected to a file or other
* destination that is typically not continuously monitored.
*/
(看不懂吧。。。我也看不懂(笑哭。。))
4.security :
/* The security manager for the system.
*/
private static volatile SecurityManager security = null;
说明:
- 注释只有这一句话,好像是java系统的安全管理者 ,但是又没有对其进行初始化。。。
volatile是一个类型修饰符(type specifier),就像大家更熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了SecurityManager:安全管理器是一个允许应用程序实现安全策略的类。它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。应用程序可以允许或不允许该操作。 –来自java API
5.cons
private static volatile Console cons = null;
说明:
- 不太明白,好像通过这个对象能直接与操作系统交互
6.props
private static Properties props;
说明:表示当前系统的各种属性(JDK和操作系统),有一堆重载的getProperties()方法
常用方法:
1.currentTimeMillis():返回当前时间的毫秒数(与1970年1月1日的差值)
public static native long currentTimeMillis();
说明:
- 此方法是native方法,所以具体怎么实现的咱们也就不要去操心了
- 实际用处举例:可以通过与Calendar类的结合返回当前时间(当然我们一帮不用这种方式获得时间)
2.nanoTime():可以得到毫微秒的更加准确的时间
public static native long nanoTime();
3.arraycopy(Object src, int srcPos, Object dest, int destPos,int length):数组拷贝
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
说明:
- System最常用的几个方法之一(尤其是在设计类库的时候)
- 5个参数的说明:
- 第一个参数表示被复制数组
- 第二个参数表示要开始复制的起始位置
- 第三个数组表示目标数组
- 第四个表示要复制到dest的起始位置
- 第五个参数表示要复制的长度(从srcPos 到 srcPos+length-1(即不包括第length个元素))
- 关于异常的情况:具体看API,这里只说当src和dest不是数组的时候会抛出ArrayStoreException异常
- 一定程度上也说明了数组的本质也是对象
4.identityHashCode(Object x):返回引用对象的HashCode().
public static native int identityHashCode(Object x);
说明:
再次重温HashCode的原则:相同对象的hashCode()相同
5.exit(int status):终止当前正在运行的 Java 虚拟机
public static void exit(int status) {
Runtime.getRuntime().exit(status);
}
说明:参数用作状态码;根据惯例,非零的状态码表示异常终止。
6.gc():调用垃圾回收器
public static void gc() {
Runtime.getRuntime().gc();
}
gc是java进阶部分必学的知识点
方法:
1.setIn(InputStream in):将System.in重置
public static void setIn(InputStream in) { checkIO(); setIn0(in); }
说明:
重新分配“标准”输入流。
首先,如果有安全管理器,则通过 RuntimePermission(“setIO”) 权限调用其 checkPermission 方法,查看是否可以重新分配“标准”输入流。异常: SecurityException – 如果安全管理器存在并且其checkPermission方法不允许重新分配标准输入流。
例子:
// existing file
System.setIn(new FileInputStream("file.txt")); //在项目的根目录下有一个file.txt文件,里面有一堆a
// read the first character in the file
char ret = (char)System.in.read(); //读出来文件的第一个字符
// returns the first character
System.out.println(ret);
2.setOut(PrintStream out):将System.out重置
public static void setOut(PrintStream out) { checkIO(); setOut0(out); }
说明:和setIn()差不多,直接上例子
例子:
System.setOut(new PrintStream(new FileOutputStream("file1.txt"))); //当然用FileOutputStream打开文件得到的是乱码,但是可以肯定的是输入了东西了。
byte b = 2;
System.out.write(b);
3.setErr(PrintStream err):将System.err重置
public static void setErr(PrintStream err) {
checkIO();
setErr0(err);
}
4.console()
public static Console console() {
if (cons == null) {
synchronized (System.class) {
cons = sun.misc.SharedSecrets.getJavaIOAccess().console();
}
}
return cons;
}
说明:
- 和System的成员cons有关。
- 与并发动作有关。以后并发学完之后记得回来补上
- 这有一个说明JDK中的并发bug?
5.inheritedChannel():
public static Channel inheritedChannel() throws IOException {
return SelectorProvider.provider().inheritedChannel();
}
说明:
- 返回从创建此 Java 虚拟机的实体中继承的信道。
此方法返回通过调用系统级默认 SelectorProvider 对象的 inheritedChannel 方法获得的信道。
除了 inheritedChannel 中描述的面向网络的信道之外,此方法以后还可能返回其他种类的信道。
继承的信道(如果有),否则返回 null。
6.checkIO()/setIn0/setOut0/setErr0:
private static void checkIO() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setIO"));
}
}
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
这四个方法都是私有的,是setIn()/setOut()/setErr()的底层实现,而且还是native的。。
7.关于安全管理机制的三个方法getSecurityManager/setSecurityManager0/setSecurityManager。(以后再说)
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() {
public Object run() {
s.getClass().getProtectionDomain().implies
(SecurityConstants.ALL_PERMISSION);
return null;
}
});
}
security = s;
InetAddressCachePolicy.setIfNotSet(InetAddressCachePolicy.FOREVER);
}
public static SecurityManager getSecurityManager() {
return security;
}
8.各种姿势的setProperties()/getProperties():
8.0 先来一个初始化props的方法(native没什么好说的)
private static native Properties initProperties(Properties props);
8.1 getProperties():直接返回props。
public static Properties getProperties() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertiesAccess();
}
return props;
}
/**再来一段遍历的代码:
Properties properties =System.getProperties();
for (String key : properties.stringPropertyNames()) {
System.out.println(key + "=" + properties.getProperty(key));
}
*/
8.2 setProperties():重置System.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;
}
说明:
8.3 getProperty(String key):返回给定key值的value(如果不存在就返回null)
public static String getProperty(String key) {
checkKey(key);
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertyAccess(key);
}
return props.getProperty(key);
}
具体到System.props就是返回相应的系统属性的值
实际上调用的是 props.getProperty(key)方法
8.4 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);
}
具体到System.props就是获得用指定键描述的系统属性。如果没有当前系统属性的集合,将用与 getProperties 方法相同的方式首先创建并初始化系统属性的集合(def就是value)。
8.5 setProperty(String key, String value):设置系统属性的值
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);
}
8.6 clearProperty(String key):删除某个键值对
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);
}
8.7 checkKey(String 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");
}
}
8.8 getenv(String name) 返回指定的系统的环境变量(没有则为null)
public static String getenv(String name) {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getenv."+name));
}
return ProcessEnvironment.getenv(name);
}
系统属性(Property)和环境变量(env):
从概念上讲,系统属性 和环境变量 都是名称与值之间的映射。两种机制都能用来将用户定义的信息传递给 Java 进程。环境变量产生更多的全局效应,因为它们不仅对紧接着出现的 Java 子进程可见,而且对于定义它们的进程的所有子进程都是可见的。在不同的操作系统上,它们的语义有细微的差别,比如,不区分大小写。因为这些原因,环境变量更可能有意料不到的副作用。最好在可能的地方使用系统属性。环境变量应该在需要全局效应的时候使用,或者在外部系统接口要求使用环境变量时使用(比如 PATH)。
8.9 getenv():返回整个系统环境变量
public static java.util.Map<String,String> getenv() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getenv.*"));
}
return ProcessEnvironment.getenv();
}
9.runFinalization():调用已经处于挂起状态的对象的finalize 方法(但是java不鼓励程序员主动调用finalize)
public static void runFinalization() {
Runtime.getRuntime().runFinalization();
}
10.load(String filename)/loadLibrary(String libname)/mapLibraryName(String libname)
public static void load(String filename) {
Runtime.getRuntime().load0(getCallerClass(), filename);
}
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(getCallerClass(), libname);
}
public static native String mapLibraryName(String libname);
说明:
- load(String filename):从作为动态库的本地文件系统中以指定的文件名加载代码文件。文件名参数必须是完整的路径名。
- loadLibrary(String libname):加载由 libname 参数指定的系统库。将库名映射到实际系统库的方法取决于系统。
- mapLibraryName(String libname):将一个库名称映射到特定于平台的、表示本机库的字符串中。
11.nullInputStream()/nullPrintStream() :前面已经提到过
private static InputStream nullInputStream() throws NullPointerException {
if (currentTimeMillis() > 0) {
return null;
}
throw new NullPointerException();
}
private static PrintStream nullPrintStream() throws NullPointerException {
if (currentTimeMillis() > 0) {
return null;
}
throw new NullPointerException();
}
12.initializeSystemClass():?????(大BOSS 字面意思是初始化系统类??不懂。。)
private static void initializeSystemClass() {
props = new Properties();
initProperties(props);
sun.misc.Version.init();
// Load the zip library now in order to keep java.util.zip.ZipFile
// from trying to use itself to load this library later.
loadLibrary("zip");
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
// Setup Java signal handlers for HUP, TERM, and INT (where available).
Terminator.setup();
// The order in with the hooks are added here is important as it
// determines the order in which they are run.
// (1)Console restore hook needs to be called first.
// (2)Application hooks must be run before calling deleteOnExitHook.
Shutdown.add(sun.misc.SharedSecrets.getJavaIOAccess().consoleRestoreHook());
Shutdown.add(ApplicationShutdownHooks.hook());
Shutdown.add(sun.misc.SharedSecrets.getJavaIODeleteOnExitAccess());
// Initialize any miscellenous operating system settings that need to be
// set for the class libraries. Currently this is no-op everywhere except
// for Windows where the process-wide error mode is set before the java.io
// classes are used.
sun.misc.VM.initializeOSEnvironment();
// Set the maximum amount of direct memory. This value is controlled
// by the vm option -XX:MaxDirectMemorySize=<size>. This method acts
// as an initializer only if it is called before sun.misc.VM.booted().
sun.misc.VM.maxDirectMemory();
// Set a boolean to determine whether ClassLoader.loadClass accepts
// array syntax. This value is controlled by the system property
// "sun.lang.ClassLoader.allowArraySyntax". This method acts as
// an initializer only if it is called before sun.misc.VM.booted().
sun.misc.VM.allowArraySyntax();
// Subsystems that are invoked during initialization can invoke
// sun.misc.VM.isBooted() in order to avoid doing things that should
// wait until the application class loader has been set up.
sun.misc.VM.booted();
// The main thread is not added to its thread group in the same
// way as other threads; we must do it ourselves here.
Thread current = Thread.currentThread();
current.getThreadGroup().add(current);
// Allow privileged classes outside of java.lang
sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){
public sun.reflect.ConstantPool getConstantPool(Class klass) {
return klass.getConstantPool();
}
public void setAnnotationType(Class klass, AnnotationType type) {
klass.setAnnotationType(type);
}
public AnnotationType getAnnotationType(Class klass) {
return klass.getAnnotationType();
}
public <E extends Enum<E>>
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();
}
public void blockedOn(Thread t, Interruptible b) {
t.blockedOn(b);
}
});
}
13 getCallerClass():实际调用的是 Reflection.getCallerClass(3),和java反射机制有关。
/* returns the class of the caller. */
static Class getCallerClass() {
// NOTE use of more generic Reflection.getCallerClass()
return Reflection.getCallerClass(3);
}