异常
- Java当中所有"问题"的体系结构
Throwable
[可以向外抛出的]
Error Exception
[错误] [异常]
⬇️
RuntimeException
[运行时异常]
- Error 和 Exception 有什么区别
Error 通常是指由于【硬件环境】或者【系统原因】导致的,相对较严重的问题,比如说内存OutOfMemoryError
Exception 就是指程序运行过程当中出现的例外情况而已,相对较轻
//OutOfMemoryError: Java heap space
import java.util.*;
public class TestError{
public static void main(String[] args){
//List<Integer> list = new ArrayList<>(2147483647);
List<Student> et2305 = new ArrayList<>();
while(true){
Student stu = new Student();
et2305.add(stu);
}
}
}
class Student{
byte[] data = new byte[1<<20];//1*1024*1024 相当于1mb内存
}
- 运行时异常 和 非运行时异常 有什么区别
运行时异常在编译的时候不要求给出处理方案,编译能够直接通过 问题在运行时直接体现(cmd终端报异常)
非运行时异常在编译的时候 就必须要求给出处理方案,否则编译不能通过(编译器控制台报错)
- 只要是个异常 一定都在运行的时候出现!!
常见的运行时异常有哪些(13种)
-
运算符:1
int num = 5; System.out.println(num / 0); //ArithmeticException算术异常
-
数组:2
int[] data = new int[-5]; //NegativeArraySizeException负数数组大小异常 int[] data = new int[]{1,2,3}; System.out.println(data[3]); //ArrayIndexOutOfBoundsException数组索引值超出边界异常
-
字符串:3
String str = null; System.out.println(str.length()); //NullPointerException空指针异常 String str = "etoak"; System.out.println(str.charAt(5)); //StringIndexOutOfBoundsException字符串索引值超出边界异常 String str = "123a"; Integer num = Integer.parseInt(str); System.out.println(num + 1); //NumberFormatException数字格式异常
-
类型转换:1
Person stu = new Student(); Teacher tea = (Teacher) stu; System.out.println(tea); //ClassCastException类型造型异常 class Person{} class Student extends Person{} class Teacher extends Person{}
-
集合:4
List<Integer> list = new ArrayList<>(-7); //IllegalArgumentException非法参数异常 List<Integer> list = new ArrayList<>(); Collections.addAll(list,11,22,33); System.out.println(list.get(3)); //IndexOutOfBoundsException索引值超出边界异常 List<Integer> list = new ArrayList<>(); Collections.addAll(list,11,22,33); Iterator<Integer> car = list.iterator(); car.remove(); //IllegalStateException非法状态异常 List<Integer> list = new ArrayList<>(); Collections.addAll(list,11,22,33); Iterator<Integer> car = list.iterator(); while(car.hasNext()){ Integer value = car.next(); list.add(12); } //ConcurrentModificationException并发修改异常 //如果在迭代过程中修改了集合的内容,就会抛出该异常
-
线程: 2
Thread thread = new Thread(); thread.start(); thread.start(); //IllegalThreadStateException:线程状态非法异常,例如在线程非活动状态调用启动方法。 public class IllegalMonitorStateExceptionExample { public static void main(String[] args) { final Object lock = new Object(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }); // Start the first thread thread1.start(); Thread thread2 = new Thread(new Runnable() { @Override public void run() { //synchronized(lock){ lock.notify();//没有获得锁标记就调用notify方法,出现异常 //} } }); // Start the second thread thread2.start(); } } //IllegalMonitorStateException:非法锁标记状态异常,必修拿到锁标记,如果不拿到锁标记调用wait、notify()方法会出现此异常
为什么要处理异常?
A.非运行时异常如果不做处理 编译都无法通过
B.运行时异常,一旦程序运行过程当中出现未作处理的异常,将直接被虚拟机中断,异常代码处以下所有的代码将都不会执行
如何处理异常?
-
抛还上级(throws)
throws 出现在方法签名的最后,用于表达本方法出现指定种类的异常,方法当中不做处理,直接抛还给调用的上级进行处理,足以解决非运行时异常,但无力解决运行时异常
public class TestThrows{ //学生会主席 public static void main(String[] args) throws Exception{ first(); System.out.println("GOGOGO"); } //各部门部长 public static void first() throws Exception{ second(); } //各部门干事 public static void second() throws Exception{ Runtime.getRuntime().exec("notepad");//打开记事本 } }
-
自行处理(try catch finally)
-
语法结构
try{ //可能出现异常的语句; }catch(Exception(要捕获的异常种类) e(异常代号) ){ /* 对捕获到的异常进行处理: 0.隐瞒不报,直接隐藏异常 1.简要的审 e(异常代号).getMessage(); 2.详细的审 e(异常代号).printStackTrace(); 3.谎报 throw new XxxxException(); */ }finally{ //无论是否出现异常,最终都要执行的操作,通常是释放和关闭资源的操作; }
-
一个try 后面可以跟上多个不同类型的catch,注意要求必须前小后大或者并列关系,总之不能前大后小的关系
try{ }catch(NullPointerException npe){ }catch(IndexOutOfBoundsException iofbe){ }catch(RuntimeException rte){ }catch(Exception e){ }finally{ }
-
如果多种不同种类的异常在捕获之后,想要进行相同的处理,在JDK7.0之前也必须将 catch 和其中操作写多次
从JDK7.0开始 支持多重捕获 catch(类型1 | 类型2 | 类型3 e){…}try{ }catch(NullPointerException | IndexOutOfBoundsException | IllegalStateException e){ //相同的处理.... }finally{ }
-
finally当中永远不该出现return语句,否则try catch当中的return,就都失去意义了
public class TestFinallyPlus{ public static void main(String[] args){ System.out.println(get(100)); System.out.println(get(50)); System.out.println(get(0)); System.out.println(get(50)); System.out.println(get(100)); } public static int get(int x){ try{ return 100 / x; }catch(Exception e){ e.printStackTrace(); return 0; }finally{ return 1212; } } } /* 打印结果⬇️ 1212 1212 java.lang.ArithmeticException: / by zero at TestFinallyPlus.get(TestFinallyPlus.java:11) at TestFinallyPlus.main(TestFinallyPlus.java:5) 1212 1212 1212 请按任意键继续. . . */
-
throw 和 throws的区别
“throw” 和 “throws” 是两个在编程中经常使用的关键字,用于处理异常(exception)。
- “throw”(动词)用于在程序中抛出(或抛出)异常。当某个条件满足时,你可以使用"throw"语句显式地抛出一个异常对象。例如:
throw new Exception("Something went wrong!");
- “throws”(关键字)用于在方法声明中指定该方法可能抛出的异常。当我们在方法声明中使用"throws"关键字时,它告诉调用者,“该方法可能会抛出指定的异常,请在调用此方法时进行适当的异常处理”。例如:
public void doSomething() throws IOException {
// 方法体
}
在上面的例子中,我们声明了一个"doSomething"方法,它可能会抛出"IOException"异常。因此,如果你在调用该方法时没有进行异常处理,编译器将发出警告或错误。
总结:
- “throw” 是用于抛出异常对象的动词,用于在代码中明确指定抛出异常的地方。
- “throws” 是方法声明中的关键字,用于指定该方法可能抛出的异常类型,提醒调用者进行适当的异常处理。
如何自定义异常
自己开发一个类 选择继承 Exception / RuntimeException( 非运行时异常 / 运行时异常),在其构造方法的首行使用super() 指定异常的描述信息
import java.util.*;
public class Exec1{
public static void main(String[] args){
/*try {
ShowSeason(-19);
} catch (IlleageSeasonException e) {
System.out.println(e.getMessage()); // 输出异常信息"月份异常"
}*/
ShowSeason(9);
ShowSeason(-19);
ShowSeason(1);
}
public static void ShowSeason(int month){
if(month < 0 || month >12){
//自定义异常
throw new IlleageSeasonException();
}
if(month <=3){
System.out.println("Spring");
}else if(month <= 6 ){
System.out.println("Summer");
}else if(month <= 9){
System.out.println("Autumn");
}else if(month <= 12){
System.out.println("Winter");
}
}
}
class IlleageSeasonException extends RuntimeException{
//继承RuntimeException的好处是可以将异常声明为unchecked异常,
//不需要在方法上显式声明或捕获该异常。这意味着在代码中使用该异常的方法时,
//不需要使用try-catch块来捕获这个异常,也不需要在方法签名中添加throws声明。
public IlleageSeasonException(){
//描述信息
super("月份异常");
//自定义异常类的构造方法被重写,并且调用了super关键字去调用父类RuntimeException的构造方法。
//super(“月份异常”)语句的作用是将字符串"月份异常"作为异常信息传递给父类RuntimeException。
}
}
当类体当中的某个静态属性是通过调用有异常声明的方法来完成赋值
-
当类体当中的某个静态属性,是通过调用有异常声明的方法来完成赋值的时候,我们不能在类体上直接throws
更不能在类体当中直接 try catch,此时必须借助静态初始化块在其中进行异常处理,而如果要初始化的是个非静态变量 ,则可以使用非静态的初始化块 / 构造方法public class TestExceptionPlus1{ public static void main(String[] args){ X x = new X(); System.out.println(x.a); } } class X{ static int a; static{ try{ a = get(); }catch(Exception e){ e.printStackTrace(); a = -1; } } public static int get() throws Exception{ int x = (int)(Math.random()*5);//0-4 if(x == 2 || x == 4){ throw new Exception("生成的数字不吉利"); } return x; } }
在方法覆盖的时候,如果父类的方法没有任何异常声明,子类的方法在覆盖它的时候,能不能向外抛出异常呢?
-
在方法覆盖的时候 如果父类的方法没有任何异常声明,子类的方法在覆盖它的时候,能不能向外抛出异常呢?
可以 但是只能抛出运行时异常(RuntimeException) 因为 Java当中每个方法默认都抛出所有的运行时异常 相当于 每一个方法的最后都有一行 throws RuntimeException 而我们写出几个运行时异常 并没有扩大其范围 但是这样的行为 没有任何意义
-
public class TestExceptionPlus2{
public static void main(String[] args)throws RuntimeException{
C cc = new C();
cc.test();
}
}
class A extends Object{
A() throws RuntimeException{ //A类的构造方法,默认不写,系统自动添加
super();
}
public void test() throws RuntimeException{//系统默认添加throws RuntimeException
System.out.println("这是A类的test方法");
}
}
class C extends A{
@Override
public void test()throws RuntimeException{
System.out.println("这是C类的test方法");
}
}
连环try~
当我们的代码逻辑当中出现连续的多个有异常声明的方法调用,我们希望无论前者调用是否出现异常,都要尝试执行后者,此时必须借助try catch finally的finally当中,嵌套使用try catch,我们把这种语法称作连环try~
import java.util.*;
public class Exec3{
public static void main(String[] args){
SLT s1 = new SLT();
SLT s2 = new SLT();
SLT s3 = new SLT();
try{
s1.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
s2.close();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
s3.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
class SLT{
public void close() throws Exception {
int x = (int)(Math.random()*2);//0 or 1
if(x == 0){
throw new Exception("宁坏了,关不上了");
}
System.out.println("colse normally!");
}
}
异常时变量的作用范围
-
为了进行异常处理而添加的try catch语法结构,是有大括号的,而这大括号能够控制变量的作用范围,如果我们想要在下文程序当中继续使用这些变量,就不能在大括号当中定义它们,我们应该把变量的定义拿到try的前面去,以默认值赋值,在大括号当中只做重新赋值,而不做变量定义
public class TestExceptionPlus4{ public static void main(String[] args){ int num = 0; try{ num = get(); }catch(Exception e){ e.printStackTrace(); } System.out.println(num); /* x == 2 || x == 4时,打印初始值0 x == 1 || x == 3时,打印1或者3 */ } public static int get()throws Exception{ int x = (int)(Math.random()*5); if(x == 2 || x == 4){ throw new Exception("生成的数字不吉利"); } return x; } }
在某些场景下学会使用异常处理的方式代替传统分支和判断会有奇效!
public class TestExceptionPlus5{
public static void main(String[] args){
System.out.println(check("123发"));
}
/*
如果参数str的内容全是数字 则返回true 否则返回false
*/
public static boolean check(String str){
try{
Integer.parseInt(str);//如果全为数字,执行return true, 如果不全为数字,出现异常,执行catch中的 return false
return true;
}catch(Exception e){
return false;
}
/*
普通方法
for(int i = 0;i<str.length();i++){
char c = str.charAt(i);
if(c < '0' || c > '9'){
return false;
}
}
return true;
*/
}
}