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那事儿的更多相关文章

  1. Proxy那点事儿

    全文转载,原文地址:Proxy 那点事儿 Proxy,也就是"代理"了.意思就是,你不用去做,别人代替你去处理.比如说:赚钱方面,我就是我老婆的 Proxy:带小孩方面,我老婆就是 ...

  2. 一文带你深扒ClassLoader内核,揭开它的神秘面纱!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! 前言 Clas ...

  3. java 编程基础 Class对象 反射:动态代理 和AOP:java.lang.reflect.Proxy:(Proxy.newProxyInstance(newProxyInstance​(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h))

    为什么我们使用动态代理 静态代理会让类变多了,多了代理类,工作量变大了,且不易扩展.比如我们上节课的例子,要实现不同的扩展方法就要编写不同的代理类,非常麻烦.   Proxy类的使用规则 Proxy提 ...

  4. 聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)

    本系列Netty源码解析文章基于 4.1.56.Final版本 在上篇文章<聊聊Netty那些事儿之从内核角度看IO模型>中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以 ...

  5. 说说Makefile那些事儿

    说说Makefile那些事儿 |扬说|透过现象看本质 工作至今,一直对Makefile半知半解.突然某天幡然醒悟,觉得此举极为不妥,只得洗心革面从头学来,以前许多不明觉厉之处顿时茅塞顿开,想想好记性不 ...

  6. 总结iOS开发中的断点续传那些事儿

    前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...

  7. 使用自定义 classloader 的正确姿势

    详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了jdk 源码, 说了罗里吧嗦, 还是不很明白: 到底如何正确自定义ClassLoader, 需要注意什么 ExtClassLoade ...

  8. Atitti 载入类的几种方法    Class.forName ClassLoader.loadClass  直接new

    Atitti 载入类的几种方法    Class.forName ClassLoader.loadClass  直接new 1.1. 载入类的几种方法    Class.forName ClassLo ...

  9. setTimeout那些事儿

    一.setTimeout那些事儿之单线程 一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序. 但是,不知道大家有疑问没——就是我们 ...

随机推荐

  1. java集合介绍(List,Set,Map)

    前言 介绍java的常用集合+各个集合使用用例 欢迎转载,请注明作者和出处哦☺ 参考: 1,<Java核心编程技术(第二版)> 2, http://www.cnblogs.com/Litt ...

  2. SQL语句将一个表的数据写入到另一个表中

    在日常的数据库运维过程中,有时候需要将Select查询出来的数据集写入到另一个数据表中,其中一种方式是通过存储过程循环写入数据,另一种简便的方式是直接使用Insert Into语句后面跟上Select ...

  3. Vue的安装及使用快速入门

    一.安装vue 1.安装node.js,安装完node.js之后,npm也会自动安装 查询是否安装成功的命令: node -v npm -v 2.全局安装脚手架工具vue-cli,命令如下: npm ...

  4. Ubuntu 16.04 安装Google 浏览器

    Ubuntu安装好后,自带Firefox浏览器,有时我们需要再安装几个浏览器,那么Google Chrome,就是首选, 安装如下: 下载浏览器安装包, 下载链接:https://dl.google. ...

  5. 面试中被问Spring循环依赖的三种方式!!!

    什么是循环依赖? 循环依赖其实就是循环引用,也就是两个或则两个以上的 Bean 互相持有对方,最终形成闭环.比如A依赖于B,B依赖于C,C又依赖于A.如下图: 如果在日常开发中我们用new 对象的方式 ...

  6. 小A的柱状图

    链接 [https://ac.nowcoder.com/acm/contest/549/H] 题意 [] 分析 很显然你必须找到该高度下往左右找到第一个高度比该位置小的.这个区间的宽*该高度.就当前能 ...

  7. MySQL数据库聚合函数

    +++++++++++++++++++++++++++++++++++++++++++标题:MySQL数据库聚合函数时间:2019年2月25日内容:MySQL数据库聚合函数重点:MySQL数据库聚合函 ...

  8. git 解决多人修改相同的文件导致的冲突

    git冲突处理     (场景:A和B修改相同的文件,A先提交到远程仓库,然后B提交push报错,远程仓库有更改,git自动合并失败,需要手动合并 提示错误:Automatic merge faile ...

  9. protobuf使用详解

    https://blog.csdn.net/skh2015java/article/details/78404235 原文地址:http://blog.csdn.net/lyjshen/article ...

  10. MyBatis 3源码解析(一)

    一.SqlSessionFactory 对象初始化 //加载全局配置文件 String resource = "mybatis-config.xml"; InputStream i ...