第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

java的标量和聚合量_第5节:Java基础 - 必知必会(下)相关推荐

  1. java的标量和聚合量_欧尼酱讲JVM(16)——如何基于逃逸分析进行代码优化

    代码优化是一个涉及面很广的"工程",但是今天呢,本姑娘主要给大家分享基于逃逸分析,如何给代码做优化.那么逃逸分析是什么呢?我前面的文章也仔细的讲解过了,这里就不过多的赘述了.有不明 ...

  2. java的标量和聚合量_JVM 角度看代码优化

    从JVM角度看,有这几种优化手段: 栈上分配: 把对上分配对象空间的行为转化成栈上分配,减少YGC,提供性能 同步省略 同步代码块锁消除 标量替换 为栈上分配提供了基础,和栈上分配时搭配做的 这几个优 ...

  3. java mysql单库多表_第69节:Java中数据库的多表操作

    第69节:Java中数据库的多表操作 前言 学习数据库的多表操作,去电商行业做项目吧!!! 达叔,理工男,简书作者&全栈工程师,感性理性兼备的写作者,个人独立开发者,我相信你也可以!阅读他的文 ...

  4. SpringBoot入门到精通_第6篇 _必知必会

    接上一篇:SpringBoot入门到精通_第5篇 _SpringBoot Actuator监控 https://blog.csdn.net/weixin_40816738/article/detail ...

  5. 必知必会系列-JAVA虚拟机原理

    系列文章 必知必会系列-Spring技术原理 必知必会系列-JAVA虚拟机原理 必知必会系列-Redis技术原理 引言 随着技术的不断演进,在不同时间阶段都会有不同的技术产物,那么如何快速的学习和掌握 ...

  6. Java架构师必知必会,带走不谢

    可以说,Java是现阶段中国互联网公司中,覆盖度最广的研发语言,掌握了Java技术体系,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能有立足之地. 成为Java架构师,需要掌握哪些技能呢 ...

  7. c2064 项不会计算为接受0个参数的函数_【JS必知必会】高阶函数详解与实战

    本文涵盖 前言 高级函数概念 函数作为参数的高阶函数 map filter reduce sort详解与实战 函数作为返回值的高阶函数 isType函数与add求和函数 如何自己创建高阶函数 前言 一 ...

  8. tensorflow 按维度相加_人工智能 TensorFlow 必知必会编程概念整理

    内容概括: 学习 TensorFlow 编程模型的基础知识,重点了解以下概念: 张量 指令 图 会话 构建一个简单的 TensorFlow 程序,使用该程序绘制一个默认图并创建一个运行该图的会话 概念 ...

  9. SpringBoot入门到精通_第7篇 _必知必会总结

    接上一篇:SpringBoot入门到精通_第6篇 _必知必会

最新文章

  1. 每日一皮:这真的是亲妈吗...
  2. javax.swing super()方法
  3. 解决eclipse中git插件中的cannot open git-upload-pack问题
  4. C++11 标准新特性: 右值引用与转移语义(点评)
  5. 如何重做计算机系统软件,电脑卡如何一键重做Win7旗舰版
  6. 生动形象的理解什么是装饰器!
  7. 修改webpack配置,在react中使用less
  8. python日志输入 print 常用格式化符号 logging 日志输出
  9. 未成年人互联网普及率达99% 腾讯等倡议加强青少年网络素养教育
  10. java索引丢失怎么解决_java.sql.SQLException: 索引中丢失 IN 或 OUT 参数:: 1解决办法...
  11. 在IE7 中遇到的几个小问题,有解决方案
  12. 【笔试/面试】排列组合与概率计算(三)
  13. CppUnit快速入门
  14. 【编程题目】求1+2+…+n, 要求不能使用乘除法、for、while、if、else、switch、case和条件语句...
  15. Mac下Tomcat下载及使用
  16. c语言二进制微粒群算法,离散二进制微粒群算法.pdf
  17. 示波器带宽定义与来源
  18. Xubuntu Linux发行版放弃即时消息软件Pidgin
  19. 机器学习笔记 - EfficientNet论文解读
  20. 汽车修理厂计算机管理,最新汽车维修厂管理系统

热门文章

  1. 用矩阵点积的办法构造神经网络的迭代次数1:0.6:0.1=1:1:1
  2. 哈尔滨工程大学计算机学院保研政策,哈尔滨工程大学计算机科学与技术学院(专业学位)计算机技术保研夏令营...
  3. jdbc和mysql面试题_JDBC数据访问技术面试题(附答案)
  4. TCL with SNPS get_attributesget_lib_attributelist_attributsreport_attribute
  5. DFT实际应用-User-Defined Test Points Example
  6. 【s操作】轻松优雅的保存微信群图片和朋友圈图片
  7. 【测试】用示波器抓取红外遥控器NEC信号
  8. 六 运行级别调整增加启动项
  9. 操作系统(四) | 经典进程的同步问题(生产者--消费者问题、哲学家进餐问题、读者--写者问题)
  10. matlab 读取nc