粉丝2.6万获赞8.4万

这么中常见的运行时异常。一,数组下标越界异常。当读取数组数据时,中括号中的下标超出数组中最后一个数据的下标时,会发生数组下标越界异常。来看一个例子吧, 当我创建好一个数组,数组中最后一个数据的下标为三,我非要获取下标为四的数据,泡一下看看, 看这个爆错,算了,我单独截图出来吧,这就是数组下标越界异常。 二,空指针异常。当你所调用或获取的对象为 no, 空指针异常就会发生。来看一个例子吧,我造一个空指针,先输出这个数组的数据, 泡一下,哎,不好意思,忘了把上面的打印注视掉了。好了,泡一下,这就是空指针一程,哎,别忘了关注。

今天的话呢,我们来看一个熟悉又陌生的老朋友,大家看到我选中的这一块东西就是异常了,对吧?来回想一下啊,咱们前面选过的代码里面啊,是不是有一刀异常,对不对?哎,你看到我们在讲这个什么?讲这个类型转换的时候,是吧?我们是不是遇到这个类型转换异常啊?对吧? ok, 然后的话呢,还有像宿主,宿主月这一场,对不对?然后呢当时回想一下,当时我们遇到这异常的时候呢,他的反应是什么?他是不是一下的这个程序就崩了,现在程序就崩溃了,然后程序就结束了, 往下就行了,然后呢就会在我们的这个控制台里面有一些红色的警告易报警信息,对不对?哎?然后我们根据这个红色的报警信息的话呢,就可以看到到底是哪里出了问题,对不对?哎,回想一下是这么一个处理过程啊, 好。但是在这里面的话,有些时候啊,我们程序应该做好一些,什么叫预案,就是如果发生异常信息,我们应该有些预案,比如说我们可能发生了这个错误,之后呢, 可能程序还得往下走,哎,那是这个时候不不能让他直接崩溃了,那怎么办呢?哎,这个时候的话呢,在抓里面有一套异常机制,就是帮助我们做一个统一的处理啊。 ok, 这是咱们这节课的话呢,要谈的异常体系好,所以长的话呢,咱们并不陌生了,所以接下来我们来看一下异常的一个处理流程啊,同学,跟着我来看一下啊, 他到底会经历什么?虽然我们曾经接触过,所以大家统一来说一下,你看,注意看啊,这个时候的话呢,有一段设计代码,首先的话呢,是什么呢?创建了一个学生对象,但是注意这个对象的话,此刻给的是一个空,不是六啊。 然后接下来的话呢,往下走这个输出,输出什么?输出? s 点 get name, 那这位方什么故事呢?对吧?当然了,如果你此刻要看后面的话,你就知道他告诉你会抛出空指针异常,这为什么呢?很简单,我们知道我们直接画这个内存分布图啊,有个站,还有一个堆,是不是站和堆? 那很明显的话,此刻当我们创建了一个 s, 这个 s 在这是吧?那这 s 的话指向空意味着什么呢?来写一下 s, 这还有点,这个放下了啊,我再放上一点啊。 gs 呢?指向空意味着什么?意味着此刻他根本就没有指向任何一块堆内存。没有,没得指啊,没得指,那没得指意味着什么?根本就没有任何数据可以指向到。 所以这个时候我们这样说来,那个你去把名字拿来一下啊,他怎么去拿名字?他都是指向空的地方,他没有指向任何一个区域,所以这个时候的话呢,在这里头啊,就拿不到名字了,可以拿不到。所以这个时候的话呢,当我们的骄傲心理机执行到这行代码的时候 啊,接到具体要求,接到这行代码的时候啊,他发现,哎,这个 s 不是空吗?怎么这么闹呢?这肯定不行啊,胡扯对吧?胡闹啊,所以这个时候他会干嘛?他就会抛出什么,他就会开始启动他的异常处理机制,觉得这是会,这是有问题的啊,这有问题的他就会启动 他的异常处理机制,然后干嘛创建一个叫做空指针的这么一个异常对象,然后去再去看嘛,再去看一下当前这个代码,快,你们到底有没有做好预案啊?有没有针对这个异常做好预案?如果有交给你们处理, 然后呢又发现什么呢?发现这边没有做好预案,那行了,这事呢就干嘛?只能是启动默认的处理机制,把这个异常的战信息带到屏幕,然后整个程序 obe, 哎,这是我们异常的处理流程,好吧,哎,这是我们这块要知道的信息啊,咱把这个呢切掉啊, 所以呢接下来的话呢,我们来看一下我们该说的,如果你有我们就让你处理,对吧?所以接下来给大家体验一把啊,先来一来,快速的体验一把这个异常的一个处理啊, 来跟我看一眼,很简单,在这一套的话呢,我们要查一个结构,叫做 try catch 的结构,这个什么怎么理解呢?所谓这踹就是把我们要执行的代码,代码啊,要执行的代码放在这里, ok, 比如说呢 这一块代码的话呢,是可能啊,会发生异常的一个代码,好,如果发生异常之后怎么办呢? catch, 就是抓住啊,抓住这个异常,捕获这个异常,抓住异常之后呢,这时候呢会产生一个异常对象,然后注意到这里来,所以这个时候的话呢,接下来我们可以干嘛?就这里头的话,我们去写我们这个处理异常的逻辑,你到底想怎么处理? 哎,那就写在这里啊, ok, 就 catch 意思啊, try catch。 好,这个呢,咱们先简单来体验一把啊,先简单体验一把,因为呢后面的话呢,我们再来看一下这个完整的异常体系该正确怎么处理啊?这块我们现在做一个简单体验啊,跟着来看看。 下面的话呢,在这边我建了一个包叫 exception 啊异常包。然后呢我们来现在快速体验一把这个异常的感受啊,叫 exception test。 好,在这里头的话呢,跟着我来看方大啊,这个方大呢主要是照顾大家,可能在这个看手机的时候会好 看一下啊。好,来看看,这说的话我们看下妹,好,接下来的话跟着我来看一下。首先的话呢,我们来很简单做一个演示,你看一眼啊,这个 student s 等于空,对吧?啊?对,空好,这个 student 的话呢,这个地方我们应该是定义多个啊,我们引入的是这个这个吧。啊,在, 好的,接下来的话呢,我们做一个输出说这个 s 点 get net, 你看编译层面的话是没有问题的啊,但是呢在这个执行层面的话呢,就会发现问题了,咱们走一个啊。 好的,我可以看到啊,你看这时候的话就爆了异常是吧,说什么呢?首先说这异常发生了,然后发生了一个什么?异常发生了一个空指之异常,空指之异常是一个我们要掌握的东西啊。 ok, 好,然后的话呢,接下来接下来再看一下啊,下一个东西是什么?就是我们这异常方在哪呢?发生在这第十四行,你看这样看的很敏感,是不是在第十四行啊?好,所以就是我们看 这个正常异常的一个处理模式啊。呃,这里头的话有一些人关的控制异常,我说一下哈,就是会弄一个比较高大上的名字啊,所以说呢,这个简写会让你看起来很头疼啊,叫 npe, 你看,我们说我们来谈下 n p 异常,你看 n p 什么玩意?一看就看蒙了对不对?但是你要说人话就是空城异常啊。好,那么咱们接下来的话呢,来看一下啊,那么这条怎么办?这时我们可以直接 try catch 来看看,叫 try catch 捕获到一个什么异常好,然后再放下来啊,来看一下啊, 然后这个时候的话呢,我们把这个什么把这个可能方异常代码给他,干嘛?给他扔到这里面来啊?扔到这里面来,然后紧跟着的话,这样抛什么异常呢?抛的是这个空指的异常,对不对?所以,哎,所以这个时候的话呢,在这里头我们可以这么写啊,我们说如果抓到了空指的异常,就是这是什么抓到了这个异常类, 行,看到吗?哎,你可以干什么事呢?哎,这时候的话呢,很简单,比如说我们可以自己简单做个处理啊,比如说我们说发生了这个空指针异常,看到没有?哎,你看这样的话就变成你要你的处理逻辑了啊,但是具体你的处理逻辑的话呢,我们后面再说具体应该怎么办啊?好,那目前我们先感受一下啊,先感受一下来看看走。 好的,我可以看到你看这个时候的话就没有那些常规的输出了,没有那些警告信息了,因为为什么呢?也此刻就按照我们的版本,按照我们的预期来做一些处理了,所以这边写什么,这边就会出现什么,看懂意思吧,但最可怕的是什么?注意啊,最可怕是什么?有些同学啊,不知道为啥就有时候呢会忘记了,在 cash 里面居然是留空, 这很可怕的一件事情,为什么呢?因为这样的话呢,是这样的结果,你看走,你看啥都没有,就是会出现什么状态,就是我们程序的执行结果,是不是我们预想的那样,但是去看 日志,看后台有没有一点错误,又没有一点错误,这种线是最可怕的啊,这个东西大家不要觉得是这个东西,觉得是那个,怎么说呢?觉得是, 呃,不可能发生的,但真的是我们在线上的话呢,发生了很,就在实际上翻了很多次啊。但是后期的话,其实做一些什么?做一些代码的检测,直接扣的 review, 那基本上问题就解决了啊。那早期的话呢,确实没有这些机制的话,就很多时候啊,大家由于经验上的问题啊,这些地方的话呢,就留空了, 然后就导致一些程序出现问题,但是看起来日志又一切正常的状态,这是绝对不允许的啊,绝对不允许留空啊,哪怕你说出一段话都可以认识吗? ok, 好,所以这边一定要注意啊,绝不允许留空啊,好,绝不允许留空,此处 绝不允许留空。所以这里头的话呢,攻钻键的重点还是要干嘛?还是要来这个有, 我们要通过一种机制,比如像刚才说我们说的 code review 是吧?在公司里面的话呢,一些我们会做 code review, 就是我对我们同学提要的代码做下检测啊,让他看到他有没有问题啊? ok, 或者说我们可以借助一些工具来做下检测,好这一块的话呢?我们就说到这,咱们先体验了一把啊。

今天跟大家分享一个 java 打印日志吞异常对战的一个问题,前几天有个同学跟我说, java 打印日志的时候呢,异常对战信息被吞掉了,导致定位不出问题, 那我就出现了这样的一个问题,那我们这边有一个外循环,在这个循环里面呢,不断的把这个 cod 次数加加,然后在这个里面就制造了一个控制成异常,每一次异常呢我们都会获得他的一个对战信息的长度 啊,然后呢我们就打印这个次数和这个异常对战的一个长度,如果说这个异常对战是零,就说明这个异常对战就没有了,然后呢我们就停止这个循环,那我们来运行一下这个方法,这边次数到五千七百九十的时候呢,对战信息就没有了, 然后呢我就找到了这样的一篇文章啊,然后看到了这样的一段话,大概的意思是说为了提高性能呢, gvm 会针对一些内溅的异常进行优化,在这些异常被某方 法多次抛出的时候, gvm 可能会重新编一个方法,这时候呢就可能会使用不提供对战信息的预分配异常。如果想要完全进入预分配异常呢,可以使用这样的一个参数复制一下,然后我们填写在虚拟机参数这边应用一下。 好,我们再执行一下,那现在的情况就是不断的答应,没有再出现过异常对象信息丢失的情况。如果说大家想要这样的一个 demo 以及之前的 demo 的话呢,可以到这来这,然后给他去发一个消息,像这样就可以拿到我们的 demo。 好,今天的一个分享呢就到这。

接下来我们来看一下这个异常的处理方式,那这一头的话有两块,其中第一块的话叫捕获异常,哎,这个模式的话呢,我们在前面已经讲过了,就是用 try catch, 此刻呢你会看到多了一个叫 finally, 这个 finally 什么意思呢?就是最终无论如何不管发生异常还是没发生异常都会做的事啊,就这个意思啊。啊, 好,那么在这里头的话写了两个注意,咱们来看一下啊。第一个点的话是说呢,我们前面讲过了,不能留空白,这个千万别犯这个错误啊。第二的话就是什么这个异常出现之后呢,我们要合理的记录异常信息。什么叫合理呢?什么叫不合理呢?来说一下啊,就很简单, 比如说我们现在的话呢,这个程序啊,一般来说我们程序比较程序的执行结果啊,这个执行结果啊,不如执行结果呢,就是不是我们预期要的啊,不是预期要的结果,对吧?好,那这个时候怎么办呢?那这个时候的话,我们就可以看一下异常信息对不对?所以这个异常信息需要 能够帮助我们定位到问题,能够帮助我们定位到问题,所以大家伙可以思考一下怎么帮助我们定位到问题呢?对不对?哎,所以这个时候的话呢,应该一般来说的话,需要告诉我们这东西放在哪个类是吧?在哪个类的哪个方法,在第几行? ok, 然后出现了具体什么异常, 哎,这个信息的话必须得给我们知道,我才能够定位到问题,想要解决问题,对吧? ok, 然后的话呢,另外一个要注意点啊,就是像这种内容的话呢,如果你直接 丢到前面给用户看,由于看很一脸懵逼,说这我用的程序怎么我用个软件是吧?怎么还需要知道学点东西呢?不需要,对不对?我不需要懂技术的,所以一般来说的话呢,在我们后续的一个整体项目开发中来说的话,像这些内容的话呢,一般我们会放到什么?我们会放到日志里面去 啊,放入这里面去,如果说你的日子很多,我们还会放到一个日子平台里面去, ok 啊,日子平台好是这么一种玩法啊,日子平台 好,但对用户来说的话呢,用户不需要知道这这个东西都是给谁看的,是给程序员定位问题看的,理解吧。啊?我们需要把东西放哪呢?我们一般来说给用户一些用户看得懂的文字描述啊,这些东西都是给用户看的对吧?啊?给用户看的, ok, 好,比如说我们有些时候的话呢,其实后台是出了问题,但是在用户看来会告诉你说 a b, 说我们要可能靠这种写,比如说系统防瞒是吧?哎,来写一下啊, 防盲,哎,请稍后再试,我们要看到这样的错误,其实有可能什么是后台崩溃了,但是呢,给你一个什么因为看到文字信息能懂意思吧,哎,比如说还有个什么叫四零四,你懂的,其实你可能也不懂啊,但是呢,他就给你看下文字, 表姐,哎,这是我们说的这个这样的一个分分位啊, ok, 好,继续来看第二个,那第二个的话呢,是我们要知道如果说有多重的 catch 的话意味什么?比如说这边有可能 catch 第一个, catch 第二个,就这中间这一块啊,可以写多个。那这个这个时候的话呢,其实跟我们前面讲过的衣服的逻辑类似,我们之前讲过衣服那个坑是吧?衣服 l s 那个坑啊, l s e l s e f, 这个前后的顺序很关键啊,所以这时候的话呢,当我们有多重 catch 的时候呢,这个异常的顺序也是一样要注意的。 好,这一头的话呢,接下来我们来给他写一个异常,来看一眼啊,啊,写个异常,写个异常的处理程序啊,咱们来看一眼,比如说这个时候呢,写一个,读一个文件,来读一个文件。这时候的话呢,我们这样啊,在这边的话呢,我们建一个文件吧,文件整个文件下面我们建个文件啊, 中文件的话这个就是我们,比如说啊,就是这个叫啊 redmi 啊, redmi 点贴 t。 好,在这里头的话没什么东西,我们写个 a, b, c 啊,写三个东西,看懂了吧?好,接下来我们来看咱们读这文件,虽然咱们现在没选 l 流。没有关系啊,咱们简单看一下就行。好吧啊,简单看 看一眼就行,来跟着我看看啊,这周的话呢,我们再来写一个,就是关于这个还是 deception 啊? deception, deception 呢? test two。 好, 这一头的话呢,我们接下来干什么事呢?就是来演示读文件,虽然你现在没有接触过 l 流,但是问题不大,朋友们跟着我来看一眼,很简单,首先呢我们要读取一个文件啊,读取文件,那这时候的话呢,我们需要创建一个啊, 创建一个 fire read 这么一个对象啊, fire read 对象,好,那怎么办呢?这就是叫 fire, 很明显的读文件看到没有?读文件,在这个抓 l 这个包里面就会有这么一个东西,好,跟着我来看一下 read 啊,然后给他先敷一个空,紧跟着的话呢,接下来我们要完成一个什么?完成一个初始化工作啊,初始化工作 好了,怎么做呢?那是 read 等于 new 啊,也是一样也要 new file reader 好, new file reader 好,那这个时候你会犯,我这样写的话,他把我的一个错,很明显这是语法错误啊,咱们看一下,他告诉我们说什么呢?他说没有这样的空的东西啊,他只能接受什么?要么你给我传一个文件名,看到没有, 哎,这文件的路径在哪?要么给他存一个文件对象,或者一个文件的描述服务等等。 ok, 好,所以这时候的话,其实我们可以给他一个文件的路径,那在哪呢?就是我们可以拿到这个文件的位置,大家很明显可以看到目前是在地盘下面的一个 dr s e 这个东西,然后我们就这样了,这样,我们这样子啊,大家看在这里头的话呢,实际上来说我们选择这个文件之后啊,然后应该是有一个,哎,有一个路径的啊, copy 啊, copy 一个 pass, 看到没有?复制一个路径右键, 然后的话呢,我们可以复制一个绝对路径,看到没有,哎,然后点一下,好,只要拿过来之后呢,放到这边来好,大家伙也可以看到,就是指定好一个路径,哎,说指定好路径之后,你怎么还报错呢?哎,还报错呢,注意看啊,注意,这个时候,这个时候就是我们知道的异常体系出现, 发现了一什么,就是他发挥作用了,他告诉我们说,兄弟啊,这个时候别人给你一个路径,不一定有啊,不一定真的存在啊,你要做好预案啊,懂事没有? ok, 说的话,你看他告诉我们说这时候可能会发生什么,我一定找不着的异常,所以你要做好预案,看到没?哎,所以这时候的话按一下,然后做好预案,这时候做好预案,你会发现有两种办法,一个方法是吧?就是我们声明这个地方是有异常的,不管啦,一种就是我们讲讲的踹 catch, 来,咱们讲个踹 catch 方式啊, 好,所以踹开了这里面的话,他会干嘛?他就会在这里头的话呢,他会捕获到我们这个文件,会找到异常,然后去重新抛出异常。那这时候的话呢,你可以做一下你想做的事,你可以把这注射掉啊,然后比如说我们最简单做一个输出,我们说 请输入正确的这个文件路径啊,当前,当前文件未找着啊,不存在,当前文件路文件不存在。 ok, 这个给用户一个提示,说你这东 先别闹啊,给的是一个错的路径,懂我意思没? ok, 好了,这样就可以了啊,打完之后你可以去读里面内容了,比如说我们可以去 red, 如果有的话就可以 red 点,大家可以看到这个叫 red, 这是读什么?读某一个字符啊,每个字符,比如说我们读一个吧啊?读一个来,这个时候的话呢,我们把它读一下,然后把它转成一个字符给大家看一眼,像什么东西啊? c 等于,然后这边要强转一下加串,哎,这样又报错啊,报错呢,是因为这里头发也是有异常的,所以我们要再加一个异常,叫 l e 长,看到没有,哎, ok, 所以这个是时候是吗?这个时候是我们发现这个读取啊文件内容时,啊,发生了错误。 好,这边我都敢用文字性的东西来描述啊,就我们暂时也不打异常信息出来,这异常信息的话,你要看异常对象信息都很简单,注意看打印异常对象信息是这样子的,大家注意看啊,打印这个异常的 堆这个站信息啊,站信息怎么打印呢?是这样子的,就 e 这个对象,然后点,大家看到叫打印,叫 print 啊, stack trunk, 就是什么串,就是我们的这个站的一个跟踪信息啊,同理呢,下面也是一样啊,你可以这样打印, ok, 好,所以这个是给用户看的啊,这是给用户看的,这是给程序员看的,理解吧。哎, ok, 那这边的话也一样,你可以做这个打印。好,那我们可以来感受一下,然后我们把这个第一个字符输出来,看是什么东西,大家伙跟着我来看一眼,来,咱们走一个看看啊,走起来, 好,来看一下,你看打印书是不是?哎呀,因为车子文件存在,所以后面这些预处理方案就用不上,没事,打一个正常走了就没事,这边程序是没有问题的,就没有他们的事了,但是如果说我们一手抖又给不小心敲到这个六,对吧,那这个时候这文件是不是不在了呀?所以这个时候你再看一下啊,咱们的预案就发挥作用了, 咱们看一下,你看这一回的话呢,这个第一个这个信息是什么?是我们熟悉的那个异常的战信息,也说是由这句话说出来的,而后面这个呢是我们经过自己的一个文字性的描述说出来的,看到没有?哎, ok, 就这么一个方式啊,那么在最后呢还有个什么东西呢?还有叫 finally, 来注意看,叫 finally 这个 fat 一下,什么意思呢?来跟我看一眼啊,就是说无论如何都会执行的东西,你发生异常不发生异常都会执行说,我们来可以写一下啊,无论是否发生异常都会执行。好,我们来验证一下啊,来,现在的话呢是发生异常的情况,我们来看一眼,走一个, 好看看啊,来,大家伙可以看到啊,这一遍大家发现看到什么?是不是发生了,对吧?好,接下来的话呢,我们再来给他一个正确的啊,给他一个正确的,然后的话呢再看一眼走好,大家看到正确的话呢, 也会发生,那这个东西到底有什么用呢?这个东西一般来说是干什么事呢?这个东西一般是来帮助我们关闭一些资源的,谁要关闭些资源,就是我们其实啊在这每一次的话呢,我们去创建一个文件的读取流,现在打开了一个文件 啊,那这时候的话呢,在我们系统上会占用资源的,那当我每次呢我们用完之后,其实应该是要把这个资源关闭掉的啊,释放这个资源,让我们的超系统能够出来去打开其他的文件。 ok, 所以这个时候啊,我们一般来说需要去关闭哪个资源呢?就是我们这个 文件流啊,就是个读文件这个东西,那怎么关闭?所以这个时候的话呢,你看啊,注意看,我都这么写,看行不行? read 点叫 close 好,就是扣子方法啊,然后的话这边会有方异常,再开除一下, ok, 一般就这么一个写法啊。好,那注意个事是什么呢?在这里投啊,同学们,这里投的话,这个东西这样注意,这样写行不行?看一下啊,回忆一下 有没有问题。那很明显的话呢,这样写法呢,是有点问题的,因为的话你要注意这个时候我们要做什么。一个编辑的判断说,如果这个 red 不等于空,我才能去调他的 cloth 方法,否则的话,此处如果刚好是空的呢?那是不是可能会发生空整的异常啊? 对吧?也有可能你在执行这段话的时候就出了问题,而出了问题,你这边没有做非空判断,那是不是有可能再出错上加错,对吧?错上加错能懂这意思不? 哎, ok, 好,所以这个地方的话呢,要注意啊,就是我们如果前面这段话已经出了问题,那么他的初始化就就失败,那他的依然还是空的,而他如果是空的,那么你再直接没判断摘掉空指针,摘掉这个扣子的话呢,就会再出现空指针异常啊,所以一般我们要关闭资源就用这种方式。 好,以上的这个玩法呢,就是一种 try catch value 的玩法啊,大家可以掌握一下。然后的话呢,还有玩法是什么呢?就是我们不管了,这个事呢,我们 就是抛出去,现在这种模式是什么?就是我自己来处理是吧?我自己处理,有异常我自己处理,还有种模式是有异常,我不管了,我直接抛给上面,我在方法上声明一下,说这个事啊,这个方法是有可能出现异常的哦,你们都要说注意,你们要自己去处理一下 啊,就这个意思, ok, 所以这个时候的话呢,孩子可以怎么玩呢?可以这么玩。咱们再拿一个第三个吧,咱们再拿一个啊,来,这是我们 ctrl c, ctrl vico 评分,然后第三个来讲一下啊,这种情况下的话呢,是什么样子的?是这样子的,这个时候我们依然是把它拿出来,然后, 呃,在这一头的话,我们把前路系头全部干掉啊,全部干掉 好之后依然丢在这里,丢在这里呢,依然会有问题,是吧?会问题的话,你注意看这个时候的话呢,实际上来说除了 try catch 以外它的利用,利用方式是什么?就是声明抛去,你看此刻在这个方法里面就有个 slows, 然后说我们这个方法可能会抛出这样的异常,看到吗?然后同理呢,这边也是一样,你看这边的话呢,你还可以干嘛?也是一个声明, ok, 这时候你发现,哎,怎么突然变成一个了?因为是什么?因为在声明的时候呢,如果就因为这个 io 异常呢?比这个什么?比这个文阶位找到异常要大啊,所以他就直接选择一个抛出去了, ok, 你看这个时候的话,你啥事都不用干啊,啥事都不用干, 看到没?哎, ok, 一般来说我们在这里头的话呢,还可以去做一个,还是得做这个资源的关闭啊,还是得做资源的关闭啊?好,这是一种抛出异常的方式啊,抛异常的方式啊, 就是这种模式,好,这是我们整个的一个什么,一个两种玩法啊,两种玩法,所以我们在发现未来,我们在学习 api 的时候啊,我们发现,哎,我们钓那个方法,他那上面有这么一个声明,说明什么呢?所以我们在钓他的时候要考虑这个方法可能抛出这样一场,我们要做一些处理理解了吧?哎, ok 啊,我们这个,然后呢?最后还有一个什么,还有个地方就是刚才还漏讲了,就是我们这个 catch 啊,有多种 catch 的时候,呃,如果你的一个大一长不能放在前面,就像这个东西是大的,你看他默认就放在后面,对不对?而小的放在前面,看到吗?就更细致的一长放在前面,而更宽的一长放在后面, 比如说我们假设我们这边再放一个更宽的一长啊,更宽的一长,比如说叫 catch 啊, exception, exception, 你看很明显这一长是不是更宽,因为这一长的话呢,他比我们后面一长更宽,你看这时候他们都报错了,都报错了,看到没有?因为这个时候的话呢,他会告诉你什么?你看来看一眼啊, 他说你现在做的这个 final fire section 就是什么在前面已经被捕获了啊,前已经被捕获了,被这个东西捕获了,所以其实我们这东西已经没有存在的价值了, 看到没有?哎, ok, 好,只要注意这个异常的信号顺序啊,就是从小到大啊,从小到大,好,这一块呢,我们就给大家说到这啊,关于异常的一个生命式的处理,就说到这。

哈喽,小伙伴们大家好,今天的话还是给大家去分享一个八个啊, 今天分享的 bug 的话,还是结合这个具体的业务常情给大家分享的一个 bug 啊,对吧?好,我们来看一下现在的话我们存在的一个 bug 啊,打开浏览器啊,去访问一下啊, 朋友们看一下现在的话我们的一个业务场景啊,来先刷新一下啊。比如现在的话这里边呢我们有这么一个结算单,结算单的话,这里的话还是类似于我们这个超市当中的那个小票啊,你去超市的话,你这个买完东西以后不要去进行结算吗? 鞋带的话在前面那里边他会进行扫码的一个操作,给你去创建一个小票对吧,小票当中有你消费的名气啊。那现在的话我们在做的这个项 当中的话也有这么一个结算单的一个需求,结算单这个需求的话,这里边他也是一些基本的增长改查对吧?那这里边有一个明细啊,明细的话就是这个结算单的话他消费了哪些登记对吧?我们点一下啊,然后进来老师给大家去演示一下这个八个啊。来 看了看了朋友们啊,比如现在的话,这个右侧的话就是呢我们的一个商品对吧,你可以去选择这个商品啊, 当你选择这个商品以后的话,那么他会在上面的话这里呢会计算这个总金额,还有这个实付金额对吧?下面是你的邮费的一个价格,但这里的优惠价格的话,我们做的比较简单啊,就直接在这里去进行输入价格了。 好了,同学们进来看一下,我们给大家去演示这个 bug 啊,比如现在的话,我们的价格的话,我们去写个两万好了点,确定 好推呢?大家可以看一下,发现了一个现象的吧,发现了一个什么现象呢?当我们这时候两万以后的话,他是把它清成零的对不对?把它清成零以后的话,你会发现这个总金额跟这个实付金额的话他不一致,你没发现吗? 我们的实付金额的话是应该是我们的总金额去减去我们的优惠金额才是我们的实付金额呀,但是现在的话我们的优惠金额是等于零的,你三二七六减距离应该还是实付金额,实名应该也是三二七六吧, 所以现在的话遇到一个神奇的八格对吧?我们的总金额跟我们的实付金额的一个价格不一致的对不对?其实的话,我们这个平时在开发当中的话,不怕那些 这个不是不怕那些这个比如说他出错的办法对吧?我们是怕的是那些数据不一致,他不报错对吧,他不报 也不跑异常,但是数据呢,就跟你想的是不一致的,这种 bug 是最恶心的啊,抖友们啊。好了,那我们来看一下这个 bug 产生的一个原因啊。首先我们来带大家去看一下我们现在的这个代码啊,来打开一下我们的代码, 代码的话就是这个代码,我们来简单从上往下过一遍对吧?首先的话是获取到我们左侧表格的一个数据啊,左侧 table 表格的一个数据,然后呢去进行一个便利的一个操作,便利完以后的话,这里呢去计算出来我们的一个总金额对吧?然后呢呢?拿到我们的一个优惠金额对吧? 拿到优惠金额以后呢,很拿我们的总金额去减去我们的优惠金额对吧?那是得看到的话是没有任何问题的。好嘞,往下走对吧。如果你的这个优惠金额小于零,那么我们会让他变成零。如果你的这个长度就是你的这里边的这个表格的长度等于 零的吧,说明你的表格没有数据对吧?那没有数据的话,我会让所有的数据呢,都变成零。这里边他是总金额啊,总金额优惠金额不会实付金额,我会让他变成零啊。好,关键的就是在这里啊,同学们, 当我们的一个总金额,这应该是我们的折扣金额,当我们的折扣金额大于我们的总金额,那么我们会让这个 discount amount 就是我们的优惠金额变成零。 那大家看到我再给大家演示一下啊,抖友们看好了啊,我去点击这个名气啊,然后的话我去选数据对吧? 然后这里的话我输入两千啊,朋友们看好啊,我点确定。同学们可以看到这里边是不是弹出二了对不对?弹出二的话,说明他进入到哪里了?说明的话是不是他进入到了这里啊,对不对? 进到这里的话,那我们做什么事情让他变成零?那你一让这个折扣进格变成零对吧?让你的折扣进格变成零的话。好, ok。 那这里的话我们还需要做什么操作呢?朋友们 还学为什么操作,你这里确实变成零了对吧?但是你的总金额跟你的实付金额是不一致的对吧?那其实解决这个问题是非常简单啊。抖友们啊, 解决这个问题其实非常简单,看好我们怎么做啊?来,切,错了。当我们进入到这里的话,我们要做什么事情呢?当我们进入到这里的话,那我们这里只需要把它就打开就可以了啊。 让我们的实付金额等于我们的总金额啊。让我们的实付金额等于总金额,其实就可以去解决这个问题了。因为你想一下你预期的效果是有什么效果啊,当他变成零以后的话,他们二者是要保 所以说的话。那么当这个条件出发以后的话,我们只需要让我们的总金额等于我们的实付金额,就可以很好的解决这个问题了。抖友们啊,好, ok, 那这就是我们我们先编一下看能不能解决这个问题啊,来 刷新一下啊,点击明细啊,好看。好啊,老铁们啊,点加号,点加号对吧?好。然后我们来输入一个两万对吧?好,点确定。 ok! 大家可以看到的话,这里边是不是二七六,二七六,是不是他们两个就保持一致来好, ok, 那这个版本我们就分享到这里啊。


例外例外,都是 through able 类,直接的,此类直接击着 through able 的 对象才能被 through 和 cash。 核心区别是, exception 代表程序运行过程中可以预料可以恢复的异常情况,属于 有逻辑范畴,应该也能被合理地处理。常见的 non porter exception, io exception, 当 e r o r 表示 g p m。 本身的系统环境的严重错误,程序通常无法恢复,出现后往往意味着进程需要终止或重启。常见的 auto over normal error, static overflow error 等。统一来说, exception 表示可以被处理的程序异常, error 表示系统级的不可 恢复的操作。关于 exception 又分为 check 欺的 exception 授权异常和 uncheck uncheck 欺的 exception 非授权异常。 check 欺的 exception 继承 exception, 但不继承 right。 run time exception 在 翻译时必须显示处理。使用 try catch 块可以通过 through 声明抛出,如 i o exception, cir cir circle exception, an x an check 替代 exception 继承了 run time exception, 不 需要显示,不过运行时才抛出。 non pointer exception, 呃 index output, border exception 等等。异常的是 through able。 through able 是 所有异常的复联。一、若是系统及错误, out of memory, out of memory 与 i 与 error 和 static overflow error 是 jvm 崩溃,资源耗尽,不可恢复。 exception 程序即强 制处理。 check t 的 exception 受检异常 i o exception 和 circle exception。 原 time exception, 非受检异常原 now point exception 和 in text out o out o out o borders。 逻辑错误, i o 问题可处理。 check catch 污染的时候是异常。处理是需要注意的六个点是,异常处理尽量不要补过。类似于 exception 这样通常通用异常,而应该补过 特定的异常。程序工程师一门写作的艺术,在日常的开发中,我们有义务使自己的代码更能直观清晰地表达出我们想要表达的信息。但是如果你有什么异常都用了 exception, 那 别的开发同事就不能一眼得知这段代码实际上或不过异常,并且这样的代码也会补过到,可能 你希望它抛出而不希望抛。捕获异常,不要吞了异常。如果我们捕获了异常,不把异常抛出,如果没有写到日期里,那么出会出现什么情况? 线上除了 bug, 莫名其莫名其妙的没有任何的信息,你都不知道哪里出错误以及出错误的原因。这里可能会让一个 bug 的 变得难以针对整顿。而且有些同学比较喜欢 catch, 之后阴一点。 print stack trace。 在我们产品中通常不推荐用这种方法。一情况下这样是什么是没有问题的,但是这个方法突出的是标 标准错误流。比如在分布式系统中发生异常,但是找不到 jack trash trash, 所以 最好是 输入到日记里。我们产产品可以制定一个编辑的格式,将把详细的信息传输到日记系统中,适合清晰高效的排查错误,不要延迟处理异常。比如你有一有个方法,参数是 name 函数内部掉掉了别的好几个方法,其实你的 name 传的闹直,但是你没有在进入这个方法,或者这个方法一开始就处理。 注意这个情况,而是在你掉了别的几好几个方法,然后爆出这个控制人,这样的话,明明你的出错的对战信息,只需要帮助一点点信息就能够定位到这个错误所在的地方,经过了好多地方之后,可能就是一坨 对战信息,只在需要加载开始的地方加载开始,加载开始的范围能小则小,只要必,只要必要的代码段使用加载开始不要不分轻重的往哪加载住一坨代码,因为加载开始中的代码会影响 jvm 对 代码的优化,比如说文件不要通过异常来控制 程序流程,一些可以使用 a, f, l, s 的 条件语句判断到就不要用异常,异常肯定是在比一些条件语句低效的,也就 t, p, u 分 支预测的优化等。而且每十的一个三部分就会对这样进行 快照,相对而言,这是一个比较重的操作,如果数量过多,开销就会对这样进行快照,相对而言,这是一个比较重的操作,如果数量过多,开销就会在翻到中被确认 或者处理返回值会让发生很诡异的事情,比如 try 覆盖了 try 中的 return, 或者屏蔽了异常。

我亲眼见过一个转账接口,代码写得挺规范, at transactional 也加了,结果上线第一周, a 账户扣了钱, b 账户没收到,十万块钱就这么没了。 查了三天三夜,最后发现事务压根儿没生效,你猜问题出在哪儿?一个 try catch 把异常吞了。 这种 bug 恶心的地方在于测试环境跑得好好的,一上线就出事。今天聊聊 at transactional 失效的七种情况。你的项目里八成有。第一个方法不是 public, spring 事务靠 a o p 代理实现,代理只认 public 方法。你把注解加在 private 的 方法上, spring 看都不看一眼。 protected 的 也一样,别问我怎么知道的。第二个,同一个类理方法互相调用, 这个坑我踩过, a 方法挑 b 方法, b 方法上有 action 呢,你觉得有事务没有,因为内部调用走的是 this, 不是 代理对象, 注解等于白加。想解决的话,要么把 b 方法挪到别的类,要么注入自己用代理调,后者写出来挺丑的,一般不这么干。第三个,异常被吃掉了, 这是我见过最多的坑。 spring 默认只对 run time exception 回滚,你抛个普通 exception 他 不管。更要命的是,很多人习惯 try catch 打个日制就完事了,事务以为执行成功直接提交,开头说的那十万块钱就是这么没的。 转账逻辑里开始了异常,日制打了钱没了。解决办法要么别吞异常,要么 catch 里手动调 setback 内括号, 要么注解上加 rowback four 等于 exception class。 第四个,传播机制搞错了, at transactional 有 个 propagation 参数,默认 required, 有 事务就加入,没有就新建,这个没毛病,但有人改成 requires new, 以为新事物更安全, 结果外层回滚了,内层早提交了,数据就不一致了,还有人又 not supported, 直接不要事务。你确定知道自己在干啥?不懂传播机制就别动,这个参数默认的够用。第五个,数据库引擎不支持现在少见了,但老项目可能有 myiscam, 不 支持事务,你注解加的再对也白搭。迁移老项目的时候检查一下表引擎,别被坑了。第六个,多数据源没指定事务管理器, 项目里配了多个数据源。 at transactional 默认只管主库,你操作从库事务是不管的。微服务架构里这个问题特别常见,要在注解里指定 transaction manager。 第七个类没被 spring 管理,没加 at service or at component, 或者自己扭出来的对象。 spring 不 认识他, 哪来的代理哪来的事务?还有就是 final 类和 final 方法, c g l i b 代理不了,自己对照检查一下,方法是 public 的 吗? 有没有内部调用异常吞了没?传播机制改了没?表引擎对不对?多数据源指定了没类,被 spring 管着没七条过一遍能排除大部分事物问题。面试被问到这个怎么答? 先说七种情况,然后重点说内部调用和异常被吞,因为这俩最常见,而且不报错,出了事才知道。 面试官要是追问怎么排查,就说开 d e b u g 日制 logging dot level, dot org, dot spring framework, dot transaction equals debug, 能看到事物什么时候开,什么时候提交,一眼就知道生没生效。事物这东西看着简单,一个注解的事,但真出了问题,轻的数据错乱,重的资金损失, 半夜被叫起来查问题的滋味不好受。这就是 at transactional 的 七个坑,觉得有用就点个赞。我是程序员 mike, 专门聊那些文档里不写出事才知道的坑。下期见。