最全Java基础学习之异常处理(1),彻底剖析JVM类加载机制系列

最后

为什么我不完全主张自学?
平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。

除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。

我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。

应该学哪些技术才能达到企业的要求?(下图总结)

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

// in the case when the exception translator hasn’t been initialized yet.

String sql = getSql(action);

JdbcUtils.closeStatement(stmt);

stmt = null;

DataSourceUtils.releaseConnection(con, getDataSource());

con = null;

// 完成受检查异常到运行时异常的转化

throw translateException(“StatementCallback”, sql, ex);

}

finally {

JdbcUtils.closeStatement(stmt);

DataSourceUtils.releaseConnection(con, getDataSource());

}

}

2. 自定义异常

不必局限于Java提供的异常类型。我们可以自定义异常类来表示程序中可能会遇到的特定问题。

要自定义异常类,必须从已有异常类继承,最好的方式是选择意思相近的异常类继承。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// 业务异常

class BizException extends RuntimeException{

public BizException() {

super();

}

public BizException(String message) {

super(message);

}

public BizException(String message, Throwable cause) {

super(message, cause);

}

public BizException(Throwable cause) {

super(cause);

}

}

异常一般是用名称代表发生的问题,并且异常的名称应该可以望文知意。

2.1. 异常继承体系

异常本身也是类,存在一个完整的继承体系。

2.2. Throwable

Throwable被用来表示任何可以作为异常被抛出的类。

Throwable对象可以分为俩种类型(从Throwable继承而来):

  • Error 用于表示编译时和系统错误,出特殊情况外,开发人员不必关系

  • Exception 表示可以被抛出的基础类型。在Java类库、用户方法以及运行时故障都可能抛出Exception异常。这是开发人员最关系的异常。

throwable主要是对异常栈进行维护,核心方法如下:

方法 | 含义

—|—

printStackTrace | 打印调用栈信息,输出到标准错误输出(System.error)

printStackTrace(PrintStream) | 指定Stream打印调用栈信息

printStackTrace(PrintWriter) | 指定Print打印调用栈信息

getStackTrace() | 获取调用栈序列信息

fillInStackTrace() | 记录栈帧的当前状态

异常栈记录了”把你带到异常抛出点”的方法调用序列,是问题排查的主要信息之一。

1

2

3

4

5

6

7

8

9

10

11

public static void main(String… arg){

try {

// 正常处理流程,正确执行过程做什么事

Path path = Paths.get(“var”, “error”);

List<String> lines = Files.readAllLines(path, Charset.defaultCharset());

System.out.println(lines);

} catch (IOException e) {

// 异常处理流程,出了问题怎么办

e.printStackTrace();

}

}

运行程序,获得结果,异常栈如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

java.nio.file.NoSuchFileException: var/error

at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)

at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)

at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)

at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)

at java.nio.file.Files.newByteChannel(Files.java:361)

at java.nio.file.Files.newByteChannel(Files.java:407)

at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384)

at java.nio.file.Files.newInputStream(Files.java:152)

at java.nio.file.Files.newBufferedReader(Files.java:2781)

at java.nio.file.Files.readAllLines(Files.java:3199)

at com.geekhalo.exception.Demo.main(Demo.java:15)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:483)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

2.3. Exception

Exception是与编程有关的所有异常类的基类。

| 方法 | 含义 |

| — | — |

| getMessage | 获取详细信息 |

| getLocaliedMessage | 获取本地语言表示的详细信息 |

2.4. RuntimeException

从RuntimeException派生出来的异常成为“不受检查异常”。这种异常会自动被Java虚拟机抛出,所以不必在方法的异常说明中列出来。

1

2

3

private void throwRuntimeException(){

throw new RuntimeException();

}

RuntimeException 及其子类 无需在方法中进行声明。

3. 常见异常处理策略

完成自定义异常后,下一个关键点便是如何处理异常。

3.1. 异常恢复

异常处理程序的目的就是处理所发生的异常。因此,第一个异常处理策略便是,处理异常,进行异常恢复。

1

2

3

4

5

6

7

8

9

10

11

12

13

private void recoveryException(String filePath){

try {

List<String> lines = readFromFile(filePath);

lines.forEach(System.out::println);

}catch (IOException e){

// 打印日志,从异常中恢复程序

LOGGER.error(“failed to read from file {}”, filePath, e);

}

}

private List<String> readFromFile(String filePath) throws IOException {

Path path = Paths.get(filePath);

return Files.readAllLines(path, Charset.defaultCharset());

}

3.2. 重新抛出异常

当你无法得到足够信息,从而对异常进行恢复时。可以把刚刚捕获的异常重新抛出。在catch子句中已经获得了对当前异常对象的引用,可以直接将其抛出。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

private void printFile(String filePath) throws IOException{

try {

List<String> lines = readFromFile(filePath);

lines.forEach(System.out::println);

}catch (IOException e){

// 重新抛出异常

throw e;

}

}

// 方法异常说明

private List<String> readFromFile(String filePath) throws IOException {

Path path = Paths.get(filePath);

return Files.readAllLines(path, Charset.defaultCharset());

}

重抛异常会把异常抛给上一级调用,同一try后的catch子句被忽略。如果只是把当前异常抛出,那么printStackTrace显示的是原来异常抛出点的调用链信息,而非重新抛出点的信息。如果想要更新调用信息,可以调用fillInStackTrace方法,返回另一个Throwable对象,它将当前调用栈信息填入原来的异常对象。

3.3. 异常链

如果想要在捕获一个异常后抛出另一个新异常,并希望把原始异常信息保留下来,这成为异常连。

Throwable的子类在构造器中都可以接受一个cause对象,用于表示原始异常,这样把原始异常传递给新异常,使得当前位置创建并抛出的新异常,通过异常链追踪到异常最初发生的位置。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

private void printFile2(String filePath){

try {

List<String> lines = readFromFile(filePath);

lines.forEach(System.out::println);

}catch (IOException e){

// 异常链

throw new BizException(e);

}

}

// 方法异常说明

private List<String> readFromFile(String filePath) throws IOException {

Path path = Paths.get(filePath);

return Files.readAllLines(path, Charset.defaultCharset());

}

Throwable子类中,只有Error、Exception、RuntimeException在构造函数中提供了cause参数,如果要把其他异常链起来,可以使用initCause方法。

4. 异常实战

异常是框架设计不可遗漏的点。

框架中的异常处理,同样遵循固定的操作流程:

  1. 根据需求自定义异常;

  2. 提供异常处理器,统一对异常进行处理;

4.1. Spring MVC 统一异常处理

Spring MVC 是最常见的 Web 框架,上手简单,开发迅速。

遵循正常流程与异常处理分离策略。研发人员只需关心正常逻辑,由框架对异常流程进行统一处理。那应该怎么操作呢?

4.1.1. 定义业务异常

首先,需要定义自己的业务异常。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public abstract class BusinessException extends RuntimeException{

/**

  • 异常处理码

*/

private final int code;

/**

  • 异常消息

*/

private final String msg;

private final String timestamp = String.valueOf(System.currentTimeMillis());

protected BusinessException(int code, String msg){

this.code = code;

this.msg = msg;

}

protected BusinessException(int code, String msg, Exception e) {

super(e);

this.code = code;

this.msg = msg;

}

}

4.1.2. 异常处理

可以使用 HandlerExceptionResolver 扩展,对异常进行定制。

RestHandlerExceptionResolver 对 Rest 请求的服务异常进行处理。将异常统一转化为 JSON 返回给用户。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

@Component

public class RestHandlerExceptionResolver implements HandlerExceptionResolver {

private static final Logger LOGGER = LoggerFactory.getLogger(RestHandlerExceptionResolver.class);

@Override

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

// 是 Rest 请求 并且能够处理

if(isRestRequest(handler) && isAcceptException(ex)){

// 将异常传化为 RestResVo 对象

RestResVo<Void> restResVo = RestResVo.error((BusinessException)ex);

try {

// 以 Json 格式进行写回

response.getWriter().println(JSON.toJSONString(restResVo));

}catch (Exception e){

LOGGER.error(“failed to write json {}”, restResVo, e);

}

// empty ModelAndView说明已经处理

return new ModelAndView();

}

return null;

}

private boolean isRestRequest(Object handler) {

if (handler instanceof HandlerMethod){

HandlerMethod handlerMethod = (HandlerMethod) handler;

return AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class) !=null ||

AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), ResponseBody.class) != null;

}

return false;

}

private boolean isAcceptException(Exception ex) {

return ex instanceof BusinessException;

}

}

PageHandlerExceptionResolver 对页面请求的异常进行处理。将异常统一转发到 error 视图。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

@Component

public class PageHandlerExceptionResolver implements HandlerExceptionResolver {

@Override

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

// 是页面请求并且能够处理当前异常

if(isPageRequest(handler) && isAcceptException(ex)){

// 返回 error 视图

ModelAndView mv = new ModelAndView(“error”);

mv.addObject(“error”, ex);

return mv;

}

return null;

}

private boolean isPageRequest(Object handler) {

if (handler instanceof HandlerMethod){

HandlerMethod handlerMethod = (HandlerMethod) handler;

return AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class) == null

&& AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), ResponseBody.class) == null;

}

return true;

}

private boolean isAcceptException(Exception ex) {

return ex instanceof BusinessException;

}

}

4.2. Spring Cloud 异常穿透

在使用 Spring Cloud 进行微服务时,如果 Server 端发生异常,客户端会收到一个 5xx 错误,从而中断当前正常请求逻辑。但,异常中所含有的业务信息也一并丢失了,如何最大限度的保持异常信息呢?

4.2.1. 定义业务异常

首先,仍旧是定义自己的业务异常类。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

@Data

public class CodeBasedException extends RuntimeException {

private Integer code;

private String msg;

private Object data;

public CodeBasedException(){

super();

}

public CodeBasedException(String msg) {

super(msg);

this.msg = msg;

}

public CodeBasedException(Integer code, String msg, Object data) {

this.code = code;

this.msg = msg;

this.data = data;

}

public CodeBasedException(String message, Integer code, String msg, Object data) {

super(message);

this.code = code;

this.msg = msg;

this.data = data;

}

}

4.2.2. Server 端处理

在 Server 端,捕获业务异常,并将信息通过 Header 进行写回。

HandlerInterceptorBasedExceptionBinder 在业务处理完成后,捕获 CodeBasedException 异常,并将异常信息通过 Response 对象回写到 Header 中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public class HandlerInterceptorBasedExceptionBinder implements HandlerInterceptor {

private static final Logger LOGGER = LoggerFactory.getLogger(HandlerInterceptorBasedExceptionBinder.class);

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

if (ex == null){

return;

}

if (ex instanceof CodeBasedException){

CodeBasedException codeBasedException = (CodeBasedException) ex;

response.addHeader(SoaConstants.HEADER_ERROR_CODE, String.valueOf(codeBasedException.getCode()));

response.addHeader(SoaConstants.HEADER_ERROR_MSG, encode(codeBasedException.getMsg()));

response.addHeader(SoaConstants.HEADER_ERROR_EXCEPTION_MSG, encode(codeBasedException.getMessage()));

return;

}

response.setHeader(SoaConstants.HEADER_ERROR_CODE, “500”);

response.setHeader(SoaConstants.HEADER_ERROR_MSG, encode(ex.getMessage()));

response.setHeader(SoaConstants.HEADER_ERROR_EXCEPTION_MSG, encode(String.valueOf(ex.getStackTrace())));

LOGGER.error(“failed to handle request.”, ex);

}

}

如果是 Spring Boot 项目,我们需要完成 HandlerInterceptorBasedExceptionBinder 的注册。

1

2

3

4

5

6

7

@Configuration

public class SoaWebMvcConfigurer implements WebMvcConfigurer{

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new HandlerInterceptorBasedExceptionBinder()).addPathPatterns(“/**”);

}

}

4.2.3. Client 端处理

客户端在获取请求结果后,从 Header 中提取异常信息,并重新组装并抛出异常。

FeignErrorDecoderBasedExceptionConverter 从 Header 中提取异常信息,并重新组装并抛出 SoaRemoteCallException

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

public class FeignErrorDecoderBasedExceptionConverter implements ErrorDecoder {

private static final Logger LOGGER = LoggerFactory.getLogger(FeignErrorDecoderBasedExceptionConverter.class);

public FeignErrorDecoderBasedExceptionConverter() {

}

@Override

public Exception decode(String methodKey, Response response) {

Map<String, Collection<String>> headers = response.headers();

report(methodKey, response);

return checkException(headers);

}

private void report(String methodKey, Response response) {

String message = format(“status %s reading %s”, response.status(), methodKey);

try {

if (response.body() != null) {

String body = Util.toString(response.body().asReader());

message += “; content:\n” + body;

}

} catch (IOException ignored) { // NOPMD

}

LOGGER.error(“status {}, message {}”, response.status(), message);

}

private Exception checkException(Map<String, Collection<String>> headers) {

String code = getValue(headers, SoaConstants.HEADER_ERROR_CODE);

String msg = HeaderValueUtils.decode(getValue(headers, SoaConstants.HEADER_ERROR_MSG));

String exceptionMsg = HeaderValueUtils.decode(getValue(headers, SoaConstants.HEADER_ERROR_EXCEPTION_MSG));

Integer errorCode = NumberUtils.isNumber(code) ? Integer.valueOf(code) : -1;

return new SoaRemoteCallException(exceptionMsg, errorCode, msg, “”);

}

private String getValue(Map<String, Collection<String>> headers, String key) {

Collection<String> values = headers.get(key);

if (values != null && values.size() == 1){

return values.iterator().next();

}

LOGGER.debug(“failed to find value of {} in header {}”, key, headers);

return null;

}

}

最后,需要完成 FeignErrorDecoderBasedExceptionConverter 的注册。

1

2

3

最后

在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例

MyBatis答案解析
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

大家看完有什么不懂的可以在下方留言讨论也可以关注。

觉得文章对你有帮助的话记得关注我点个赞支持一下!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

private Exception checkException(Map<String, Collection<String>> headers) {

String code = getValue(headers, SoaConstants.HEADER_ERROR_CODE);

String msg = HeaderValueUtils.decode(getValue(headers, SoaConstants.HEADER_ERROR_MSG));

String exceptionMsg = HeaderValueUtils.decode(getValue(headers, SoaConstants.HEADER_ERROR_EXCEPTION_MSG));

Integer errorCode = NumberUtils.isNumber(code) ? Integer.valueOf(code) : -1;

return new SoaRemoteCallException(exceptionMsg, errorCode, msg, “”);

}

private String getValue(Map<String, Collection<String>> headers, String key) {

Collection<String> values = headers.get(key);

if (values != null && values.size() == 1){

return values.iterator().next();

}

LOGGER.debug(“failed to find value of {} in header {}”, key, headers);

return null;

}

}

最后,需要完成 FeignErrorDecoderBasedExceptionConverter 的注册。

1

2

3

最后

在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例
[外链图片转存中…(img-IOCQrYNs-1715567416387)]

[外链图片转存中…(img-kU8RPUyG-1715567416387)]
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

大家看完有什么不懂的可以在下方留言讨论也可以关注。

觉得文章对你有帮助的话记得关注我点个赞支持一下!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,JVM调优是为了优化Java程序的性能和内存管理。为了进行JVM调优,可以采取以下几个步骤: 1. 调整JVM参数: 可以通过修改JVM启动参数来调整JVM的堆大小、永久代空间大小等。例如,通过设置"-Xmx"参数来增加堆大小,通过设置"-XX:MaxPermSize"参数来调整永久代空间大小。这可以帮助程序更有效地使用内存资源。 2. 优化垃圾回收: JVM使用垃圾回收机制来自动管理内存。可以通过调整垃圾回收算法和参数来改善垃圾回收性能。例如,可以使用并行垃圾回收器(Parallel GC)或CMS垃圾回收器(Concurrent Mark-Sweep GC)来提高垃圾回收的效率。 3. 检查类加载器和重复类: 当运行Java应用程序时,可能会出现类加载器和重复类的问题。可以使用工具如jmap和Eclipse MAT来检查并解决这些问题。通过分析加载较慢或重复加载的类,可以减少不必要的内存开销和提高性能。 4. 分析内存分配和使用情况: 可以使用工具如jmap、jstat和VisualVM来监视和分析Java应用程序的内存分配和使用情况。这些工具可以提供有关堆、永久代和线程等方面的详细信息,帮助定位内存泄漏和性能瓶颈。 通过以上方法,可以进行JavaJVM的调优,以提高程序的性能和效率。注意,在调优过程中,应该根据具体情况进行调整,并进行有效的测试和验证,以确保调优策略的有效性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Java JVM调优](https://blog.csdn.net/weixin_40213018/article/details/116602377)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [javaJVM调优](https://blog.csdn.net/qq_39291929/article/details/80874097)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值