00:00
那刚才啊,咱们是从这个自解码的这个角度,咱们来看一下这个对象的一个创建和初始化的过程,对吧?那么接下来啊,咱们从这个执行的一个步骤上来讲一讲,说这个创建对象的一个步骤啊,这块呢,我一共罗列了是有六个步骤啊,这个大家注意,我这呢是有六个步骤,嗯,你如果说面试中问到的话呢,你说六个步骤也可以,那有的时候呢,大家看到一些书上或者帖子上呢,有的可能是写成了四步或者五步啊,这个都没有关系,都可以啊,那有可能是把这里边儿的其中的这个一步两步或者是三步呢,给大家做了一个合并啊,这个是具具体说是几步,这个没关系,关键呢,就是这个过程当中涉及到这几个事儿呢,是一定都得有的。对吧,哎,这个是这个这个都得有,就跟说我们把这个大象呢,装到冰箱里边分三步啊分几步,你说分三步是吧,把这个大象呢拿起来,这是第一步,第二呢,把冰箱门打开是吧,第二步,然后第三步呢,呃是把这个大象呢放进去。啊,这呢是分成三步,那你说呢,非得分成四步啊,说你这里边还有个把这个冰箱门关住是吧,那我们呢,把大象装进去呢,和关门呢,我可能就给合成一起了,OK,所以说至于说呢,是三步还是四步,还是你说是两步,哎这个呢都不重要,关键呢就是说里边这几个事儿呢,都得有。
01:15
好,那我们下边呢,就来看一看具体的是有哪几个事儿,那我在PPT当中啊,实际上呢,这里边也已经写清楚了这样几个事儿,那么咱呢,就不在这儿看了,咱就直接在这个脑图这块看,哎,我觉得这个效果呢,可能会更好一些,好,那我们创建对象的这个步骤了啊,哎,步骤的话呢,一共是有六步啊,这块是我明确出来的,第一步呢,就来判断说你这个对象,我们要创建的这个对象,它所对应这个类啊,是否被加载过了啊,是否加载链接和初始化,实际上呢,就是整个我们这个类呢,一个初始化的一个三个步骤,对吧。说的直白一点呢,就是你创建这个对象所属这类啊,在内存当中是不是有了,那么详细来讲呢,在我们这呢,写了这样的一些信息,当我们通过这个new指令呢,去new一个对象的时候呢,先在这个咱们圆空间当中,是咱们是JD8之后的这样一个叫法,看一下这个常量池当中,定位到这个呃,类的这个符号引用啊,我们说对应的是有一个字符串的一个是吧,一个标识,你看一下你这个当前这个类啊,你有没有了,如果说要是已经被加载了,那挺好,那我们就直接用,如果说呢,你要是没有被加载,那这时候呢,我们要在双亲委派机制的模式下,这个咱们前面也说过了,对吧,那你看一下你当前这个,呃,使用当前这个类加载器,以class so,加包名加类名,呃,那对我们这个自建码文件呢,进行一个查找。
02:35
就是我们具体的类啊,你得看一下需要使用什么样的类的加载器,这个咱们前面已经讲到过了,对吧?哎,不同的类呢,我们应该使用不同的类的加载器。那使用这个类的加载器对应的,我们再去找这个包名和这个类名,看看一下你对应的这个自解码文件呢,是不是有。那那如果有呢,我们就进行一个加载,那加载这个过程呢,又是一个加载链接和初始化这样一个过程啊,生成我们对应这个大的class这个类对象,那如果说你这个文件都没有的话呢,我们就直接报一个叫class not found exception,这个大家应该之前在开发中多少啊,可能都会出现过是吧?OK,这就是我们说的第一个过程,说的直白一点,第一个过程就是加载我们要创建这个对象的所属的类。
03:17
哎,所属的类这个呢,需要加载过来,OK,这呢是我们说的第一个步骤,那么第二个过程呢,就是为我们这个对象呢,叫分配内存。哎,为这个对象分配内存。啊,那么我们这个分配内存的话呢,我们说在进行这个类型加载之后呢,我们能够判断呢,现在这个内存当中已经是有当前这个类了,那接下来呢,我们就会新生的这个对象呢,去分配这个内存空间,那首先的话呢,我们就需要呢,去计算一下,诶,当前你这个对象呢,所占用的一个空间的大小。啊,就是你占用这个空间的大小,那么我们刚才通过这个词间码的时候呢,我也说过这个事儿,就是咱们这个对象的大小,空间大小啊,实际上呢,在这个环节就已经是能够确定下来了,就是当我们加载这个类之后,你要造这个对象,这个对象呢,到底是需要占在堆空间中,占用多大这个空间,这个时候呢,是已经非常明确了,那我们就在这个堆里边呢,给你画一块这个内存,诶画给你。
04:10
诶画给你就可以了啊,那么画这个内存的时候呢,我们说这个不同的这个类型的这个属性,它所占这个空间大小呢,是不一样子的,那举个例子啊,比如说呢,我这块呢,又写了一个程序呢,叫诶customer是吧,针对我们这样一个类来讲,我们要是造它的这个对象呢,它也会有这样的三个属性,那这个属性呢,是个int型,那它呢,就占四个字节,像这个string类型,Count,它都属于引用类型,它们呢也是占四个字节。啊,对于我们说的short be啊,布尔是吧?叉啊,这哪也都是四个字节啊,Float也是是吧?那么这个double和long的话呢,是八个字节,所以呢是非常明确的,哎,你属性的个数呢,咱们说字检码文件当中当时就已经标识了有几个了,然后呢,这个字节的大小呢,空间咱们也知道了,所以呢,一计算咱就很容易的能够知道说到底应该占用多大的空间,诶这个大家要明确一下是吧?那么知道占用多大空间以后呢,我们堆空间去分配的时候呢,这里涉及到个问题,就是我们这个堆空间,看看它是不是规整的。
05:08
啊,就像大家呢,你看你自己的房间一样啊,你这个房间里边如果摆东西呢,都是非常或者叫仓库吧,都是非常规整的,那我们再放东西呢,就非常容易,那你要说这个呃屋这个仓库,这个行李呢,放的就是乱七八糟的,哎,那你现在呢,得去找到一块空间,能够放下我们当前这个对象大小的这样一个完整空间,连续空间是吧,出来才可以,所以这呢,我们就分成这个堆空间内存是规整的还是不规整的,哎,不同的处理方式。那如果说呢,你这个内存空间啊,它这个规整的,我们这里使用的呢,叫做指针碰撞,这一听呢,感觉很高大上是吧,其实呢,也没什么大不了的啊,这个就是这样的意思,比如呢,这就是咱们这个堆空间,这个呀,是咱们已经占用的这个空间。嗯,然后我再换一个颜色,这个红色的是咱们现在这个所谓的叫空闲空间,那中间呢,咱这有个指针。啊,这就是我们这里边所谓的这个指针,那这呢,你已经占用了,这是你空闲的,然后这个空闲的空间呢,也能够存放的下,咱们当前你要用的这个对象,那我们这时候呢,就把这个对象呢,是不是就给它扭出来啊,比如说这个空间呢,占用的是这么多扭出来,那现在的这个指针你往这移呗。
06:14
啊,移到呢,我们现在的这个,呃,占用的这个空间和还空闲的空间中间这个位置就可以了,这呢就叫指针碰撞法啊,说的非常的高大上是吧?哎,这里边其实呢,这个原理呢,是非常简单的啊,就跟咱们平时放东西一样,就是这个,我们说这就是你整齐的往里边摆是吧,往里边放,哎,就是这个道理。行,那这呢提到了说我们后边要讲的这个垃圾收集器,说垃圾收集器呢,针对于这个叫串行的叫C和我们这个叫并行的叫他new啊这两个垃圾收集器,我们后边会讲,他们俩采用呢叫标记压缩算法,或者说呢,叫标记整理算法,那它标记压缩呢,它会解决这个碎片化的问题,那进而的话呢,使得它的内存呢,是比较规整的,他们俩呢,呃,在分配这个数据的时候呢,用的叫指针碰撞。
07:00
啊,因为人家俩呢,这个内存呢,比较规整啊,就是这个道理,行,那如果要是内存不规整的时候怎么办呀,那内存不规整的时候呢,咱们需要维护一个列表啊,这呢我们称为呢叫空闲列表,那使用这种空闲列表这个方式呢,我们来进行一个分配啊,那什么意思,那就是这样子的,不规整的时候,那我们这个内存呢,可能是交交错的,说白了就是我们诶会有大量的这个碎片啊的问题,那我们呢,就会维护一个列表。记录呢,我们说哪些内存块呢是可用的,哪些呢是不可用的,然后呢,我们要在分配的时候呢,咱们就从这些可用的这些零散的块当中找到一块足够大的空间,能够放你当前这个对象实例。然后再更新一下你这个列表啊,就是列表上这个用了多少还剩多少是吧,就这个意思啊,那这个呢,其实上面我们说对应的这个叫啊,叫标记压缩算法,那下边这个呢,其实我们说可以对应一个叫,呃,这个标记清除算法啊,像我们说这个CMS。
08:01
啊,CMS这个垃圾收集器呢啊,它属于一个并发的垃圾收集器啊,它呢就是用的叫标记清除算法啊,它的这个内存空间在GC以后呢,就不是规整的啊,就得使用这种叫空闲列表的一个方式。那这样的话,大家把这个知识点呢,就能给它,哎贯通了是吧,哎就知道什么意思了啊。行,这边有说明呢,就是说至于说采取哪种方式呢,来进行这个。这个这个分配是吧,还得取决于我们具体垃圾收集器啊,是不是有这个压缩整理的这个功能啊,其实呢,就决定于他们是否使用的叫标记整理或者叫标记压缩算法了。啊,就这意思行,上面我不是也都举例子了吗?是吧,像这是我们说的这个第二个环节,那么第三个环节,有的时候呢,在一些书或者帖子当中,把这个三呢就跟二呢就合并在一起了,我这呢是单独的给它列了个三就要处理的所谓的叫并发问题啊,咱们知道呢,在分配这个内存空间的时候,你想想咱们这个在堆空间是不是来逆这个对象是吧,那堆空间呢,咱们说绝大部分空间是不是都是共享的呀,那你对于这个共享的区域来讲,我们要想放这个都去究对象多个线程,就有可能会出现并发的安全问题。
09:08
啊,因为咱们说这个创建对象的操作啊,实际上是非常频繁的是吧,那出现这个安全问题怎么办啊,我们这里边采取了两种具体的策略,第一个呢,叫CS失败重置,还有这叫区域加锁啊,CS呢,这个是叫compare and swap啊就是做一个对比和交换啊这个细节呢,咱们就不在这展开来说了啊这个大家呢,可以去搜索相关的这个帖子啊,这个可以再专门看一看啊,我们这里边这个锁的这个问题啊,行,这是一个,然后另外一个呢,咱们在前面讲堆的时候也提到过,咱们可以在这个伊甸园区是不是给每一个线程呢,预留一块叫TLED,就是专门归具体某一个线程,每个线程分别一块,哎,就是让他们就不去这个共享了是吧?哎,就避免了这个并发的这个问题啊,都去抢这个共享资源了,那这呢,我们说可以使用这样的一个指令呢,参数来设置,咱们在这个GD8这个场景下呢,它已经默认呢,是使用了这个TL了啊这个大家了解一下就行,就知道呢,已经自动的会有这样一块区域,但是呢,这个区域呢不大是吧。
10:05
啊,这个区域不大啊,这个大家注意一下。行,这呢,就是我们说这个并发问题啊,大家要关注一下,这里边延伸出来一个叫CS啊,这个算法的问题,行,再往下的话呢,叫初始化,呃,这个初始化分配到的这个空间。这个初始化分配到空间,说的直白一点,就是对我们这个对象的属性啊,进行一个默认初始化啊,那比如说大家来看我们这个情况啊,我这个customer里边呢,是不是声明呢,有三个属性,那这三个属性呢,咱们知道他们占用多大的空间了,但是你此时分配到这个ID呢,是占四个字节里边还没数呢。对吧,都还没数呢,那这个怎么办啊,我们说给他们诶就要付默认初始化值。哎,就是我们这里边儿这个步骤啊,其实就诶在这看啊,这个呢,其实就是我们所称为的叫负默认初始化值。你看这个大家再回忆一下,我们这个以前讲这个Java语言的时候呢,其实这个提到过这样的这个问题是吧,就是给啊对象的这个属性赋值的操作啊,都有哪些呢?我们说有这样的几个操作啊,第一个呢,是有这个属性的一个叫默认初始化,对吧?那默认初始化之后呢,我们说还有这个叫代码块,诶还有一个叫显示初始化。
11:20
显示。诶,这个属性的一个叫显示初始化,那显示初始化呢,跟这个咱们所说的这个叫这是我写的一个二啊,跟我们这个三三的话呢,叫做代码块中的一个初始化。哎,他们两个的话呢,是算是叫并列的结构啊,就是它俩,至于说谁先执行谁后执行,那就看我们这个代码块跟这个显示初始化,到底是谁先写谁后写了是吧,这个问题啊,那除了之外呢,我们说还剩一个情况,这后写成一个四,就是在这个构造期当中是吧。
12:01
哎,做一个初始化。OK,那这呢,就是我们给这个属性呢,赋值的这样几个步骤,当然呢,你构造其中初始化以后呢,我们还可以呢,呃,对象已经创建好,你还可以通过这个对象呢去调属性,调方法,再去修改这个属性值是吧?那个呢就是后话了,你可以执行多次,咱们不再现在要讲的这个对象这个实体化的过程,这个环节里边,那这呢就是我们讲了这四个事儿,那这四个事儿里边,现在咱们说的这个,呃,初始化分配到这个空间,它指的呢,就是我们谈的第一个事儿,就是给我们这些属性啊来一个叫默认初始化,那么一旦默认初始化以后呢,咱们就能够保证呢,这个对象这个实力这个字段呢,在不复制的情况下呢,就可以直接使用了。哎,就好比是咱们像这个内和这个account,咱们如果说在这个类的外部,我们要拗个对象的话呢,这两个变量呢,这直接呢可以打印出来值就是no,那这个ID的话呢,咱们如果不在这块去赋值它呢,打印出来值呢就是零。这呢就是因为我们有过一个叫默认初始化对吧?诶这个呢就很清楚了,好,这呢是我们说的这叫诶第四个环节,那下一个呢,叫设置对象的一个诶对象头,哎这个对象头是什么意思啊,就是咱们真正你扭出来的一个对象在对空间当中的是吧?那我们这呢是有这个叫诶对象头的,哎这个咱们下一节讲这个内存布局的时候呢,关键来说这个对象头的这个事儿啊,就是在这个对象头里边呢,我们会记录一下你当前这个对象所属的类,那那相应的它是不是就指向你这个方法去了。
13:31
哎,你所属的类啊是谁,然后呢,你这个对象的这个哈希值是多少,对象的这个JC信息所信息等等,哎,都存储在我们这叫对象头当中,这个过程呢,有具体的这个虚拟啊来去实现设置方式呢啊由虚拟机来实现,行这个呢大家先了解一下,咱们下一节呢,具体来说这个事。嗯,OK,那第四个呢,提到了属性的一个叫默认初始化,那下边呢,第六个环节就涉及到它的一个叫显示初始化,包括呢,这个代码块和构造其中的初始化,这常提到我们叫in这个方法,那以前的话呢,咱们讲这个虚拟站的时候呢,也提到过,那这个in的方法呢,实际上就是对应的咱们说的是类的构造器的一个调用啊嗯,咱们到这块呢,才涉及到这个类的构造器的一个调用。
14:16
就是咱们Java当中类的那个代码当中的一个构造器啊,那这里边这个细节呢,咱们就呃这个大家也可以看看哈,主要呢,其实想说明的问题呢,就是诶咱们现在的这个二三和四这三个环节,就是在咱们这个第六步这块来实现的。那对应的叫伊的这个方法。啊,咱们那会儿呢,演示这个object的时候呢,不是也说过这样的问题是吧,咱们把这块呢,可以再进行一个查看。是吧,再进行查看,这里边儿呢,是咱们那个位方法,这个code的在这个位置,咱们是不是就调用了这个叫所谓的引力的方法,这就是咱们所谓这个叫构造器方法。没问题是吧,就是构造器的,呃,这个构造器的这个对应的这个方法啊,不能叫构造类构造器方法,这个里边有好几个叫法,像我们还有这个叫CL啊,这呢是涉及到我们这个类当中一些静态的一些属性的一些初始化,这叫类构造器方法是吧?我们这个呢,只能说叫类的构造器对应的这个方法啊,叫做in,是从我们这个字解码的这个角度来看的。
15:17
啊,就是这样一个环节啊,那如果我们拿这个customer来说的话呢,我们把它呢,也进行一个编译,呃,然后呢,我们也查看一下它的这个结构的话。哎,然后我们刷新一下是吧,这个结构里边,比如说我们看这个谁呀。嗯,咱们就直接找这里边这个所谓的in就行是吧,这个in呢,其实就是相当于我们当前这个类当中构成它这个构造器相关的这个,诶就是我们这个类呢,肯定有构造器啊,这个构造器相关的这个操作呢,就在我们这个init里边,关键呢,我们看一下这个code,这个code里边大家看当前呢,我声明的这个类中呢,有三个属性,这三个属性呢,你看我这里边写的还是非常讲究的啊,我呢就展示的不同的场景,这个呢叫显示附了个值,这呢是代码画中赋值,这呢是构造七种赋制,对吧?那么这三个操作对应的就都是我刚才上边讲的是不是这里边的这个234啊。
16:12
嗯,在这儿呢,是这个234,那这个234呢,你看就都出现在我们的这个,呃,我们的这个叫伊这里边了。啊,这里边儿比如说首先提到我们这个ID是一个1001,那这里边是不是关于我们的1001,给咱们这个ID进行一个复制。没问题是吧,你看这不你看给这个ID过来了,给大家做一个复制行,然后呢,我们这个name的话呢,我通过代码框呢,进行了一个复制,这就涉及到这个接口。哎,Put field是吧,把我们的name呢做了一个复制,然后呢,针对我们这个A这个account呢,这块呢,也进行了一个创建啊,这是account构造器的一个调用啊,这不也是做了一个复制吗。也就是说我们提到了这三个操作呢,都是在咱们的最后一步当中啊来实现的。行,那这呢,就构成了咱们整个的这六步。整个这六步大家回忆一下啊,看能不能你把这六步呢,给大家描述出来,咱们这里边儿写的最简洁一些,最简洁一些啊第一步呢,是不是咱们要求呢,要加载这个类源信息啊,就是你对应这个对象所属的类啊,你得给它加载过来对吧?好然后呢,这个第二步是什么呢。
17:19
回忆一下第二步呢,是不是叫为对象啊,这个分配内存是吧。就是这个时候呢,我们需要呢,在这个对空间当中,就把你这个对象的这个空间啊,给人家开辟出来了,这个时候我们也能够确定这个空间的大小了。OK,这是一步,那这呢我又分出来一个第三步,其实你把这个三跟二呢合一起也行,这个三呢就涉及到我们要处理这个叫并发问题是吧。哎,这个呢,就是我们在涉及到分配这个堆空间中这个对象实体空间的时候呢,你到底呃往哪放啊,真的就涉及到叫指针碰撞啊,还是空间列表啊,对吧,那这样的这个情况,诶不是在这说的叫直针碰撞和空间列表,在这呢,主要是我们如何去解决这个并发问题啊CS是吧,还有这个枷锁,还有这个TLED啊这个问题。
18:07
行,然后这呢,是我们说的这个第三个,然后这个第四个呢,想想啊,是不是就我们这里边提到关于我们这些呃,属性的一些叫默认初始化,或者也称为呢,叫零值初始化是吧。那这样问题啊,这呢是我们说这个第四步啊,那在接下来我们这个第五步啊,第五步的话呢,想想。哎,我们这呢,就是在这个第四步的时候呢,只主要呢,是关于针对我们这些属性啊,进行了一个初始化,没有涉及到我们这个对象的这个对象头的信息,所以这个五的话呢,我们就要设置啊对象头的信息。啊,这个对象头的信息呢,就相当于是我们要指向你对公在这个方法区里边,你这个呃内源信息了啊,使得他们呢关联在一起啊这样子咱们下一节呢,讲这个内存布局的时候呢,说这个事啊,哎对象的这个内存布局,然后第六个问题呢,啊,那就是我们最后呢,要进行这个属性的一些啊是不是叫显示,哎初始化。
19:16
啊还有呢,这个哎代码块中哎初始化还有呢,哎叫构造其中哎初始化是吧?啊那整个这六步呢,执行完以后,咱们这个对象呢,就算是实例化完成了。哎,这就是整个的这样的一个环节啊,就是整个这个环节都执行了,咱们才认为这个对象呢,是完完整整已经出生了,那应该是这样的去说啊,以前呢,咱们这个讲课的时候呢,经常说我们造个对象,用谁造啊,用构造器造啊,拗一个对象,那这时候呢,我们讲完这个自解码指令是吧,这呢,咱们也看这个自己码指令了。啊,就是刚才我们看这个object的时候,这叫new是吧,这个呢叫扩造器,相当于呢,把代码当中的这一行操作呢,你看是不是肢解成了至少好几行,至少能说把它俩就分开了是吧?那么如果说有同学呢,学完这些内容之后呢,去问别人说,你说我们到底这个造对象是这个new的环节,是就把对象造好了呢,还是说呢,我们这个调构造器这块呢,才算是把对象造好了呢?
20:12
或者说呢,这个又是造对象呢,还是我们这块呢,才算是叫造对象呢。大家应该怎么去回答这个问题啊?怎么回答问题?哎,能理解我刚才问的这个事吧。嗯嗯,要回答这个问题的话呢,我觉得,呃,生活中有个场景就比较像啊,比如说咱们这个有好多这个叫什么传承人是吧,咱就比如说啊,我想了个例子,就捏这个面人这个事儿吧,我这画的不太像啊,比如说呢,就是捏一个孙悟空。两个腿是吧,还有一个这个小花边的豹纹裙子啊,这个一个金箍棒啊,行,那这呢,比如说就是孙悟空了哈,呃,那这个孙悟空的话呢,我们是想把这个齐天大圣给它捏出来,呃,捏出来你说怎么?呃,这个这个捏完以后是吧,完全的是OK了,这个呢,就相当于这个对象已经创建成功了,那现在呢,我们如果说这个捏的环节啊,就有点像你把这个用面面来捏吧,捏完以后呢,这个形都出来了,但是呢,没有上色,还都是这个白面是吧,白色的,然后呢,你要是再调一下这个构造器呢,涉及到你把这里边,比如这个人家的豹纹裙子呀,给人家画出来,这个小上衣呢,整成个黄色的是吧,这个金箍棒呢,这个也有两头是红色的啊,火眼金睛是吧,你把这个颜色呢都补上,补颜色这个事儿就有点像呢,咱们掉这个构造器呢,进行这些属性的是不是叫显示初始化呀。
21:29
哎,那都构造器也掉完了,相当于这个着色也着完以后,这个对象呢,才完整的构成了,所以你说呢,是捏完这个面人,这就算呃对象已经OK了,还是说呢,我们着色完以后才算是OK了呢,这就看你怎么看了。啊,你要说给一个相对来说大家通用的一个标准来说的话呢,那肯定还是我们这六步呢,都执行完以后,才算是对象创建完成。哎,就好比是你这个孙悟空呢,已经完全的都有颜色了,五颜六色了是吧,然后我们才算是这个已经作品完成。
22:03
哎,这个大家稍微注意一下这个事儿啊。
我来说两句