ClassLoader那事儿
ClassLoader是什么
ClassLoader中文类加载器,java编写出来的是.java文件,然后编译成.class文件,而ClassLoader就是把class文件加载到jvm内存中;但jvm启动时,通过不同的类加载器,动态的加载class文件;java比较重要的三类加载器Bootstrap ClassLoader、 Extention ClassLoader、Appclass Loader。
Java中的类加载器
Bootstrap ClassLoader、 Extention ClassLoader、Appclass Loader这三个类加载器分别负责加载不同路径下的文件
Bootstrap CLassloder
它是最顶级的类加载,主要加载载核心类库,其路径为%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等,而这个路径是可以通过jvm启动参数-Xbootclasspath来设置
Extention ClassLoader
扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
AppClassLoader
当前应用的classpath的所有类
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
- package sun.misc;
- import java.io.File;
- import java.io.IOException;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.net.URLStreamHandler;
- import java.net.URLStreamHandlerFactory;
- import java.security.AccessControlContext;
- import java.security.AccessController;
- import java.security.CodeSource;
- import java.security.PermissionCollection;
- import java.security.PrivilegedAction;
- import java.security.PrivilegedActionException;
- import java.security.PrivilegedExceptionAction;
- import java.security.ProtectionDomain;
- import java.security.cert.Certificate;
- import java.util.HashSet;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import sun.net.www.ParseUtil;
- public class Launcher {
- private static URLStreamHandlerFactory factory = new Launcher.Factory();
- private static Launcher launcher = new Launcher();
- private static String bootClassPath = System.getProperty("sun.boot.class.path");
- private ClassLoader loader;
- private static URLStreamHandler fileHandler;
- public static Launcher getLauncher() {
- return launcher;
- }
- public Launcher() {
- Launcher.ExtClassLoader var1;
- try {
- var1 = Launcher.ExtClassLoader.getExtClassLoader();
- } catch (IOException var10) {
- throw new InternalError("Could not create extension class loader");
- }
- try {
- this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
- } catch (IOException var9) {
- throw new InternalError("Could not create application class loader");
- }
- Thread.currentThread().setContextClassLoader(this.loader);
- String var2 = System.getProperty("java.security.manager");
- if (var2 != null) {
- SecurityManager var3 = null;
- if (!"".equals(var2) && !"default".equals(var2)) {
- try {
- var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
- } catch (IllegalAccessException var5) {
- ;
- } catch (InstantiationException var6) {
- ;
- } catch (ClassNotFoundException var7) {
- ;
- } catch (ClassCastException var8) {
- ;
- }
- } else {
- var3 = new SecurityManager();
- }
- if (var3 == null) {
- throw new InternalError("Could not create SecurityManager: " + var2);
- }
- System.setSecurityManager(var3);
- }
- }
- public ClassLoader getClassLoader() {
- return this.loader;
- }
- public static URLClassPath getBootstrapClassPath() {
- return Launcher.BootClassPathHolder.bcp;
- }
………………………………- }
从以上源码可以看到
1. 没有看到 Bootstrap ClassLoader,只有bootClassPath,这个应该就是顶级类加载器的加载路径;
2.Launcher 先初始化了Extention ClassLoader ,然后把传入它实例再初始化AppClassLoader。
- static class ExtClassLoader extends URLClassLoader {
- public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
//获取加载的文件,进入getExtDirs可以看到加载的文件路径String var0 = System.getProperty("java.ext.dirs")
- final File[] var0 = getExtDirs();
- try {
- return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
- public Launcher.ExtClassLoader run() throws IOException {
- int var1 = var0.length;
- for(int var2 = 0; var2 < var1; ++var2) {
- MetaIndex.registerDirectory(var0[var2]);
- }
- //创建类加载
- return new Launcher.ExtClassLoader(var0);
- }
- });
- } catch (PrivilegedActionException var2) {
- throw (IOException)var2.getException();
- }
- }
- void addExtURL(URL var1) {
- super.addURL(var1);
- }
- public ExtClassLoader(File[] var1) throws IOException {
//调用父类方法创建,留意到第二个参数为null,这个参数是父加载器,预测ExtClassLoader是没有父加载器的
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
- }
- private static File[] getExtDirs() {
- String var0 = System.getProperty("java.ext.dirs");
- File[] var1;
- if (var0 != null) {
- StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
- int var3 = var2.countTokens();
- var1 = new File[var3];
- for(int var4 = 0; var4 < var3; ++var4) {
- var1[var4] = new File(var2.nextToken());
- }
- } else {
- var1 = new File[0];
- }
- return var1;
- }
- private static URL[] getExtURLs(File[] var0) throws IOException {
- Vector var1 = new Vector();
- for(int var2 = 0; var2 < var0.length; ++var2) {
- String[] var3 = var0[var2].list();
- if (var3 != null) {
- for(int var4 = 0; var4 < var3.length; ++var4) {
- if (!var3[var4].equals("meta-index")) {
- File var5 = new File(var0[var2], var3[var4]);
- var1.add(Launcher.getFileURL(var5));
- }
- }
- }
- }
- URL[] var6 = new URL[var1.size()];
- var1.copyInto(var6);
- return var6;
- }
- public String findLibrary(String var1) {
- var1 = System.mapLibraryName(var1);
- URL[] var2 = super.getURLs();
- File var3 = null;
- for(int var4 = 0; var4 < var2.length; ++var4) {
- File var5 = (new File(var2[var4].getPath())).getParentFile();
- if (var5 != null && !var5.equals(var3)) {
- String var6 = VM.getSavedProperty("os.arch");
- File var7;
- if (var6 != null) {
- var7 = new File(new File(var5, var6), var1);
- if (var7.exists()) {
- return var7.getAbsolutePath();
- }
- }
- var7 = new File(var5, var1);
- if (var7.exists()) {
- return var7.getAbsolutePath();
- }
- }
- var3 = var5;
- }
- return null;
- }
- private static AccessControlContext getContext(File[] var0) throws IOException {
- PathPermissions var1 = new PathPermissions(var0);
- ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
- AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
- return var3;
- }
- static {
- ClassLoader.registerAsParallelCapable();
- }
- }
看看ExtenTionClassLoader初始化做了什么
从上面可以看到ExtClassLoader extends URLClassLoader,初始化ExtClassLoader先读取java.ext.dirs路径(D:\java7\jre\lib\ext;C:\Windows\Sun\Java\lib\ext)下的文件,然后调用URLClassLoader.supper()-->
SecureClassLoader.supper()-->ClassLoader.supper()-->方法创建,因为传入的parent是null,可以看到ExtClassLoader 没有父加载器的;简单说就是加载java.ext.dirs的文件创建了个无父加载器的类加载器。
- public static void main(String[] args) {
- System.out.println(System.getProperty("java.ext.dirs"));
- }
- /*
- D:\java7\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
- */
- /*
URLClassLoader extends SecureClassLoader implements Closeable
- */
- public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls, factory);
acc = AccessController.getContext();
}
/******************************************************************************/
/*
SecureClassLoader extends ClassLoader
*/
- protected SecureClassLoader(ClassLoader parent) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
initialized = true;
}
/******************************************************************************/
- /*
- ClassLoader类
- */
- protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
- private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
看看APPClassLoader初始化做了什么
- static class AppClassLoader extends URLClassLoader {
- public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
- final String var1 = System.getProperty("java.class.path");
- final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
- return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
- public Launcher.AppClassLoader run() {
- URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
- return new Launcher.AppClassLoader(var1x, var0);
- }
- });
- }
- AppClassLoader(URL[] var1, ClassLoader var2) {
- super(var1, var2, Launcher.factory);
- }
- public Class loadClass(String var1, boolean var2) throws ClassNotFoundException {
- int var3 = var1.lastIndexOf(46);
- if (var3 != -1) {
- SecurityManager var4 = System.getSecurityManager();
- if (var4 != null) {
- var4.checkPackageAccess(var1.substring(0, var3));
- }
- }
- return super.loadClass(var1, var2);
- }
- protected PermissionCollection getPermissions(CodeSource var1) {
- PermissionCollection var2 = super.getPermissions(var1);
- var2.add(new RuntimePermission("exitVM"));
- return var2;
- }
- private void appendToClassPathForInstrumentation(String var1) {
- assert Thread.holdsLock(this);
- super.addURL(Launcher.getFileURL(new File(var1)));
- }
- private static AccessControlContext getContext(File[] var0) throws MalformedURLException {
- PathPermissions var1 = new PathPermissions(var0);
- ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
- AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
- return var3;
- }
- static {
- ClassLoader.registerAsParallelCapable();
- }
- }
从上面看APPClassLoader也是继承URLClassLoader,与ExtClassLoader不同的是,
1.读取的路径是java.class.path
2.把ExtClassLoader的实例作为APPClassLoader的父加载器,也就是说APPClassLoader的父类加载器是ExtClassLoader
3.加载顺序Bootstrap ClassLoader 到 ExtClassLoader 到 APPClassLoader
父加载器
首先我们先要明确一点是父加载器并不是父类,我们可以通过下面的代码看到
- public class MyClassLoader extends URLClassLoader {
- public MyClassLoader(URL[] repositories) {
- super(repositories);
- }
- public MyClassLoader(URL[] repositories, ClassLoader parent) {
- super(repositories, parent);
- }
- }
- public static void main(String[] args) {
- ClassLoader cl = MyClassLoader.class.getClassLoader();
- System.out.println("ClassLoader is:"+cl.toString());
- System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());
- // System.out.println("ClassLoader\'s grand father is:"+cl.getParent().getParent().toString());
- }
- ClassLoader is:sun.misc.Launcher$AppClassLoader@31fc6b2
- ClassLoader's parent is:sun.misc.Launcher$ExtClassLoader@1b2dd1b8
1.MyClassLoader 继承URLClassLoader,但是可以到店MyClassLoader的父加载器是AppClassLoader
2.AppClassLoader 的父加载器是ExtClassLoader,这个在上面分析初始化的APPClassLoader的时候,就可以看到传入ExtClassLoader的实例作为它的加载器,那么ExtClassLoader的父加载器上面初始时是null,我们可以验证一下
- public static void main(String[] args) {
- ClassLoader cl = MyClassLoader.class.getClassLoader();
- // System.out.println("ClassLoader is:"+cl.toString());
- // System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());
- System.out.println("ClassLoader\'s grand father is:"+cl.getParent().getParent().toString());
- }
- Exception in thread "main" java.lang.NullPointerException
- at com.suntek.vdm.gw.util.NotifyHandler.main(NotifyHandler.java:373)
从上面的代码可以看到,我们的上面是对的ExtClassLoader的父加载器的确是null.
3.那么其他的没有指定父加载器的ClassLoader的父加载器是什么呢?从上面的代码我们可以看到MyClassLoader的的是APPClassLoader,那么这是为什么呢?难道没有指定父加载器的ClassLoader的默认是APP
- /*
- 这ClassLoader是没有指定父加载器创建方法
- */
- protected ClassLoader() {
- this(checkCreateClassLoader(), getSystemClassLoader());
- }
- //这里我们可以看到getSytemClassLoader()这个方法获取父加载器
- private ClassLoader(Void unused, ClassLoader parent) {
- this.parent = parent;
- if (ParallelLoaders.isRegistered(this.getClass())) {
- parallelLockMap = new ConcurrentHashMap<>();
- package2certs = new ConcurrentHashMap<>();
- domains =
- Collections.synchronizedSet(new HashSet<ProtectionDomain>());
- assertionLock = new Object();
- } else {
- // no finer-grained lock; lock on the classloader instance
- parallelLockMap = null;
- package2certs = new Hashtable<>();
- domains = new HashSet<>();
- assertionLock = this;
- }
- }
- //看到return的是scl,而scl赋值是initSystemClassLoader()方法
- public static ClassLoader getSystemClassLoader() {
- initSystemClassLoader();
- if (scl == null) {
- return null;
- }
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- checkClassLoaderPermission(scl, Reflection.getCallerClass());
- }
- return scl;
- }
- //真相就在这里了,就是Launcher的getClassLoader(),而Luancher初始化的前面有看过了,也可以往下看
- private static synchronized void initSystemClassLoader() {
- if (!sclSet) {
- if (scl != null)
- throw new IllegalStateException("recursive invocation");
- sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
- if (l != null) {
- Throwable oops = null;
- scl = l.getClassLoader();
- try {
- scl = AccessController.doPrivileged(
- new SystemClassLoaderAction(scl));
- } catch (PrivilegedActionException pae) {
- oops = pae.getCause();
- if (oops instanceof InvocationTargetException) {
- oops = oops.getCause();
- }
- }
- if (oops != null) {
- if (oops instanceof Error) {
- throw (Error) oops;
- } else {
- // wrap the exception
- throw new Error(oops);
- }
- }
- }
- sclSet = true;
- }
- }
- //这个是Launcher对的getClassLoader,然后在看看下面的图,明白了,就是APPClassLoader
- public ClassLoader getClassLoader() {
- return this.loader;
- }
一层层往下最终APPClassLoader作为默认的ClassLoader;
也就是没有指定的父加载器的默认为是APPClassLoader;
双亲委托
有了父加载器的概念之后,我们就可以开始双亲委托这个玩意了!这部分,发现个哥们写得不错 https://blog.csdn.net/briblue/article/details/54973413,于是就复制了!
一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托。
整个流程可以如下图所示:
大家可以看到2根箭头,蓝色的代表类加载器向上委托的方向,如果当前的类加载器没有查询到这个class对象已经加载就请求父加载器(不一定是父类)进行操作,然后以此类推。直到Bootstrap ClassLoader。如果Bootstrap ClassLoader也没有加载过此class实例,那么它就会从它指定的路径中去查找,如果查找成功则返回,如果没有查找成功则交给子类加载器,也就是ExtClassLoader,这样类似操作直到终点,也就是我上图中的红色箭头示例。
用序列描述一下:
1. 一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。
2. 递归,重复第1部的操作。
3. 如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面,它首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class
下面的路径。找到就返回,没有找到,让子加载器自己去找。
4. Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs
路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找。
5. ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path
路径下查找。找到就返回。如果没有找到就让子类找,如果没有子类会怎么样?抛出各种异常。
上面的序列,详细说明了双亲委托的加载流程。我们可以发现委托是从下向上,然后具体查找过程却是自上至下。
这里要说一下ExtClassLoader 这个是没有父加载器,就通过BootStrapClassLoader这个来查找的,下面这个代码的ClassLoader这个类的方法,
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- synchronized (getClassLoadingLock(name)) {
- // First, check if the class has already been loaded
// 首先到缓存寻找- Class c = findLoadedClass(name);
- if (c == null) {
- long t0 = System.nanoTime();
- try {
- if (parent != null) {
//如果父加强是空,往上找- c = parent.loadClass(name, false);
- } else {
//否则就通过BootStrapClassLoader找,也就应该是ExtClassLoader- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {
- // ClassNotFoundException thrown if class not found
- // from the non-null parent class loader
- }
- if (c == null) {
- // If still not found, then invoke findClass in order
- // to find the class.
- long t1 = System.nanoTime();
- c = findClass(name);
- // this is the defining class loader; record the stats
- sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
- sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
- sun.misc.PerfCounter.getFindClasses().increment();
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
自定义类加载器
了解我们可以自己写一个简单的加载类
- public class MyClassLoader extends URLClassLoader {
- public MyClassLoader(URL[] repositories) {
- super(repositories);
- }
- public MyClassLoader(URL[] repositories, ClassLoader parent) {
- super(repositories, parent);
- }
- }
- public final class MyClassLoaderFactory {
- public static ClassLoader createClassLoader(File unpacked[], final ClassLoader parent)
- throws Exception {
- Set<URL> set = new LinkedHashSet<URL>();
- if (unpacked != null) {
- for (int i = 0; i < unpacked.length; i++) {
- File file = unpacked[i];
- if (!file.exists() || !file.canRead()){
- continue;
- }
- file = new File(file.getCanonicalPath() + File.separator);
- URL url = file.toURI().toURL();
- set.add(url);
- }
- }
- final URL[] array = set.toArray(new URL[set.size()]);
- return AccessController.doPrivileged(new PrivilegedAction<MyClassLoader>() {
- @Override
- public MyClassLoader run() {
- if (parent == null){
- return new MyClassLoader(array);
- }else {
- return new MyClassLoader(array, parent);
- }
- }
- });
- }
- }
- private static void startWithClassloader(String path) throws Exception {
- String classPath= path;
- File[] packed = new File[] { new File(path) };
- ClassLoader catalinaLoader = MyClassLoaderFactory.createClassLoader(null, packed, null);
- Thread.currentThread().setContextClassLoader(catalinaLoader);
- Class<?> startupClass = catalinaLoader.loadClass(MAIN_CLASS);
- Object startupInstance = startupClass.newInstance();
- Method method = startupInstance.getClass().getMethod("start");
- method.invoke(startupInstance);
- }
一步一步来吧,首先自定义一个MyClassLoader类继承了URlClassLoader,再对比一下AppClassLoader 的创建,可以说基本一样;
a.获取路径的File[] 文件;
b.然后就调用了AccessController 的native <T> T doPrivileged(PrivilegedAction<T> action),而关于这个方法的说法,网友们是这么说的:一个调用者在调用doPrivileged方法时,可被标识为 "特权"。在做访问控制决策时,如果checkPermission方法遇到一个通过doPrivileged调用而被表示为 "特权"的调用者,并且没有上下文自变量,checkPermission方法则将终止检查。如果那个调用者的域具有特定的许可,则不做进一步检查,checkPermission安静地返回,表示那个访问请求是被允许的;如果那个域没有特定的许可,则象通常一样,一个异常被抛出
c.调用福利的supper方法创建
Thread.currentThread().setContextClassLoader(catalinaLoader) 这个句有什么用??
a.在java的应用中,每个线程都有一个contextClassLoad,而充init()方法可以看到,默认是父线程的上下文类加载器,当月我们也可以通过Thread.currentThread().setContextClassLoader(catalinaLoader)来设置;
b.这个上下文的加载器的作用简单的说,就是当这个线程加载某些类时,通过这加载器进行加载;
c.网友说法:
每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类
系统默认的contextClassLoader是systemClassLoader,所以一般而言java程序在执行时可以使用JVM自带的类、$JAVA_HOME/jre/lib/ext/中的类和$CLASSPATH/中的类
可以使用Thread.currentThread().setContextClassLoader(...);更改当前线程的contextClassLoader,来改变其载入类的行为
ClassLoader被组织成树形,一般的工作原理是:
1) 线程需要用到某个类,于是contextClassLoader被请求来载入该类
2) contextClassLoader请求它的父ClassLoader来完成该载入请求
3) 如果父ClassLoader无法载入类,则contextClassLoader试图自己来载入
注意:WebApp?ClassLoader的工作原理和上述有少许不同:
它先试图自己载入类(在ContextBase?/WEB-INF/...中载入类),如果无法载入,再请求父ClassLoader完成
- public
- class Thread implements Runnable {
- /* Make sure registerNatives is the first thing <clinit> does. */
- private static native void registerNatives();
- static {
- registerNatives();
- }
- private char name[];
- private int priority;
- private Thread threadQ;
- private long eetop;
- /* Whether or not to single_step this thread. */
- private boolean single_step;
- /* Whether or not the thread is a daemon thread. */
- private boolean daemon = false;
- /* JVM state */
- private boolean stillborn = false;
- /* What will be run. */
- private Runnable target;
- /* The group of this thread */
- private ThreadGroup group;
- /* The context ClassLoader for this thread */
- private ClassLoader contextClassLoader;
- ………………
- }
- private void init(ThreadGroup g, Runnable target, String name,
- long stackSize, AccessControlContext acc) {
- if (name == null) {
- throw new NullPointerException("name cannot be null");
- }
- this.name = name.toCharArray();
- Thread parent = currentThread();
- SecurityManager security = System.getSecurityManager();
- if (g == null) {
- /* Determine if it's an applet or not */
- /* If there is a security manager, ask the security manager
- what to do. */
- if (security != null) {
- g = security.getThreadGroup();
- }
- /* If the security doesn't have a strong opinion of the matter
- use the parent thread group. */
- if (g == null) {
- g = parent.getThreadGroup();
- }
- }
- /* checkAccess regardless of whether or not threadgroup is
- explicitly passed in. */
- g.checkAccess();
- /*
- * Do we have the required permissions?
- */
- if (security != null) {
- if (isCCLOverridden(getClass())) {
- security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
- }
- }
- g.addUnstarted();
- this.group = g;
- this.daemon = parent.isDaemon();
- this.priority = parent.getPriority();
- if (security == null || isCCLOverridden(parent.getClass()))
- this.contextClassLoader = parent.getContextClassLoader();
- else
- this.contextClassLoader = parent.contextClassLoader;
- this.inheritedAccessControlContext =
- acc != null ? acc : AccessController.getContext();
- this.target = target;
- setPriority(priority);
- if (parent.inheritableThreadLocals != null)
- this.inheritableThreadLocals =
- ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
- /* Stash the specified stack size in case the VM cares */
- this.stackSize = stackSize;
- /* Set thread ID */
- tid = nextThreadID();
- }
最后通过反射把程序运行起来
一个题目
- /**
- * a.加载->校验->准备->初始化->使用->卸载(准备和初始化)
- * b.父类静态->本身静态->父类变量和代码块->父类构造器->本身变量和代码块->本身构造器
- * c.静态只会加载一次,静态变量一开始会在准备阶段赋值默认值,如st = null, flag = false;
- * c.而st = new StaticTest()这步骤被镶嵌到static StaticTest st = new StaticTest();
- * d.开始执行new StaticTest(),因为静态变量都已经初始化,因此进入:本身变量和代码块->本身构造器
- * e.结束new StaticTest(),进入下一个静态变量初始化System.out.println("2");再下一个static boolean flag = true;
- * f.执行方法staticFunction();
- */
- public class StaticTest {
- public static void main(String[] args) {
- staticFunction();
- }
- {
- System.out.println("1");
- }
- static StaticTest st = new StaticTest();
- static {
- System.out.println("2");
- }
- StaticTest() {
- System.out.println("3");
- double temp = 3*0.1;
- if (temp == a){
- System.out.println(a+","+flag);
- }else {
- System.out.println(a+","+flag);
- }
- }
- private static void staticFunction(){
- if (flag){
- System.out.println("4");
- }else {
- System.out.println("5");
- }
- }
- double a=0.3;
- static boolean flag = true;
- }
1
3
0.3,false
2
4
ClassLoader那事儿的更多相关文章
- Proxy那点事儿
全文转载,原文地址:Proxy 那点事儿 Proxy,也就是"代理"了.意思就是,你不用去做,别人代替你去处理.比如说:赚钱方面,我就是我老婆的 Proxy:带小孩方面,我老婆就是 ...
- 一文带你深扒ClassLoader内核,揭开它的神秘面纱!
「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! 前言 Clas ...
- java 编程基础 Class对象 反射:动态代理 和AOP:java.lang.reflect.Proxy:(Proxy.newProxyInstance(newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h))
为什么我们使用动态代理 静态代理会让类变多了,多了代理类,工作量变大了,且不易扩展.比如我们上节课的例子,要实现不同的扩展方法就要编写不同的代理类,非常麻烦. Proxy类的使用规则 Proxy提 ...
- 聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)
本系列Netty源码解析文章基于 4.1.56.Final版本 在上篇文章<聊聊Netty那些事儿之从内核角度看IO模型>中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以 ...
- 说说Makefile那些事儿
说说Makefile那些事儿 |扬说|透过现象看本质 工作至今,一直对Makefile半知半解.突然某天幡然醒悟,觉得此举极为不妥,只得洗心革面从头学来,以前许多不明觉厉之处顿时茅塞顿开,想想好记性不 ...
- 总结iOS开发中的断点续传那些事儿
前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...
- 使用自定义 classloader 的正确姿势
详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了jdk 源码, 说了罗里吧嗦, 还是不很明白: 到底如何正确自定义ClassLoader, 需要注意什么 ExtClassLoade ...
- Atitti 载入类的几种方法 Class.forName ClassLoader.loadClass 直接new
Atitti 载入类的几种方法 Class.forName ClassLoader.loadClass 直接new 1.1. 载入类的几种方法 Class.forName ClassLo ...
- setTimeout那些事儿
一.setTimeout那些事儿之单线程 一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序. 但是,不知道大家有疑问没——就是我们 ...
随机推荐
- asp.net的Request.ServerVariables参数说明
Request.ServerVariables["SERVER_NAME"] '获取服务器IPRequest.ServerVariables["HTTP_REFERER& ...
- ztree搜索节点并展开
web <div class="zTreeC"> <div class="searchL" lay-filter="searchL& ...
- Webstorm 2017.3激活破解
之前尝试过各种激活破解办法,不过随着版本的不断升级,激活信息都失效了(毕竟咱不是通过正常途径激活的),只能重新激活.而且难度越来越大,记得早先网上有人分享激活码,激活的server地址,破解程序等等, ...
- 伺服电机&旋转变压器&光电编码器
旋转变压器与光电编码器是目前伺服领域应用最广的测量传感器. 一.伺服系统 又称为随动系统,精确的跟随或者复现某个过程的反馈系统. 使物体的位置.方位.状态等输出被控量能够跟随目标(设定)的任意变化的自 ...
- kerberos环境下spark消费kafka写入到Hbase
一.准备环境: 创建Kafka Topic和HBase表 1. 在kerberos环境下创建Kafka Topic 1.1 因为kafka默认使用的协议为PLAINTEXT,在kerberos环境下需 ...
- 网站通信协议升级到HTTPS&HTTP2
为何要升级到HTTPS和HTTP2? http://baijiahao.baidu.com/s?id=1602041305989767011&wfr=spider&for=pc htt ...
- python_while
while 格式 while 条件 : pass 使用 while True : print("精忠报国") print("粉红的回忆") print(&quo ...
- 转:SVN 版本服务器搭配全过程详解(含服务端、客户端)
1.为什么要用VisualSVN Server,而不用Subversion? 回答: 因为如果直接使用Subversion,那么在Windows 系统上,要想让它随系统启动,就要封装SVN Serve ...
- Windows之常用命令
1. 重启/关机 shutdown命令 #关机 shutdown -s -t -f #重启 shutdown -r -t //30秒之后,重启 00是立即 #注销 shutdown -l -t #休眠 ...
- mongodb解决只能本地连接不能远程连接问题
本机windows7 ,装了vagrant盒子,并在盒子上装了mongodb服务,本机连接虚拟机时连不上. 解决方法: 修改虚拟机上 mongodb.conf 文件,将bind_ip = 127.0. ...