00:00
好,同学们,咱们接着来看这个专题九啊,关于idea的断点调试啊,我们也称为呢,叫做debug。诶我们说呀,如果呢,诶大家是Java学习的新手啊,刚开始接触Java啊,刚开始来接触我们的idea,那这个时候呢,这个debug断点调试呢,诶大家呢,有必要呢,重点的去学习,而且呢,在实际开发当中啊,要不断的去使用啊这个断点调试的功能,这样呢,你在后期代码量这个比较大的时候啊,你才能够灵活的去使用咱们的debug。啊,那么如果呢,你之前呢,使用的不是idea啊,已经做过Java开发了啊,你可能用的是eclips啊,或者是这个S啊等等这样一些这个开发工具,现在呢,只是换成idea了啊,断点调试呢,以前也经常用,呃,这块的话呢,我们说上手呢,实际上还是比较快的,只不过呢,需要熟悉一下在idea当中,我们断电调试呢,相应的一些按键啊是什么就行了。啊,所以这里边儿呢,更多的还是一个经验的积累啊。啊,那么就按照新手来讲的话呢,诶,我们就会提一个问题,说为什么需要debug。
01:01
来这里呢,有一段话啊,说呢,编好的程序呢,在执行过程当中可能会出现错误,那如果出现错误了,我们该如何快速的定位和查找这个错误的,呃,位置呢啊,今日做这个修改是吧?啊那一方面啊,你就可以硬看是吧。啊,但是硬看的话呢,那我们说需要的功力呢,实在是太高了啊,你想本身你在写的过程当中都没有发现的问题啊,这个你想通过硬看就看出来,这个往往呢,有时候呢,你会有一些经验性的一些盲点是吧?诶所以这块呢,我们凭这个硬看呢,就不太靠谱了,那这个时候呢,我们就需要借助一个工具啊,那就是bug了。啊,那说到这儿的话呢,我们可以呢,呃,简单的做一个总结啊,就是我们再去啊运行编写好的诶程序时。啊,编辑好的这个,诶,程序时啊,可能出现的几种情况。哎,这个呢,咱们捋一捋啊,大家你看一看,首先的话呢,我们说这个叫情况一。啊,就是我们这个代码呢,写好了,诶我们一运行啊,我们说呢,没有任何bug,说这个程序呢,执行非常正确。
02:06
诶,这个呢是我们最希望看到的诶,但是多数情况下呢,我们都很难啊,一步到位啊编写的程序,然后呢,一运行1.bug也没有啊,这个难度还是挺大的是吧,尤其呢,你是一个比较大的一个项目的话呢,这个呢,往往有我们欠缺考虑的地方啊,这呢是一种情况,那么第二种情况呢,就是我们呃运行以后。啊,运行以后啊,然后出现了这个,诶错误,或者呢,是出现了一些异常信息。但是这时候呢,还有一点好处,就是它告诉了我们出现异常的位置啊,说但是啊,通过这个日志文件啊,或者呢,诶是这个诶控制台,然后呢,显示了。啊,这个异常信息的这个位置。那这呢其实也挺幸运的,你想想他都把这个异常信息的位置啊,包括什么样的异常都告诉我们了,那我们直接呢,就可以定位到你相应的位置啊,做相关的一些修改。
03:01
对吧?诶这是我们说的第二种情况啊,那么这个情况三呢,啊,还有什么情况呢?就是我们这个运行以后啊,注意啊,诶我们运行以后呢,诶得到了这个结果,诶但是这个结果呢,诶不是我们呃想要的。啊,这个有点意思啊。就是这个程序运行呢,诶出结果了,没有出现异常信息啊,但是这个结果呢,我们说诶不应该是这个结果呀,是吧,应该是另外一个结果呀,哎,这就有点特别了,那这呢也是一种情况啊,哎,这个大家你看能不能产生一这个共鸣啊。那么还有一种情况呢,哎,我们这个运行以后啊。诶,咱们说怎么着呢,哎,说得到了结果。啊,说这个结果呢,哎,在这个,呃,多次运行时。哎,应该这样说吧,呃,运营以后呢,得到这个结果啊,说这个结果,哎,大概率。啊,是哎,我们想要的。哎,想要的是吧,哎,但是呢,诶多次运行的话。
04:00
啊,可能会出现。啊,可能会出现这个,哎,不是我们想要的这个情况啊。我们呃想要的这个情况啊,这个呢,话有点绕哈,这个我直接给大家举例子吧,诶比如说诶怎么着呢,我们说这个诶多线程,诶这个情况下呢,诶处理这个线程的安全问题。啊,那比如说我们没有加相关的一些同步机制,这时候你会发现呢,诶有的时候呢,它是正确的,有的时候呢,它是错误的,不像上边这个第三种情况呢,是100%出现的,就不是我们想要的了。这儿呢,我就简单的罗列这样的四种情况啊,那么在这四种情况当中,咱们说呀,除了第一种情况。啊,因为呢,你运行没有任何bug是吧,完美啊,但是这种概率呢,实在是太小了啊,诶除了第一种情况,剩下的这三种情况啊,我说呀,咱们其实都有必要啊使用第八个。啊,这块我们这么着一下啊,诶这样啊。哎,这个我这样加一条线啊说呢,哎,如果出现。
05:02
哎,这个诶如下的三种情况啊,都有必要。呃,使用啊这个debug,你比如说我们这个青蛙啊,虽然说呢,它提示了我们这个异常信息的位置,但是你说诶为什么这块就是个控制针呢,诶我感觉已经附上值了呀,啊那这块你也可以呢,通过这个debug的方式,你找一找,你看是不是我们给这个变量做了正确的一个值。哎,就是下边这些情况呢,咱们都可以使用啊这个debug,所以说debug呢,对于我们来讲呢,还是十分重要的啊,应该呢,尤其是这个老成员啊,这个debug呢,用的就非常溜了。啊,新手的话呢,一定要呢,多去练一练啊,多去在这个实践当中去使用。那下边的话呢,就涉及到我们debug的一个步骤啊,这块我写了写啊,第一个添加这个断点,第二呢叫启动调试啊,单步执行啊,观察变量和执行的流程啊,找到并解决问题啊这块呃,熟的人呢,一看呢,感觉很清晰啊,也很简单,那不熟的同学呢,看到以后呢,也挺迷糊的。
06:01
诶,所以下边的话呢,咱们就通过具体的这个几种情况啊,来给大家呢进行一个讲解。啊这呢,我一共列举出来了这样的一些情况啊,有行断点,方法断点,字段断点啊,条件断点啊,异常断点啊,线程调试强制结束等等,诶这样的一些情况呢,给大家呢做一个讲解,哎,当然了,我们在实际的开发当中啊,其实遇到代码量比较大的时候呢,很多时候呢,都是结合着去使用的。啊,所以呢,我们对于首次接触断点调试来讲呢,咱们就把它呢,诶各程内的力度呢,就比较小。好,那么这块呢,咱们直接呢,就来看一下这个,呃,这个module了啊。在咱们当前这个功能下呢,我提前已经把这个呃Mo呢写好了啊,就没有必要呢,咱们去献血了啊,关键呢,大家还是去熟悉诶首先呢,咱们来看第一个啊叫DEBUG01。啊,通过这个例子呢,我们想给大家演示一下这个行断点的问题啊,以及呢,呃,来熟悉一下咱们debug这个过程当中常见的一些按钮的操作情况。
07:01
啊,OK。行,那咱们来看一看当前这个代码啊,这个代码的话呢,没方法里边啊,我定义了两个变量啊,打印一下这两个变量的值,呃,下边这两个,呃,这几个代码呢,我暂时呢,先都给它注释掉啊。打开这两个变量值,然后呢,我这呢调了一个方法叫做方法,在这个方法里边呢,我们把MN呢进去,呃,这呢相当于是交换两个变量的值。交换完以后呢,回来我们再打印M和N的值啊,这块呢,我们看一看啊,是不是能够把M和N呢给它交换一下啊这呢,如果我们不去使用debug的话呢,直接就点这个run就可以了。好,我们做了一个运行啊,那运行完以后的话呢,你发现这个程序呢,它没有报错,但是呢,我们说这个结果呢,不是我们想要的。啊,那么就对应着啊,咱们刚才上面说的是哪一种情况呀。哎,是不是说我们这个叫情况三是吧,说运营以后呢,得到了结果,但是结果不是我们想要的。我们希望呢,把M和N这两个值啊交换一下,结果你发现呢,没有换乘。
08:00
啊,不太理想啊,这时候我们就要想,诶,这为什么不对呢。诶,我们就想看一看这样一个具体的过程了,是吧?好,那么针对于我们这样的几行代码在左边啊,每一行代码呢,有一个编号,编号后边你看这块有一段空间,这个空间呢,就是方便我们来打断点的啊,那么在这个位置打的这个断点呢,就叫做行断点。啊,你比如说我在这个位置呢,我打了个断点啊,大家点一下这个鼠标左键就可以了啊,诶按一下就行啊,你说我这块再按一下就没了啊这个双击呢,好像也没什么区别是吧,也没什么用哈,诶双击的话呢,不会说有什么变哈,就是点一下就行。好,那我在这个位置呢,打一个断点,哎,那这块呢,我们调了一个方法这块,哎,我也打一个断点,哎相当于这时候呢,我就打了哎两个断点。没问题是吧,好,那么打完断点以后呢,呃,如果这时候我们还去执行这个run啊,这时候呢,你跟没打断点呢是一样的。啊,没什么关系是吧,哎,是一样的,那么你打了断点以后呢,要想这个断点起作用啊,注意我们这时候点的时候呢,你要选选第二个选项叫debug。
09:02
啊,Debug这有个小臭虫啊,这个臭虫就是bug的意思啊,诶debug就是调试的意思了,诶那或者的话呢,你在呃我们这个空白处呢,诶这个右键这块也有个debug,诶或者的话呢,你在这个位置选这个小臭虫也行啊,有好多位置呢,都可以供我们去选择啊好,那么这时候我点这个debug这个操作啊,走起。好大家看啊,这个时候呢,你会发现呢,诶咱们这个代码呢,哎,我说啊,就停留到这个位置了。哎,那这个位置有什么特别的呢?哎,它是我们第一个打断点的位置。是吧,诶我第一个打断点的位置,所以说呢,诶当我们打好断电以后呢,我一运行debug,这时候呢,它会直接运行到我们第一个啊断点的位置上。啊,那么在这个位置上的时候呢,当前啊,我们这个呃,方法当中啊,涉及到的这个变量有谁呢?第一个呢,是我们这个行参叫做X。啊,它这里边没有任何的数据啊,你看是空的啊,然后呢,我们呃,这个定义了一个变量叫做M,它的值呢是十,你看这块呢,都有显示,呃,这边显示的就是咱们当前这个线程啊,这个主线程当中的啊,这个方法的执行,这是一个战争了哈,啊这个没方法的当中呢,有这样两个变量。
10:12
诶同学说,诶这个N这块怎么没显示出来呢,诶不是这个已经执行到这一行代码了吗?诶注意啊,咱们现在呢,是走到这个断点位置,实际上这一行代码呢,还没有执行。诶注意还没有执行啊好,那么接着的话呢,我们就看这里边的一些这个诶操作按键了,那咱们debug的话呢,主要使用的就是这样一些操作。呃,然后这个位置还有一个,呃,我们debug的时候呢,它的一个诶控制台,如果你要有输出语句呢,它就在这儿显示了,好这个我们还回到这儿来啊呃,那么首先的话呢,我们给大家说一下这个操作啊叫step over啊,它对应的叫F8。诶,它是什么意思呢?就是我们每点一下,它就执行一步啊,你看我们执行到这一步的时候呢,相当于上一步呢,呃就是呃这个debug呢,就是走到这一步了,然后我们上一步呢,就执行完了,诶那么这时候你看我们这个N呢是20,那这个时候就已经得到了。
11:02
没有问题是吧,好,所以呢,我们这一行如果去打印M和N的话呢,你看这块也有显示啊,一个是十,一个是20,所以我再点一下,这就是下一步了。那么这块呢,这个输出语句在哪呢?你再看一下这个console。哎,这样是不是就我们打印出来这样M和N的这个值了。好,这个我们再拉回来是吧,好拉回来以后呢,咱们把M和N的这个值呢,诶是不是就作为两个十参啊,这个传送到我们swap这个方法里边了,对吧?诶注意啊,如果我这块再去点这个step呢,它就呃又往下走一步。啊,又往下走一步,也就是说呢,这个层呢,就是一步一步的,哎,执行就是单步执行的意思啊,那我们再往下走一步,直接就奔到这儿了啊,我们再往后走一步啊,整个呢,程序呢就结束了。哎,这个呢,就是单步执行的意思比较清楚,好,我们再接着把它第八个走起来。啊,又走到这儿了啊好,那么接着的话呢,我们再往下走一步,是不是就到这儿了,哎,到这儿的话呢,我们也,哎没什么感兴趣的点啊,我就再往下走一步,走到这儿的时候。
12:02
我们现在呢,想看一看这个swap方法内部的一个执行情况了,哎,那么这时候我们就用到后边这样两个来操作了。哎,后边这两个操作啊,后边两个操作的话呢,第一个叫做step into。啊,Into就是进去的意思,后边这个呢,叫force step into啊,强制进入。好,那这时候我们就关心,那什么叫这个,呃进入怎么就要强制进入呢?诶注意如果我们遇到了一个方法啊,或者呢,是一个构造器啊等等啊,就是它调用了一个另外的结构,我们可以通过这个into的方式呢,进入到这个结构的内部。啊,你比如我们这个方法了是吧,哎,我现在就想进入这个方法的内部,我们就点这个按钮。啊说这时候点这个行不行啊,点他俩呢,谁其实都可以。那俩有什么区别呢?哎,咱们一会儿说啊好,那这块比如我们点第二个叫step into啊点一下。这时候你就发现呢,我们就进入到这个swap方法里边了。还是在咱们这个主线程啊,May的这个线程当中啊,这呢对应的是一个占空间了哈,呃,刚开始呢,我们是没方法这样的一个呃战,然后呢,在没方法当呃这个战争啊,在没方法里边,我们是不是又调用了这个swap这个方法了啊现在呢,咱们相当于是在这个啊swap这个方法里边的啊,那么在这个方法当中,咱们把上边的这个M和N的这个十和20呢,是不是就给它哎传递过来了。
13:20
给了此外这个方法里边的啊两个变量,哎,所以这两个变量呢,M和N啊,一个是十,一个是20。那么在这里边的话呢,我们在单步执行。哎,咱们又声明这个变量叫做temp,这个变量呢,拿这个M的值负的啊,所以这个temp的话呢,你看还是十是吧,好,那么接着走这一步操作的话,我们走一下,哎,我们把这个M呢,你看。哎,拿N去赋值,是不是改成20了。对吧,哎,改成20了,好,那么接着的话呢,我们把这个time这个值呢,再付给这N啊time呢付给这个N,所以呢,再走一步,你看这个N呢,就变成了十。诶,这时候有同学想,诶,这不是M和NM和N已经交换了呢,是吧,我们不是目的就要交换,这不是这不就交换成功了吗?诶,但是这块你要小心一点,咱们这个M和N呢,是咱们S方法里的M和N,我们may方法里边是不是这个M和N没有变呀。
14:14
啊,这个呢,就咱们这道题目的一个本质啊,所以说呢,回过来,哎,这个程序走到这儿呢,诶这块其实我们已经看出这个情况了啊,如果我点这个单步执行,现在就跳出来了,诶或者的话呢,大家如果你走到上边这个位置已经看出来的话呢,诶你就可以走这个操作叫step out。相当于咱们刚才呢,进到这个方法里了啊,比如我们走到这儿了啊,或者走到这儿了是吧,我现在就不想看这个方法里边我已经清楚了,你就点这个out啊,点一下诶直接呢就走出我们这个方法了。然后这块我们再去输入,你注意现在咱们这个站里边呢,就只有一个may方法了。啊,那那方法里边M和N是不是还是十和20啊。你看是没有变的是吧,诶所以这时候我们再去打印的话,那就是还是十个20,诶这个事儿呢,我们就清楚了,那清楚了这块,诶大家呢,直接可以点这个操作哈,我们直接诶走一下。
15:04
哎,这样就结束了。这是我们把这个题目呢给分析了一下啊,那我们再说一下其中的几个,诶,这个按钮的一个操作啊,好接着说。现在的话呢,我们一运行这个debug呢,它的呃,就停留在我们的第一个这个断点的位置上,呃,中间呢,可能有好多行代码啊,那比如说我们这行代码执行完以后,我就不想让它一步步往下走了,我就直接想让它跳到下一个断点的位置。啊,中间呢,可能有几十行代码啊,那这时候我们就可以点这个操作啊,你看我点一下是不是直接呢,就定位到我们这个断点的位置了。啊,没问题是吧,诶那我要再点一下呢,哎,再点一下的话呢,如果我们下边还有一个断点,那就走到下一个断点,如果后边没有了,那就直接呢,就这个程序呢,就执行结束了。哎,这呢,就是我们这个按钮的一个操作啊,好,我们再去给它debug起来。啊,第八个起来,好,那刚才呢,我们在,诶往下这个走一步哈,诶是不是走到这儿了,好,那么走到这儿的时候呢,诶大家你会发现咱们这是不是也是一个方法呀?啊那么这个方法呢,你说我想看看它的这个源码是不是也是个方法吗?诶如果你点这个step into,你看我们点一下啊。
16:09
哎,你会发现呢,是不是不好使。不好使是吧?哎,那这个是为什么呢?这个呢,就是如果我们调用的是IDK啊,里边码的一些方法的话呢,这个into它就不好使了。啊,我重新再去跑一下啊,啊,你看我们这块呢,再往下走,走到这的时候呢,我们点这个叫force啊,Step into啊我们点一下。啊,这个我们在这呢,相当于是调我们这个,呃,加载的相关的一些这个方法了,我们再退出来啊。啊,这个撇。啊,这个我们把它再推出来。这个我们再去点一下这个false step into啊。好,这时候呢,你发现呢,我们就进入到这个printline啊,是不是这个方法里了,是吧?啊进到这个方法里,如果你要有兴趣的话呢,诶你就可以一步一步的啊往下走啊这个你往下呢去执行就行,那比如说呢,我现在我这个方法呢,已经很清楚了,不想看了,你可以呢叫step out。
17:02
直接呢就跳出来。哎,跳出来以后呢,就走到这个了。啊,走到这个的话呢,这是咱们自己定义的方法啊,刚才我们也演示过,用step into呢是可以进去的,那force step into呢,当然了更可以进去了。诶是没问题的,哎说这里边我已经很清楚不想看了,这个你可以再跳出来,哎顺带呢,咱们再说一下后边这个按键啊,哎这呢,你看有个光标,那它是这个思啊,你比如说我光标呢,我现在在这儿。哎,我我可以的啊,我把光标调到这儿了,这个时候呢,如果我们点这个按钮呢,它就直接呢,呃,由你当前这个执行的这一行,直接跳转到我们的光标这个位置了,你看我点一下。哎,是不是就到这儿了。啊,没问题啊行,那如果这个方法你不想看了,直接呢,就让他out。就出来就行。啊,那如果在执行的过程当中,你说我这个代码我就不想让他执行了,你也可以呢,让他强制呢,做一个stop。是吧,强调一个stop,好,下边这个位置呢,其实就显示我们整个,呃,这个程序执行过程当中啊,我们相关加这个断点的这些位置啊,你看这块呢,是有显示的啊行啊这呢,就是我们简单的熟悉熟悉了一下这里边几个按钮的操作啊,这个我们比如说再跑起来,诶你看这个还有个它哈。
18:12
哎,这个什么意思呢?比如我们这个程序呢,你你你往下看啊,看到好多的位置了,看完以后的话,你说诶我们现在断点在哪儿呢?哎,你这时候你可以点一下它,哎直接呢,就诶呈现出来你当前这个断点呢所处的位置了。哎,这就是这个按钮的一个作用。啊,这样呢,我们就呃说清楚它了啊好,这个我们先停一下,接着的话呢,咱们把这两行代码,这几行代码呢,我也打开。诶打开打开的话呢,来咱们整体上去看一下啊呃,下边这块呢,你看它还有一个诶这样的一个操作哈,什么意思?诶我这儿呢,是一个in的型的数组,这呢是一个型的数组,这在对的数组,我们要去打印的话,你发现就是个址值数组打印呢,发现它就是ABC,好像跟我们想的乎不太一样,你看我点一。啊,这时候呢,我们不会去调这个第八个功能的,哎这块呢是一个地址值,而我们下边这呢是一个ABC,你说感觉很奇怪啊,说为什么打印的,哎都是一个数组的引用打印的这个结果就不一样呢,诶我们就想看一看这个debug的一个情况是吧?诶所以这时候你就可以诶把这个方法这块呢,比如说就打上断点了。
19:19
然后我们这时候呢,再去做一个这个第八个。啊,诶上面这个呢,其实咱们已经都调试OK了啊,诶你可以直接点一下这个按钮,是不是直接就调到这儿了,诶调到这儿的话呢,我们说诶为什么这个打印的就是一个地址值呢?诶我们可以呢,诶这个你点它就不好使了,因为这是源码了啊不行是吧?诶你就点我们这个false step into。这块呢,就进到我们这个诶这个方法里边了,呃,具体的细节的话呢,大家你可以去看啊,但是整体上呢,我们想说的是此时呢,它调的实际上呢是诶这个方法。啊,那这个方法呢,默认呢,其实就是打印,打印你当前这个呃,对象的一个地址了啊,把它打印出来好这呢,我们就直接呢就跳出来了啊然后呢,我再呢去诶走到下一个断点的位置。
20:02
是不是就到这了,哎,我们在,哎这个force step into,哎,那么这个时候呢,他调的这个方法跟上一个,你发现它不是一个啊。哎,他调的是这个方法。啊,这个方法呢,它内部呢,就是对我们这个插音数字呢,进行一个便利的哎操作。哦,原来如此啊,所以我们就直接呢就出来了,出来以后呢,我们就程序呢,让他就结束了。哎,为什么这个结果不一样呢?因为我们调的是不同的方法。那么通过刚才这样一个航断点的一个演示啊,大家呢,去熟悉一下我们debug当中,呃,相关的这样的一些案件。啊,这个我在这块呢,其实也有写啊,大家呢熟悉一下。
我来说两句