求解答关于Java的程序加载顺序问题

这主要是关于Java的程序加载问题与类加载顺序问题
(个人理解在后面)
代码如下:


public class A 
{
    static B b = new B();
    A()
    {
        System.out.println("A构造器");
    }
    static
    {
        System.out.println("A静态初始化块");
    }
}
public class B 
{
    static A a = new A();
    B()
    {
        System.out.println("B构造器");
    }
    static 
    {
        System.out.println("B静态初始化块");
    }
}
public class Main 
{
    public static void main(String[] args) 
    {
        System.out.println("程序开始");
        B b = new B();
    }
}

运行结果如下:

程序开始
B构造器
A静态初始化块
A构造器
B静态初始化块
B构造器

我不太明白为什么运行结果会是这个,我的理解是程序在执行到创建B对象时,会先去加载B类,然后发现B类有一个静态的A对象,于是又去加载A的对象,然后又发现A又有一个B对象,但这时B类已经被加载过一次了(但是没加载完,可能jvm已经知道它已将被添加到加载的序列里了),所以只是生成一个B对象,就输出“B构造器”,接着就到A的静态初始化块了,然后A类就加载完了,接着就是创建A类,于是就执行其构造方法,最后就是以同样的流程执行B的相关操作了。而且类的加载是执行到某个类时(创建对象或者调用方法)才会去加载这个类。

这是我的个人理解,不知道是否正确,而且其更底层原理也并不清楚,希望有人能帮忙解释一下。

差不多是这样,实例化B由于没有类信息先类加载B 需要对象A 对象A需要B对象,由于B已经在类加载所以只调用B的init方法执行。

你的类B里有个A对象,类A里同样有个B对象
子类加载时要先加载父类
所以new B()的时候正常来说应该先加载类A
但是类A里又有个静态的B对象,所以又回头去加载B,所以是B先加载了
你要看明白执行顺序,要一点一点往上面添加代码,不要一股脑搞的跟翻花绳一样,鬼能看懂

  • 你可以看下这个问题的回答https://ask.csdn.net/questions/7762378
  • 除此之外, 这篇博客: 看完这篇缓存双写分析,你面试不再有问题呢~中的 今天天朗气清,吃饱撑之余,回想了想之前面试阿里的时候,面试官经常会问的缓存和数据库双写一致性问题怎么保证,同时一个好的实践对业务来说是有很大提升,我在网上看了很多博客写关于这块的内容,很多直接拷贝过来的不说,写的也是站在一个小白的立场上,基本看的云里雾里的,那我就今天讲一讲我熟悉的业务场景,以及面对这些业务场景,我们一般会用什么解决方案来处理,前提要记住一点,面试官问你一些比较刁钻的问题的时候,一定是针对业务逻辑去做的,在实际开发中,我们更偏向于舍我求全的方案,没有一个解决方案是完美的,一定是舍弃某些,牺牲某些,而采用较优的方案!!!废话不多说了,开始正文! 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 作为开发,如果不了解缓存,那么你一定知道数据库,为什么会有缓存的出现?其实很简单,数据库的读写是基于磁盘IO的,数据库的性能,极大程度由你服务器的磁盘性能,硬件性能决定,目前数据库在软件层面的优化,已经做到了很不错了,但是你硬件

    跟不上,最终的下场就是GG,而面对目前互联网的流量越来越大,对数据库的要求,还是只能满足软件层面,硬件的成本太高,所以大部分的企业是无法承担这样的费用,业务越来越复杂,对数据库的读写压力会越来越大,撑不住会经常打挂几台,集群有时

    也扛不住,怎么办呢?

    那么缓存就出来了,挡在数据库的前面,洪峰流量先走缓存,抗住一波后,再根据业务走数据库;市面上有很多缓存,比如Redis,Memcache等等,这些都是基于内存模型的,学过计算机的都知道,内存的读写速度要大于磁盘的读写速度。

    所以内存的读写一定要快的多,这样也就降低了数据库压力,降低系统的瓶颈。

    虽然缓存出现了,带来极大的好处,同时给业务上带来了额外的问题。

    比如有

    • 数据库和缓存数据不一致,业界称为双写不一致
    • 缓存数据一大批集体失效,导致流量一瞬间直接打到数据库上,给数据库服务打挂了,这个现象业界称为缓存雪崩
    • 缓存和数据库中,都不存在某个数据,但是用户恶意攻击频繁请求一些大字段,大返回结果,以及不存在的数据,导致数据库压力过大而挂掉,这个业界称为缓存穿透
    • 缓存中某条数据不存在,但是数据库中存在,这样的场合一般是某个数据缓存到期了,这个时候读缓存没读到,并发请求上来都去读数据库的这条请求,导致数据库压力很大,这个称为缓存击穿

    其实雪崩和击穿很类似,但是不一样的地方在于:

    缓存击穿指并发查同一条数据,而缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

    进入章节: