00:00
哈喽,大家好,我是上硅谷家外艺学科的讲师柴林燕,今天呢想给家分享一道面试题,什么样的面试题呢?我们一起来看一下。这是一道代码题,因此呢,我们可以暂停一下视频啊,然后呢,自己分析一下这个运行结果是什么。好,下面我们一起来验证一下运行结果,对不对呢?我在里面呢,把代码已经写好了,我们来运行一下。好,答案呢是215115。如果你的答案是不对的,那下面要仔细听了,看一看哪块地方没有考虑到,如果是对的,不妨听一听咱俩的分析步骤是不是一样的呢?我们下面来看一下这道题的考点有哪些。这道题考点主要是四个方面啊,第一一个关于变量的就近原则,第二变量的分类,第三个呢是关于非进量代码块的执行,还有关于方法的调用。那因此我们来一一分析一下。
01:00
在这个当中啊,我们把这个eclipse打开啊。这个当中第八行啊,有一个int的I等于一。在第五行又一个int I,那么在第九行的I加加是指哪个I呢?这就涉及到就近原子,那么第九行的这个I加加其实是第八行生命的这个I。不是第五行的这个I,同样的道理。第13行有一个int j,那么第六行有个int j,那么我们第14行的int j是哪一个呢?那么它也是究竟原则是第13行的J。这叫就近原子,那么问题又来了,这里面有个in特I啊,这有个in特I,那么对于第十五行的in特I,它是找谁呢?那么究竟原则是不是找这个呢?这里呢?不是,为什么呢?这里究竟原则除了要离他最近的声明的,还要关于他的中誉,第八行的这个当中的I,它的中域呢?到第12行这个大块就结束了,因此我们这个地方不是找它,而是继续往上找,找第五行的。
02:07
所以呢,就近原则啊,我们要注意首先离他最近的声明的,但同时也要考虑到中域的问题啊,最近的问题,那什么是中域,一会我们给大家来再讲解一下。这第一个究竟原则,那么我们是不是考虑到不同的这个变量啊,它到底是代表是哪个?第二个呢?变量有分类,那么变量有很多种,有成员变量,局部变量,成员变量当中又分为类变量跟实例变量,那也就是说我们这ecl里面这么多个变量分别代表的是什么?分别代表的是什么?哪个是成面变量,哪是局部变量,哪个是内变量,哪个是实例变量?它们有什么不同?这会给我们分析一下。好,第三个考点呢,就是关于非静态代码块的执行,那也就是说我们啊,这段代码什么时候会执行,从下面主方法里面好像没有看到去呃调用它,那么他也没法没有名字,怎么去调用,那么什么时候会执行它呢?
03:06
哎,我们呢,一会来分析一下,最后的话就是这个,呃,我们的方法调用方法调用,那么我们在这里大家看到这个代码当中出现了三次的test,那这三次test有什么不同啊,它对我们变量的这个值的修改有什么影响?因此我们要分析这么几个考点啊,下面呢,先来看一下这个我们的变量的分类啊。那么局部变量与成员变量的一个区别啊,什么是局部变量与什么是成员变量?那么我们是通过声明的位置不同来区别它们的局部变量是在方法体里面,行参里面代码块当中去声明的,那么我们来分一下这个代码当中哪些数据不变。方法体里面OK,方法体里面。那么因此这个是我们的局部变量。
04:01
OG1啊,局部变量OG2,然后呢,再就是我们的啊,Sorry,这行参行参是吧,哎,那我们的这个地方是。行参这个是行参对吧,行仓啊,也是局部变量,那么就是others。这个地方也是形态,也是局部变量,这。还有呢,就是第三个就是我们的代码块里面,代码块里面,那么它就是我们。这个这是大板块,这是非金的大板块啊,那么就是局部变量。变量I,好,这些是局部变量。第二个我们的成员变量,它的内中方法外,那因此呢,我们来啊看一下。
05:03
内中方法外,这是成员变量。这个是成员变量,这成员变量,那这样呢,我们所有的变量给大家呢,分开了。那么层面变量当中又分成两大类,一类是类变量,一类是实例变量。有静态的我们叫内变量,没有静态的叫实例变量。来,那我们来看一下啊,也就是说这个呢,是我们把它叫类变量,这个呢我们把它叫实例变量,这个也是实例变量,哎,这个就分开了,那么我们这个概念是从哪来的呢?哎,在我们的这个规范里面啊,官方文档里面有个规范啊,大家在官网上欧官网上是可以找到的,这个规范里面说了啊。一个静态的啊,这个数这个成为变量,对吧,那我们有的时候sometimes获的一个类变量。而我们一些这个,呃,这个成员变量当中非静态的声明的,那么我们呢,叫非静态的啊,这个成员变量,或者是说叫instance实例变量。
06:08
因此这个啊,两种叫法。那么它们有什么不同呢?首先我们来总结一下第一个不同,关于修饰符,虽然我们这道题当中没有涉及到修饰符,那我们一块总结一下,修饰符局部变量只能有一个修饰符final,那表示它是指不能修改的,而我们的成员变量的修饰符有很多种,同样在我们的这个官方文档当中啊,我们往上找的话,这个哎,成员变量当中修饰符就是有这么多啊,全限修饰符静态的BY的啊,然后呢,是否这个序这个序列化是吧,然后呢,以及关于这个线程的这块啊。安全的问题对吧,好,那我们呢。哎,来回来,也就是我们的这个修饰符啊,是不一样的。当然我们这道题当中的涉及这个,那下呃,就主要涉及了一个static的问题,对吧,Static的问题就是分为静态的和这个非静态的,好,然后呢,接下来第三个这个不同是吧,局部变量跟成员变量,那么它的值的存储的位置,不同的值的存储,那么我们局部变量是存储在站里面的。
07:15
啊,我们的成员变量呢,那么分两种,实力变量是在堆里面,内变量是在方法区里面,这里呢提到了三个啊内存的区域,那因此呢,下面我们看一下加入虚拟机内存是这样的,加入虚拟机内存呢,就分为啊这么五块区域,但是我们这道题当中涉及的呢,是三块啊方法区,虚拟基站和堆,那这三块分别是存储什么的呢。来,我们来看一下。这个堆当中是存储实例对象的,也就是我们啊这个实例变量,那么对象当中的实例变量是存储在堆里面的啊,还有呢,就是我们的站占,就是虚拟机站当中,它存储的是局部变量,所以所有局部变量都是在我们站当中的。
08:00
那么关于这个啊,这个方法区,那么它是存储一些不太变化的东西啊,比如像加载类信息常量静态变量啊这种的啊,是在我们方法区里面。啊,那么这里呢,像这个说了这个账当中的对象引用什么意思呢?就是比如说我们的这个OBG1OBG2属于对象引用,那它俩是局部变量,是在占里面,但右边的这个实例对象是在堆里面,啊是这这个意思,那我们OG这是属于这个引用,那么它存在占在中,那右边的个对象是存储在我们堆里面的。啊,那么这个堆当中,那么这些是属于实例变量,在堆里面是属于我们堆象的,那么这个类变量属于我们的这个呃,方法区里面的这个区域。好,这是我们的存储位置的不同,那一会我们给大家详细画一下图。先接着看啊。局部变量跟成员变量的区别啊,那么我们这个第四点就是关于局部变量是从声明处开始到所属的大括号结束,也就是说局部变量是有中域的。
09:06
那么在我们这个当中啊,比如说这个I,它的作用语,那到这就结束了,我们的J到这儿就结束了,那么这个呃。二这里没涉及到,所以我就不讨论了,那OBG1O之二它的作率呢,就到这就结束了,那么关于这个成面变量呢,它是一般不谈这个作用域的啊,那么实例变量我们。是在当前类当中啊,就用Z点访问,或者甚至我们缺省它可以直接访问,那么这里呢,就要如果缺省的话,就要考虑到一个就近原则问题,是否与。局部变量同名啊,那么在其他类当中,我们用对象敏感访问,而类名类变量呢,就是用类敏点访问,当然也可以缺省,缺省的话同样也要考虑这个与局部变量同名的问题,然后呢,在其他类当中呢,用类敏点或对象敏点访问,那也就是这个当中啊,我们的这个这个S你看就都缺省了,缺省了,但比如说这个I也缺省了,那么就这个时候呢,我们就要看它,如果局部变量有这个同名的,那这个I,那就究竟原则,但如果你这个地方加上this点,那它就不一样了啊,就是代表的是上面的这个啊,如果没有this点,那就代表的是就近原则,就这个I。
10:21
这个这也是一样的,如果没有这个Z的点,那代表是它,如果加上Z的点,那代表就是上面的上面的。那么这里因为S不涉及到同名,如果有同名,那同样的要用类名点访问啊,这是我们的一个问题,那么这个当中啊,下面这个主方法里面,虽然也是在本类当中,但这里的里面的访问的格式就跟其他类当中,这里写的代码之后跟其他类访问一样的,比如INJ啊,那么它是用对象去访问,对象去访问,那么。呃,这是呃他们的方式访问方式,因为咱们的study是不能直接访问,非study就像同一类当中,因此我们用这种跟其他类访问方式一样的对象去访问,而这个S按理数本来这里是可以直接S的,但是呢,我们这里也是用对象去访问也是可以的啊,通过对象访问,同样这里用类名点访问也是可以的,那么这个是我们的这个访问的方式啊,以及我们的这个中域的问题。
11:15
最后呢,就是生命周期局部变量啊,它的生命周期是很短的啊,并且它每个线程啊,它是也是独立的,每一次调用都是新的生命周期。这个一会我们画图的时候能体现出来,然后呢,实例变量呢,它是随着对象的创建而初始化,随着对象的回收而被下完,也就每一个对象的实例变量是独立的,而我们的类变量呢,是随着类的初始化而初始化,随着类的消亡而消亡,那么该类的所有对象是这个类变量是共享的啊,这个是独立的,你看这是它俩不同,那么这个呢,生命周期非常短,每一次调用这是新的生命周期,那这里不涉及到多线程,如果现在多线程,每一个线程也是独立的。那么下面呢,我们给大家呢,这个画一下图啊,画一下图,我这里呢,有一个画图工具啊,我把代码呢粘过来,那么我们来说这里内存的涉及了三块内容,三块内容那我们来一一的去分析一下,代码呢,从主方法开始运行,当然这个X不涉及,我就不画它了,往下来啊,我们一行一行画,比如当我们执行到这行的时候,那么它内存是什么样的,首先呢,我们站当中是以方法为单位啊来。
12:27
分。块的,那么这个当中呢,哎,就是首先给主方法分配了一大块空间。接下来呢,我们O倍之一是它的局部变量,那么它是在方法。中开辟了一个局部变量啊,那么它是叫啊这个exam的five five的OBG1。然后呢,我们呢,哎,有两个局部变量,我们这道题当中主要关注这两个啊,一个呢是OG2都是我们的exam five类型的啊。
13:03
那么接下来呢,我们这两个局部变量指向的是什么呢?好,第一条红色的这个当中啊,我们会在右边呢,用一个。又一个,那么又一个呢,就是我们的five,它里面存的是什么呢?这个当中。在我们的实例里面存什么?主要看上面的实例变量,那么因此我们这上面呢,有两个实例变量,Int I然int j,它们是有默认值的啊,局部变量没有默认值,但是我们的成因变量是有默认值的。好,这是我们刚用的时候,初始化是这样子,但是我们这里还涉及到一块代码的执行,那就是我们的非静态代码块,非静态蛋板块,它是会随着啊实力啊这个。化的时候啊,那么我们也会初始化,我们呢,为什么呢?因为我们实例化的过程,其实是实例化,其实啊执行的是。
14:03
执行的是一个这样的init的实例化方法啊,实例化方法,那么这个方法呢,它是由我们的啊,显示初始化实例的显示初始化实力变量的显示初始化,以及我们飞溅代码块,还有勾刀器组成的啊,总之呢,它会这个执行,所以这里看起来是勾刀器,但其实是执行的是这个方法,那么它里面呢,就会把这个包含进去啊,包含进去那么我每一次实例它都会执行,因此我们啊这个当中它会执行执行的,那么怎么执行的呢?这个时候我们就为它开辟一块空间,因为有方法嘛,那方法它就会开辟一块空间,它叫做OG1的啊这个in ni方法。In方法,那么它里面呢,是有一个局部变量啊,叫做int I。In特an对吧,In特I这里呢,颜色深一点啊in an,那么这个里面是什么值呢?哎,就初始化为一了,就是它了。
15:06
然后呢,遇到了I加加这一行,这一行呢,那么这个时候呢,他就会把这个值改掉,改成二。因为它是就近原则,这个找的是我们这一行的,接着呢,那么我们接下来啊,这加加这加加建是谁呢?那么我们呢,哎,就近原子往上找,找的是这个G,这个G说明是O倍之一的成现变量,因此我们这个当中啊。把它呢,改成了啊一。接着呢是S加加,S加加,那么S是我们的类变量,它在哪呢?OK,那就是刚才在exam five啊这个内加载的时候,其实在这个地方会有一块区域,关于啊,咱们的关于我们的exam five的类的信息的exam exam的啊这个。类信息。
16:02
那个five.class那么它里面呢,哎,就会有我把这个小一点啊。就会有这样的一块区域啊,叫做S。叫做什么呢?Int s int s啊,那把这个呢,再弄大一点点。In s,那么这个int s里面初始化值也是零也是零,但是呢,我们这张因为S加加了嘛,加了一次它是所有实例共享的,所以说你每次加它都会影响就变成一了。这就是我们啊,这个第一行代码执行的时候,它的效果是这样子。接着呢,我们代码呢,就往下走了,那这个时候也是这个实例化过程已经完成了,那完成以后呢,那么这个代码就要。哎,移走了啊,它呢,就是相当于已经释放掉了,就完成了,那么接下来我们会执行第二句,第二句呢,我们用绿色的啊框框框一下。
17:05
这个时候我们呢,哎,又涉及到一个实例对象的创建,那因此呢,我们啊OG2。哎,又一个对象,当然这两个对象分别是怎么指向的呢?哎,也就OG1刚才是指向的是这个。对象,那么现在这回啊,OG2指向的就是它了,它里面呢,也同样会有一个成员变量啊,初始值成原变量啊,初始值好就完成了,那么这个时候同样涉及到一个。非机的代码块的一个初始化过程,那么一样的,我们呢,再来给大家分配一块区域,对吧。他没法去,那么它呢,是。OVG2的初始化方法,初始化方法,那么它里面呢,也会有这样的一个。点啊这样的一个局部变量。
18:01
它也是int的I。局部变量int,哎,它是独立的啊,独立的跟上一个没关系啊,因为它是局部变量,每一次调用它都是独立的,它的初始化值仍然是一。那么A加加,那么变成了二,变成了二。二然后呢,这加加这回的G啊,这回的J是谁呢?因为它是O之二的乘为变量啊,O之二的成员变量,那因此呢,我们改的是这个里面的一,然后呢,S加加S呢是类变量,类变量的话,那么它是所有对要共享的,所以就一份只有一份,那么就改的是这个地方。好,这是我们的这个第二步。他完成以后,当然呢这个呢也会走。一会走啊。往这来。当然我们的这个啊还在啊,这个还在,这个还是不变的,那接下来呢,我们要往下走了,代码呢,比如说到蓝色的这一步啊,我们画个框框OBGE的test设计的方法调用,同样这个方法也会开辟画空间,对吧。
19:16
它呢叫做啊OBGE1的test方法,OBGE的test方法,那这个方法调用的时候呢,因为test方法涉及到局部变量int j,那么我们这儿呢,就会开辟一块空间啊,Int j int j,那么这个值是多少呢?我们这里有一个船舱的过程十传给它,那因此它的初始值呢是十。就写完了,好,那么他十完了以后呢,就这加加了,这个是究竟原则找的是它,所以这个里面呢,改的是它里面的11。接下来呢,就是I加加,I加加,那这个I它究竟原则找的是上面的乘员变量,那乘远变量的话,那么找的是OG1的成员变量,OG1的乘以变量I加加变成一。
20:05
S加加所有的类变量都是啊。共享的,那么我们呢,不管哪个对象里面都是它变成了三。OK,三。三。好,那么这个呢,就过程就完事了,完事之后这块的内容呢,又释放掉了。跑出来了,我拿走了,接着呢,我们又调用一次啊,比如我用这个。橙色的在这同样呢,它也是一个test的方法,在这个账当中啊,那我们呢,叫做。OGOG也是一一的太子,但这回是第二次调用。是独立的啊,也就是我们站当中是这个独立的,对吧,每一次方法电是独立的。它是这个,呃。没有这样的一个啊相关性的,那因此我们这个地方啊,接着来这个G跟上一个G就没关系了,另外一个G又是一个局部变量,那它的初始值是多少呢?同样的道理,我们这个20会传给他。
21:12
那这个里面呢,就是20。20,那么这个J加加呢,同样是就近原则是它,那么因此我们这个地方改的是这个地方21,那么这个I加加是属于啊当前对象的啊,这个I加加其实就相当于Z就A加加了,那么I加加那么属于当前对象,那当前对象谁呢?因为OGE就是当前对象了,同样是改的是我们的这个二。那S呢,那么是我们全局的,那么我们这个地方呢,改的是它变成四了,改加。好,这一次呢,他也执行完了。执行完了,那我们呢,把它拿走。再来一次,最后一次啊,我们呢,用紫色的最后一次OVG2的test,那么这个时候呢,我们同样的在这里面的开辟空间啊。
22:05
叫做OJR的past,这回不一样了。二那么它也是跟前面截值是独立的,但是这里啊,我们这个当中,哎,局物变量是独立的,它的初始值是多少,30传给他,所以呢,这个地方呢,就是30。然后呢,这加加就近原则找的是它,所以呢,这个里面啊31。对吧,然后呢,A加加,注意这个I加加呢,是OBG2的A加加,所以呢,我们呢,OG2在这改的是它,那S加加还是同一个S加加五。那么他完税以后就拿走了这部分。拿走以后,然后呢,又回到我们的主方法当中啊,去运行IJ,那这个时候呢,我们就看OAJ1的I2J是一,S是五,所以呢,21521OBG2。
23:12
的IJS,那么就115啊,也就是我们的这个一五分析出来,这就是我们的内存当中的这个,呃,存储情况保存一下。那么这里就是我们啊,实际开发当中,当我们如果出现重名的时候,如果说我们希望这个I是上面的这个I,我这个J是上面的J,那我们刚才说了怎么办呢?也就是我们考虑到这个重名的问题,局部变量与实例变量或者是内变量重名的时候,怎么去区别呢?我们如果是局部变量与实力变量重名,我们就哎加上this点,如果是局部变量与内变量重名,我们在内变量前面加。类敏感。那么我们这个地方应该是叫实例变量对吧。
24:03
一变量啊,所以呢,我们这个当中啊,我们这个呃,如果要表示的是。上面的,那么这个地方就加this。这个一样啊,This如果重名的话。没有重名就没问题,那我们运行一下,如果加了this点有什么不同啊,所以这个就不是这个了,也不是这个呢,也不是这个了,是吧?哎,那我们看看它效果会完全不同。王一博,所以呢,你要看重名的时候怎么考虑,对吧?好了,这是我们的这道题。那么我在上硅谷等你,谢谢收听。
我来说两句