JAVA的srcPos_src/java/lang/System.java · 醉明月/LearningJDK - Gitee.com

/*

* Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.

* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

*

* This code is free software; you can redistribute it and/or modify it

* under the terms of the GNU General Public License version 2 only, as

* published by the Free Software Foundation. Oracle designates this

* particular file as subject to the "Classpath" exception as provided

* by Oracle in the LICENSE file that accompanied this code.

*

* This code is distributed in the hope that it will be useful, but WITHOUT

* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or

* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License

* version 2 for more details (a copy is included in the LICENSE file that

* accompanied this code).

*

* You should have received a copy of the GNU General Public License version

* 2 along with this work; if not, write to the Free Software Foundation,

* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

*

* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA

* or visit www.oracle.com if you need additional information or have any

* questions.

*/

package java.lang;

import jdk.internal.HotSpotIntrinsicCandidate;

import jdk.internal.logger.LazyLoggers;

import jdk.internal.logger.LocalizedLoggerWrapper;

import jdk.internal.logger.LoggerFinderLoader;

import jdk.internal.misc.JavaLangAccess;

import jdk.internal.misc.SharedSecrets;

import jdk.internal.misc.VM;

import jdk.internal.module.ModuleBootstrap;

import jdk.internal.module.ServicesCatalog;

import jdk.internal.reflect.CallerSensitive;

import jdk.internal.reflect.Reflection;

import jdk.internal.util.StaticProperty;

import sun.nio.ch.Interruptible;

import sun.reflect.annotation.AnnotationType;

import sun.security.util.SecurityConstants;

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.Console;

import java.io.FileDescriptor;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintStream;

import java.io.UnsupportedEncodingException;

import java.lang.annotation.Annotation;

import java.lang.module.ModuleDescriptor;

import java.lang.reflect.Constructor;

import java.lang.reflect.Executable;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

import java.net.URI;

import java.nio.channels.Channel;

import java.nio.channels.spi.SelectorProvider;

import java.nio.charset.CharacterCodingException;

import java.nio.charset.Charset;

import java.security.AccessControlContext;

import java.security.AccessController;

import java.security.PrivilegedAction;

import java.security.ProtectionDomain;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import java.util.Objects;

import java.util.Properties;

import java.util.PropertyPermission;

import java.util.ResourceBundle;

import java.util.concurrent.ConcurrentHashMap;

import java.util.function.Supplier;

import java.util.stream.Stream;

/**

* The {@code System} class contains several useful class fields

* and methods. It cannot be instantiated.

*

* Among the facilities provided by the {@code System} class

* are standard input, standard output, and error output streams;

* access to externally defined properties and environment

* variables; a means of loading files and libraries; and a utility

* method for quickly copying a portion of an array.

*

* @since 1.0

*/

// 系统工具类

public final class System {

/** @see #initPhase2() */

static ModuleLayer bootLayer;

/** The security manager for the system. */

// 当前使用的安全管理器,默认为null

private static volatile SecurityManager security;

// 当前系统关联的控制台,在IDE中通常为null

private static volatile Console cons;

/**

* System properties. The following properties are guaranteed to be defined:

*

*

java.version Java version number

*

java.version.date Java version date

*

java.vendor Java vendor specific string

*

java.vendor.url Java vendor URL

*

java.vendor.version Java vendor version

*

java.home Java installation directory

*

java.class.version Java class version number

*

java.class.path Java classpath

*

os.name Operating System Name

*

os.arch Operating System Architecture

*

os.version Operating System Version

*

file.separator File separator ("/" on Unix)

*

path.separator Path separator (":" on Unix)

*

line.separator Line separator ("\n" on Unix)

*

user.name User account name

*

user.home User home directory

*

user.dir User's current working directory

*

*/

/*

* 环境变量属性集,可通过-D运行参数向其添加自定义条目。

* 该属性集是删减过的,完整的初始加载的环境变量保存在VM的字段savedProps中。

*/

private static Properties props;

// 行分隔符,在windows上是'\r\n'

private static String lineSeparator;

/**

* The "standard" input stream. This stream is already

* open and ready to supply input data. Typically this stream

* corresponds to keyboard input or another input source specified by

* the host environment or user.

*/

public static final InputStream in = null; // 标准输入流,会关联到某个默认的输入设备

/**

* The "standard" output stream. This stream is already

* open and ready to accept output data. Typically this stream

* corresponds to display output or another output destination

* specified by the host environment or user.

*

* For simple stand-alone Java applications, a typical way to write

* a line of output data is:

*

 
  

* System.out.println(data)

*

*

* See the {@code println} methods in class {@code PrintStream}.

*

* @see java.io.PrintStream#println()

* @see java.io.PrintStream#println(boolean)

* @see java.io.PrintStream#println(char)

* @see java.io.PrintStream#println(char[])

* @see java.io.PrintStream#println(double)

* @see java.io.PrintStream#println(float)

* @see java.io.PrintStream#println(int)

* @see java.io.PrintStream#println(long)

* @see java.io.PrintStream#println(java.lang.Object)

* @see java.io.PrintStream#println(java.lang.String)

*/

public static final PrintStream out = null; // 标准输出流,会关联到某个默认的输出设备

/**

* The "standard" error output stream. This stream is already

* open and ready to accept output data.

*

* 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}, has been redirected to a file or other

* destination that is typically not continuously monitored.

*/

public static final PrintStream err = null; // 标准错误流,会关联到某个默认的输出设备

static {

registerNatives();

}

/** Don't let anyone instantiate this class */

private System() {

}

/*▼ 标准流 ████████████████████████████████████████████████████████████████████████████████┓ */

/**

* Reassigns the "standard" input stream.

*

* First, if there is a security manager, its {@code checkPermission}

* method is called with a {@code RuntimePermission("setIO")} permission

* to see if it's ok to reassign the "standard" input stream.

*

* @param in the new standard input stream.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPermission} method doesn't allow

* reassigning of the standard input stream.

* @see SecurityManager#checkPermission

* @see java.lang.RuntimePermission

* @since 1.1

*/

// 重定向标准输入流:使得"标准输入流"变为in,即从in中读取数据

public static void setIn(InputStream in) {

checkIO();

setIn0(in);

}

/**

* Reassigns the "standard" output stream.

*

* First, if there is a security manager, its {@code checkPermission}

* method is called with a {@code RuntimePermission("setIO")} permission

* to see if it's ok to reassign the "standard" output stream.

*

* @param out the new standard output stream

*

* @throws SecurityException if a security manager exists and its

* {@code checkPermission} method doesn't allow

* reassigning of the standard output stream.

* @see SecurityManager#checkPermission

* @see java.lang.RuntimePermission

* @since 1.1

*/

// 重定向标准输出流:使得"标准输出流"变为out,即向out中写入数据

public static void setOut(PrintStream out) {

checkIO();

setOut0(out);

}

/**

* Reassigns the "standard" error output stream.

*

* First, if there is a security manager, its {@code checkPermission}

* method is called with a {@code RuntimePermission("setIO")} permission

* to see if it's ok to reassign the "standard" error output stream.

*

* @param err the new standard error output stream.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPermission} method doesn't allow

* reassigning of the standard error output stream.

* @see SecurityManager#checkPermission

* @see java.lang.RuntimePermission

* @since 1.1

*/

// 重定向标准错误流:使得"标准错误流"变为err,即向err中写入数据

public static void setErr(PrintStream err) {

checkIO();

setErr0(err);

}

// 为字段System.in关联(初始化)标准输入流

private static native void setIn0(InputStream in);

// 为字段System.out关联(初始化)标准输出流

private static native void setOut0(PrintStream out);

// 为字段System.err关联(初始化)标准错误流

private static native void setErr0(PrintStream err);

/*▲ 标准流 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 复制 ████████████████████████████████████████████████████████████████████████████████┓ */

/**

* Copies an array from the specified source array, beginning at the

* specified position, to the specified position of the destination array.

* A subsequence of array components are copied from the source

* array referenced by {@code src} to the destination array

* referenced by {@code dest}. The number of components copied is

* equal to the {@code length} argument. The components at

* positions {@code srcPos} through

* {@code srcPos+length-1} in the source array are copied into

* positions {@code destPos} through

* {@code destPos+length-1}, respectively, of the destination

* array.

*

* If the {@code src} and {@code dest} arguments refer to the

* same array object, then the copying is performed as if the

* components at positions {@code srcPos} through

* {@code srcPos+length-1} were first copied to a temporary

* array with {@code length} components and then the contents of

* the temporary array were copied into positions

* {@code destPos} through {@code destPos+length-1} of the

* destination array.

*

* If {@code dest} is {@code null}, then a

* {@code NullPointerException} is thrown.

*

* If {@code src} is {@code null}, then a

* {@code NullPointerException} is thrown and the destination

* array is not modified.

*

* Otherwise, if any of the following is true, an

* {@code ArrayStoreException} is thrown and the destination is

* not modified:

*

*

The {@code src} argument refers to an object that is not an

* array.

*

The {@code dest} argument refers to an object that is not an

* array.

*

The {@code src} argument and {@code dest} argument refer

* to arrays whose component types are different primitive types.

*

The {@code src} argument refers to an array with a primitive

* component type and the {@code dest} argument refers to an array

* with a reference component type.

*

The {@code src} argument refers to an array with a reference

* component type and the {@code dest} argument refers to an array

* with a primitive component type.

*

*

* Otherwise, if any of the following is true, an

* {@code IndexOutOfBoundsException} is

* thrown and the destination is not modified:

*

*

The {@code srcPos} argument is negative.

*

The {@code destPos} argument is negative.

*

The {@code length} argument is negative.

*

{@code srcPos+length} is greater than

* {@code src.length}, the length of the source array.

*

{@code destPos+length} is greater than

* {@code dest.length}, the length of the destination array.

*

*

* Otherwise, if any actual component of the source array from

* position {@code srcPos} through

* {@code srcPos+length-1} cannot be converted to the component

* type of the destination array by assignment conversion, an

* {@code ArrayStoreException} is thrown. In this case, let

* k be the smallest nonnegative integer less than

* length such that {@code src[srcPos+}k{@code ]}

* cannot be converted to the component type of the destination

* array; when the exception is thrown, source array components from

* positions {@code srcPos} through

* {@code srcPos+}k{@code -1}

* will already have been copied to destination array positions

* {@code destPos} through

* {@code destPos+}k{@code -1} and no other

* positions of the destination array will have been modified.

* (Because of the restrictions already itemized, this

* paragraph effectively applies only to the situation where both

* arrays have component types that are reference types.)

*

* @param src the source array.

* @param srcPos starting position in the source array.

* @param dest the destination array.

* @param destPos starting position in the destination data.

* @param length the number of array elements to be copied.

*

* @throws IndexOutOfBoundsException if copying would cause

* access of data outside array bounds.

* @throws ArrayStoreException if an element in the {@code src}

* array could not be stored into the {@code dest} array

* because of a type mismatch.

* @throws NullPointerException if either {@code src} or

* {@code dest} is {@code null}.

*/

// 数组复制,从src的srcPos索引处复制length个元素放入dest的destPos索引处

@HotSpotIntrinsicCandidate

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

/*▲ 复制 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 系统属性 ████████████████████████████████████████████████████████████████████████████████┓ */

/**

* Sets the system property indicated by the specified key.

*

* First, if a security manager exists, its

* {@code SecurityManager.checkPermission} method

* is called with a {@code PropertyPermission(key, "write")}

* permission. This may result in a SecurityException being thrown.

* If no exception is thrown, the specified property is set to the given

* value.

*

* @param key the name of the system property.

* @param value the value of the system property.

*

* @return the previous value of the system property,

* or {@code null} if it did not have one.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPermission} method doesn't allow

* setting of the specified property.

* @throws NullPointerException if {@code key} or

* {@code value} is {@code null}.

* @throws IllegalArgumentException if {@code key} is empty.

* @apiNote Changing a standard system property may have unpredictable results

* unless otherwise specified.

* See {@linkplain #getProperties getProperties} for details.

* @see #getProperty

* @see java.lang.System#getProperty(java.lang.String)

* @see java.lang.System#getProperty(java.lang.String, java.lang.String)

* @see java.util.PropertyPermission

* @see SecurityManager#checkPermission

* @since 1.2

*/

// 添加一条系统属性:键值对

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

}

/**

* Removes the system property indicated by the specified key.

*

* First, if a security manager exists, its

* {@code SecurityManager.checkPermission} method

* is called with a {@code PropertyPermission(key, "write")}

* permission. This may result in a SecurityException being thrown.

* If no exception is thrown, the specified property is removed.

*

* @param key the name of the system property to be removed.

*

* @return the previous string value of the system property,

* or {@code null} if there was no property with that key.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPropertyAccess} method doesn't allow

* access to the specified system property.

* @throws NullPointerException if {@code key} is {@code null}.

* @throws IllegalArgumentException if {@code key} is empty.

* @apiNote Changing a standard system property may have unpredictable results

* unless otherwise specified.

* See {@linkplain #getProperties getProperties} method for details.

* @see #getProperty

* @see #setProperty

* @see java.util.Properties

* @see java.lang.SecurityException

* @see java.lang.SecurityManager#checkPropertiesAccess()

* @since 1.5

*/

// 移除指定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);

}

/**

* Gets the system property indicated by the specified key.

*

* First, if there is a security manager, its

* {@code checkPropertyAccess} method is called with the key as

* its argument. This may result in a SecurityException.

*

* If there is no current set of system properties, a set of system

* properties is first created and initialized in the same manner as

* for the {@code getProperties} method.

*

* @param key the name of the system property.

*

* @return the string value of the system property,

* or {@code null} if there is no property with that key.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPropertyAccess} method doesn't allow

* access to the specified system property.

* @throws NullPointerException if {@code key} is {@code null}.

* @throws IllegalArgumentException if {@code key} is empty.

* @apiNote Changing a standard system property may have unpredictable results

* unless otherwise specified.

* See {@linkplain #getProperties getProperties} for details.

* @see #setProperty

* @see java.lang.SecurityException

* @see java.lang.SecurityManager#checkPropertyAccess(java.lang.String)

* @see java.lang.System#getProperties()

*/

// 返回指定key对应的系统属性的值。如果key不存在,返回null

public static String getProperty(String key) {

checkKey(key);

SecurityManager sm = getSecurityManager();

if(sm != null) {

sm.checkPropertyAccess(key);

}

return props.getProperty(key);

}

/**

* Gets the system property indicated by the specified key.

*

* First, if there is a security manager, its

* {@code checkPropertyAccess} method is called with the

* {@code key} as its argument.

*

* If there is no current set of system properties, a set of system

* properties is first created and initialized in the same manner as

* for the {@code getProperties} method.

*

* @param key the name of the system property.

* @param def a default value.

*

* @return the string value of the system property,

* or the default value if there is no property with that key.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPropertyAccess} method doesn't allow

* access to the specified system property.

* @throws NullPointerException if {@code key} is {@code null}.

* @throws IllegalArgumentException if {@code key} is empty.

* @see #setProperty

* @see java.lang.SecurityManager#checkPropertyAccess(java.lang.String)

* @see java.lang.System#getProperties()

*/

// 返回指定key对应的系统属性的值。如果key不存在,返回默认值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);

}

/**

* Sets the system properties to the {@code Properties} argument.

*

* First, if there is a security manager, its

* {@code checkPropertiesAccess} method is called with no

* arguments. This may result in a security exception.

*

* The argument becomes the current set of system properties for use

* by the {@link #getProperty(String)} method. If the argument is

* {@code null}, then the current set of system properties is

* forgotten.

*

* @param props the new system properties.

*

* @throws SecurityException if a security manager exists and its

* {@code checkPropertiesAccess} method doesn't allow access

* to the system properties.

* @apiNote Changing a standard system property may have unpredictable results

* unless otherwise specified.

* See {@linkplain #getProperties getProperties} for details.

* @see #getProperties

* @see java.util.Properties

* @see java.lang.SecurityException

* @see java.lang.SecurityManager#checkPropertiesAccess()

*/

// 设置系统属性集(会整体替换掉上次设置的属性值,初始时默认为系统属性集)

public static void setProperties(Properties props) {

SecurityManager sm = getSecurityManager();

if(sm != null) {

sm.checkPropertiesAccess();

}

if(props == null) {

props = new Properties();

// 加载环境变量以填充props

initProperties(props);

}

System.props = props;

}

/**

* Determines the current system properties.

*

* First, if there is a security manager, its

* {@code checkPropertiesAccess} method is called with no

* arguments. This may result in a security exception.

*

* The current set of system properties for use by the

* {@link #getProperty(String)} method is returned as a

* {@code Properties} object. If there is no current set of

* system properties, a set of system properties is first created and

* initialized. This set of system properties always includes values

* for the following keys:

*

*

Shows property keys and associated values

*

*

Key

*

Description of Associated Value

*

*

*

{@code java.version}

*

Java Runtime Environment version, which may be interpreted

* as a {@link Runtime.Version}

*

{@code java.version.date}

*

Java Runtime Environment version date, in ISO-8601 YYYY-MM-DD

* format, which may be interpreted as a {@link

* java.time.LocalDate}

*

{@code java.vendor}

*

Java Runtime Environment vendor

*

{@code java.vendor.url}

*

Java vendor URL

*

{@code java.vendor.version}

*

Java vendor version

*

{@code java.home}

*

Java installation directory

*

{@code java.vm.specification.version}

*

Java Virtual Machine specification version, whose value is the

* {@linkplain Runtime.Version#feature feature} element of the

* {@linkplain Runtime#version() runtime version}

*

{@code java.vm.specification.vendor}

*

Java Virtual Machine specification vendor

*

{@code java.vm.specification.name}

*

Java Virtual Machine specification name

*

{@code java.vm.version}

*

Java Virtual Machine implementation version which may be

* interpreted as a {@link Runtime.Version}

*

{@code java.vm.vendor}

*

Java Virtual Machine implementation vendor

*

{@code java.vm.name}

*

Java Virtual Machine implementation name

*

{@code java.specification.version}

*

Java Runtime Environment specification version, whose value is

* the {@linkplain Runtime.Version#feature feature} element of the

* {@linkplain Runtime#version() runtime version}

*

{@code java.specification.vendor}

*

Java Runtime Environment specification vendor

*

{@code java.specification.name}

*

Java Runtime Environment specification name

*

{@code java.class.version}

*

Java class format version number

*

{@code java.class.path}

*

Java class path (refer to

* {@link ClassLoader#getSystemClassLoader()} for details)

*

{@code java.library.path}

*

List of paths to search when loading libraries

*

{@code java.io.tmpdir}

*

Default temp file path

*

{@code java.compiler}

*

Name of JIT compiler to use

*

{@code os.name}

*

Operating system name

*

{@code os.arch}

*

Operating system architecture

*

{@code os.version}

*

Operating system version

*

{@code file.separator}

*

File separator ("/" on UNIX)

*

{@code path.separator}

*

Path separator (":" on UNIX)

*

{@code line.separator}

*

Line separator ("\n" on UNIX)

*

{@code user.name}

*

User's account name

*

{@code user.home}

*

User's home directory

*

{@code user.dir}

*

User's current working directory

*

*

*

* Multiple paths in a system property value are separated by the path

* separator character of the platform.

*

* Note that even if the security manager does not permit the

* {@code getProperties} operation, it may choose to permit the

* {@link #getProperty(String)} operation.

*

* @return the system properties

*

* @throws SecurityException if a security manager exists and its

* {@code checkPropertiesAccess} method doesn't allow access

* to the system properties.

* @apiNote Changing a standard system property may have unpredictable results

* unless otherwise specified.

* Property values may be cached during initialization or on first use.

* Setting a standard property after initialization using {@link #getProperties()},

* {@link #setProperties(Properties)}, {@link #setProperty(String, String)}, or

* {@link #clearProperty(String)} may not have the desired effect.

* @implNote In addition to the standard system properties, the system

* properties may include the following keys:

*

*

Shows property keys and associated values

*

*

Key

*

Description of Associated Value

*

*

*

{@code jdk.module.path}

*

The application module path

*

{@code jdk.module.upgrade.path}

*

The upgrade module path

*

{@code jdk.module.main}

*

The module name of the initial/main module

*

{@code jdk.module.main.class}

*

The main class name of the initial module

*

*

* @see #setProperties

* @see java.lang.SecurityException

* @see java.lang.SecurityManager#checkPropertiesAccess()

* @see java.util.Properties

*/

// 获取系统属性集

public static Properties getProperties() {

SecurityManager sm = getSecurityManager();

if(sm != null) {

sm.checkPropertiesAccess();

}

return props;

}

/*▲ 系统属性 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 环境变量 ████████████████████████████████████████████████████████████████████████████████┓ */

/**

* Returns an unmodifiable string map view of the current system environment.

* The environment is a system-dependent mapping from names to

* values which is passed from parent to child processes.

*

*

If the system does not support environment variables, an

* empty map is returned.

*

*

The returned map will never contain null keys or values.

* Attempting to query the presence of a null key or value will

* throw a {@link NullPointerException}. Attempting to query

* the presence of a key or value which is not of type

* {@link String} will throw a {@link ClassCastException}.

*

*

The returned map and its collection views may not obey the

* general contract of the {@link Object#equals} and

* {@link Object#hashCode} methods.

*

*

The returned map is typically case-sensitive on all platforms.

*

*

If a security manager exists, its

* {@link SecurityManager#checkPermission checkPermission}

* method is called with a

* {@code {@link RuntimePermission}("getenv.*")} permission.

* This may result in a {@link SecurityException} being thrown.

*

*

When passing information to a Java subprocess,

* system properties

* are generally preferred over environment variables.

*

* @return the environment as a map of variable names to values

*

* @throws SecurityException if a security manager exists and its

* {@link SecurityManager#checkPermission checkPermission}

* method doesn't allow access to the process environment

* @see #getenv(String)

* @see ProcessBuilder#environment()

* @since 1.5

*/

// 返回所有环境变量

public static Map getenv() {

SecurityManager sm = getSecurityManager();

if(sm != null) {

sm.checkPermission(new RuntimePermission("getenv.*"));

}

return ProcessEnvironment.getenv();

}

/**

* Gets the value of the specified environment variable. An

* environment variable is a system-dependent external named

* value.

*

*

If a security manager exists, its

* {@link SecurityManager#checkPermission checkPermission}

* method is called with a

* {@code {@link RuntimePermission}("getenv."+name)}

* permission. This may result in a {@link SecurityException}

* being thrown. If no exception is thrown the value of the

* variable {@code name} is returned.

*

*

System

* properties and environment variables are both

* conceptually mappings between names and values. Both

* mechanisms can be used to pass user-defined information to a

* Java process. Environment variables have a more global effect,

* because they are visible to all descendants of the process

* which defines them, not just the immediate Java subprocess.

* They can have subtly different semantics, such as case

* insensitivity, on different operating systems. For these

* reasons, environment variables are more likely to have

* unintended side effects. It is best to use system properties

* where possible. Environment variables should be used when a

* global effect is desired, or when an external system interface

* requires an environment variable (such as {@code PATH}).

*

*

On UNIX systems the alphabetic case of {@code name} is

* typically significant, while on Microsoft Windows systems it is

* typically not. For example, the expression

* {@code System.getenv("FOO").equals(System.getenv("foo"))}

* is likely to be true on Microsoft Windows.

*

* @param name the name of the environment variable

*

* @return the string value of the variable, or {@code null}

* if the variable is not defined in the system environment

*

* @throws NullPointerException if {@code name} is {@code null}

* @throws SecurityException if a security manager exists and its

* {@link SecurityManager#checkPermission checkPermission}

* method doesn't allow access to the environment variable

* {@code name}

* @see #getenv()

* @see ProcessBuilder#environment()

*/

// 返回指定名称的环境变量

public static String getenv(String name) {

SecurityManager sm = getSecurityManager();

if(sm != null) {

sm.checkPermission(new RuntimePermission("getenv." + name));

}

return ProcessEnvironment.getenv(name);

}

/*▲ 环境变量 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 时间 ████████████████████████████████████████████████████████████████████████████████┓ */

/**

* Returns the current time in milliseconds. Note that

* while the unit of time of the return value is a millisecond,

* the granularity of the value depends on the underlying

* operating system and may be larger. For example, many

* operating systems measure time in units of tens of

* milliseconds.

*

*

See the description of the class {@code Date} for

* a discussion of slight discrepancies that may arise between

* "computer time" and coordinated universal time (UTC).

*

* @return the difference, measured in milliseconds, between

* the current time and midnight, January 1, 1970 UTC.

*

* @see java.util.Date

*/

/*

* 返回当前时间点与新纪元时间点之间的毫秒差值(具体粒度由底层操作系统决定)

*

* 新纪元时间点:UTC/GMT时间1970年1月1号0时0分0秒

*/

@HotSpotIntrinsicCandidate

public static native long currentTimeMillis();

/**

* Returns the current value of the running Java Virtual Machine's

* high-resolution time source, in nanoseconds.

*

* This method can only be used to measure elapsed time and is

* not related to any other notion of system or wall-clock time.

* The value returned represents nanoseconds since some fixed but

* arbitrary origin time (perhaps in the future, so values

* may be negative). The same origin is used by all invocations of

* this method in an instance of a Java virtual machine; other

* virtual machine instances are likely to use a different origin.

*

*

This method provides nanosecond precision, but not necessarily

* nanosecond resolution (that is, how frequently the value changes)

* - no guarantees are made except that the resolution is at least as

* good as that of {@link #currentTimeMillis()}.

*

*

Differences in successive calls that span greater than

* approximately 292 years (263 nanoseconds) will not

* correctly compute elapsed time due to numerical overflow.

*

*

The values returned by this method become meaningful only when

* the difference between two such values, obtained within the same

* instance of a Java virtual machine, is computed.

*

*

For example, to measure how long some code takes to execute:

*

 {@code

* long startTime = System.nanoTime();

* // ... the code being measured ...

* long elapsedNanos = System.nanoTime() - startTime;}

*

*

To compare elapsed time against a timeout, use

 {@code

* if (System.nanoTime() - startTime >= timeoutNanos) ...}

* instead of

 {@code

* if (System.nanoTime() >= startTime + timeoutNanos) ...}

* because of the possibility of numerical overflow.

*

* @return the current value of the running Java Virtual Machine's

* high-resolution time source, in nanoseconds

*

* @since 1.5

*/

// 返回一个纳秒级的时间,不与具体的日期挂钩,只反应某段流逝的时间,可用来计数

@HotSpotIntrinsicCandidate

public static native long nanoTime();

/*▲ 时间 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 日志 ████████████████████████████████████████████████████████████████████████████████┓ */

/* 默认实现:LoggingProviderImpl$JULWrapper*/

/**

* Returns an instance of {@link Logger Logger} for the caller's

* use.

*

* @param name the name of the logger.

*

* @return an instance of {@link Logger} that can be used by the calling

* class.

*

* @throws NullPointerException if {@code name} is {@code null}.

* @throws IllegalCallerException if there is no Java caller frame on the

* stack.

* @implSpec Instances returned by this method route messages to loggers

* obtained by calling {@link LoggerFinder#getLogger(java.lang.String,

* java.lang.Module) LoggerFinder.getLogger(name, module)}, where

* {@code module} is the caller's module.

* In cases where {@code System.getLogger} is called from a context where

* there is no caller frame on the stack (e.g when called directly

* from a JNI attached thread), {@code IllegalCallerException} is thrown.

* To obtain a logger in such a context, use an auxiliary class that will

* implicitly be identified as the caller, or use the system {@link

* LoggerFinder#getLoggerFinder() LoggerFinder} to obtain a logger instead.

* Note that doing the latter may eagerly initialize the underlying

* logging system.

* @apiNote This method may defer calling the {@link

* LoggerFinder#getLogger(java.lang.String, java.lang.Module)

* LoggerFinder.getLogger} method to create an actual logger supplied by

* the logging backend, for instance, to allow loggers to be obtained during

* the system initialization time.

* @since 9

*/

// 获取一个名为name的Logger实例

@CallerSensitive

public static Logger getLogger(String name) {

Objects.requireNonNull(name);

// 获取getLogger()方法的调用者所处的类

final Class> caller = Reflection.getCallerClass();

if(caller == null) {

throw new IllegalCallerException("no caller frame");

}

return LazyLoggers.getLogger(name, caller.getModule());

}

/**

* Returns a localizable instance of {@link Logger Logger} for the caller's use.

* The returned logger will use the provided resource bundle for message localization.

*

* @param name the name of the logger.

* @param bundle a resource bundle.

*

* @return an instance of {@link Logger} which will use the provided

* resource bundle for message localization.

*

* @throws NullPointerException if {@code name} is {@code null} or

* {@code bundle} is {@code null}.

* @throws IllegalCallerException if there is no Java caller frame on the

* stack.

* @implSpec The returned logger will perform message localization as specified

* by {@link LoggerFinder#getLocalizedLogger(java.lang.String,

* java.util.ResourceBundle, java.lang.Module)

* LoggerFinder.getLocalizedLogger(name, bundle, module)}, where

* {@code module} is the caller's module.

* In cases where {@code System.getLogger} is called from a context where

* there is no caller frame on the stack (e.g when called directly

* from a JNI attached thread), {@code IllegalCallerException} is thrown.

* To obtain a logger in such a context, use an auxiliary class that

* will implicitly be identified as the caller, or use the system {@link

* LoggerFinder#getLoggerFinder() LoggerFinder} to obtain a logger instead.

* Note that doing the latter may eagerly initialize the underlying

* logging system.

* @apiNote This method is intended to be used after the system is fully initialized.

* This method may trigger the immediate loading and initialization

* of the {@link LoggerFinder} service, which may cause issues if the

* Java Runtime is not ready to initialize the concrete service

* implementation yet.

* System classes which may be loaded early in the boot sequence and

* need to log localized messages should create a logger using

* {@link #getLogger(java.lang.String)} and then use the log methods that

* take a resource bundle as parameter.

* @since 9

*/

// 获取一个名为name的Logger实例,其打印的消息会依据bundle来做本地化转换

@CallerSensitive

public static Logger getLogger(String name, ResourceBundle bundle) {

Objects.requireNonNull(name);

final ResourceBundle rb = Objects.requireNonNull(bundle);

final Class> caller = Reflection.getCallerClass();

if(caller == null) {

throw new IllegalCallerException("no caller frame");

}

final SecurityManager sm = System.getSecurityManager();

/*

* We don't use LazyLoggers if a resource bundle is specified.

* Bootstrap sensitive classes in the JDK do not use resource bundles when logging.

* This could be revisited later, if it needs to.

*/

if(sm != null) {

final PrivilegedAction pa = () -> LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller.getModule());

return AccessController.doPrivileged(pa, null, LoggerFinder.LOGGERFINDER_PERMISSION);

}

return LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller.getModule());

}

/*▲ 日志 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 加载本地库 ████████████████████████████████████████████████████████████████████████████████┓ */

/**

* Loads the native library specified by the filename argument. The filename

* argument must be an absolute path name.

*

* If the filename argument, when stripped of any platform-specific library

* prefix, path, and file extension, indicates a library whose name is,

* for example, L, and a native library called L is statically linked

* with the VM, then the JNI_OnLoad_L function exported by the library

* is invoked rather than attempting to load a dynamic library.

* A filename matching the argument does not have to exist in the

* file system.

* See the JNI Specification

* for more details.

*

* Otherwise, the filename argument is mapped to a native library image in

* an implementation-dependent manner.

*

*

* The call {@code System.load(name)} is effectively equivalent

* to the call:

*

 
  

* Runtime.getRuntime().load(name)

*

*

* @param filename the file to load.

*

* @throws SecurityException if a security manager exists and its

* {@code checkLink} method doesn't allow

* loading of the specified dynamic library

* @throws UnsatisfiedLinkError if either the filename is not an

* absolute path name, the native library is not statically

* linked with the VM, or the library cannot be mapped to

* a native library image by the host system.

* @throws NullPointerException if {@code filename} is {@code null}

* @see java.lang.Runtime#load(java.lang.String)

* @see java.lang.SecurityManager#checkLink(java.lang.String)

*/

// 加载指定名称的本地库(要求filename是本地库的绝对路径)

@CallerSensitive

public static void load(String filename) {

Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);

}

/**

* Loads the native library specified by the {@code libname}

* argument. The {@code libname} argument must not contain any platform

* specific prefix, file extension or path. If a native library

* called {@code libname} is statically linked with the VM, then the

* JNI_OnLoad_{@code libname} function exported by the library is invoked.

* See the JNI Specification

* for more details.

*

* Otherwise, the libname argument is loaded from a system library

* location and mapped to a native library image in an implementation-

* dependent manner.

*

* The call {@code System.loadLibrary(name)} is effectively

* equivalent to the call

*

 
  

* Runtime.getRuntime().loadLibrary(name)

*

*

* @param libname the name of the library.

*

* @throws SecurityException if a security manager exists and its

* {@code checkLink} method doesn't allow

* loading of the specified dynamic library

* @throws UnsatisfiedLinkError if either the libname argument

* contains a file path, the native library is not statically

* linked with the VM, or the library cannot be mapped to a

* native library image by the host system.

* @throws NullPointerException if {@code libname} is {@code null}

* @see java.lang.Runtime#loadLibrary(java.lang.String)

* @see java.lang.SecurityManager#checkLink(java.lang.String)

*/

// 加载指定名称的本地库,如"net"是指本地网络库

@CallerSensitive

public static void loadLibrary(String libname) {

Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);

}

/**

* Maps a library name into a platform-specific string representing a native library.

*

* @param libname the name of the library.

*

* @return a platform-dependent native library name.

*

* @throws NullPointerException if {@code libname} is {@code null}

* @see java.lang.System#loadLibrary(java.lang.String)

* @see java.lang.ClassLoader#findLibrary(java.lang.String)

* @since 1.2

*/

// 返回指定名称的本地库在当前平台上的名称,如从"net"映射到"net.dll"

public static native String mapLibraryName(String libname);

/*▲ 加载本地库 ████████████████████████████████████████████████████████████████████████████████┛ */

/**

* Runs the garbage collector.

*

* Calling the {@code gc} method suggests that the Java Virtual

* Machine expend effort toward recycling unused objects in order to

* make the memory they currently occupy available for quick reuse.

* When control returns from the method call, the Java Virtual

* Machine has made a best effort to reclaim space from all discarded

* objects.

*

* The call {@code System.gc()} is effectively equivalent to the

* call:

*

 
  

* Runtime.getRuntime().gc()

*

*

* @see java.lang.Runtime#gc()

*/

// 建议JVM执行垃圾回收

public static void gc() {

Runtime.getRuntime().gc();

}

/**

* Runs the finalization methods of any objects pending finalization.

*

* Calling this method suggests that the Java Virtual Machine expend

* effort toward running the {@code finalize} methods of objects

* that have been found to be discarded but whose {@code finalize}

* methods have not yet been run. When control returns from the

* method call, the Java Virtual Machine has made a best effort to

* complete all outstanding finalizations.

*

* The call {@code System.runFinalization()} is effectively

* equivalent to the call:

*

 
  

* Runtime.getRuntime().runFinalization()

*

*

* @see java.lang.Runtime#runFinalization()

*/

// 建议JVM执行已失去引用的对象的finalize()方法

public static void runFinalization() {

Runtime.getRuntime().runFinalization();

}

/**

* Returns the same hash code for the given object as

* would be returned by the default method hashCode(),

* whether or not the given object's class overrides

* hashCode().

* The hash code for the null reference is zero.

*

* @param x object for which the hashCode is to be calculated

*

* @return the hashCode

*

* @see Object#hashCode

* @see java.util.Objects#hashCode(Object)

* @since 1.1

*/

/*

* 返回当前对象的哈希码,具体行为由JVM实现

* 如果当前对象没有重写Object#hashCode方法,则Object#hashCode与identityHashCode的行为可看做一致。

*/

@HotSpotIntrinsicCandidate

public static native int identityHashCode(Object x);

/**

* Returns the system-dependent line separator string. It always

* returns the same value - the initial value of the {@linkplain

* #getProperty(String) system property} {@code line.separator}.

*

*

On UNIX systems, it returns {@code "\n"}; on Microsoft

* Windows systems it returns {@code "\r\n"}.

*

* @return the system-dependent line separator string

*

* @since 1.7

*/

// 返回当前环境下的换行标记,比如在windows下返回"\r\n"

public static String lineSeparator() {

return lineSeparator;

}

/**

* Terminates the currently running Java Virtual Machine. The

* argument serves as a status code; by convention, a nonzero status

* code indicates abnormal termination.

*

* This method calls the {@code exit} method in class

* {@code Runtime}. This method never returns normally.

*

* The call {@code System.exit(n)} is effectively equivalent to

* the call:

*

 
  

* Runtime.getRuntime().exit(n)

*

*

* @param status exit status.

*

* @throws SecurityException if a security manager exists and its {@code checkExit}

* method doesn't allow exit with the specified status.

* @see java.lang.Runtime#exit(int)

*/

// 结束当前正在运行中的java虚拟机,一般用status=0表示正常退出

public static void exit(int status) {

Runtime.getRuntime().exit(status);

}

/**

* Returns the unique {@link java.io.Console Console} object associated

* with the current Java virtual machine, if any.

*

* @return The system console, if any, otherwise {@code null}.

*

* @since 1.6

*/

// 返回为当前JVM环境关联的终端(在IDE中运行项目时,此处往往返回null)

public static Console console() {

Console c;

if((c = cons) == null) {

synchronized(System.class) {

if((c = cons) == null) {

cons = c = SharedSecrets.getJavaIOAccess().console();

}

}

}

return c;

}

/**

* Returns the channel inherited from the entity that created this Java virtual machine.

*

* This method returns the channel obtained by invoking the

* {@link java.nio.channels.spi.SelectorProvider#inheritedChannel

* inheritedChannel} method of the system-wide default

* {@link java.nio.channels.spi.SelectorProvider} object.

*

*

In addition to the network-oriented channels described in

* {@link java.nio.channels.spi.SelectorProvider#inheritedChannel

* inheritedChannel}, this method may return other kinds of

* channels in the future.

*

* @return The inherited channel, if any, otherwise {@code null}.

*

* @throws IOException If an I/O error occurs

* @throws SecurityException If a security manager is present and it does not

* permit access to the channel.

* @since 1.5

*/

// 返回从创建此Java虚拟机的实体继承的通道。

public static Channel inheritedChannel() throws IOException {

return SelectorProvider.provider().inheritedChannel();

}

/**

* Gets the system security interface.

*

* @return if a security manager has already been established for the

* current application, then that security manager is returned;

* otherwise, {@code null} is returned.

*

* @see #setSecurityManager

*/

// 获取当前使用的安全管理器

public static SecurityManager getSecurityManager() {

return security;

}

/**

* Sets the System security.

*

* If there is a security manager already installed, this method first

* calls the security manager's {@code checkPermission} method

* with a {@code RuntimePermission("setSecurityManager")}

* permission to ensure it's ok to replace the existing

* security manager.

* This may result in throwing a {@code SecurityException}.

*

*

Otherwise, the argument is established as the current

* security manager. If the argument is {@code null} and no

* security manager has been established, then no action is taken and

* the method simply returns.

*

* @param s the security manager.

*

* @throws SecurityException if the security manager has already

* been set and its {@code checkPermission} method

* doesn't allow it to be replaced.

* @see #getSecurityManager

* @see SecurityManager#checkPermission

* @see java.lang.RuntimePermission

*/

// 设置安全管理器

public static void setSecurityManager(final SecurityManager s) {

if(security == null) {

// ensure image reader is initialized

Object.class.getResource("java/lang/ANY");

}

if(s != null) {

try {

s.checkPackageAccess("java.lang");

} catch(Exception e) {

// no-op

}

}

setSecurityManager0(s);

}

/**

* Initialize the system class. Called after thread initialization.

*/

// VM初始化第一阶段

private static void initPhase1() {

/*

* VM might invoke JNU_NewStringPlatform() to set those encoding sensitive properties

* (user.home, user.name, boot.class.path, etc.) during "props" initialization, in which it may need access, via System.getProperty(),

* to the related system encoding property that have been initialized (put into "props") at early stage of the initialization.

* So make sure the "props" is available at the very beginning of the initialization and all system properties to be put into it directly.

*/

// 创建空的Properties容器

props = new Properties(84);

// 加载环境变量以填充props

initProperties(props); // initialized by the VM

/*

* There are certain system configurations that may be controlled by VM options

* such as the maximum amount of direct memory and Integer cache size used to support the object identity semantics of autoboxing.

* Typically, the library will obtain these values from the properties set by the VM.

* If the properties are for internal implementation use only, these properties should be removed from the system properties.

* See java.lang.Integer.IntegerCache and the VM.saveAndRemoveProperties method for example.

* Save a private copy of the system properties object that can only be accessed by the internal implementation.

* Remove certain system properties that are not intended for public access.

*/

// 将加载的环境变量设置给虚拟机,并移除一些需要外部设置的环境变量

VM.saveAndRemoveProperties(props);

lineSeparator = props.getProperty("line.separator");

// 初始化一些只读的系统属性

StaticProperty.javaHome(); // Load StaticProperty to cache the property values

// 初始化JDK版本信息

VersionProps.init();

// 在Java层构造标准输入流、标准输出流、标准错误流对象

FileInputStream fdIn = new FileInputStream(FileDescriptor.in);

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);

FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);

// 为字段System.in关联(初始化)标准输入流

setIn0(new BufferedInputStream(fdIn));

// 为字段System.out关联(初始化)标准输出流

setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));

// 为字段System.err关联(初始化)标准错误流

setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));

// Setup Java signal handlers for HUP, TERM, and INT (where available).

Terminator.setup();

/*

* Initialize any miscellaneous 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.

*/

// 设置一些操作系统相关的选项

VM.initializeOSEnvironment();

/*

* 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);

/* register shared secrets */

// 初始化JavaLangAccess后门

setJavaLangAccess();

/*

* Subsystems that are invoked during initialization can invoke VM.isBooted() in order to avoid doing things

* that should wait until the VM is fully initialized.

* The initialization level is incremented from 0 to 1 here to indicate the first phase of initialization has completed.

* IMPORTANT: Ensure that this remains the last initialization action!

*/

VM.initLevel(1); // VM初始化第一阶段已完成

}

/**

* Invoked by VM. Phase 2 module system initialization.

* Only classes in java.base can be loaded in this phase.

*

* @param printToStderr print exceptions to stderr rather than stdout

* @param printStackTrace print stack trace when exception occurs

*

* @return JNI_OK for success, JNI_ERR for failure

*/

// VM初始化第二阶段:初始化模块系统

private static int initPhase2(boolean printToStderr, boolean printStackTrace) {

try {

bootLayer = ModuleBootstrap.boot();

} catch(Exception | Error e) {

logInitException(printToStderr, printStackTrace, "Error occurred during initialization of boot layer", e);

return -1; // JNI_ERR

}

// module system initialized

VM.initLevel(2); // VM初始化第二阶段已完成

return 0; // JNI_OK

}

/**

* Invoked by VM. Phase 3 is the final system initialization:

* 1. set security manager

* 2. set system class loader

* 3. set TCCL

*

* This method must be called after the module system initialization.

* The security manager and system class loader may be custom class from

* the application classpath or modulepath.

*/

// VM初始化第三阶段(初始化安全管理器)和第四阶段(初始化系统类加载器,并将其设置到当前线程的上下文中)

private static void initPhase3() {

// set security manager

String cn = System.getProperty("java.security.manager");

// 如果定义了"java.security.manager"属性

if(cn != null) {

// 使用默认的安全管理器实现

if(cn.isEmpty() || "default".equals(cn)) {

System.setSecurityManager(new SecurityManager());

// 使用自定义的安全管理器类

} else {

try {

// 创建自定义安全管理器的类对象

Class> c = Class.forName(cn, false, ClassLoader.getBuiltinAppClassLoader());

Constructor> ctor = c.getConstructor();

// Must be a public subclass of SecurityManager with a public no-arg constructor

if(!SecurityManager.class.isAssignableFrom(c) || !Modifier.isPublic(c.getModifiers()) || !Modifier.isPublic(ctor.getModifiers())) {

throw new Error("Could not create SecurityManager: " + ctor.toString());

}

// custom security manager implementation may be in unnamed module or a named module but non-exported package

ctor.setAccessible(true);

// 实例化自定义安全管理器

SecurityManager sm = (SecurityManager) ctor.newInstance();

// 设置安全管理器

System.setSecurityManager(sm);

} catch(Exception e) {

throw new Error("Could not create SecurityManager", e);

}

}

}

// initializing the system class loader

VM.initLevel(3); // VM初始化第三阶段已完成

// system class loader initialized

ClassLoader scl = ClassLoader.initSystemClassLoader(); // 初始化系统类加载器,默认为内置的AppClassLoader

// set TCCL

Thread.currentThread().setContextClassLoader(scl); // 将系统类加载器设置为当前线程的上下文类加载器

// system is fully initialized

VM.initLevel(4); // VM初始化第四阶段已完成

}

/**

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

private static void checkIO() {

SecurityManager sm = getSecurityManager();

if(sm != null) {

sm.checkPermission(new RuntimePermission("setIO"));

}

}

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");

}

}

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;

}

// 加载环境变量以填充props

private static native Properties initProperties(Properties props);

/**

* Create PrintStream for stdout/err based on encoding.

*/

// 用指定的最终输出流构造开启了自动刷新的字节打印流,encoding用来指定打印流用到的字符集

private static PrintStream newPrintStream(FileOutputStream fos, String encoding) {

if(encoding != null) {

try {

return new PrintStream(new BufferedOutputStream(fos, 128), true, encoding);

} catch(UnsupportedEncodingException uee) {

}

}

return new PrintStream(new BufferedOutputStream(fos, 128), true);

}

/**

* Logs an exception/error at initialization time to stdout or stderr.

*

* @param printToStderr to print to stderr rather than stdout

* @param printStackTrace to print the stack trace

* @param msg the message to print before the exception, can be {@code null}

* @param e the exception or error

*/

private static void logInitException(boolean printToStderr, boolean printStackTrace, String msg, Throwable e) {

if(VM.initLevel()<1) {

throw new InternalError("system classes not initialized");

}

PrintStream log = (printToStderr) ? err : out;

if(msg != null) {

log.println(msg);

}

if(printStackTrace) {

e.printStackTrace(log);

} else {

log.println(e);

for(Throwable suppressed : e.getSuppressed()) {

log.println("Suppressed: " + suppressed);

}

Throwable cause = e.getCause();

if(cause != null) {

log.println("Caused by: " + cause);

}

}

}

private static void setJavaLangAccess() {

// Allow privileged classes outside of java.lang

SharedSecrets.setJavaLangAccess(new JavaLangAccess() {

public List getDeclaredPublicMethods(Class> klass, String name, Class>... parameterTypes) {

return klass.getDeclaredPublicMethods(name, parameterTypes);

}

public jdk.internal.reflect.ConstantPool getConstantPool(Class> klass) {

return klass.getConstantPool();

}

public boolean casAnnotationType(Class> klass, AnnotationType oldType, AnnotationType newType) {

return klass.casAnnotationType(oldType, newType);

}

public AnnotationType getAnnotationType(Class> klass) {

return klass.getAnnotationType();

}

public Map, Annotation> getDeclaredAnnotationMap(Class> klass) {

return klass.getDeclaredAnnotationMap();

}

public byte[] getRawClassAnnotations(Class> klass) {

return klass.getRawAnnotations();

}

public byte[] getRawClassTypeAnnotations(Class> klass) {

return klass.getRawTypeAnnotations();

}

public byte[] getRawExecutableTypeAnnotations(Executable executable) {

return Class.getExecutableTypeAnnotationBytes(executable);

}

public > E[] getEnumConstantsShared(Class klass) {

return klass.getEnumConstantsShared();

}

public void blockedOn(Interruptible b) {

Thread.blockedOn(b);

}

public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {

Shutdown.add(slot, registerShutdownInProgress, hook);

}

public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) {

return new Thread(target, acc);

}

@SuppressWarnings("deprecation")

public void invokeFinalize(Object o) throws Throwable {

o.finalize();

}

public ConcurrentHashMap, ?> createOrGetClassLoaderValueMap(ClassLoader cl) {

return cl.createOrGetClassLoaderValueMap();

}

public Class> defineClass(ClassLoader loader, String name, byte[] b, ProtectionDomain pd, String source) {

return ClassLoader.defineClass1(loader, name, b, 0, b.length, pd, source);

}

public Class> findBootstrapClassOrNull(ClassLoader cl, String name) {

return cl.findBootstrapClassOrNull(name);

}

public Package definePackage(ClassLoader cl, String name, Module module) {

return cl.definePackage(name, module);

}

public String fastUUID(long lsb, long msb) {

return Long.fastUUID(lsb, msb);

}

public void addNonExportedPackages(ModuleLayer layer) {

SecurityManager.addNonExportedPackages(layer);

}

public void invalidatePackageAccessCache() {

SecurityManager.invalidatePackageAccessCache();

}

public Module defineModule(ClassLoader loader, ModuleDescriptor descriptor, URI uri) {

return new Module(null, loader, descriptor, uri);

}

public Module defineUnnamedModule(ClassLoader loader) {

return new Module(loader);

}

public void addReads(Module m1, Module m2) {

m1.implAddReads(m2);

}

public void addReadsAllUnnamed(Module m) {

m.implAddReadsAllUnnamed();

}

public void addExports(Module m, String pn, Module other) {

m.implAddExports(pn, other);

}

public void addExportsToAllUnnamed(Module m, String pn) {

m.implAddExportsToAllUnnamed(pn);

}

public void addOpens(Module m, String pn, Module other) {

m.implAddOpens(pn, other);

}

public void addOpensToAllUnnamed(Module m, String pn) {

m.implAddOpensToAllUnnamed(pn);

}

public void addOpensToAllUnnamed(Module m, Iterator packages) {

m.implAddOpensToAllUnnamed(packages);

}

public void addUses(Module m, Class> service) {

m.implAddUses(service);

}

public boolean isReflectivelyExported(Module m, String pn, Module other) {

return m.isReflectivelyExported(pn, other);

}

public boolean isReflectivelyOpened(Module m, String pn, Module other) {

return m.isReflectivelyOpened(pn, other);

}

public ServicesCatalog getServicesCatalog(ModuleLayer layer) {

return layer.getServicesCatalog();

}

public Stream layers(ModuleLayer layer) {

return layer.layers();

}

public Stream layers(ClassLoader loader) {

return ModuleLayer.layers(loader);

}

public String newStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException {

return StringCoding.newStringNoRepl(bytes, cs);

}

public byte[] getBytesNoRepl(String s, Charset cs) throws CharacterCodingException {

return StringCoding.getBytesNoRepl(s, cs);

}

public String newStringUTF8NoRepl(byte[] bytes, int off, int len) {

return StringCoding.newStringUTF8NoRepl(bytes, off, len);

}

public byte[] getBytesUTF8NoRepl(String s) {

return StringCoding.getBytesUTF8NoRepl(s);

}

});

}

/**

* {@code System.Logger} instances log messages that will be

* routed to the underlying logging framework the {@link System.LoggerFinder

* LoggerFinder} uses.

*

* {@code System.Logger} instances are typically obtained from

* the {@link java.lang.System System} class, by calling

* {@link java.lang.System#getLogger(java.lang.String) System.getLogger(loggerName)}

* or {@link java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle)

* System.getLogger(loggerName, bundle)}.

*

* @see java.lang.System#getLogger(java.lang.String)

* @see java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle)

* @see java.lang.System.LoggerFinder

* @since 9

*/

// 日志接口

public interface Logger {

/**

* Returns the name of this logger.

*

* @return the logger name.

*/

// 返回Logger的名称

public String getName();

/**

* Checks if a message of the given level would be logged by

* this logger.

*

* @param level the log message level.

*

* @return {@code true} if the given log message level is currently

* being logged.

*

* @throws NullPointerException if {@code level} is {@code null}.

*/

public boolean isLoggable(Level level);

/**

* Logs a message.

*

* @param level the log message level.

* @param msg the string message (or a key in the message catalog, if

* this logger is a {@link

* LoggerFinder#getLocalizedLogger(java.lang.String,

* java.util.ResourceBundle, java.lang.Module) localized logger});

* can be {@code null}.

*

* @throws NullPointerException if {@code level} is {@code null}.

* @implSpec The default implementation for this method calls

* {@code this.log(level, (ResourceBundle)null, msg, (Object[])null);}

*/

// 打印不同级别的日志

public default void log(Level level, String msg) {

log(level, (ResourceBundle) null, msg, (Object[]) null);

}

/**

* Logs a message associated with a given throwable.

*

* @param level the log message level.

* @param msg the string message (or a key in the message catalog, if

* this logger is a {@link

* LoggerFinder#getLocalizedLogger(java.lang.String,

* java.util.ResourceBundle, java.lang.Module) localized logger});

* can be {@code null}.

* @param thrown a {@code Throwable} associated with the log message;

* can be {@code null}.

*

* @throws NullPointerException if {@code level} is {@code null}.

* @implSpec The default implementation for this method calls

* {@code this.log(level, (ResourceBundle)null, msg, thrown);}

*/

public default void log(Level level, String msg, Throwable thrown) {

this.log(level, null, msg, thrown);

}

/**

* Logs a lazily supplied message.

*

* If the logger is currently enabled for the given log message level

* then a message is logged that is the result produced by the

* given supplier function. Otherwise, the supplier is not operated on.

*

* @param level the log message level.

* @param msgSupplier a supplier function that produces a message.

*

* @throws NullPointerException if {@code level} is {@code null},

* or {@code msgSupplier} is {@code null}.

* @implSpec When logging is enabled for the given level, the default

* implementation for this method calls

* {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), (Object[])null);}

*/

public default void log(Level level, Supplier msgSupplier) {

Objects.requireNonNull(msgSupplier);

if(isLoggable(Objects.requireNonNull(level))) {

log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null);

}

}

/**

* Logs a lazily supplied message associated with a given throwable.

*

* If the logger is currently enabled for the given log message level

* then a message is logged that is the result produced by the

* given supplier function. Otherwise, the supplier is not operated on.

*

* @param level one of the log message level identifiers.

* @param msgSupplier a supplier function that produces a message.

* @param thrown a {@code Throwable} associated with log message;

* can be {@code null}.

*

* @throws NullPointerException if {@code level} is {@code null}, or

* {@code msgSupplier} is {@code null}.

* @implSpec When logging is enabled for the given level, the default

* implementation for this method calls

* {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), thrown);}

*/

public default void log(Level level, Supplier msgSupplier, Throwable thrown) {

Objects.requireNonNull(msgSupplier);

if(isLoggable(Objects.requireNonNull(level))) {

this.log(level, null, msgSupplier.get(), thrown);

}

}

/**

* Logs a localized message associated with a given throwable.

*

* If the given resource bundle is non-{@code null}, the {@code msg}

* string is localized using the given resource bundle.

* Otherwise the {@code msg} string is not localized.

*

* @param level the log message level.

* @param bundle a resource bundle to localize {@code msg}; can be

* {@code null}.

* @param msg the string message (or a key in the message catalog,

* if {@code bundle} is not {@code null}); can be {@code null}.

* @param thrown a {@code Throwable} associated with the log message;

* can be {@code null}.

*

* @throws NullPointerException if {@code level} is {@code null}.

*/

public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown);

/**

* Logs a message with an optional list of parameters.

*

* @param level one of the log message level identifiers.

* @param format the string message format in {@link

* java.text.MessageFormat} format, (or a key in the message

* catalog, if this logger is a {@link

* LoggerFinder#getLocalizedLogger(java.lang.String,

* java.util.ResourceBundle, java.lang.Module) localized logger});

* can be {@code null}.

* @param params an optional list of parameters to the message (may be

* none).

*

* @throws NullPointerException if {@code level} is {@code null}.

* @implSpec The default implementation for this method calls

* {@code this.log(level, (ResourceBundle)null, format, params);}

*/

// 格式化日志

public default void log(Level level, String format, Object... params) {

this.log(level, null, format, params);

}

/**

* Logs a message with resource bundle and an optional list of

* parameters.

*

* If the given resource bundle is non-{@code null}, the {@code format}

* string is localized using the given resource bundle.

* Otherwise the {@code format} string is not localized.

*

* @param level the log message level.

* @param bundle a resource bundle to localize {@code format}; can be

* {@code null}.

* @param format the string message format in {@link

* java.text.MessageFormat} format, (or a key in the message

* catalog if {@code bundle} is not {@code null}); can be {@code null}.

* @param params an optional list of parameters to the message (may be

* none).

*

* @throws NullPointerException if {@code level} is {@code null}.

*/

public void log(Level level, ResourceBundle bundle, String format, Object... params);

/**

* Logs a message produced from the given object.

*

* If the logger is currently enabled for the given log message level then

* a message is logged that, by default, is the result produced from

* calling toString on the given object.

* Otherwise, the object is not operated on.

*

* @param level the log message level.

* @param obj the object to log.

*

* @throws NullPointerException if {@code level} is {@code null}, or

* {@code obj} is {@code null}.

* @implSpec When logging is enabled for the given level, the default

* implementation for this method calls

* {@code this.log(level, (ResourceBundle)null, obj.toString(), (Object[])null);}

*/

public default void log(Level level, Object obj) {

Objects.requireNonNull(obj);

if(isLoggable(Objects.requireNonNull(level))) {

this.log(level, (ResourceBundle) null, obj.toString(), (Object[]) null);

}

}

/**

* System {@linkplain Logger loggers} levels.

*

* A level has a {@linkplain #getName() name} and {@linkplain

* #getSeverity() severity}.

* Level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG},

* {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF},

* by order of increasing severity.

*

* {@link #ALL} and {@link #OFF}

* are simple markers with severities mapped respectively to

* {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and

* {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}.

*

* Severity values and Mapping to {@code java.util.logging.Level}.

*

* {@linkplain System.Logger.Level System logger levels} are mapped to

* {@linkplain java.util.logging.Level java.util.logging levels}

* of corresponding severity.

*
The mapping is as follows:

*

*

*

System.Logger Severity Level Mapping

*

*

System.Logger Levels

*

java.util.logging Levels

*

*

*

{@link Logger.Level#ALL ALL}

*

{@link java.util.logging.Level#ALL ALL}

*

{@link Logger.Level#TRACE TRACE}

*

{@link java.util.logging.Level#FINER FINER}

*

{@link Logger.Level#DEBUG DEBUG}

*

{@link java.util.logging.Level#FINE FINE}

*

{@link Logger.Level#INFO INFO}

*

{@link java.util.logging.Level#INFO INFO}

*

{@link Logger.Level#WARNING WARNING}

*

{@link java.util.logging.Level#WARNING WARNING}

*

{@link Logger.Level#ERROR ERROR}

*

{@link java.util.logging.Level#SEVERE SEVERE}

*

{@link Logger.Level#OFF OFF}

*

{@link java.util.logging.Level#OFF OFF}

*

*

*

* @see java.lang.System.LoggerFinder

* @see java.lang.System.Logger

* @since 9

*/

public enum Level {

// for convenience, we're reusing java.util.logging.Level int values the mapping logic in sun.util.logging.PlatformLogger depends on this.

/**

* A marker to indicate that all levels are enabled.

* This level {@linkplain #getSeverity() severity} is

* {@link Integer#MIN_VALUE}.

*/

ALL(Integer.MIN_VALUE), // typically mapped to/from j.u.l.Level.ALL

/**

* {@code TRACE} level: usually used to log diagnostic information.

* This level {@linkplain #getSeverity() severity} is

* {@code 400}.

*/

TRACE(400), // typically mapped to/from j.u.l.Level.FINER

/**

* {@code DEBUG} level: usually used to log debug information traces.

* This level {@linkplain #getSeverity() severity} is

* {@code 500}.

*/

DEBUG(500), // typically mapped to/from j.u.l.Level.FINEST/FINE/CONFIG

/**

* {@code INFO} level: usually used to log information messages.

* This level {@linkplain #getSeverity() severity} is

* {@code 800}.

*/

INFO(800), // typically mapped to/from j.u.l.Level.INFO

/**

* {@code WARNING} level: usually used to log warning messages.

* This level {@linkplain #getSeverity() severity} is

* {@code 900}.

*/

WARNING(900), // typically mapped to/from j.u.l.Level.WARNING

/**

* {@code ERROR} level: usually used to log error messages.

* This level {@linkplain #getSeverity() severity} is

* {@code 1000}.

*/

ERROR(1000), // typically mapped to/from j.u.l.Level.SEVERE

/**

* A marker to indicate that all levels are disabled.

* This level {@linkplain #getSeverity() severity} is

* {@link Integer#MAX_VALUE}.

*/

OFF(Integer.MAX_VALUE); // typically mapped to/from j.u.l.Level.OFF

private final int severity;

private Level(int severity) {

this.severity = severity;

}

/**

* Returns the name of this level.

*

* @return this level {@linkplain #name()}.

*/

public final String getName() {

return name();

}

/**

* Returns the severity of this level.

* A higher severity means a more severe condition.

*

* @return this level severity.

*/

public final int getSeverity() {

return severity;

}

}

}

/**

* The {@code LoggerFinder} service is responsible for creating, managing,

* and configuring loggers to the underlying framework it uses.

*

* A logger finder is a concrete implementation of this class that has a

* zero-argument constructor and implements the abstract methods defined

* by this class.

* The loggers returned from a logger finder are capable of routing log

* messages to the logging backend this provider supports.

* A given invocation of the Java Runtime maintains a single

* system-wide LoggerFinder instance that is loaded as follows:

*

*

First it finds any custom {@code LoggerFinder} provider

* using the {@link java.util.ServiceLoader} facility with the

* {@linkplain ClassLoader#getSystemClassLoader() system class

* loader}.

*

If no {@code LoggerFinder} provider is found, the system default

* {@code LoggerFinder} implementation will be used.

*

*

* An application can replace the logging backend

* even when the java.logging module is present, by simply providing

* and declaring an implementation of the {@link LoggerFinder} service.

*

* Default Implementation

*

* The system default {@code LoggerFinder} implementation uses

* {@code java.util.logging} as the backend framework when the

* {@code java.logging} module is present.

* It returns a {@linkplain System.Logger logger} instance

* that will route log messages to a {@link java.util.logging.Logger

* java.util.logging.Logger}. Otherwise, if {@code java.logging} is not

* present, the default implementation will return a simple logger

* instance that will route log messages of {@code INFO} level and above to

* the console ({@code System.err}).

*

* Logging Configuration

*

* {@linkplain Logger Logger} instances obtained from the

* {@code LoggerFinder} factory methods are not directly configurable by

* the application. Configuration is the responsibility of the underlying

* logging backend, and usually requires using APIs specific to that backend.

*

For the default {@code LoggerFinder} implementation

* using {@code java.util.logging} as its backend, refer to

* {@link java.util.logging java.util.logging} for logging configuration.

* For the default {@code LoggerFinder} implementation returning simple loggers

* when the {@code java.logging} module is absent, the configuration

* is implementation dependent.

*

* Usually an application that uses a logging framework will log messages

* through a logger facade defined (or supported) by that framework.

* Applications that wish to use an external framework should log

* through the facade associated with that framework.

*

* A system class that needs to log messages will typically obtain

* a {@link System.Logger} instance to route messages to the logging

* framework selected by the application.

*

* Libraries and classes that only need loggers to produce log messages

* should not attempt to configure loggers by themselves, as that

* would make them dependent from a specific implementation of the

* {@code LoggerFinder} service.

*

* In addition, when a security manager is present, loggers provided to

* system classes should not be directly configurable through the logging

* backend without requiring permissions.

*

* It is the responsibility of the provider of

* the concrete {@code LoggerFinder} implementation to ensure that

* these loggers are not configured by untrusted code without proper

* permission checks, as configuration performed on such loggers usually

* affects all applications in the same Java Runtime.

*

* Message Levels and Mapping to backend levels

*

* A logger finder is responsible for mapping from a {@code

* System.Logger.Level} to a level supported by the logging backend it uses.

*
The default LoggerFinder using {@code java.util.logging} as the backend

* maps {@code System.Logger} levels to

* {@linkplain java.util.logging.Level java.util.logging} levels

* of corresponding severity - as described in {@link Logger.Level

* Logger.Level}.

*

* @see java.lang.System

* @see java.lang.System.Logger

* @since 9

*/

public static abstract class LoggerFinder {

/**

* The {@code RuntimePermission("loggerFinder")} is

* necessary to subclass and instantiate the {@code LoggerFinder} class,

* as well as to obtain loggers from an instance of that class.

*/

static final RuntimePermission LOGGERFINDER_PERMISSION = new RuntimePermission("loggerFinder");

private static volatile LoggerFinder service;

/**

* Creates a new instance of {@code LoggerFinder}.

*

* @throws SecurityException if a security manager is present and its

* {@code checkPermission} method doesn't allow the

* {@code RuntimePermission("loggerFinder")}.

* @implNote It is recommended that a {@code LoggerFinder} service

* implementation does not perform any heavy initialization in its

* constructor, in order to avoid possible risks of deadlock or class

* loading cycles during the instantiation of the service provider.

*/

protected LoggerFinder() {

this(checkPermission());

}

private LoggerFinder(Void unused) {

// nothing to do.

}

/**

* Returns an instance of {@link Logger Logger}

* for the given {@code module}.

*

* @param name the name of the logger.

* @param module the module for which the logger is being requested.

*

* @return a {@link Logger logger} suitable for use within the given

* module.

*

* @throws NullPointerException if {@code name} is {@code null} or

* {@code module} is {@code null}.

* @throws SecurityException if a security manager is present and its

* {@code checkPermission} method doesn't allow the

* {@code RuntimePermission("loggerFinder")}.

*/

public abstract Logger getLogger(String name, Module module);

/**

* Returns the {@code LoggerFinder} instance. There is one

* single system-wide {@code LoggerFinder} instance in

* the Java Runtime. See the class specification of how the

* {@link LoggerFinder LoggerFinder} implementation is located and

* loaded.

*

* @return the {@link LoggerFinder LoggerFinder} instance.

*

* @throws SecurityException if a security manager is present and its

* {@code checkPermission} method doesn't allow the

* {@code RuntimePermission("loggerFinder")}.

*/

public static LoggerFinder getLoggerFinder() {

final SecurityManager sm = System.getSecurityManager();

if(sm != null) {

sm.checkPermission(LOGGERFINDER_PERMISSION);

}

return accessProvider();

}

static LoggerFinder accessProvider() {

// We do not need to synchronize: LoggerFinderLoader will

// always return the same instance, so if we don't have it,

// just fetch it again.

if(service == null) {

PrivilegedAction pa = () -> LoggerFinderLoader.getLoggerFinder();

service = AccessController.doPrivileged(pa, null, LOGGERFINDER_PERMISSION);

}

return service;

}

private static Void checkPermission() {

final SecurityManager sm = System.getSecurityManager();

if(sm != null) {

sm.checkPermission(LOGGERFINDER_PERMISSION);

}

return null;

}

/**

* Returns a localizable instance of {@link Logger Logger}

* for the given {@code module}.

* The returned logger will use the provided resource bundle for

* message localization.

*

* @param name the name of the logger.

* @param bundle a resource bundle; can be {@code null}.

* @param module the module for which the logger is being requested.

*

* @return an instance of {@link Logger Logger} which will use the

* provided resource bundle for message localization.

*

* @throws NullPointerException if {@code name} is {@code null} or

* {@code module} is {@code null}.

* @throws SecurityException if a security manager is present and its

* {@code checkPermission} method doesn't allow the

* {@code RuntimePermission("loggerFinder")}.

* @implSpec By default, this method calls {@link

* #getLogger(java.lang.String, java.lang.Module)

* this.getLogger(name, module)} to obtain a logger, then wraps that

* logger in a {@link Logger} instance where all methods that do not

* take a {@link ResourceBundle} as parameter are redirected to one

* which does - passing the given {@code bundle} for

* localization. So for instance, a call to {@link

* Logger#log(Logger.Level, String) Logger.log(Level.INFO, msg)}

* will end up as a call to {@link

* Logger#log(Logger.Level, ResourceBundle, String, Object...)

* Logger.log(Level.INFO, bundle, msg, (Object[])null)} on the wrapped

* logger instance.

* Note however that by default, string messages returned by {@link

* java.util.function.Supplier Supplier<String>} will not be

* localized, as it is assumed that such strings are messages which are

* already constructed, rather than keys in a resource bundle.

*

* An implementation of {@code LoggerFinder} may override this method,

* for example, when the underlying logging backend provides its own

* mechanism for localizing log messages, then such a

* {@code LoggerFinder} would be free to return a logger

* that makes direct use of the mechanism provided by the backend.

*/

public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module module) {

return new LocalizedLoggerWrapper<>(getLogger(name, module), bundle);

}

}

}

一键复制

编辑

Web IDE

原始数据

按行查看

历史

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值