第5节:Java基础 - 必知必会(下)

第5节:Java基础 - 必知必会(下)

本小节是Java基础篇章的第三小节,主要讲述Java中的Exception与Error,JIT编译器以及值传递与引用传递的知识点。

一、Java中的Exception和Error有什么区别

Exception和Error的主要区别可以概括如下:

  • Exception是程序正常运行中预料到可能出现的错误,并且应该被捕获并进行相应处理,是一种异常现象。
  • Error是正常情况下不可能发生的错误,Error会导致JVM处于已追踪不可恢复的状态,不需要捕获处理,比如说OutOfMemoryError

解析:

Exception又分为了运行时异常编译异常

编译异常(受检异常)表示当前调用的方法体内部抛出了一个异常,所以编译器检测到这段代码在运行时可能会出现异常,所以我们必须对异常进行相应处理,可以捕获异常或者抛给上层调用方。

运行时异常(非受检异常)表示在运行时出现的异常,常见的运行异常包括:空指针异常,数组越界异常,数字转换异常以及算数异常等。

前面说到了异常Exception应该被捕获,我们可以使用try-catch-finally来处理异常,并且使得程序恢复正常。

那么我们捕获异常应该遵循哪些基本原则呢?

  • 尽可能捕获比较详细的异常,而不是使用Exception一起捕获。
  • 当本模块不知道捕获之后该怎么处理异常时,可以将其抛给上层模块。上层模块拥有更多的业务逻辑,可以进行更好的处理。
  • 捕获异常后至少应该有日志记录,方便之后的排查。
  • 不要使用一个很大的try-catch包住整段代码,不利于问题的排查。

NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

从名字中,我们可以看出前者是一个错误,后者是一个异常。我们先来看下JDK中对ClassNotFoundException异常的阐述:

大概意思就是在说,当我们使用例如Class.forName方法来动态的加载该类的时候,传入了一个类名,但是其并没有在类路径中被找到的时候,就会报ClassNotFoundException异常。出现这种情况,一般都是类名字传入有误导致的。

我们再来看下JDK中对该错误NoClassDefFoundError的阐述:

大概意思是这样的,如果JVM或者ClassLoader实例尝试加载(可以通过正常的方法调用,也可能是使用new来创建新的对象)类的时候却找不到类的定义。但是要查找的类在编译的时候是存在的,运行的时候却找不到了。这个时候就会导致NoClassDefFoundError。出现这种情况,一般是由于打包的时候漏掉了部分类或者Jar包被篡改已经损坏。

二、JIT编译器

前面我们谈到了Java是一种先编译,后解释执行的语言。那么我们就来说下何为JIT编译器吧。

JIT编译器全名叫Just In Time Compile 也就是即时编译器,把经常运行的代码作为"热点代码"编译成与本地平台相关的机器码,并进行各种层次的优化。JIT编译除了具有缓存的功能外,还会对代码做各种优化,包括逃逸分析、锁消除、 锁膨胀、方法内联、空值检查消除、类型检测消除以及公共子表达式消除等。

解释:

JIT编译器属于Java基础中的比较有深度的题目了,回答出来算是一个亮点了。既然说到了JIT编译器,我们来看下JIT对代码优化使用到的逃逸分析技术吧。

逃逸分析:

逃逸分析的基本行为就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。JIT编译器的优化包括如下:

  • 同布省略:也就是锁消除,当JIT编译器判断不会产生并发问题,那么会将同步synchronized去掉
  • 标量替换

我们先来解释下标量和聚合量的基本概念。

  • 标量(Scalar)是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量。
  • 聚合量(Aggregate)是还可以分解的数据。Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量。

在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过JIT优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。标量替换的好处就是对象可以不在堆内存进行分配,为栈上分配提供了良好的基础。

那么逃逸分析技术存在哪些缺点呢?

    技术不是特别成熟,分析的过程也很耗时,如果没有一个对象是不逃逸的,那么就得不偿失了。

三、Java中的值传递和引用传递

值传递和引用传递的解释可以概括如下。

  • 值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。
  • 引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。

我们先看一个值传递的例子:

public class Test {
    public static void main(String[] args)  {
        int x=0;
        change(x);
        System.out.println(x);
    }
    static void change(int i){
        i=7;
    }
}

  

毫无疑问,上边的代码会输出0。因为如果参数是基本数据类型,那么是属于值传递的范畴,传递的其实是源对象的一个copy副本,不会影响源对象的值。

我们再来分析一个引用传递的例子:

public class Test {
    public static void main(String[] args)  {
        StringBuffer x = new StringBuffer("Hello");
        change(x);
        System.out.println(x);
    }
    static void change(StringBuffer i) {
        i.append(" world!");
    }
}

  

通过运行程序,输出为Hello world!接下来我们通过图片来分析下程序执行过程种的内存变化吧。

由图中我们可以看出x和i指向了同样的内存地址,那么i.append操作将直接修改了内存地址里边的值,所以当方法结束,局部变量i消失,先前变量x所指向的内存值已经发生了变化,所以输出为Hello world!

接着,我们修改下change方法,代码如下所示:

public class Test {
    public static void main(String[] args)  {
        StringBuffer x = new StringBuffer("Hello");
        change2(x);
        System.out.println(x);
    }
    static void change2(StringBuffer i) {
        i = new StringBuffer("hi");
        i.append(" world!");
    }
}

  先给出答案,上边Demo的输出为Hello,我们依然来画图分析内存变化。

由图中我们可以看出来,在函数change2中将引用变量i重新指向了堆内存中另一块区域,下边都是对另一块区域进行修改,所以输出是Hello。

最后,我们继续升级该题目代码如下:

public class Test {
    public static void main(String[] args)  {
        StringBuffer sb = new StringBuffer("Hello ");
        System.out.println("Before change, sb = " + sb);
        changeData(sb);
        System.out.println("After change, sb = " + sb);
    }
    public static void changeData(StringBuffer strBuf) {
        StringBuffer sb2 = new StringBuffer("Hi,I am ");
        strBuf = sb2;
        sb2.append("World!");
    }
}

  

输出为:

  Before change, sb = Hello

   After change, sb = Hello

原文地址:https://www.cnblogs.com/csushl/p/11973502.html

时间: 2024-10-05 00:34:19

第5节:Java基础 - 必知必会(下)的相关文章

第4节:Java基础 - 必知必会(中)

第4节:Java基础 - 必知必会(中) 本小节是Java基础篇章的第二小节,主要讲述抽象类与接口的区别,注解以及反射等知识点. 一.抽象类和接口有什么区别 抽象类和接口的主要区别可以总结如下: 抽象类中可以没有抽象方法,JDK8版本开始提供了接口总方法的default实现 抽象类和类一样是单继承的:接口可以实现多个父类 抽象类中可以存在普通的成员变量:接口中的变量必须是static final类型的,必须被初始化,接口中只能有常量,没有变量 解析: 在Java中,我们用abstract来定义抽

图解 & 深入浅出Java初始化与清理:构造器必知必会

Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆         瓣:BYSocket FaceBook:BYSocket Twitter    :BYSocket 在面向对象编程中,编程人员应该在意"资源".比如 ? 1 <font color="#000000">String hello = "hello": </font> 在代码中,我们很在意在内存中Stri

迈向高阶:优秀Android程序员必知必会的网络基础

1.前言 网络通信一直是Android项目里比较重要的一个模块,Android开源项目上出现过很多优秀的网络框架,从一开始只是一些对HttpClient和HttpUrlConnection简易封装使用的工具类,到后来Google开源的比较完善丰富的Volley,再到如今比较流行的Okhttp.Retrofit. 要想理解他们之间存在的异同(或者具体点说,要想更深入地掌握Android开发中的网络通信技术),必须对网络基础知识.Android网络框架的基本原理等做到心中有数.信手拈来,关键时刻才能

一个老师程序员说:这是学Java 必知必会的 20 种常用类库和 API

一个有经验的Java开发人员特征之一就是善于使用已有的轮子来造车.<Effective Java>的作者Joshua Bloch曾经说过:"建议使用现有的API来开发,而不是重复造轮子".在本文中,我将分享一些Java开发人员应该熟悉的最有用的和必要的库和API.顺便说一句,这里不包括框架,如Spring和Hibernate因为他们非常有名,都有特定的功能.最后,如果大家如果在自学遇到困难,想找一个java的学习环境,可以加入我们的java学习圈,点击我加入吧,会节约很多时

SSL证书必知必会:数字证书及CA基础知识

SSL证书必知必会:数字证书及CA基础知识 数字证书 数字证书是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet上验证通信实体身份的方式,数字证书不是数字***,而是身份认证机构盖在数字***上的一个章或印(或者说加在数字***上的一个签名).它是由权威机构--CA机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的身份.最简单的证书包含一个公开密钥.名称以及证书授权中心的数字签名.数字证书的一个重要的特征就是只在特

.NET零基础入门09:SQL必知必会

一:前言 仿佛到了更进一步的时候了,每一个程序员迟早都会遇到数据存储的问题.我们拿什么来存储程序产生的数据?举例来说,用什么来存储我们的打老鼠游戏每次的成绩呢?选择如下: 1:内存中.缺点,退出游戏,数据就没了: 2:文件中.好办法!缺点,自己解析文本,把文本变成我们程序中的数据,这个解析的过程叫做协议.协议这个词听上去够恐怖吧,实际上说白了无非就是数据格式怎么样,API接口怎么样之类的东东. 3:数据库.好办法!好吧,数据库文件其实也就是硬盘上的文件,只不过数据库本身就已经为我们定义好了数据格

基础入门_Python-模块和包.深入Celery之常用架构/方案选型/必知必会?

简单介绍: 说明: 此模块是一个专注于分布式消息传递的异步任务队列,所谓任务就是消息,消息中的有效载荷中包含要执行的任务需要的全部数据 几大特性: 1. Celery易于使用和维护,且不需要配置文件,默认配置启动时自动写入消息代理. 2. Celery高可用,连接丢失或失败时客户端或消费者会自动重试,并且可通过消息代理的双主/主从模式来提高高可用性 3. Celery快速,单个进程每分钟可处理百万任务,且优化后可保持往返延迟在亚毫秒级别 4. Celery灵活,几乎所有部分都支持扩展或单独使用,

《MySQL必知必会学习笔记》:正则表达式

正则表达式的应用 前面的几节的过滤例子允许用匹配.比较和通配操作符来寻找数据.对于基本的数据过滤,这样就足够了.但是随着过滤条件的复杂性的增加,where子句本身的复杂性也有必要增加.这也就是正则表达式变得有用的地方. 正则表达式是用来匹配文本中特殊的字符集合. 正则表达式不是自己第一次见,在JAVA/C++ 这些语言中,都有正则表达式,不过他们的用途是一样的,都是匹配文本中的字符串.可能具体的用法不太一样,但也差不多.下面将一一进行学习.介绍. 基本字符的匹配 当我们想查找某列中的字符中是否有

必知必会JVM垃圾回收——对象搜索算法与回收算法

垃圾回收(GC)是JVM的一大杀器,它使程序员可以更高效地专注于程序的开发设计,而不用过多地考虑对象的创建销毁等操作.但是这并不是说程序员不需要了解GC.GC只是Java编程中一项自动化工具,任何一个工具都有它适用的范围,当超出它的范围的时候,可能它将不是那么自动,而是需要人工去了解与适应地适用. 拥有一定工作年限的程序员,在工作期间肯定会经常碰到像内存溢出.内存泄露.高并发的场景.这时候在应对这些问题或场景时,如果对GC不了解,很可能会成为个人的发展瓶颈. 接下来的两文将详细学习下JVM中垃圾