class Child extends Parent{
public static int a =3;
}
Child.a = 4;
除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化
package JVM.classloader;
public class Test1 {
public static void main(String[] args) {
Class clazz = null;
Class clazz2 = null;
try {
clazz = Class.forName("java.lang.String");
//获取类加载器,如果是根类加载器返回null
System.out.println(clazz.getClassLoader());
clazz2 = Class.forName("JVM.classloader.C");
System.out.println(clazz2.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class C{
}
结果是:
null
sun.misc.Launcher$AppClassLoader@18b4aac2
public class Sample{
private static int a = 1;
public static long b;
static{
b = 2;
}
...
}
public void gotoWork(){
car.run(); //这段代码在Worker类的二进制数据中表示为符号引用
}
public class Sample{
private static int a = 1; //在静态变量的声明处进行初始化
public static long b;
public static long c;
static{
b = 2; //在静态代码块中进行初始化
}
...
}
package JVM.classloader;
public class Singleton {
/**
* 主动使用(6)
* 调用静态方法会加载这个类
* 给所有静态变量赋默认值
* singleton = null; counter1 = 0; counter2 = 0;
* 注意这里counter2的0不是等号后面的0,而是int类型的默认值0
* 然后顺序执行,将singleton = new Singleton();
* 调用构造方法,counter1 == 1; counter2 == 1;
* 然后再执行counter2 = 0;
* 所以结果时counter1 = 1;counter2 = 0;
*/
private static Singleton singleton = new Singleton();
public static int counter1;
public static int counter2 = 0;
private Singleton(){
counter1++;
counter2++;
}
public static Singleton getInstance(){
return singleton;
}
}
class MyTest{
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("counter1 = " + singleton.counter1);
System.out.println("counter2 = " + singleton.counter2);
}
}
结果是:
counter1 = 1
counter2 = 0
如果将
private static Singleton singleton = new Singleton();
public static int counter1;
public static int counter2 = 0;
改为
public static int counter1;
public static int counter2 = 0;
private static Singleton singleton = new Singleton();
结果是:
counter1 = 1
counter2 = 1
public class Sample{
static int a = 1;
static { a = 2; }
static { a = 4; }
public static void main(String args[]){
System.out.println("a = " + a); // 打印a=4
}
}
package JVM.classloader;
class FinalTest{
//x的为常量,在编译时就确定了,调用常量不会初始化FinalTest类
public static final int x = 6/3;
static {
System.out.println("FinalTest static block");
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println(FinalTest.x);
}
}
结果是:
2
package JVM.classloader;
import java.util.Random;
class FinalTest2{
//x的为变量,在运行时才确定它的值,所以会初始化FinalTest2
public static final int x = new Random().nextInt(100);
static {
System.out.println("FinalTest static block");
}
}
public class Test3 {
public static void main(String[] args) {
System.out.println(FinalTest2.x);
}
}
结果是:
FinalTest static block
32
package JVM.classloader;
class Parent{
static int a = 3;
static {
System.out.println("Parent static block");
}
}
class Child extends Parent{
static int b = 4;
static {
System.out.println("Child static block");
}
}
public class Test4 {
static {
System.out.println("Test4 static block");
}
public static void main(String[] args) {
System.out.println(Child.b);
}
}
结果是:
Test4 static block
Parent static block
Child static block
4
package JVM.classloader;
class Parent2{
static int a = 3;
static {
System.out.println("Parent2 static block");
}
}
class Child2 extends Parent2{
static int b = 4;
static {
System.out.println("Child2 static block");
}
}
public class Test5 {
static {
System.out.println("Test5 static block");
}
public static void main(String[] args) {
Parent2 parent;
System.out.println("-----------");
parent = new Parent2();
System.out.println(Parent2.a);
System.out.println(Child2.b);
}
}
结果是:
Test5 static block
Parent2 static block
3
Child2 static block
4
package JVM.classloader;
class Parent3 {
static int a = 3;
static{
System.out.println("Parent3 static block");
}
static void doSomething(){
System.out.println("do something");
}
}
class Child3 extends Parent3{
static{
System.out.println("Child3 static block");
}
}
public class Test6 {
public static void main(String[] args) {
System.out.println(Child3.a);
Child3.doSomething();
}
}
结果是:
Parent3 static block
3
do something
package JVM.classloader;
class C1{
static{
System.out.println("Class C1");
}
}
public class Test7 {
public static void main(String[] args) throws Exception{
//获得系统类加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass("JVM.classloader.C1");
System.out.println("-----------");
clazz = Class.forName("JVM.classloader.C1");
}
}
结果是:
Class C1
类加载器用来把类加载到Java虚拟机中。从JDK1.2版本开始,类的加载过程采用父亲委托机制,这种机制能更好地保证Java平台的安全。在此委托机制中,除了Java虚拟机自带的根类加载器以外,其余的类加载器都有且只有一个父加载器。当Java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则由父加载器完成加载任务,否则才由加载器loader1本身加载Sample类。
类加载器的父亲委托机制(Parent Delegation)
Java虚拟机自带了以下几种加载器。
父子加载器并非继承关系,也就是说子加载器不一定是继承了父加载器。
除了以上虚拟机自带的加载器以外,用户还可以定制自己的类加载器(User-defined Class Loader)。Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器应该继承ClassLoader类。
Class sampleClass = loader2.loadClass("Sample");
loader2首先从自己的命名空间中查找Sample类是否已经被加载,如果已经加载,就直接返回代表Sample类的Class对象的引用。
如果Sample类还没有被加载,loader2首先请求loader1代为加载,loader1再请求系统类加载器代为加载,系统类加载器再请求扩展类加载器代为加载,扩展类加载器再请求根类加载器代为加载。若根类加载器和扩展类加载器都不能加载,则系统类加载器尝试加载,若能加载成功,则将Sample类所对应的Class对象的引用返回给loader1,loader1再将引用返回给loader2,从而成功将Sample类加载进虚拟机。若系统类加载器不能加载Sample类,则loader1尝试加载Sample类,若loader1也不能成功加载,则loader2尝试加载。若所有的父加载器及loader2本身都不能加载,则抛出ClassNotFoundException异常。
若有一个类加载器能成功加载Sample类,那么这个类加载器被称为定义类加载器,所有能成功返回Class对象的引用的类加载器(包括定义加载器)都被称为初始类加载器。
ClassLoader loader1 = new MyClassLoader();
//参数loader1将作为loader2的父加载器
ClassLoader loader2 = new MyClassLoader(loader1);
protected ClassLoader()
Creates a new class loader using the ClassLoader returned by the method getSystemClassLoader() as the parent class loader.
要创建用户自己的类加载器,只需扩展java.lang.ClassLoader类,然后覆盖他的findClass(String name)方法即可,该方法根据参数指定的类的名字,返回对应的Class对象的引用。
protected Class> findClass(String name) throws ClassNotFoundException
Finds the class with the specified binary name. This method should be overridden by class loader implementations that follow the delegation model for loading classes, and will be invoked by the loadClass method after checking the parent class loader for the requested class. The default implementation throws a ClassNotFoundException.
当执行loader2.loadClass(“Sample”)时,先由它上层的所有父加载器尝试加载Sample类。loader1从D:\myapp\serverlib目录下成功地加载了Sample类,因此loader1是Sample类的定义类加载器,loader1和loader2是Sample类的初始类加载器。
当执行loader3.loadClass(“Sample”)时,先由它上层的所有父加载器尝试加载Sample类。loader3的父加载器为根类加载器,它无法加载Sample类,接着loader3从D:\myapp\otherlib目录下成功地加载了Sample类,因此loader3是Sample类的定义类加载器及初始类加载器。
package JVM.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader{
private String name;//类加载器的名字
private String path = "d:/";//加载类的路径
private final String fileType = ".class";//class文件的扩展名
public MyClassLoader(String name) {
super();//让系统类加载器成为该类加载器的父加载器
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public MyClassLoader(ClassLoader parent, String name) {
super(parent);//显式指定该类加载器的父加载器
this.name = name;
}
@Override
public String toString() {
return this.name;
}
//这里不显式调用这个方法,而是通过loadClass调用
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = this.loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}
private byte[] loadClassData(String name) {
InputStream inputStream = null;
byte[] data = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
name = name.replace(".", "\\");
inputStream = new FileInputStream(new File(path + name + fileType));
byteArrayOutputStream = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = inputStream.read())) {
byteArrayOutputStream.write(ch);
}
data = byteArrayOutputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
finally {
try {
inputStream.close();
byteArrayOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
}
public static void main(String[] args) throws Exception{
//loader1的父加载器是系统加载器
MyClassLoader loader1 = new MyClassLoader("loader1");
loader1.setPath("E:\\IdeaProjects\\Java\\out\\production\\Java\\");//D:/myapp/serverlib
//loader2的父加载器是loader1
MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
loader2.setPath("E:\\IdeaProjects\\Java\\out\\production\\Java\\");//D:/myapp/clientlib
//loader3的父加载器是根加载器
MyClassLoader loader3 = new MyClassLoader(null, "loader3");
loader3.setPath("E:\\IdeaProjects\\Java\\out\\production\\Java\\");//D://myapp/otherlib
/**
*因为loader2的父加载器是loader1,loader1的父加载器是系统加载器
* 而Sample.class和Dog.class和MyClassLoader.class是在一个目录的
* 所以系统加载器就能加载,当系统加载器加载不了时,就交给loader1加载
* 如果loader1加载不了就交给loader2加载,如果loader2加载不了就会抛出异常
*/
test(loader2);
test(loader3);
}
public static void test(ClassLoader loader) throws Exception{
Class clazz = loader.loadClass("JVM.classloader.Sample");
Object object = clazz.newInstance();
}
}
package JVM.classloader;
public class Sample {
public int v1 = 1;
public Sample(){
System.out.println("Sample is loaded by:" + this.getClass().getClassLoader());
new Dog();
}
}
package JVM.classloader;
public class Dog {
public Dog(){
System.out.println("Dog is loaded by:" + this.getClass().getClassLoader());
}
}
结果是:
Sample is loaded by:sun.misc.Launcher A p p C l a s s L o a d e r @ 18 b 4 a a c 2 D o g i s l o a d e d b y : s u n . m i s c . L a u n c h e r AppClassLoader@18b4aac2 Dog is loaded by:sun.misc.Launcher AppClassLoader@18b4aac2Dogisloadedby:sun.misc.LauncherAppClassLoader@18b4aac2
Sample is loaded by:loader3
Dog is loaded by:loader3
package JVM.classloader;
public class Test8 {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
while (null != classLoader) {
classLoader = classLoader.getParent();
System.out.println(classLoader);
}
}
}
结果是:
sun.misc.Launcher A p p C l a s s L o a d e r @ 18 b 4 a a c 2 系 统 类 加 载 器 s u n . m i s c . L a u n c h e r AppClassLoader@18b4aac2 系统类加载器 sun.misc.Launcher AppClassLoader@18b4aac2系统类加载器sun.misc.LauncherExtClassLoader@330bedb4 扩展类加载器
null 根类加载器
同一个命名空间内的类是相互可见的。
子加载器的命名空间包含所有父加载器的命名空间。因此由子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。
由父加载器加载的类不能看见子加载器加载的类。
如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。
用反射可以访问
Class clazz = loader3.loadClass("JVM.classloader.Sample");
System.out.println(clazz.hashCode());
Object object = clazz.newInstance();
loader3 = null;
clazz = null;
object = null;
loader3 = new MyClassLoader("loader1");
loader3.setPath("E:\\IdeaProjects\\Java\\out\\production\\Java\\");
clazz = loader3.loadClass("JVM.classloader.Sample");
System.out.println(clazz.hashCode());
结果是:
1265094477
Sample is loaded by:loader3
Dog is loaded by:loader3
2125039532
运行以上程序时,Sample类由loader3加载。在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。另一方面,一个Class对象总是会引用它的类加载器,调用Class对象的getClassLoader()方法,就能获得它的类加载器。由此可见,代表Sample类的Class实例与loader3之间为双向关联关系。
一个类的实例总是引用代表这个类的Class对象。在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用,此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。