什么是异常?
异常就是指程序执行过程中遇到的非正常情况,导致JVM非正常停止。
Java中有哪些异常?
Java中的异常有两个体系:
- Error:
error
是严重的错误问题,也就是无法处理的错误,只能事先避免,比如内存溢出; - Exception:Exception是我们最常遇到的,
java.lang.Exception
被称作异常类,它表示程序本身可以处理的问题。
所有的异常都继承Throwable
类,Throwable
继承Object
类。我们考虑到的Error
和Exception
都是直接继承Throwable
的。
作为一名Java程序员,在软件开发中,Exception
是不可能避免的,所以你至少应当具备分析异常原因、处理异常的能力。
Exception
就是标题所介绍的异常,异常还分为编译时异常和运行时异常
- 编译时异常也可称为检查异常,IDE中会提示处理,否则代码无法编译;
- 运行时异常也可称为非检查异常,这样的异常代码不会报错,但是在程序运行的时候可能会出现,你也可以在代码中就处理,但是这样你就不能通过日志来排查问题了。
- 运行时异常有个独立的类
RuntimeException
,所有的运行时异常都需要继承它,而除此之外的异常我们都将其归类为编译时异常或者检查异常。
异常处理 try-catch
可以看下面这个例子:
int[] arr = {12,32,34};
try {
System.out.println(arr[3]);//尝试访问越界索引,会被catch捕获
}catch (ArrayIndexOutOfBoundsException e){//异常类型 名称
e.printStackTrace(); //在命令行打印异常信息在程序中出错的位置及原因。
System.out.println("数组下标越界异常"); //自定义catch内代码
System.out.println("catch语句继续往下执行");
}
当程序抛出一个异常后,在异常代码处跳出,Java虚拟机检测和寻找和try关键字匹配的处理该异常的catch块。如果找到,将控制权交给catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行;如果没有找到处理该异常的catch块,最终运行时会报错。
处理多个异常
int[] arr = {12,32,34};
try {
System.out.println(arr[3]);
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("数组下标越界异常");
System.out.println("catch语句继续往下执行");
}catch (RuntimeException e ){
System.out.println("父类异常");
}catch (Exception e ){
System.out.println("总异常");
}
当代码块中的代码可能有多个异常时,可以使用连起来的catch块。
如果同时存在父类和子类异常,必须把子类异常放在前面,如果子类异常已经能够处理,父类异常就不会再处理。
上面的方式看起来很冗余,如果是没有继承关系的异常,可以这么写:
public static void main(String[] args) {
int[] arr = {12, 32, 34};
try {
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException | NullPointerException | ArithmeticException e) {
e.printStackTrace();
}
}
如果编译时异常的数量实在太多,添加很麻烦,你可以用它们的父类直接代替,比如用Exception,能处理所有异常。
public static void main(String[] args) {
int[] arr = {12, 32, 34};
try {
System.out.println(arr[3]);
} catch (Exception e) {
e.printStackTrace();
}
}
异常处理 finally
int[] arr = {12,32,34};
try {
System.out.println(arr[3]);
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("数组下标越界异常");
System.out.println("catch语句继续往下执行");
}finally {
System.out.println("释放资源,此语句必须执行");
}
- finally语句为异常处理提供的一个统一的出口,使得在控制流程到程序其它部分之前,能够对程序的状态作统一的管理。
- 无论try所指的程序是否抛出异常,finally所指定的代码必须执行。
- 通常finally在语句中可以进行资源的释放工作,如关闭打开的文件,关闭数据库连接等。
throws
程序中会声明很多方法,这些方法中可能会因某些错误而引发异常,但不希望在这个方法中处理这些异常,而希望调用它的方法统一处理,这时候就需要使用throws
关键字声明在这个方法上抛出异常。
- 运行时异常可以不处理,出现问题后,需要我们回来修改代码。
/**
* throws抛运行时异常
*/
static public void throwsDemo1() throws ArrayIndexOutOfBoundsException{
int[] arr = {12,32,34};
System.out.println(arr[3]);
}
- 编译时异常必须要处理
/**
* throws抛编译时异常
*/
static public void throwsDemo2() throws ParseException {
String date = "2045-2-23";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse(date);
System.out.println(date1);
}
try-catch
与throws
的区别?
try-catch即时处理,处理后就不需要再处理了。
throws是将来谁调用谁来处理。
如果一直throws,需要throws到入口程序
public static void main(String[] args)throws ArrayIndexOutOfBoundsException,ParseException {
throwsDemo1();
throwsDemo2();
}
throw
- 当程序发生错误而无法处理时,会抛出对应的异常对象,除此之外,若想要自行抛出异常,此时需要用到“throw”关键字,并生成指定的异常对象后抛出。
- throw定义在方法内,用来抛出一个异常对象。
/**
* throw关键字
*/
static public void throwDemo3(int[] arr){
if (arr == null) {
throw new NullPointerException("访问的为数组空");
}
}
RuntimeException
RuntimeException类属于非检查异常,因为普通JVM操作引起的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在Java应用程序中会频繁出现。因此它们不受编译期检查与处理或者声明规则的限制。
常见RuntimeException异常
子类异常 | 说明 |
---|---|
NullPointerException | 当应用程序试图在需要对象的地方使用null时,抛出该异常 |
ArrayIndexOutOfBoundsException | 当使用的数组下标超出数组允许范围时,抛出该异常 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当的格式时,抛出该异常 |
自定义异常
异常机制可以保证程序更安全和健壮。虽然类库已经提供很多可以直接处理异常的类,但是有时候为了更加精准地捕获和处理异常以呈现更好的用户体验,需要开发者自定义异常。
public class ScoreException extends RuntimeException{
public ScoreException(){
super();
}
public ScoreException(String message){
super(message);
}
}
public class TestScoreException {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int score = sc.nextInt();
checkScore(score);
}
static public void checkScore(int score) {
if (score < 60 || score > 100) {
throw new ScoreException("输入的分数不在在指定范围内:60-100");
} else {
System.out.println(score);
}
}
}
/**
* @Description: 父类构造抛出编译时异常
*/
public class Son1 extends Father{
public Son1() throws ParseException {
super();//这里super必须在第一行,不能使用try-catch
//只能通过throws来处理
}
}
/**
* @Description: 父类构造抛出运行时异常
*/
public class Son2 extends Father {
public Son2(){
super(new Object());//不抛出异常
}
}
垃圾回收机制GC
垃圾回收机制
- 垃圾回收机制简称GC,它是JVM自带的一个线程,用于回收没有任何指向的对象;
- Java程序员不用考虑内存管理,因为垃圾收集器会自动进行回收管理;
- 一般情况下,当我们需要GC线程即刻回收无用对象时,可以调用
System.gc()
方法; System.gc()
用于建议虚拟机马上调度GC线程回收资源,具体的实现由JVM决定。
finelize()
是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用。
内存泄漏
-
不再使用的内存没有被及时回收,严重的内存泄漏会因过多的内存占用而导致程序的崩溃;
-
GC线程判断对象是否可以回收的依据是该对象是否有引用指向,因此,当该对象不再使用时,应该及时将其引用设置为null。