2024年8月

自从上个月写了失业连载之后,一个月都挺忙的,前几天有小伙伴催更,赶紧再续上一篇。

裁员后一两周,在家基本都是睡到自然醒,悠闲的做个午饭或者晚饭。计划是有了赔偿金之后休息两个月再找工作。开始在深圳周边玩,去了周边的塘朗山、盐田港海边和福田博览园。国庆之后就去了香港玩,2019年去过一次香港,后面疫情就一直没去过了,这次去发现香港街头的人少了很多,商场、购物的店也相对之前冷清了很多。最后去重庆完了一周后,就开始准备找工作了。

国庆回家,拍家里的猫哥

准备找工作

本来计划玩两三个月再找工作,但是看着没有收入,银行卡的余额又一点点减少,特别是当时是9月份,天天吹空调,电费的支出又更多了。当时感觉玩的也差不多了,就开始边玩边看看工作机会。

之前工作一直没修改简历,就更新了一下简历,主要增加了工作经历和项目经验。开始投递简历。我基本是在 boss 上投简历,boss 会根据你职位会给你对应职位推荐,比如我是 Java 开发,我看到都是 Java 开发的岗位。给我推荐的基本都是 Java 开发的岗位。

招聘已读不回

开始投简历的时候,会根据岗位的大体要求和自身和岗位的匹配程序来投递简历。开始的时候基本是没人回复,那个时候还没有意识到就业形势的严峻性。

开始的时候,基本是没人回,偶尔有几个会的都是外包。发完几个简历之后,基本都没有下文了。如果有外包之后,基本都是问一些信息,然后也没有通知。

咖啡店的失业者

在家白天外面比较吵,一般都会去附近的星巴克或者麦当劳点一杯咖啡修改简历,去了才发现平时工作日人也特别多,有一部分其实也是在上班,只是工作地点比较自由。也有一部分漫无目的的待着,从他们迷茫的眼神就看的出失业已经有一段时间了,这种人群比之前也多了很多。

有点希望的曙光

在投递了几天之后,我终于收到了一面试邀请。

第一次面试

该公司是一家跨境物流查询的企业。有点类似于快递100的服务平台,提供快递信息的查询服务。

为了了解该公司产品,当时还特意去注册了他们的公司的账号,体验一下服务员,主要就是api接口调用,然后通过回调地址更新物流信息。下午就接到他们公司销售的电话,我当时还以为是人事,还说“不是下周一才去面试吗?”对方才反应过来,说了一句原来是“自己人”。当时想想还挺搞笑的。

面试是
一轮笔试+一轮面试
模式,如果面试过了,还有第二轮面试。

笔试有几个简单问题:

  • session

    cookie
    异同的关系
  • 抽象类和接口的区别
  • String

    StringBuffer
    的区别
  • Mysql
    索引的作用,优缺点
  • 查询表某个字段重复的值和次数
  • 常见的
    Linux
    命令
  • 单例模式和冒泡排序

笔试题目都是比较基础的题目,我之前的文章都有相关的记录和总结。

笔试结束在等面试官

面试首先要自我介绍,其实自我介绍就是介绍
上一份工作经历和项目经历
,当时主要讲了自己负责模块。讲的比较简单,比如做了xx模块、和xx模块.问了一些技术原理之后。就开始问三联问:

  • 你在项目中遇到最大的问题是什么?
  • 如何解决这个问题,获得的收获是什么?
  • 你在你上家公司哪方面提升的最多?

回答的比较简单,就感觉面试脸色都不好看。估计是项目介绍的太简单了,他就想用这种简单快速的方式结束面试。

上面这几个比较

的问题,是面试简单筛人的问题,通过这几个问题,他可以考察你的问题解决能力、总结能力、思考能力。一般都要及时的总结工作中的一些问题,解决问题之后,及时的记录和总结。如果工作中没有相关的经历,也要多去准备类似的经历,不然很难给面试官好印象。

面试完了之后,我知道面试挂了。过了三天之后,对方公司的人事也通知我面试没有过。

总结失败原因

这次面试一轮游之后,主要有两个问题:

  • 简历写的简单
  • 面试准备的不够

简历问题

面试完了之后,有个之前公司的人事问我面试的如何,还帮我分析了一下简历。主要问题就是项目经历写的简单,没有突出一个整体。我上家公司主要是做电商服务,我负责商品、订单模块。有点类似于流水账,比如一下订单有下单、支付、出库等功能。

没有项目亮点
,只是简单的模块叠加,在众多的求职者中无法脱颖而出。比如项目重构的经验,系统的从第一代升级到第二代,单体服务升级到微服务,服务升级遇到哪些问题,如何解决这些问题,这样面试成功的概率就会高一点。

还有面试公司人事会根据公司业务,来匹配相同公司业务的面试者,所以除了业务代码之外,还需要简单介绍公司的业务。

面试问题

面试不单单要准备项目相关经历、八股文,还要准备上面的几个

问题的解答。从这个几个方面,面试官看出你的分析、总结和表达能力,这样才能在众多面试者中更容易的脱颖而出。

90% 外包回复

开始投递简历 90% 都是来自外包的回复,而且话术都是统计的:

  • 身份证、学位证等可以查询的信息
  • 是否接受加班
  • 工作地点是xx是否接收

然后就没有下文了!

总结

找了一个月之后,才发现原来工作这么难找。前两年面试大厂或者中厂还有能有面试机会,虽然面试没过,但是起码还有面试机会

现在是大厂、中厂都是消息已读不回,偶尔有一个小公司回消息,其他都是外包问了很多问题,就没有下文了。小公司面试也是各种挑剔

今天给大家推荐一个安全测试相关的开源项目:
nccgroup/house

1、介绍

它是一个由 NCC Group 开发的,一个基于Frida和Python编写的动态运行时移动应用分析工具包,提供了基于 Frida 的 Web GUI 界面,旨在简化动态函数挂钩的过程,让研究人员能够更轻松地评估 Android 应用的安全性。由于集成了Frida,提供了代码注入功能,允许实时修改运行中的应用行为。

项目地址:

https://github.com/nccgroup/house

2、主要特点

  • 直观易用:通过Web界面,用户无需深入了解Frida语法即可启动复杂的动态分析。
  • 灵活性:支持对动态加载的Dex/Jar文件进行挂钩,适应性强。
  • 扩展性:提供了一系列内建脚本和模板,用户可以根据需要定制自己的分析任务。
  • 调试友好:内置的REPL(Read-Evaluate-Print Loop)环境,便于用户探索和调试函数行为。

3、应用场景

  • 移动应用渗透测试:检测潜在漏洞,如隐私泄露、不安全的网络通信等。
  • 应用程序行为分析:监控文件操作、数据共享和其他敏感活动。
  • 教育与研究:帮助学生和研究人员更好地理解移动应用的工作原理。

4、项目安装

克隆House仓库:首先,你需要从GitHub上克隆House项目到你的本地机器,并安装相关依赖。

git clone https://github.com/nccgroup/house
cd house
pip3 install -r requirements.txt
pip3 install pipenv
pipenv --python=/usr/bin/python3 install
pipenv --python=/usr/bin/python3 shell
python3 app.py <PORT>

# or:
mkvirtualenv --python=/usr/local/bin/python3 house
workon house
pip install -r requirements.txt
python app.py <PORT>

# or: (only for Mac OS)
git clone https://github.com/nccgroup/house
cd house
pip3 install -r requirements.txt
pip3 install pipenv
pipenv --python=/usr/local/bin/python3 install
pipenv --python=/usr/local/bin/python3 shell
python3 app.py <PORT>

5、使用方法

1、启动 House:启动 House 的后端服务,可以通过命令行执行
python main.py
来启动 House。启动后,可以通过浏览器访问指定的地址和端口来使用 Web GUI 界面,通常是http://localhost:8000。

2、选择目标应用程序:在 Web GUI 界面上选择需要分析的目标应用程序,可以上传应用程序的安装包或者指定应用程序的包名。(也可以用项目自带的test_apk来尝鲜)

3、选择分析模块:根据分析需求选择需要的分析模块,可以选择内置的模块或者自定义的模块。

4、执行分析任务:点击开始分析按钮,House 将会启动 Frida 进行动态分析,并在 Web GUI 上实时显示分析结果。

5、查看分析结果:在分析完成后,可以在 Web GUI 上查看分析结果,包括应用程序的运行时行为、API 调用情况等。

6、小结

综上所述,
nccgroup/house
是一个功能强大且灵活的移动应用动态分析工具,适用于各种场景,包括渗透测试、行为分析和学术研究。通过 Frida 和 Python 的结合,它提供了一个高效且用户友好的分析平台。安装和配置简便,且具有丰富的特性和扩展性,使得研究人员和开发人员能够有效地探索和评估移动应用的安全性。

前言

最近经常有小伙伴问我,遇到了线上问题要如何快速排查。

这非常考验工作经验了。

有些问题你以前遇到,如果再遇到类似的问题,就能很快排查出导致问题的原因。

但如果某个问题你是第一次遇到,心中可能会有点无从下手的感觉。

这篇文章总结了,我之前遇到过的一些线上问题排查思路,希望对你会有所帮助。

1 OOM问题

OOM问题在生产环境中,一旦出现,一般会是非常严重的问题,服务可能会挂掉。

但是OOM问题有多种情况,不同的情况,出现问题的原因不一样。

1.1 堆内存OOM

服务器的日志一般会打印下面的内容:

java.lang.OutOfMemoryError: Java heap space

这种是出现最多的OOM问题。

在Java服务启动时,可以增加下面的参数:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof

在发生OOM时,程序会自动把当时的内存使用情况,dump保存到指定的文件。

然后使用MAT(Memory Analyzer Tool),或者使用JDK自带的 Java visualvm,来分析dump 文件,找出导致OOM 的代码 。

1.2 栈内存OOM

出现栈内存OOM问题的异常信息如下:

java.lang.OutOfMemoryError: unable to create new native thread

如果实际工作中,出现这个问题,一般是由于创建的线程太多,或者设置的单个线程占用内存空间太大导致的。

这个时候需要排查服务的线程数量。

推荐使用线程池,可以减少线程的创建,有效控制服务中的线程数量。

1.3 栈内存溢出

出现栈内存溢出问题的异常信息如下:

java.lang.StackOverflowError

该问题一般是由于业务代码中写的一些递归调用,递归的深度超过了JVM允许的最大深度,可能会出现栈内存溢出问题。

如果生产环境中,出现了这个问题,可以排查一下递归调用是否正常,有可能出现了无限递归的情况。

1.4 GC OOM

出现GC OOM问题时异常信息如下:

java.lang.OutOfMemoryError: GC overhead limit exceeded

GC OOM一般是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略。

在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。

1.5 元空间OOM

出现元空间OOM问题时异常信息如下:

java.lang.OutOfMemoryError: Metaspace

JDK8之后使用
Metaspace
来代替
永久代
,Metaspace是方法区在HotSpot中的实现。

这个问题一般是由于加载到内存中的类太多,或者类的体积太大导致的。

如果生产环境中出现了这个问题,可以通过下面的命令修改元空间大小:

-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

我在这里列举了OOM问题的最常见的情况,大家如果想了解更多,可以看一下我之前写的一篇文章《
工作中最常见的6种OOM问题
》,里面有更详细的介绍。

2 CPU100%问题

线上服务出现CPU100%问题,也很常见。

出现这个问题,是由于服务长时间占用CPU资源导致的。

主要原因有下面这几种:

定位这个问题,可以使用JDK自带的
jstack
工具,或者用阿里开源的
Arthas
探测工具。

如果对CPU100%问题比较感兴趣,可以看看我的另一篇文章《
糟糕,CPU100%了!!!
》,里面有更详细的介绍。

3 接口超时问题

不知道你有没有遇到过这样的场景:我们提供的某个API接口,响应时间原本一直都很快,但在某个不经意的时间点,突然出现了接口超时。

导致接口超时的原因有很多,我们需要挨个逐一排查。

下面这张图中给大家列举出现了,生产环境接口突然出现超时问题时的常见原因:

如果大家想进一步了解接口超时问题,可以看看我的另一篇文章《
接口突然超时10宗罪。。。

4 索引失效问题

不知道你有没有遇到过,生成环境明明创建了索引,但数据库在执行SQL的过程中,索引竟然失效了。

由于索引失效,让之前原本很快的操作,一下子变得很慢,影响了接口的性能。

我们可以通过
explain
关键字,查看sql的执行计划,可以确认索引是否失效。

如果索引失效了,可能是哪些原因导致的问题呢?

下面这张图给大家列举了常见原因:

想进一步了解索引失效问题的小伙伴,可以看一下我的另一篇文章《
聊聊索引失效的10种场景,太坑了
》,里面有非常详细的介绍。

5 死锁问题

如果你使用的是MySQL数据库,在生产环境肯定遇到死锁问题。

死锁
是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,这些事务将无法继续向前推进。

在Java中,使用MySQL数据库时,如果遇到MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction异常,意味着数据库检测到了死锁。

MySQL死锁通常由以下原因造成:

  • 资源竞争:多个事务同时竞争相同的资源,比如都试图获取对方持有的锁。
  • 循环等待:事务之间形成了一种互相等待对方释放资源的循环关系。
  • 不当的事务设计:事务执行顺序不合理、执行时间过长等。
  • 并发操作冲突:在高并发环境下,多个事务对同一组数据进行操作,容易引发锁冲突导致死锁。
  • 索引使用不当:如果索引设计不合理,可能导致事务在获取锁时出现问题。

如何减少死锁问题?

  1. 设置合理的事务隔离级别。
  2. 避免大事务的业务代码。
  3. 优化sql性能。
  4. 增加锁等待超时处理。
  5. 增加监控和分析

6 磁盘问题

服务器磁盘问题是众多线上问题中,最好排查的了。

磁盘问题一般有两种:

  1. 磁盘坏了
  2. 磁盘空间不足

如果是磁盘坏了,运维一般在短时间内,很难及时修复好。

因此,需要及时更换磁盘。

如果是磁盘空间不足。

一般需要登录到那台服务器,
使用命令:

df -Hl

查看当前服务器的磁盘使用情况。

  • 总大小
  • 已使用多少
  • 可用多少

最快的解决办法是,将/tmp文件夹中的文件删除,可以释放一些磁盘空间。

然后找到日志文件,删除7天以前的日志。

这两种方式,一般会释放不少磁盘空间,暂时解决磁盘空间不足的问题。

从常用来看,我们需要对服务器的磁盘使用情况做监控,如果超过阀值有预警。

同时需要需要规范业务系统,哪些场景需要打印日志,哪些场景不需要,不应该所有的场景,都打印日志。

特别是有些业务查询接口调用非常频繁,一次性返回的数据很多,这种情况下,会导致服务器上的日志迅速膨胀,占用过多的磁盘空间。

7 MQ消息积压问题

如果你使用过MQ消息中间件,在生产环境肯定遇到过MQ消息积压问题。

出现这个问题,一般是MQ消费者消费消息的速度,比MQ生产者生产消息的速度慢。

如果之前一直都是好好的,突然有一天出现了MQ消息积压问题。

可能是下面的原因导致的:

  1. MQ生产者批量发送消息。
  2. 随着数据越来越多,MQ消费者的在处理业务逻辑时,mysql索引失效或者选错索引,导致处理消息的速度变慢。

如果生产环境出现MQ消息积压问题,先确认MQ生产者有没有批量发送消息。

如果有,则可以把MQ消费者中线程池的核心线程数和最大线程数调大一些,让更多的线程去处理业务逻辑,提升消费能力。

这套方案的前提是MQ消费者中,已经使用了线程池消费消息。

如果没有使用线程池,则只能临时增加服务器节点了。

如果MQ生产者没有批量发送消息,则需要排查MQ消费者的业务逻辑中,哪些地方出现了性能问题,需要做代码优化。

优化的方向是:

  1. 优化索引
  2. 优化sql语句
  3. 异步处理
  4. 批量处理

等等,还有其他的。

如果大家对性能优化的技巧比较感兴趣的小伙伴,可以看看我的另一篇文章《
我用这11招,让接口性能提升了100倍
》,里面有非常详细的介绍。

8 调用接口报错

我们生产环境的程序,有时候会出现,之前调用某个API一直都是正常的,但突然出现报错的情况,即返回码不是200。

那么,这种问题,我们该如何排查呢?

8.1 返回401

一般生产环境出现这个问题,是由于没有通过接口的登录认证。

出现这种情况,一般用户在尝试访问受保护的资源前,需要通过某种形式的身份验证(如登录),但如果未能正确提供必要的认证信息,如Token、用户名和密码等。

就会出现返回码是401的情况。

8.2 返回403

如果生产环境请求某个接口,返回码是403,则说明目前没有访问资源的权限。

这种场景跟返回码是401有区别。

401着重于认证问题,即用户没有提供正确的身份验证信息。

而403则是在认证成功的基础上,用户没有足够的权限去访问请求的资源。

要解决这个问题,我们需要给接口的调用方,分配相应的访问权限。

8.3 返回404

不用怀疑,你请求的接口地址,现在已经不存在了,才会报404。

比如有些接口名称改了,或者接口路径中/v1/user/query改成了/v2/user/query,版本号升级了。

如果没有通知所有的接口调用方,都可能会出现请求接口返回码为404的情况。

还有一种可能也会导致请求接口报404的问题,接口地址之前注册到了API网关中,但API网关的配置出现了问题。

优先排查接口url是否修改,然后排查网关或者Nginx配置是否有问题。

8.4 返回405

如果请求的接口,返回码为405,一般是请求方式错误导致的。

最常见的是:接口只支持post方式,但发送的却是get请求。

或者接口只支持get方式,但发送的却是post请求。

这种问题一般非常好排查和解决。

8.5 返回500

如果请求的接口,返回码为500,一般是出现了服务的内部错误。

一般网关层会对接口的返回值做一次封装,不会返回真正的异常信息。

我们只能查看接口的错误日志,来定位和排查问题。

建议出现异常时,把接口请求参数打印出来,方便后面复现问题。

导致这种问题的原因有很多,我们只能根据服务器上的错误日志,和相关的业务代码逐一排查。

8.6 返回502

如果请求的接口,返回码为502,一般是出现了服务不可用的情况。

有两种情况:

  1. 服务器正在重启中。
  2. 服务挂掉了。

这时候可以查看一下服务的监控,也可以登录到服务器上查看的运行状态。

大部分情况下,重启一下服务,可以快速解决问题。

然后再根据服务器上的日志,可以定位具体的原因,比如:OOM问题导致的。

8.7 返回504

如果请求的接口,返回码为504,一般由于网关或者接口超时导致的。

接口返回数据的耗时,大于网关设置的超时时间,就会出现这个问题。

出现这种情况,一般需要优化接口相关的代码。

如果大家对接口优化,比较感兴趣可以看看我的这篇文章,《
我用这11招,让接口性能提升了100倍
》,里面有非常详细的介绍。

如果你对日常工作中的一些坑,比较感兴趣,可以看看我的技术专栏《
程序员最常见的100个问题
》,里面有很多干货,还是非常值得一看的。

最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。

最近有很多优秀的语音合成TTS工具,目前MoneyPrinterPlus已经集成了ChatTTS和fasterWhisper。应朋友们的要求,最近MoneyPrinterPlus也集成了GPT_SoVITS这个优秀的语音合成工具。

今天给大家详细讲解一下,如何在MoneyPrinterPlus中使用GPT_SoVITS。

软件准备

当然,前提条件就是你需要下载MoneyPrinterPlus软件啦。

下载地址:
https://github.com/ddean2009/MoneyPrinterPlus

用得好的朋友,不妨给个star支持一下。 在软件v4.4版本之后,MoneyPrinterPlus已经支持GPT_SoVITS本地TTS语音服务啦。

启动GPT_SoVITS

GPT_SoVITS这个工具怎么安装这里就不多讲了。

我们讲下如何跟MoneyPrinterPlus进行合作配置。

GPT_SoVITS有很多功能,包括语音训练,模型微调,TTS语音推理,变声等功能。

这里我们使用的是GPT_SoVITS的核心TTS语音推理功能。

首先我们启动GPT_SoVITS:

在1-GPT-SoVITS-TTS ---》 1C推理 ---》 开启TTS推理webUI

image-20240814101451460

然后你就可以看到这样的TTS推理界面:

image-20240814103843764

你可以选择参考音频,参考文本,参考音频的语言。

然后可以输入要合成的文本,合成语音的类别,语速,top_k, top_p和temperature。

当然,上面的都不重要,我们不需要通过webUI来调用GPT_SoVITS,我们需要的是通过API来和GPT_SoVITS进行交互。

API启动GPT_SoVITS

如果下载的是GPT_SoVITS的集合包,那么可以直接执行下面的命令来启动GPT_SoVITS的API:

启动api: runtime\python.exe api.py 

启动之后,你会看到下面的内容:

image-20240814002404640

上面的启动是最简单的启动,没有指定参考音频,如果你想指定参考音频的话,可以执行下面的命令:

python api.py -dr "123.wav" -dt "一二三。" -dl "zh" 

当然,还有其他的一些启动参数如下:

`-dr` - `默认参考音频路径`
`-dt` - `默认参考音频文本`
`-dl` - `默认参考音频语种, "中文","英文","日文","韩文","粤语,"zh","en","ja","ko","yue"`

`-d` - `推理设备, "cuda","cpu"`
`-a` - `绑定地址, 默认"127.0.0.1"`
`-p` - `绑定端口, 默认9880, 可在 config.py 中指定`
`-fp` - `覆盖 config.py 使用全精度`
`-hp` - `覆盖 config.py 使用半精度`
`-sm` - `流式返回模式, 默认不启用, "close","c", "normal","n", "keepalive","k"`
·-mt` - `返回的音频编码格式, 流式默认ogg, 非流式默认wav, "wav", "ogg", "aac"`
·-cp` - `文本切分符号设定, 默认为空, 以",.,。"字符串的方式传入`

`-hb` - `cnhubert路径`
`-b` - `bert路径`

大家可以根据需要自行选择。

默认情况下API会启动在9880端口,我们可以使用下面的命令来测试API的启动效果:

使用执行参数指定的参考音频:
GET:
    `http://127.0.0.1:9880?text=先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。&text_language=zh`
POST:
```json
{
    "text": "先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。",
    "text_language": "zh"
}
```

配置MoneyPrinterPlus

好了,回到我们的MoneyPrinterPlus页面。

在基本配置页面,本地语音TTS,我们选择GPTSoVITS, 然后输入GPTSoVITS的地址。

image-20240814105434708

在AI视频或者视频混剪区,在视频TTS语音合成区,我们选择本地服务。

image-20240814002245394

这里列出来GPTsoVITS所需要的大部分参数。

你可以使用参考音频,参考音频文本,参考音频语音。

然后可以条件temperature,top_P, top_K等信息。

image-20240814002312805

点击试听声音,如果你听到合成的声音,那么恭喜你,你的配置成功了。

同时,在GPTsoVITS服务的日志文件中,你可以看到一些语音合成的进度。

image-20240814001906393

总结

GPTsoVITS是一个非常强大的服务,和MoneyPrinterPlus结合起来使用,你将会无往不利。
点我查看更多精彩内容:www.flydean.com

JDK7新特性:Try- with-resources

try-with-resources 是 JDK 7中引入的一种新的异常处理机制,它主要用于自动管理资源,能够很容易地关闭在 try-catch 语句块中使用的资源。确保资源在不再需要时能够被正确关闭。这种机制简化了资源管理,使得资源的释放更加安全和可预测。

resource
:是指在程序完成后,必须关闭的对象(例如:文件资源File、IO流、Socket、ServerSocket网络对象、数据库链接对象等)。try-with-resources 语句确保了每个资源在语句结束时关闭。

普通开关资源方法

class Myresources1 implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("资源1关闭方法执行");
        throw new Exception("资源1关闭异常");
    }
}
class Myresources2 implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("资源2关闭方法执行");
        throw new Exception("资源2关闭异常");
    }
}

为了避免在代码执行中出现异常,使用
try-catch-finally
进行异常捕获

        Myresources1 myresources1 =null;
        Myresources2 myresources2 = null;
        try{
            myresources1 = new Myresources1();
            myresources2 = new Myresources2();
            System.out.println("hello");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (myresources1!=null){
                try {
                    myresources1.close();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if (myresources2!=null){
                        try {
                            myresources2.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

image.png

Try- with-resources控制资源语法:

    try(Myresources1 myresources1 = new Myresources1();
        Myresources2 myresources2 = new Myresources2();){
           System.out.println("hello");
        //int a = 2/0;
       }catch (Exception e){
           e.printStackTrace();
       }

try-with-resource中声明的变量会隐式的加上final 关键字,所以无法再进行赋值image.png

处理规则

  • 凡是实现了AutoCloseable或者Closeable接口的类,在try()里声明该类实例的时候,在try结束后,close方法都会被调用。不管是否出现异常(int i=1/0会抛出异常),try()里的实例都会被调用close方法

image.png
![imag

e.png](
https://cdn.nlark.com/yuque/0/2024/png/42425790/1722909280844-945838ee-91a2-4a06-9399-647b744396f1.png#averageHue=%231f2125&clientId=ua8faed9b-5ab9-4&from=paste&height=259&id=TKIhf&originHeight=259&originWidth=735&originalType=binary&ratio=1&rotation=0&showTitle=false&size=37054&status=done&style=none&taskId=u35bf969c-e938-4d98-ab98-4df1f077383&title=&width=735
)

image.png

  • 越晚声明的对象,会越早被close掉。
  • try结束后自动调用的close方法,这个动作会早于finally里调用的方法。

image.png
image.png

异常抑制问题

Myresources1 myresources1 = null;
        try{
            myresources1 = new Myresources1();
            //算数异常
            System.out.println(10/0);
        }finally {
            if (myresources1!=null)
                myresources1.close();
        }

//myresources1类
class Myresources1 implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("资源1关闭方法执行");
        throw new Exception("资源1关闭异常");
    }
}

运行异常打印:

image.png
此时可以看到,异常只打印了
close()
方法的异常,而 10/0 的异常被抑制了

        try(Myresources1 myresources1 = new Myresources1();){
            System.out.println(10/0);
        }

运行异常打印:
image.png
<br 当一个异常被抛出的时候,可能有其他异常因为该异常而被抑制住,从而无法正常抛出。这时可以通过
addSuppressed()
方法把这些被抑制的方法记录下来,然后被抑制的异常就会出现在抛出的异常的堆栈信息中,可以通过
getSuppressed()
方法来获取这些异常。这样做的好处是不会丢失任何异常,方便我们进行调试。

反编译代码:

image.png

        Myresources1 myresources1 = new Myresources1();
        Throwable var2 = null;

        try {
            System.out.println(10 / 0);
        } catch (Throwable var11) {
            var2 = var11;
            throw var11;
        } finally {
            if (myresources1 != null) {
                //判断程序运行时是否出现异常
                if (var2 != null) {	//出现异常
                    try {
                        myresources1.close();
                    } catch (Throwable var10) {
                        var2.addSuppressed(var10);	//把close()发生的异常添加为抑制异常
                    }
                } else {	//程序运行时无异常
                    myresources1.close();
                }
            }

        }

循环打印抑制异常

 try(Myresources1 myresources1 = new Myresources1();){
​            System.out.println(10/0);
​        }catch (Exception e){
​            e.printStackTrace();
​            Throwable[] suppressed = e.getSuppressed();
​            for (Throwable t : suppressed){
​                t.printStackTrace();
​            }
​        }

运行结果:

image.png

使用场景

try-with-resources
语法适用于任何需要自动管理资源关闭,以防止资源泄漏的场景。

简单粗暴点就是,ctrl点进去看当前资源,只要当前资源实现了AutoCloseable或者Closeable接口就可以用。但是,具体使用还要根据要实现的业务场景来决定。

不适合的场景:

  1. 资源未实现AutoCloseable或Closeable接口:

如果资源没有实现
AutoCloseable

Closeable
接口,那么它就不能在
try-with-resources
语句中被自动关闭。这种情况下,仍然需要手动关闭资源,或者使用其他机制来确保资源的正确关闭。

  1. 资源需要在try块外部使用:

try-with-resources
语句中的资源在 try块执行完毕后会自动关闭,因此在 try块外部无法访问这些资源。如果资源需要在
try
块外部被使用,那么就不能使用
try-with-resources
语句来管理这些资源。

  1. 需要精细控制资源关闭时机:

在某些情况下,开发者可能需要根据特定的逻辑或条件来决定何时关闭资源,而不是在
try
块结束时立即关闭。
try-with-resources
语句无法提供这种精细的控制,因此在这些场景下可能不适用。

  1. 资源关闭可能抛出异常且需要特殊处理:

虽然
try-with-resources
语句会尝试关闭资源,并捕获在关闭过程中抛出的异常,但这些异常通常会被抑制(suppressed),而不是直接抛出。如果开发者需要特别处理这些关闭异常,或者需要将这些异常与
try
块中抛出的其他异常进行区分,那么
try-with-resources
可能不是最佳选择。

  1. 资源需要在多个try块中共享:

如果一个资源需要在多个
try
块中被共享和使用,那么使用
try-with-resources
语句可能会变得复杂或不可行。因为每个
try-with-resources
语句都会尝试在结束时关闭其声明的资源,这可能会导致资源在需要时已经被关闭。

  1. 资源关闭逻辑复杂:

如果资源的关闭逻辑非常复杂,或者需要在关闭前执行一些特定的操作(如回滚事务、释放锁等),那么使用
try-with-resources
语句可能不足以满足需求。在这些情况下可能需要编写更复杂的
finally
块来确保资源的正确关闭。综上所述,
try-with-resources
语句虽然是一种强大的资源管理机制,但它并不适用于所有场景。在选择是否使用
try-with-resources
时,开发者需要根据具体的需求和资源的特点来做出决策。

优点:

  1. 简化资源管理代码:无需显式地在
    finally
    块中编写资源关闭的代码,减少了代码量和出错的可能性。
  2. 提高代码可读性:使资源的管理更加清晰和直观,让开发者能够更专注于业务逻辑。
  3. 确保资源及时关闭:即使在
    try
    块中发生异常,资源也会被自动关闭,避免了资源泄漏的风险。
  4. 支持多个资源:可以同时管理多个资源,它们都会按照声明的反顺序被正确关闭。

缺点:

  1. 支持的资源类型有限:并非所有的类都可以作为
    try-with-resources
    的资源,只有实现了
    AutoCloseable

    Closeable
    接口的类才行。

  2. 性能考虑:

    虽然
    try-with-resources
    在大多数情况下对性能的影响可以忽略不计,但在极端情况下(例如:在性能敏感的应用程序中频繁地打开和关闭大量资源),它可能会引入一些额外的开销。这是因为
    try-with-resources
    语句在编译时会生成额外的代码来管理资源的关闭。

  3. 对在
    try()
    中声明的对象不能重新赋值

     FileWriter fw = new FileWriter("a.txt");
     fw = new FileWriter("e:/b.txt");
		try(FileWriter fw = new FileWriter("a.txt")){
            fw = new FileWriter("b.txt");	//报错
        }catch (Exception e){
            e.printStackTrace();
        }

//此时会编译出错:The resource is1 of a try-with-resources statement cannot be assigned

官方文档:
It is a compile-time error if final appears more than once as a modifier for each variable declared in a resource specification.
A variable declared in a resource specification is implicitly declared final (§4.12.4) if it is not explicitly declared final.
在资源规范中声明的每个变量,如果 final 作为修饰符出现不止一次,则属于编译时错误。
在资源规范中声明的变量,如果没有显式地声明为 final,则隐式地声明为 final(§4.12.4)。

try-with-resource中声明的变量会隐式的加上final 关键字,所以无法再进行赋值


总结:

try-with-resources
的优点通常大于缺点,在大多数情况下,它是一种更安全、简洁和可靠的资源管理方式。具体的使用方式根据业务场景决定,但是一般情况下,处理必须关闭的资源时,始终有限考虑使用 try-with-resources,而不是 try–catch-finally。前者产生的代码更加简洁、清晰,产生的异常信息也更靠谱。