Java基础内容
基础语法部分摘要
因为学过很多遍这次复习只是捡漏和加深印象
强弱类型语言
-
强类型语言
-
也称为强类型定义语言。要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用
-
Java、.NET、C++等都是强制类型定义的。也就是说,一旦一个变量被指定了某个数据类型,如果不经过转换,那么它就永远是这个数据类型了。
-
安全性高,运行效率相对较慢,鱼和熊掌不可兼得!强类型定义语言在速度上可能略逊色于弱类型定义
语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。
-
-
弱类型语言
- 也称为弱类型定义语言。与强类型定义相反。像vb,php等就属于弱类型语言·
- 在VBScript中,可以将字符串‘12’和整数3进行连接得到字符串‘123’,也可以把它看成整数123,而不需要显示转换。是不是十分的随便,我们Java就不是这样的。
- 但其实它们的类型没有改变,VB只是在判断出一个表达式含有不同类型的变量之后,自动在这些变量前加了一个clong()或(int)()这样的转换函数而已。能做到这一点其实是归功于VB的编译器的智能化而已,这并非是VB语言本身的长处或短处
字节
-
位(bit):是计算机 内部数据 储存的最小单位,11001100是一个八位二进制数。
-
字节(byte):是计算机中 数据处理 的基本单位,习惯上用大写 B 来表示,
- 1B(byte,字节)= 8bit(位)
-
字符:是指计算机中使用的字母、数字、字和符号
编码所占用字节
- ASCIIS码
- 1个英文字母(不分大小写)= 1个字节的空间
- 1个中文汉字 = 2个字节的空间
- 1个ASCII码 = 一个字节
- UTF-8编码
- 1个英文字符 = 1个字节
- 英文标点 = 1个字节
- 1个中文(含繁体) = 3个字节
- 中文标点 = 3个字节
- Unicode编码
- 1个英文字符 = 2个字节
- 英文标点 = 2个字节
- 1个中文(含繁体) = 2个字节
- 中文标点 = 2个字节
进制转换
1bit表示1位,
1Byte表示一个字节 1B=8b。
1024B=1KB
1024KB=1M
1024M=1G.
电脑的32位和64位的区别
-
32位操作系统只可以使用32位的cpu,而64位的CPU既可以安装32位操作系统也可以安装64位操作系统。
-
寻址能力简单点说就是支持的内存大小能力,64位系统最多可以支达128 GB的内存,而32位系统最多只可以支持4G内存。
-
32位操作系统只可以安装使用32位架构设计的软件,而64位的CPU既可以安装使用32位软件也可以安装使用64位软件。
数据类型捡漏
- 【Java语言的整型常数默认为int型,浮点数默认是Double】
整型拓展
十进制整数,如:99, -500, 0。
八进制整数,要求以 0 开头,如:015。
十六进制数,要求 0x 或 0X 开头,如:0x15 。
浮点型拓展
-
【金融面试问:银行金融业务用什么类型表示?】
- 浮点类型flfloat, double的数据不适合在不容许舍入误差的金融计算领域。
- 如果需要进行不产生舍入误差的精确数字计算,需要使用BigDecimal类。
- 主要理由:
- 由于字长有限,浮点数能够精确表示的数是有限的,因而也是离散的。浮点数一般都存在舍入误差,很多数字无法精确表示,其结果只能是接近,但不等于;二进制浮点数不能精确的表示0.1,0.01,0.001这样10的负次幂。并不是所有的小数都能可以精确的用二进制浮点数表示。
- 最好完全避免使用浮点数比较 !
- 大数值:Java.math下面的两个有用的类:BigInteger和BigDecimal,这两个类可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点运算。
-
浮点数使用总结:
-
默认是double
-
浮点数存在舍入误差,很多数字不能精确表示。如果需要进行不产生舍入误差的精确数字计算,需要使用BigDecimal类。
-
避免比较中使用浮点数
布尔型拓展
-
【编码规范:很多新手程序员喜欢这样写】
- if (is == true && un == false )
-
只有新手才那么写。对于一个熟练的人来说,应该用如下方式来表示:
- if ( is && !un )
-
代码要精简易读
强制类型转换
常见错误和问题
- 操作比较大的数时,要留意是否溢出,尤其是整数操作时;
- L和l 的问题
- 不要命名名字为l的变量
- long类型使用大写L不要用小写。
JDK7扩展
-
二进制整数
- 我们只要以:0b开头即可。
- int a = 0b0101:
-
下划线分隔符
- 在实际开发和学习中,如果遇到特别长的数字,读懂它令人头疼!JDK7为我们提供了下划线分隔符,可以按照自己的习惯进行分割。
- int b = 1_2234_5678;
常量
- final 常量名=值;
- final double PI=3.14; final String LOVE="hello";
- 常量名一般使用大写字符。
- 程序中使用常量可以提高代码的可维护性。
变量的命名规范
-
所有变量、方法、类名:见名知意
-
类成员变量:首字母小写和驼峰原则 : monthSalary
-
局部变量:首字母小写和驼峰原则
-
常量:大写字母和下划线:MAX_VALUE
-
类名:首字母大写和驼峰原则: Man, GoodMan
-
方法名:首字母小写和驼峰原则: run(), runRun()
位运算符
-
【常见面试题:int a=2*8怎样运算效率最快?】
- System.out.println(2 << 3);
-
位操作是程序设计中对位模式按位或二进制数的一元和二元操作。 在许多古老的微处理器上, 位运算比
加减运算略快, 通常位运算比乘除法运算要快很多。 在现代架构中, 情况并非如此:位运算的运算速度
通常与加法运算相同(仍然快于乘法运算). 详细的需要了解计算机的组成原理!
包机制
创建包
-
一般利用公司域名倒置作为包名
- www.baidu.com 包名:com.baidu.www
- bbs.baidu.com 包名:com.baidu.bbs
-
我们平时也可以按照自己的公司域名去写,比如:com.kuangstudy.utils
import 关键字
- 用 import 关键字引入,使用通配符 "*" , 导入io包下的所有类!
- 【不建议这样使用,因为会全局扫描,影响速度!】
- 【编码规范:推荐参考阿里巴巴开发手册编程规范】
JavaDoc
简介
JavaDoc是一种将注释生成HTML文档的技术,生成的HTML文档类似于Java的API,易读且清晰明了。
在简略介绍JavaDoc写法之后,再看一下在Intellij Idea 中如何将代码中的注释生成HTML文档。
javadoc是Sun公司提供的一个技术,它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配
套的API帮助文档。也就是说,只要在编写程序时以一套特定的标签作注释,在程序编写完成后,通过
Javadoc就可以同时形成程序的开发文档了。javadoc命令是用来生成自己API文档的,使用方式:使用
命令行在目标文件所在目录输入javadoc +文件名.java。
-
以 /*** 开始,以 / 结束。
@author 作者名
@version 版本号
@since 指明需要最早使用的jdk版本
@param 参数名
@return 返回值情况
@throws 异常抛出情况
命令行生成Doc
- javadoc Hello.java
- -encoding UTF-8 -charset UTF-8 //解决GBK乱码问题,在中间添加编码设置
- javadoc -encoding UTF-8 -charset UTF-8 Hello.java
流程控制部分摘要
用户交互Scanner
next & nextLine
两者区别:
- next():
-
一定要读取到有效字符后才可以结束输入。
-
对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
-
只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
-
next() 不能得到带有空格的字符串。
- nextLine():
-
以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
-
可以获得空白。
顺序结构
选择结构
-
【我们平时写程序一定要严谨,不然之后修补Bug是一件十分头疼的事情,你要哦在编写代码的时候就把所有的问题都思考清除,再去一个个解决,这才是一个优秀的程序员应该做的事情,多思考,多犯错!】
-
【记住一点就好,所有的流程控制语句都可以互相嵌套,互不影响!】
switch多选择结构
- 【JDK7增加了字符串表达式】
循环结构
continue
- continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。
- 在 for 循环中,continue 语句使程序立即跳转到更新语句。
- 在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。
带标签的continue
【了解即可】
-
goto关键字很早就在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用;Java没有goto。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子---带标签的break和continue。
-
“标签”是指后面跟一个冒号的标识符,例如:label:
-
对Java来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。
-
带标签的break和continue的例子:
数组
数组概述
-
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组
本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象
本身是在堆中的。
数组声明创建
内存分析
数组长度的确定的,不可变的。如果越界,则报:ArrayIndexOutofBounds
**Java内存分析 : **
数组内存分析:
-
声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有
关。因此,声明数组时不能指定其长度(数组中元素的个数),例如: int a[5]; //非法
-
声明一个数组的时候并没有数组被真正的创建
-
构造一个数组,必须指定长度
三种初始化
静态初始化
- int[] a = {1,2,3};
动态初始化
-
int[] a = new int[2];
a[0]=1;
a[1]=2;
数组的默认初始化
- int[] a=new int[2];
Arrays 类
-
数组的工具类java.util.Arrays
-
由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
-
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而"不用"使用
对象来调用(注意:是"不用" 而不是 "不能")
-
-
java.util.Arrays 类能方便地操作数组. 使用之前需要导包!
-
具有以下常用功能
- 给数组赋值:通过 fifill 方法。
- 对数组排序:通过 sort 方法,按升序
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
- 数组转换为List集合 **List<int[]> list = Arrays.asList(array); **
常见排序算法
冒泡排序
冒泡排序算法的原理如下:
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
-
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大数。
-
针对所有的元素重复以上的步骤,除了最后一个。
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
选择排序
-
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中
选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小
(大)元素,然后放到排序序列的末尾。以此类推,直到全部待排序的数据元素排完。 选择排序是不稳
定的排序方法。
稀疏数组
简介
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组,稀疏数组的处理方法是:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
举例
即第一行第一列记录记录原始数组行数,第一行第二列记录原始数组列数,第一行第三列总共有多少个值,
第二行记录第一个有效数据,第三行记录第二个有效数据
说明:新数组是一个压缩后的二维数组
- 第一行【0】第一个值代表原数组行数=6行 第二个值代表原数组列数=7列 第三个值代表原数组有多少个有效值(除0,或者说除了重复值--这个数组的重复值就是0以外的值)
- 从第二行开始就代表了原数组的每一个数据,
第一二个值分别代表这个数据在原数组的行列位置 第三个值代表此数据值
实现代码
package org.westos.sparsearr;
public class SparseArr {
public static void main(String[] args) {
//创建一个二维数组 11*11
//0表示没有棋子,1表示黑棋,2表示蓝棋
int[][] chessArr = new int[11][11];
chessArr[1][2] = 1;
chessArr[2][3] = 2;
//输出原始的二维数组
System.out.println("原始的二维数组:");
for (int i = 0; i < chessArr.length; i++) {
for (int j = 0; j < chessArr[i].length; j++) {
System.out.print(chessArr[i][j]+"\t");
}
System.out.println();
}
//将二维数组转换为稀疏数组
//1.先遍历二维数组得到非零数据的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (chessArr[i][j] != 0){
sum++;
}
}
}
//2.创建对应的系数数组
int[][] sparseArr = new int[sum+1][3];
//给系数数组赋值
sparseArr[0][0] = 11;
sparseArr[0][1] = 11;
sparseArr[0][2] = sum;
//遍历二维数组将非零的值存放到稀疏数组
int count = 0;
for (int i = 0; i < chessArr.length; i++) {
for (int j = 0; j < chessArr[i].length; j++) {
if (chessArr[i][j] != 0){
count++;
sparseArr[count][0] = i;
sparseArr[count][1] = j;
sparseArr[count][2] = chessArr[i][j];
}
}
}
//输出稀疏数组
System.out.println();
System.out.println("稀疏数组:");
for (int i = 0; i < sparseArr.length; i++) {
System.out.println(sparseArr[i][0]+"\t"+sparseArr[i][1]+"\t"+sparseArr[i][2]);
}
//将稀疏数组恢复成二维数组
//1.先读取稀疏数组的第一行,根据第一行创建二维数组
int[][] chessArr2 = new int[sparseArr[0][0]][sparseArr[0][1]];
//2.读取稀疏数组后几行赋值给二维数组
//注意这里是从第二行开始
for (int i = 1; i < sparseArr.length; i++) {
chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
System.out.println();
System.out.println("恢复后的二维数组:");
for (int[] row : chessArr2) {
for (int data : row) {
System.out.print(data+"\t");
}
System.out.println();
}
}
}
方法
- JAVA中只有值传递!
方法的重载
重载的方法必须拥有不同的参数列表。你不能仅仅依据修饰符或者返回类型的不同来重载方法。
拓展命令行传参
命令行参数是在执行程序时候紧跟在程序名字后面的信息。
【命令行】
【错误: 找不到或无法加载主类,解决方法】
- 在项目输出的项目目录下执行java命令,写完整路径即可。
- $ java com.kuang.chapter3.Demo03 Hello World
可变参数
JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法
- 声明:typeName... parameterName
- 在方法声明中,在指定参数类型后加一个省略号(...)
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明
递归
递归算法重点
- 递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己
- 利用递归可以用简单的程序来解决一些复杂的问题。
- 递归结构包括两个部分:
- 递归头。解答:什么时候不调用自身方法。如果没有头,将陷入死循环。
- 递归体。解答:什么时候需要调用自身方法。
注意:
-
递归其实是方便了程序员难为了机器,递归可以通过数学公式很方便的转换为程序。其优点就是易理
解,容易编程。但递归是用栈机制实现的,每深入一层,都要占去一块栈数据区域,对嵌套层数深的一
些算法,递归会力不从心,空间上会以内存崩溃而告终,而且递归也带来了大量的函数调用,这也有许
多额外的时间开销。所以在深度大时,它的时空性就不好了。(会占用大量的内存空间)
-
能不用递归就不用递归,递归都可以用迭代来代替
面向对象
面向过程&面向对象
面向过程的思维模式
面向过程的思维模式是简单的线性思维,思考问题首先陷入第一步做什么、第二步做什么的细节中。
这种思维模式适合处理简单的事情,比如:上厕所。
如果面对复杂的事情,这种思维模式会陷入令人发疯的状态!比如:如何造神舟十号!
面向对象的思维模式
面向对象的思维模式说白了就是分类思维模式。思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
这样就可以形成很好的协作分工。比如:设计师分了10个类,然后将10个类交给了10个人分别进行详细设计和编码!
显然,面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理.
OOP详解
Java的编程语言是面向对象的,采用这种语言进行编程称为面向对象编程(Object-Oriented Programming, OOP)。
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
抽象(abstract)
-
忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了
解全部问题,而只是选择其中的一部分,暂时不用关注细节。
例如:要设计一个学生成绩管理系统,那么对于学生,只关心他的班级、学号、成绩等,而不用去关心他的
身高、体重这些信息。 抽象是什么?就是将多个物体共同点归纳出来,就是抽出像的部分!
封装(Encapsulation)
- 封装是面向对象的特征之一,是对象和类概念的主要特性。封装是把过程和数据包围起来,对数据的访问只能通过指定的方式。
- 在定义一个对象的特性的时候,有必要决定这些特性的可见性,即哪些特性对外部是可见的,哪些特性用于表示内部状态。
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
- 信息隐藏是用户对封装性的认识,封装则为信息隐藏提供支持。
- 封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。
继承(inheritance)
- 继承是一种联结类的层次模型,并且允许和支持类的重用,它提供了一种明确表述共性的方法。
- 新类继承了原始类后,新类就继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。
- 派生类(子类)可以从它的基类(父类)那里继承方法和实例变量,并且派生类(子类)中可以修改或增加新的方法使之更适合特殊的需要继承性很好的解决了软件的可重用性问题。
多态(polymorphism)
- 多态性是指允许不同类的对象对同一消息作出响应。
- 多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
- 相同类域的不同对象,调用相同方法,表现出不同的结果
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象。类是对象的模板
方法回顾及加深
值传递和引用传递
调用方法进行传参时,分为值传递和引用传递两种。
-
如果参数的类型是基本数据类型,那么就是值传递。
-
如果参数的类型是引用数据类型,那么就是引用传递。
-
值传递是实参把自己变量本身存的简单数值赋值给形参.
-
引用传递是实参把自己变量本身存的对象内存地址值赋值给形参
-
所以值传递和引用传递本质上是一回事,只不过传递的东西的意义不同而已
类
this关键字
this在类中的作用
【调用类中的其他构造器】
注:this的这种用法,只能在构造器中使用.普通的方法是不能用的.并且这局调用的代码只能出现在构造器中的第一句
内存分析
JAVA程序运行的内存分析
栈 stack:
-
每个线程私有,不能实现线程间的共享!
-
局部变量放置于栈中。
-
栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆 heap:
-
放置new出来的对象!
-
堆是一个不连续的内存空间,分配灵活,速度慢!
方法区(也是堆):
-
被所有线程共享!
-
用来存放程序中永远是不变或唯一的内容。(类代码信息、静态变量、字符串常量)
注意:本次内存分析,我们的主要目的是让大家了解基本的内存概念。类加载器、Class对象这些更加详细的内容,我们将在后面专门讲反射的课程里面讲。
属性
在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化。
(数值:0,0.0 char:u0000, boolean:false, 所有引用类型:null)
封装
作用和意义
-
提高程序的安全性,保护数据。
-
隐藏代码的实现细节
-
统一用户的调用接口
-
提高系统的可维护性
-
便于调用者调用。
良好的封装,便于修改内部代码,提高可维护性。
良好的封装,可进行数据完整性检测,保证数据的有效性。
继承
为什么需要继承?继承的作用?
第一好处:继承的本质在于抽象。类是对对象的抽象,继承是对某一批类的抽象。
第二好处:为了提高代码的复用性。
extands的意思是“扩展”。子类是父类的扩展。
【注】JAVA中类只有单继承,没有多继承! 接口可以多继承!
父类中的构造器是不能被子类继承的,但是子类的构造器中,会隐式的调用父类中的无参构造器(默认使用super关键字)。
Object类
final修饰符
修饰类
-
用fifinal修饰的类不能被继承,没有子类。
例如:我们是无法写一个类去继承String类,然后对String类型扩展的,因为API中已经被String类定义为final的了.
修饰方法
-
用final修饰的方法可以被继承,但是不能被子类的重写。
例如:每个类都是Object类的子类,继承了Object中的众多方法,在子类中可以重写toString方法、equals方法等,但是不能重写getClass方法 wait方法等,因为这些方法都是使用fianl修饰的。
修饰变量
用final修饰的变量表示常量,只能被赋一次值.其实使用final修饰的变量也就成了常量了,因为值不会再变了。
内部类
成员内部类 (实例内部类、非静态内部类)
静态内部类
局部内部类
匿名内部类
- 什么是匿名对象?如果一个对象只要使用一次,那么我们就是需要new Object().method()。 就可以了,而不需要给这个实例保存到该类型变量中去。这就是匿名对象。
- 匿名内部类跟匿名对象是一个道理
匿名内部类:
-
匿名内部类需要依托于其他类或者接口来创建
- 如果依托的是类,那么创建出来的匿名内部类就默认是这个类的子类
- 如果依托的是接口,那么创建出来的匿名内部类就默认是这个接口的实现类。
-
匿名内部类的声明必须是在使用new关键字的时候
- 匿名内部类的声明及创建对象必须一气呵成,并且之后能反复使用,因为没有名字。
匿名内部类注意事项
-
匿名内部类除了依托的类或接口之外,不能指定继承或者实现其他类或接口,同时也不能被其他类所继承,因为没有名字。
-
匿名内部中,我们不能写出其构造器,因为没有名字。
-
匿名内部中,除了重写上面的方法外,一般不会再写其他独有的方法,因为从外部不能直接调用到。(间接是调用到的)
异常机制
异常概念
三种类型的异常
-
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如:要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
-
运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
-
错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
异常指不期而至的各种状况,如:文件找不到、网络连接失败、除0操作、非法参数等。异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程。
Java语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,所有的异常都可以用一个异常类来表示,不同类型的异常对应不同的子类异常(目前我们所说的异常包括错误概念),定义异常处理的规范,在 JDK1.4 版本以后增加了异常链机制,从而便于跟踪异常。
Java异常是一个描述在代码段中发生异常的对象,当发生异常情况时,一个代表该异常的对象被创建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常
异常体系结构
Java把异常当作对象来处理,并定义一个基类 java.lang.Throwable 作为所有异常的超类。
在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。
Java异常层次结构图
- 从图中可以看出所有异常类型都是内置类 Throwable 的子类,因而 Throwable 在异常类的层次结构的顶层。
- 接下来 Throwable 分成了两个不同的分支,一个分支是Error,它表示不希望被程序捕获或者是程序无法处理的错误。另一个分支是Exception,它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。
- 其中异常类 Exception 又分为运行时异常( RuntimeException )和非运行时异常。Java异常又可以分为不受检查异常( Unchecked Exception )和检查异常( Checked Exception )。
异常之间的区别与联系
Error 通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程; Exception 通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
检查异常和不受检查异常
检查异常:在正确的程序运行过程中,很容易出现的、情理可容的异常状况,在一定程度上这种异常的发生是可以预测的,并且一旦发生该种异常,就必须采取某种方式进行处理。
解析:除了RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常,当程序
中可能出现这类异常,要么使用try-catch语句进行捕获,要么用throws子句抛出,否则编译无法通
过。
不受检查异常:包括RuntimeException及其子类和Error。
分析: 不受检查异常 为编译器不要求强制处理的异常, 检查异常 则是编译器要求必须处置的异常。
Java异常处理机制
异常处理五个关键字
try 、 catch 、 finally 、 throw 、 throws
- try -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
- catch -- 用于捕获异常。catch用来捕获try语句块中发生的异常
- fifinally -- fifinally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
- throw -- 用于抛出异常。
- throws -- 用在方法签名中,用于声明该方法可能抛出的异常。
处理异常
-
try -catch
- 监控区域 (guarded region)的概念:它是一段可能产生异常的代码,并且后面跟着处理这些异常的代码
- 匹配原则:如果抛出的异常对象属于 catch 子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与 catch 块捕获的异常类型相匹配。
- 算术异常属于运行时异常,因而实际上该异常不需要程序抛出,运行时系统自动抛出。如果不用try-catch程序就不会往下执行了。
-
使用多重的catch语句
- 编写多重catch语句块注意事项: 顺序问题:先小后大,即先子类后父类
-
嵌套try语句
thorw
语法形式:throw ThrowableInstance;
-
这里的ThrowableInstance一定是 Throwable 类类型或者 Throwable 子类类型的一个对象。简单
的数据类型,例如 int , char ,以及非 Throwable 类,例如 String 或 Object ,不能用作异常
throws
-
如果一个方法可以导致一个异常但不处理它,它必须指定这种行为以使方法的调用者可以保护它们自己而不发生异常。要做到这点,我们可以在方法声明中包含一个 throws 子句。
-
public void info() throws Exception
- Exception 是该方法可能引发的所有的异常,也可以是异常列表,中间以逗号隔开
throws 抛出异常的规则:
- 如果是不受检查异常( unchecked exception ),即 Error 、 RuntimeException 或它们的子类,那么可以不使用 throws 关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
- 必须声明方法可抛出的任何检查异常( checked exception )。即如果一个方法可能出现受可查异常,要么用 try-catch 语句捕获,要么用 throws 子句声明将它抛出,否则会导致编译错误
- 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
- 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
finally
-
当异常发生时,通常方法的执行将做一个陡峭的非线性的转向,它甚至会过早的导致方法返回。例如,如果一个方法打开了一个文件并关闭,然后退出,你不希望关闭文件的代码被异常处理机制旁路。
- finally 关键字为处理这种意外而设计。
-
finally 创建的代码块在 try/catch 块完成之后另一个 try/catch 出现之前执行
-
finally 块无论有没有异常抛出都会执行。如果抛出异常,即使没有 catch 子句匹配,finally 也会执行。
-
一个方法将从一个 try/catch 块返回到调用程序的任何时候,经过一个未捕获的异常或者是一个明确的返回语句, finally 子句在方法返回之前仍将执行。这在关闭文件句柄和释放任何在方法开始时被分配的其他资源是很有用
-
注意: finally 子句是可选项,可以有也可以无,但是每个 try 语句至少需要一个 catch 或者 finally 子句。
try, catch,fifinally ,return 执行顺序
-
执行try,catch , 给返回值赋值
-
执行fifinally
-
return
自定义异常
- 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承 Exception 类即可
在程序中使用自定义异常类,大体可分为以下几个步骤:
- 创建自定义异常类。
- 在方法中通过 throw 关键字抛出异常对象。
- 如果在当前抛出异常的方法中处理异常,可以使用 try-catch 语句捕获并处理;否则在方法的声明处通过 throws 关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常。
浙公网安备 33010602011771号