先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
正文
符号引用:反编译 class 文件后可以查看符号引用
初始化阶段:
- 初始化阶段就是执行类构造器方法
<clinit>()
的过程 - 此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来(当代码中包含static变量时,
<clinit>()
自动生成,如果没有静态代码快则不会生成)。 <clinit>()
方法中的指令按语句在源文件中出现的顺序执行<clinit>()
不同于类的构造器。(关联:构造器是虚拟机视角下的<init>()
)- 若该类具有父类,JVM会保证子类的
<clinit>
()执行前,父类的<clinit>()
已经执行完毕 - 虚拟机必须保证一个类的
<clinit>()
方法在多线程下被同步加锁
IDEA 中安装 JClassLib 插件(重启 IDEA 后生效):
(1)File -> Settings
(2)安装插件:Plugins -> Install jclasslib
(3)选中对应的 .class文件:
(4)点击View -> Show Bytecode With jclasslib,查看反编译后的代码
(5)查看反编译后的代码:
构造器方法中指令按语句在源文件中出现的顺序执行:
public class ClassInitTest {
private static int num = 1;
private static int number = 10; //linking之prepare: number = 0 --> initial: 10 --> 20
static {
num = 2;
number = 20;
System.out.println(num);
//System.out.println(number); //报错:非法的前向引用(可以赋值,但不能调用)
}
//private static int number = 10; //linking之prepare: number = 0 --> initial: 10 --> 20
public static void main(String[] args) {
System.out.println(ClassInitTest.num);//2
System.out.println(ClassInitTest.number);//10
}
}
运行结果:分别测试静态变量在静态代码块之前、之后执行
如果静态变量在静态代码块之后执行,在静态代码块中引用该变量时会报非法向前引用错误:
构造器是虚拟机视角下的<init>()
public class ClinitTest {
//任何一个类声明以后,内部至少存在一个类的构造器
private int a = 1;
private static int c = 3;
public static void main(String[] args) {
int b = 2;
}
public ClinitTest(){
a = 10;
int d = 20;
}
}
运行结果:
若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕:
public class ClinitTest1 {
static class Father{
public static int A = 1;
static{
A = 2;
}
}
static class Son extends Father{
public static int B = A;
}
public static void main(String[] args) {
//加载Father类,其次加载Son类。
System.out.println(Son.B);//2
}
}
运行结果是2,因为Son类是Father类的子类,在执行Son类的加载前会先执行Father类的加载。
虚拟机必须保证一个类的<clinit>()
方法在多线程下被同步加锁:
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) {
}
}
}
}
结果分析:
- 两个线程同时去初始化 DeadThread 类,而 DeadThread 类中静态代码块中有一处死循环,切该类只能被初始化一次
- 所以先初始化 DeadThread 类的线程抢到了同步锁,然后在类的静态代码块中执行死循环,而另一个线程在等待同步锁的释放
- 所以运行结果是:某一个线程先执行 DeadThread 类的初始化,而另外一个类不会继续执行
类加载器的分类
JVM支持两种类型的类加载器 ,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)。
- 从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器,所以ExtClassLoader 和 AppClassLoader 都属于自定义加载器。
四者之间是包含关系,不是上层和下层,也不是子父类的继承关系:
测试类加载器:
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
}
}
- 在获取引导类加载器时,获取到的值为 null ,因为引导类加载器由 C/C++ 语言编写,我们无法获取
- 两次获取系统类加载器的值都相同 ,说明系统类加载器全局唯一
虚拟机自带的加载器:
启动类加载器(引导类加载器,Bootstrap ClassLoader)
- 这个类加载使用C/C++语言实现的,嵌套在JVM内部
- 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类
- 并不继承自java.lang.ClassLoader,没有父加载器
- 加载扩展类和应用程序类加载器,并作为他们的父类加载器(当他俩的爹)
- 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
扩展类加载器(Extension ClassLoader)
- Java语言编写,由sun.misc.Launcher$ExtClassLoader实现
- 派生于ClassLoader类
- 父类加载器为启动类加载器
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载
应用程序类加载器(系统类加载器,AppClassLoader)
- Java语言编写,由sun.misc.LaunchersAppClassLoader实现
- 派生于ClassLoader类
- 父类加载器为扩展类加载器
- 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
- 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
- 通过classLoader.getSystemclassLoader()方法可以获取到该类加载器
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Java语言编写,由sun.misc.LaunchersAppClassLoader实现
- 派生于ClassLoader类
- 父类加载器为扩展类加载器
- 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
- 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
- 通过classLoader.getSystemclassLoader()方法可以获取到该类加载器
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-acqPhqKa-1713592796425)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!