b站视频地址:
https://www.bilibili.com/video/BV1PJ411n7xZ/?p=2
评论区大佬笔记:
https://www.yuque.com/mo_ming/gl7b70/rfot9k
https://www.cnblogs.com/yanl55555/category/1686360.html
老师在这里提出了几个问题:
如下图,其中方法区和堆是多线程共享的,Java栈、本地方法栈、程序计数器是每个线程独有一份的。
执行引擎相当于把字节码文件翻译成机器语言的引擎,使程序可以在操作系统上运行
在准备阶段:
变量是这个阶段分配值得,但是被final 修饰的static 算是常量了,在编译期就已经分配值了
ps。下图,解释,类变量就是有static修饰的成员变量,所以如果Java程序中没有类变量的显式赋值动作和静态代码块,也没有调用该类的实例的情况,clinit方法是不会出现的
老师的补充:
clinit 方法
相当于类的构造器函数(与类中的静态变量赋值和静态代码块有关)
init 方法
相当于构造器函数。任何一个类在声明以后,内部至少会存在一个类的构造器,(这个构造器可能是你显示声明的,也可能是我们系统默认提供的),它总是会存在的。
如下图,可以这样把静态变量的声明写在静态代码块的后面,这是因为在“Linking”
阶段的“prepare”
阶段,默认初始化变量为零值,然后在“Initialization”
中顺序执行
方法中的静态东东,先是执行静态代码块中给number赋值为20,之后再静态变量赋值时number又变成20
举个栗子:两个线程加载同一个类
package com.atguigu.java;
/**
* @author shkstart
* @create 2020 上午 11:23
*/
public class DeadThreadTest {
public static void main(String[] args) {
Runnable r = () -> {
System.out.println(Thread.currentThread().getName() + "开始");
DeadThread dead = new DeadThread();
System.out.println(Thread.currentThread().getName() + "结束");
};
Thread t1 = new Thread(r,"线程1");
Thread t2 = new Thread(r,"线程2");
t1.start();
t2.start();
}
}
class DeadThread{
static{
if(true){
System.out.println(Thread.currentThread().getName() + "初始化当前类");
while(true){
}
}
}
}
分析上面的代码:
前面讲解了,类加载的过程,这节讲述一下有哪几种类加载。
public class ClassLoaderTest {
public static void main(String[] args) {
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取其上层:扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d
//获取其上层:获取不到引导类加载器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
//对于用户自定义类来说:默认使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
}
}
public class ClassLoaderTest1 {
public static void main(String[] args) {
System.out.println("**********启动类加载器**************");
//获取BootstrapClassLoader能够加载的api的路径
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL element : urLs) {
System.out.println(element.toExternalForm());
}
//从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader);
System.out.println("***********扩展类加载器*************");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}
//从上面的路径中随意选择一个类,来看看他的类加载器是什么:扩展类加载器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d
}
}
/**
* 自定义用户类加载器
* @author shkstart
* @create 2019 下午 12:21
*/
public class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] result = getClassFromCustomPath(name);
if(result == null){
throw new FileNotFoundException();
}else{
return defineClass(name,result,0,result.length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
throw new ClassNotFoundException(name);
}
private byte[] getClassFromCustomPath(String name){
//从自定义路径中加载指定类:细节略
//如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作。
return null;
}
public static void main(String[] args) {
CustomClassLoader customClassLoader = new CustomClassLoader();
try {
Class<?> clazz = Class.forName("One",true,customClassLoader);
Object obj = clazz.newInstance();
System.out.println(obj.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
由上图可知,扩展类加载器、系统类加载器都是间接的继承自ClassLoader的
/**
* 几种不同的方式获取类加载器
*/
public class ClassLoaderTest2 {
public static void main(String[] args) {
try {
//1.
ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader);
//2.
ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
System.out.println(classLoader1);
//3.
ClassLoader classLoader2 = ClassLoader.getSystemClassLoader().getParent();
System.out.println(classLoader2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
解释:双亲委派机制
若自定义了一个类、它所在的包也是自定义的比如com.atguigu.code.Test 加载该类时使用的是AppClassLoader ,但是系统类加载器不会立即加载,而是向上级委托给 Extension ClassLoader,扩展类加载之前讲过是加载 java.etc.dirs 目录下的类,所以不会接受这个委托,于是扩展类加载器继续向上级委托给Bootstrap ClassLoader,引导类加载器是只会加载 java javax sun开头的目录,也不会接受这个委托。
上级都没有一个接受委托的,不帮忙加载这个自定义类,那么这个委托只好向下走最后还回到本身的AppClassLoader,使用系统类加载器加载该类
问题:
分析上面的代码和截图,为什么我们自定义shkStart类就会禁止访问java.lang包,而自定义String类就没有报错?
解释:
加载的String类并非我们自定义的String类,而是引导类加载器加载的核心库中的String类。
实质上我们还是不可以自己定义一个类放进java.lang包里面 企图用引导类加载器帮我们加载,这种操作是不安全的也是不允许的,这是出于对类加载器的一种保护机制-----------沙箱安全机制
宋红康老师讲解得太详细了,很多人在评论区要PPT,其实对我这样得菜鸟来说理解这些内存啊参数啊什么得枯燥得知识点真的是有难度得!!我都会在老师开始讲解之前按下暂停自己看一遍截图,自己体会一遍,然后再听康老师讲。
结果就是真的康老师讲的会让我更好得理解,比喻也非常得加深印象,让那么多密密麻麻得文字记起来没那么困难。所以一定要听老师去讲一遍,不要只看PPT截图啊!
PPT截图实在太多了,放在一个帖子里也太庞大了。我就花了好几天时间自己在本地做了笔记,导出PDF留着以后自己回顾。
B站评论区有很多人提供PPT截图,想要得去B站看吧。我到这里就不更新啦~
完结撒花~可用了我不少时间呀