<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Java on lategege 的技术博客</title><link>https://lategege.com/categories/java/</link><description>Recent content in Java on lategege 的技术博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 04 Mar 2022 08:37:21 +0000</lastBuildDate><atom:link href="https://lategege.com/categories/java/index.xml" rel="self" type="application/rss+xml"/><item><title>java虚拟机(jvm)、android虚拟机(dalvik、art)相关总结</title><link>https://lategege.com/p/java%E8%99%9A%E6%8B%9F%E6%9C%BA-jvm-android%E8%99%9A%E6%8B%9F%E6%9C%BA-dalvik-art-%E7%9B%B8%E5%85%B3%E6%80%BB%E7%BB%93/</link><pubDate>Fri, 04 Mar 2022 08:37:21 +0000</pubDate><guid>https://lategege.com/p/java%E8%99%9A%E6%8B%9F%E6%9C%BA-jvm-android%E8%99%9A%E6%8B%9F%E6%9C%BA-dalvik-art-%E7%9B%B8%E5%85%B3%E6%80%BB%E7%BB%93/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;作为开发人员，时不时回顾知识点非常有必要，不然很容易遗忘，回顾最好的方式就是用自己的语言阐述对事物的理解，所以我以后会经常使用这种方式来记录相关知识。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、java虚拟机内存模型&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;完全用自己的理解描述，java虚拟机首先也是一个程序，恰当一点就是系统服务程序，它是java程序的载体，java进程其实是对操作系统应用程序进程的包装，虚拟机中的栈、堆、方法区、本地方法区、直接内存这些东西都是抽象的，是虚拟化的，java字节码它也不是机器指令，java虚拟机就是一个翻译机，将它翻译成机器码，而在底层本质上是不存在什么内存模型的，cpu执行程序都是从内存中拿到l3缓存-&amp;gt;l2缓存-&amp;gt;l1缓存，虚拟机这个程序和操作系统一样，将用户空间的内存划分成了多个部分。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;栈------它首先是一个先入后出的数据结构，它是线程独享的一个内存空间，这个空间中存放着两个东西，一个是程序计数器、一个是栈帧，程序计数器保存方法中字节码指令的索引，栈帧中存放局部变量表、操作数栈、动态链接、返回地址，一个方法一个栈帧，方法中定义的变量都存放在局部变量表，当要计算时从局部变量表中取出放入操作数栈，计算完成重新放入局部变量表。栈默认大小1m。当递归层级过多会造成栈内存溢出异常。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;堆------所有对象的实体存放在该区域，它受垃圾回收器管理，大体分为年轻代和老年代，多次gc后对象会进入老年代。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;方法区----存放类的信息、常量等。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;本地方法区----native方法所使用的内存区域。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;直接内存----计算机上的不经过虚拟机包装的内存区域。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;除了直接内存，其他都是虚拟机自身定义并管理的内存区域，也就是java进程执行的同时，虚拟机就向操作系统申请了一堆内存空间，然后划分为不同区域，让程序运行中各个部分的数据呆在合适的地方，就像生活中的二房东，房子并不是自己的，但它可以隔成很多房间，如果不隔房间就会非常乱，对于房东这个操作系统来说，它只要收租就好了，它不知道自己的房子内部结构也不需要知道，二房东只要按照我的规则把钱交给房东这个操作系统就可以了，对于租客来说，这个房子里就是一个小世界，租客只要拎包入住，各种大房东很多(各种操作系统)，但是二房东把不同类型的二房东都搞定了，租客到哪只要轻装上阵，这就是java虚拟机的跨平台性。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、java虚拟机配置参数详解&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;a href="https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html"&gt;https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html&lt;/a&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三、垃圾回收器&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;垃圾回收主要靠可达性分析算法，也就是gc root，静态变量，运行中的成员变量、栈和本地方法栈中引用的对象、运行中的线程等可以作为gc root，被gc root 持有的强引用是不会被回收的。代码中创建的引用默认是强引用，软引用在oom时才会触发，不太常用，弱引用在gc时会触发，虚引用随时可以被回收。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、对象分配策略&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;不是所有对象都分配在堆上，如果符合逃逸分析的对象就会分配在栈上，如果在方法中的对象不存在方法外的引用并且虚拟机打开了逃逸分析开关，对象就会分配在栈上，分配在栈上的对象不需要经过gc回收，方法出栈后自动消失，提升了效率。如果不符合逃逸分析，就会在堆年轻代中的eden区分配，大对象(图片数据，大数组，大字符串等)直接在老年代中分配。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;堆年轻代中分为eden、from、to 三个内存区域，内存空间比例8:1:1，年轻代中和老年代中的gc是不同的，年轻代中是young gc 老年代是old gc 或者full gc ，分代的目的是为了在不同区域采用不同的垃圾回收算法，年轻代采用复制回收，老年代采用标记清除和标记整理算法。复制回收算法本质是不能回收的全部复制，其他格式化，eden区大部分对象会被gc，存活的会放入from区，age+1,放入对象的对象头信息中，触发复制回收后，from存活的会复制进入to区，from区被清理，如果from,to都满，就会进入老年代，标记整理比标记清除多做了一步整理。 不同的垃圾回收器，使用的线程数(单线程、多线程)、算法是不同的。一般垃圾回收器gc的时候会停掉用户线程，cms垃圾回收器利用了用户线程来并发标记和并发清理。cms三大缺点，cpu利用率高、浮动垃圾，内存碎片。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;五、android虚拟机和虚拟机的差异&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;android中的虚拟机叫做dalvik虚拟机，是谷歌为移动设备定制的java虚拟机，它和传统的JVM相比，它是基于寄存器的虚拟机，当然这个寄存器并非真实的cpu寄存器，而是虚拟的寄存器，因为它符合cpu存取特征，所以它比JVM基于栈的指令的数目少很多，这样就提高了程序执行效率。在字节码文件方面，dalvik虚拟机压缩为一个独立的dex文件来优化文件读取，传统的jvm需要大量的class，每个class中常量也有可能存在重复，dalvik在字节码内容、文件数量方面都做了优化，提升了类加载效率、并且更节约内存。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;dalvik虚拟机的堆区包含zygote heap和active heap ，zygote heap是进程共享的堆区，而activie heap是进程独享的。除了两个堆区还包含Card Table 来记录第一次标记的垃圾信息，还有两个Heap Bitmap 分别记录上次和这次gc的存活对象。mark stack用来gc 标记清除时来遍历存活的对象。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;art虚拟机是android4.4以后发布的，art在程序安装时候有次aot 预编译，将编译的机器码存在本地，提高了运行效率，但是安装时候变长了、需要的存储空间变大了，android7.0开始art加入了即时编译器JIT,有选择的编译热点代码到机器码缓解了以上问题，dalvik是32位的，art是64位兼容32位的，art的垃圾回收器也改进了，更多的并行回收，gc暂定次数由2次减少为1次，采用了多种垃圾回收方案，art虚拟机堆区的划分也和dalvik虚拟机不一样了，它分四个堆空间，zygote space、allocation space、image space、large object space，前两个和dalvik作用一致，image space也是进程共享的。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>java多线程与并发总结</title><link>https://lategege.com/p/java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%B9%B6%E5%8F%91%E6%80%BB%E7%BB%93/</link><pubDate>Tue, 01 Mar 2022 08:46:53 +0000</pubDate><guid>https://lategege.com/p/java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%B9%B6%E5%8F%91%E6%80%BB%E7%BB%93/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;进程是操作系统调度的最小单元，线程是cpu执行的最小单元，cpu将时间分片，随机算法选择线程执行，虽然操作系统和java都有线程优先级，但是这个优先级是不可靠的。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、线程的使用方式(有人会把它称为线程有几种，我觉得会有歧义，严格来说线程在java api中有且只有一种。)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;java线程的三种执行方式，其实本质上就一种，那就是thread线程通过start方法执行runnable。thread本身继承自runnable，所以无论是外部传入的任务还是它自身，本质都是thread来执行一个任务，第三种FutureTask也是继承自runnable&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;new Thread(){ public void run(){ //执行任务}}.start &lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;new Thread(new Runnable(){ public void run(){执行任务}}.start&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;还有一种特殊的线程任务----FutureTask，这是一种有返回值的线程任务，需要构造callable回调接口。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、线程的状态&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;线程在执行过程中是有状态的，当执行start之后，默认它是在可执行状态，一但cpu执行了该线程，该线程就转入运行状态，运行结束或者被停止就变成死亡状态，死亡后线程无法重新开启，生命只有一次，在运行状态中如果调用sleep(2)或者wait(2)，线程就进入了等待超时状态，此时释放cpu执行权，sleep、wait时间一到，线程重新进入可执行状态，等待cpu执行，调用不带时间的wait,线程进入等待状态，需要notify或者notifyall来唤醒，唤醒后才能进入可执行状态，如果在运行状态调用yield，线程将释放cpu执行权限，进入可执行状态，如果在可执行状态下调用join，那么一但cpu执行了该线程，该线程就会优先于开启此线程的主线程执行，也就是主线程进入了等待状态，直到子线程执行完毕才执行主线程。线程的停止有暴力停止和非暴力停止，直接调用stop为暴力停止，可以通过interrupt向线程发送中断信号，线程中判断isInterrupt来决定是否需要结束线程，结束线程的正确方式就是让线程执行完成。当主线程开启了子线程，同时通过setDaemon(true)为守护线程的时候，主线程结束，子线程也会结束，进程就结束。否则子线程如果不结束，主线程也无法结束，只有在synchronized同步中才会进入阻塞状态，显示锁lock后底层只会进入等待状态而并非阻塞状态。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;注意: sleep、wait会清除线程中的中断信号&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三、多线程同步&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;多线程编程的时候，如果操作了同一个对象或者变量，很容易造成执行结果错误，破坏了数据的准确性。要保证数据的安全，就需要多线程同步，同步的本质就是加锁。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、锁的使用方式&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;java中锁有两种api，一种是synchronized，另一种是ReentrantLock，两种方式都是可重入锁，也就是递归调用的时候不会造成死锁。 ReentrantLock 提供了一个构造可以使其变成非重入锁，死锁大多数都是由与锁的嵌套引起的。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;synchronized可以作用在方法上，也能作用在代码块上，使用时需要传一个锁对象。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;ReentrantLock 则可以插入代码的任意一行，通过lock和unlock 这两个api使用。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;synchronized和ReentrantLock 都是非公平锁，都支持抢占式执行。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;ReentrantReadWriteLock 俗称读写锁，读写锁可以分别获读锁和写锁，来提高数据读取效率。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;五、synchronized的本质&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;synchronized的本质是在虚拟机曾引入了monitorenter monitorexit两句字节码指令，也就是拿到锁就是指持有了monitor对象，释放锁就是释放了monitor对象，在锁定的对象的对象头信息中包含了四种状态锁，也就是这个对象锁的状态不是一成不变的，如果状态是轻量级锁，那么它是通过cpu的cas指令来进行自旋的，当没有线程与之竞争的情况下会切换为偏向锁，也就是不做cas直接拿锁，因为就自己一条线程工作，如果出现了竞争线程，重新变为自旋的轻量级锁，当自旋达到一次线程上下文切换时间，锁就转换为重量级锁，重量级锁即阻塞态，当锁被回收就是gc锁。四种状态是对synchronized的一种优化手段，为了不让使用synchronized就进入阻塞态，造成上线文切换带来的得不偿失的结果。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;六、CAS的本质&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;cas是现代cpu的指令，即比较和交换，它是原子操作，当多线程计算过程中，线程首先进入cas，比较当前值是否是预期值，如果不是就说明锁已经被占用了，那就再来一遍，保证线程不阻塞。但是cas会带来aba问题，线程中无法判断是否改过，一旦被改过又被还原了这个时候线程不知道，同时cas也会让cpu使用率变高。java中AtomicInteger等原子操作类的本质就是CAS。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;七、ReentrantLock的本质&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;lock锁背后是AQS，即抽象队列同步，它是jdk提供的一套实现锁的模版方法，而AQS本质上使用了CHL方式，它是将线程放在队列中，如果没有得到锁就不断检查队列的前面成员有没有释放锁(自旋)，如果释放就使得该线程获得锁，检测有没有锁这个是cas操作。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;八、volatile 关键字&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;volatile 在java内存模型中，它保证了线程的可见性，也就是保证数据拿到的永远是最新的，虚拟机底层是通过cpu的lock指令-----在读数据的时候，将cpu缓存数据失效，强制从内存读取，在写数据时候强制从cpu缓存中写入内存，但是不能保证操作的原子性。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;九、阻塞队列&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;阻塞队列是java中同步方式的一种实现，java api提供了多种数据结构的阻塞队列，阻塞队列应用场景是生产者消费者模式。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;十、线程池&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;线程池是阻塞队列的一种应用，它的初衷是避免了创建和销毁线程带来的不必要的开销，同时线程在计算机中是稀缺资源，cpu密集型建议线程最大个数不超过cpu核心数，因为线程的上下文切换远比cpu计算时间来的长。而io密集型一般建议线程最大个数为cpu核心数*2，因为io的时间远比上下文切换来的慢，为了保证线程阻塞时有一个候补线程来工作，混合型的根据情况而定。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>Java类加载机制的本质</title><link>https://lategege.com/p/java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6%E7%9A%84%E6%9C%AC%E8%B4%A8/</link><pubDate>Tue, 17 Aug 2021 06:33:51 +0000</pubDate><guid>https://lategege.com/p/java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6%E7%9A%84%E6%9C%AC%E8%B4%A8/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;在编程领域，很多名词都非常奇怪，这就非常容易造成软件工程师不少困惑与不解，就像java中的类加载机制，网上成篇大论都谈到一个名词-----双亲委派机制，下面就是类加载的最关键代码。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:preformatted --&gt;
&lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;protected Class&amp;lt;?&amp;gt; loadClass(String name, boolean resolve)
 throws ClassNotFoundException
{
 // First, check if the class has already been loaded
 Class&amp;lt;?&amp;gt; c = findLoadedClass(name);
 if (c == null) {
 try {
 if (parent != null) {
 c = parent.loadClass(name, false);
 } else {
 c = findBootstrapClassOrNull(name);
 }
 } catch (ClassNotFoundException e) {
 // ClassNotFoundException thrown if class not found
 // from the non-null parent class loader
 }
&lt;pre&gt;&lt;code&gt; if (c == null) {
 // If still not found, then invoke findClass in order
 // to find the class.
 c = findClass(name);
 }
 }
 return c;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:preformatted --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;看到上面的代码，我会想双亲是谁？委派又从何而来？中文虽然博大精深，但是要将双亲委派和这段代码联系起来，未免太牵强。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这段代码我用一个例子来说明：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;一、儿子在家找身份证，先看了看自己周围有没有，如果没有，就打电话找他爸问问。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;二、儿子发现他周围没有，然后打电话问他爸爸，说爸爸，看见我的身份证了吗？&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;三、爸爸在他自己周边找了一圈，没有发现，爸爸就打电话给了爷爷。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;四、爷爷在他边上翻找了一圈，也没有发现，爷爷觉得这可不行，我得仔细找找，爷爷去自己的房间找了一圈没有发现，他就只能和孙子的爸爸说没找到。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;五、无奈的爸爸只能去他自己的房间找了一圈，最终也没找到，他就训斥孩子，身份证是你自己的，又不是别人的，你自己想办法。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;六、儿子无奈地回了房间，发现身份证就在他自己的房间。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;strong&gt;这个故事虽然有点假，但是已经将java类加载机制通过叙事的手段说清楚了。&lt;/strong&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;儿子就是AppClassLoader. 爸爸就是ExtClassLoader 爷爷就是BootStrapClassLoader&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;身份证就是要加载的类，儿子的周边就是他自己的缓存空间，儿子的房间是属于他管理类加载路径&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;爸爸的周边就是爸爸的缓存空间，爸爸的房间是属于爸爸所管理的类加载路径&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;爷爷的周边就是爷爷的缓存空间，爷爷的房间是属于爷爷所管理的类加载路径&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这个故事核心就是找东西，至于怎么找，先找离自己最近范围的，没有就打电话给亲人，让他们帮忙找找，最后这么一圈下来，如果最终都没找到，就报一个类未找到异常，故事中找的是儿子的身份证，那多半是在儿子自己的生活圈中，但是儿子记不清了，就像程序一样，谁都不知道哪些类是谁加载的一样。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;如果是爷爷的身份证，那就好比java中的String类一样，属于爷爷的生活范围，也只有爷爷能找到，爸爸和儿子不可能找得到，假如爷爷的东西都是非常贵重的，就像java中的核心包一样。如果有人要弄个赝品给到儿子手里，依照这个流程，只要爷爷手中的是真的，那这个赝品不会被找到，因为爷爷优先查找。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;所以java类加载机制---双亲委托机制这个说法站不住脚，但是我也没能找到一个很好的词汇来描述这套机制，因为它和我们的生活不太一样，所以我只能以上面的一个故事来讲明白这套机制。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item></channel></rss>