java 类加载器与程序加载实例分析

java 类加载器 可以分为

根加载器

扩展加载器

应用加载器

用户自定义加载器(继承自 java.lang.ClassLoder)

查看一个类是被何种加载器 可以使用以下代码


public class ClassLoder {

public static void main(String[] args) {
System.out.println(ClassLoder.class.getClassLoader());
}
}


输出的结果为


sun.misc.Launcher$AppClassLoader@19821f

可以看出 这个类 是被应用加载器加载。


判断一个类是否会被加载,可以根据 6个条件。

首次主动使用的情形:
创建某个类的新实例时--new、反射、克隆或反序列化;
调用某个类的静态方法时;
使用某个类或接口的静态字段或对该字段赋值时(final字段除外);
调用Java的某些反射方法时
初始化某个类的子类时
在虚拟机启动时某个含有main()方法的那个启动类。
除了以上几种情形以外,所有其它使用JAVA类型的方式都是被动使用的,他们不会导致类的初始化。

看一个例子


public class LoderDemo {

public static void main(String[] args) {
System.out.println(GG.a);
}
}

class GG{
public static final int a = 10;
static{
System.out.println("GG...");
}
}



这短代码会输出多少呢



答案是 10
很明显 final 修饰的常量引用并不会引起类的初始化,我们再看下面一段代码。



public class LoderDemo {

public static void main(String[] args) {
System.out.println(GG.a);
}
}

class GG{
public static final int b = 12;
public static final int a = 1+b;
static{
System.out.println("GG...");
}
}



会输出什么呢

答案是 13
虽然a 是个表达式 但是,表达式里面的量都是常量,会被jvm 提前折叠
再看下面这段代码



public class LoderDemo {

public static void main(String[] args) {
System.out.println(GG.a);
}
}

class GG{
public static int b = 12;
public static final int a = 1+b;
static{
System.out.println("GG...");
}
}




这次的结果却是
GG...
13

虽然a 是个被final 修饰的,但是表达式含有 b一个不确定的量,所以jvm 会初始化整个类。而且静态代码块优先被执行。
我们再看 下面一短代码


 class TestF{
public static TestF test = new TestF();
public static int count1 ;
public static int count2 = 0;

private TestF(){
count1++;
count2++;
}
public static TestF getInstances(){
return test;
}
}
public class SingleTownTest {
public static void main(String[] args) {
TestF f = TestF.getInstances();
System.out.println("count1="+f.count1);
System.out.println("count2="+f.count2);
}
}



输出的结果是什么呢


count1=1
count2=0


根据类的加载和初始化机制,直接在启动类里面调用其他类的静态方法,会导致初始化整个类。

根据代码执行的顺序。
TestF test 将会被赋给一个null 值,count1 count2 在第一轮赋值中都被初始化为0
new TestF(); 会调用构造函数去操作 类的两个静态成员,这个时候
count = 1 ,count2 = 1,
再往下 执行
count1 没有被再次赋值,但是count2 被赋值 为 0.
所以输出会是 1,0

我们再看下面一段代码


class TestF{
public static int count1 ;
public static int count2 = 0;
public static TestF test = new TestF();

private TestF(){
count1++;
count2++;
}
public static TestF getInstances(){
return test;
}
}
public class SingleTownTest {
public static void main(String[] args) {
TestF f = TestF.getInstances();
System.out.println("count1="+f.count1);
System.out.println("count2="+f.count2);
}
}



你会发现仅仅是将
public static TestF test = new TestF();
这句代码 移动了,但输出的结果却不同了。按照上面的分析,再来看一下。

调用 静态方法 初始化了 TestF 这个类

根据执行顺序,将 count1 count2 test 分别赋值为 0,0,null。
接着 将用户手动赋值的 0 赋值给 count2,
new TestF() 调用构造函数 并且赋值给 test
构造函数开始给 静态成员赋值,
count1 count2 分别自增1,值为1
输出。
这就是整个过程。
好好分析类加载器,其中的强大会直接影响到你的程序效率和正确性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

annan211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值