异常
异常概述
Java中将程序执行出现的一些意外情况分为两种:
-
错误
-
异常
错误(Error)
错误一般是JVM虚拟出现的一些内存不足,或者资源耗尽的系统级别错误(比如:栈溢出,JVM错误等),这些问题对于程序员来说无法从根本上解决。
异常(Exception)
异常指的是程序在编译或者执行过程中出现的一些外在因素导致的问题(比如:数组索引越界,空指针异常,类型转换异常等),这些问题程序员一般是可以解决的,因此异常需要重点关注。
异常分类
Java中将异常分为两大类,第一大类称之为编译时异常(一般异常/检查异常);第二大类称之为运行时异常(RuntimeException)
编译异常
编译异常也称之一般异常,检查异常;这种类型异常一般表现为在编译期间,由编译器直接抛出,需要由程序员进行手动处理。常见如下:
-
FileNotFoundException
-
IOException
-
ClassNotFoundException
-
ParseException
-
....
运行时异常
在编译期间正常通过,在JVM执行程序时,出现的异常称之为运行时(Runtime)异常,运行时异常是所有异常中最常见的异常,常见如下:
-
ArrayIndexOutOfBoundsException
-
NullPointerException
-
ArithmeticException
-
ClassCastException
-
...
异常处理
Java中的异常处理分为两种方式:
-
异常抛出(throw)
-
异常捕获(catch)
异常处理的常见关键字:
-
throws:一般作用于方法上,将有可能出现异常抛出,交给下一级调用者处理
-
throw:用于抛出一个异常对象,该行代码一旦执行,程序一定会出现该异常
-
try:try是一个语句块,用于将有可能出现的代码片段包裹
-
catch:catch语句块将有可能出现的异常对象进行捕获并作出处理(默认处理:将异常的堆栈信息输出到标准输出流)
-
finally:语句块,作用在异常处理中,finally语句块中的代码无论程序是否异常都会执行
Java中是否存在内存泄漏问题?
理论上Java中不存在内存泄漏问题,因为Java提供的GC(垃圾回收)机制,用于在适当的时候回收JVM中申请的内存空间;但是在实际开发中有可能使用了第三方资源(比如:文件,数据库连接,网络通道等),而这些资源没有在适当的时候关闭或者回收,将有可能引起系统级别的内存泄漏,因此为了保证这些资源能够被有效的关闭或者释放,一般在程序中的finally语句块中编写。
Java中final,finally,finalize的区别?
final是一个用于修饰类,属性,方法的关键字 finally是一个用于异常处理中的的语句块,用于处理不论是否出现异常都要执行的代码 finalize()是Object类中提供的一个用于检测对象是否达到回收标准的方法(清理对象)
异常抛出
异常抛出通常使用throws关键字,在方法的声明上将有可能出现的异常抛给下一级的调用者处理,异常抛出是一种实际开发中非常常见的处理异常的方式。
public class Demo2 { /**方法抛出了异常,将由下一级调用者处理*/ public void m1() throws FileNotFoundException{ FileInputStream fis = new FileInputStream("c:/a.txt"); } /**该方法调用了m1方法,因此需要处理m1中的异常,另外:m2方法可以抛出比m1方法范围更大的异常*/ public void m2() throws IOException{ System.out.println("第二个方法"); m1(); } /**检查异常最终可以抛给主方法由JVM进行最终处理(打印异常信息到标准输出流)*/ public static void main(String[] args) throws IOException{ new Demo2().m2(); } }
注意:通常异常的抛出为检查异常;运行时异常一般无需抛出,即便抛出运行时异常,下一级调用者也无需继续抛出或手动处理该异常,例如:
public class Demo2 { /**a方法抛出的为运行时异常*/ public void a() throws NullPointerException{ } /**b方法无需处理,也可以选择处理(编译器不会提示需要处理)*/ public void b(){ a(); } public static void main(String[] args) { new Demo2().b(); } }
throw
throw用于抛出异常,语法结构如下:
thorw 异常对象;
throw new Exception("出现异常啦");
throw这一行语句一旦执行,程序就一定会出现异常;throw主要用于程序编写过程中,程序员手动提出的异常(自定义异常)
异常抛出注意事项:
public abstract class Father { //父类方法未抛出任何异常 public abstract void drive(); }public class Son extends Father{ @Override public void drive(){ //子类方法实现过程中如果出现检查异常,则该异常必须捕获(不允许抛出) try { new PrintStream("d:/log.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
异常捕获
由于异常的抛出是一种临时性解决方案,最终异常还是需要有处理机制,因此异常的捕获就是为了解决这些问题而存在的另一种异常处理方案。异常捕获的语法结构
try{
//有可能出现异常的代码片段
}catch(异常类型 引用){
//异常的解决(打印异常信息到标准输出流/记录到日志文件)
}finally{
//不论是否出现异常都会执行(一般用于回收资源等操作)
}
public class Demo5 { public static void main(String[] args) { FileInputStream fis = null; try { //捕获有可能出现异常的语句块 fis = new FileInputStream("d:/nginx服务器配置模板"); } catch (FileNotFoundException e) { //一旦出现匹配的异常类型,则进入catch语句块 e.printStackTrace(); } finally{ //无论是否出现异常,都会进入finally语句块 try { if(fis != null){ fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
思考如下题目
public int m(){ int i = 10; try { // System.out.println(i/0); i = 20; return i; } catch (Exception e) { e.printStackTrace(); } finally{ System.out.println("finally被执行"); i = 30; } return i; }
异常出现后的关注事项:
异常的类型
异常出现位置的关键字
第一次出现的位置(行号)
自定义异常
在实际开发中JDK中内置的异常类并不能满足所有的异常需求,比如一些业务层面的异常,因此,自定义异常就十分有必要了,自定义异常类需要从java.lang.Exception类继承
public class MoneylessException extends Exception{ private int status; public MoneylessException(int status,String msg) { super(msg); this.status = status; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } }
public class TestAccount { public void change(Account a,Account b,double cash) throws MoneylessException{ if(a.getMoney() < cash){ //余额不足 throw new MoneylessException(1, "账户余额不足"); } } public static void main(String[] args) throws MoneylessException { Account a = new Account(1,30000); Account b = new Account(1,500); new TestAccount().change(a, b, 35000); } }