异常

异常简介:
1、很多事情你很难能够和正常情况下一样,比如出去旅游。
出发-到达目的地-旅游-烧烤-回家,但是当你准备烧烤时来个倾盆大雨----这就是异常
2、所以程序野有异常:表示在程序运行时的一种情况。
3、所以java中的异常会终端执行的程序,所以为了代码健壮性,我们需要异常类处理。
4、java发生异常的三种原因:
a、java内部错误发生异常,也就是虚拟机产生的异常。
b、编写程序产生的异常,如数组越界、空指针
c、通过throw手动产生异常来告知调用者必要信息。
5、 java是通过面向对象的方法来处理异常,在一个方法运行时发生了异常,则这个方法会产生一个异常
的对象,交给运行时系统处理。
a、交给运行时系统过程叫做抛出(throw)异常
b、运行时系统通过调用栈找到该异常的的对象,叫做捕获(catch)异常
看个例子:
public class Test01{
public static void main(String[] args){
System.out.println("请输入1~3之间的整数");
Scanner input = new Scanner(System.in);
int num = input.nextInt();
switch(num){
case 1:
System.out.println("one"); break;
case 2:
System.out.println("two");break;
case 3:
System.out.println("three");break;
default:
System.out.println("error");break;
}
}
}
当你输入一个非1~3的数时,如a.---那么会报Exception in thread "main" java.util.InputMismatchException

6、异常类型都是java内置类Throwable的子类:如下图


7、根据图分析:我们可以将异常分为Error和Exception异常
a、Error是灾难性的,一般是非程序可以控制的,一般JVM错误产生,如堆栈溢出
b、Exception是程序出现的错,分为运行时异常和非运行时异常。
也可以叫做不检查异常和检查异常。
运行时异常:RuntimeException. 如:ArithmeticException(算数异常)---可捕获处理也可不捕获处理
非运行时异常:Exception子类非RuntimeException.如:ClassNotFoundException--必须捕获处理
如下图:

如下是常见的 Error 和 Exception:

1)运行时异常(RuntimeException):
NullPropagation:空指针异常;
ClassCastException:类型强制转换异常
IllegalArgumentException:传递非法参数异常
IndexOutOfBoundsException:下标越界异常
NumberFormatException:数字格式异常

2)非运行时异常:
ClassNotFoundException:找不到指定 class 的异常
IOException:IO 操作异常

3)错误(Error):
NoClassDefFoundError:找不到 class 定义异常
StackOverflowError:深递归导致栈被耗尽而抛出的异常
OutOfMemoryError:内存溢出异常

下面代码会导致 Java 堆栈溢出错误
/ 通过无限递归演示堆栈溢出错误
class StackOverflow {
public static void test(int i) {
if (i == 0) {
return;
} else {
test(i++);
}
}
}
public class ErrorEg {
public static void main(String[] args) {
// 执行StackOverflow方法
StackOverflow.test(5);
}
}

=========================================
==========================================

Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常

catch可以多个,throw抛出提示,throws向上抛出处理不来的异常

public class FiveKeyWord {
public static void main(String[] args) throws Exception {
test(9);
}
private static void test(int i){
int a;
try{
a=i/0;
}catch(ArithmeticException e){
System.out.println("算数异常");
}catch(Exception e){
throw(e);//向外抛出异常
}finally {
System.out.println("释放资源");
}
}
}

============================

==================

异常结构体一:

try{发生异常位置}catch(ExceptionType e){处理异常}

{}不能省略,try里没有异常程序就会跳过catch,有的话就跳到catch里执行。

  知道有三个输出异常方法就可以:

  • printStackTrace() 方法:指出异常的类型、性质、栈层次及出现在程序中的位置。
  • getMessage() 方法:输出错误的性质。---什么错误,需要输出流打印syso(e.getMessae())
  • toString() 方法:给出异常的类型与性质。---需要输出流打印syso(e.getMessae())

 异常结构体二:

注意:a、当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。

   b、执行和上面一样。在多个 catch 代码块的情况下,当一个 catch 代码块捕获到一个异常时,其它的 catch 代码块就不再进行匹配。

  1. try {
  2. // 可能会发生异常的语句
  3. } catch(ExceptionType e) {
  4. // 处理异常语句
  5. } catch(ExceptionType e) {
  6. // 处理异常语句
  7. }

 

例子:利用异常,写输入班级总人数,总分数,计算平均分

public class ExceptionAvgScore {
public static void main(String[] args) {
Scanner input =new Scanner(System.in);
try{
System.out.println("请输入班级总人数:");
int count = input.nextInt();
System.out.println("请输入班级总成绩");
int score = input.nextInt();
int avg = score/count;//获取平均分
System.out.println(avg);
}catch(InputMismatchException e1){
System.out.println("输入数值错误");
}catch(ArithmeticException e2){
System.out.println("计算总是不能为0");
}catch(Exception e3){
e3.printStackTrace();
System.out.println("发生错误|"+e3.getMessage());
}
}
}

异常结构三

  1. try {
  2. // 可能会发生异常的语句
  3. } catch(ExceptionType e) {
  4. // 处理异常语句
  5. } finally {
  6. // 清理代码块
  1. try {
  2. // 逻辑代码块
  3. } finally {
  4. // 清理代码块
  5. }

 

在实际开发中,根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。例如,程序在 try 块里打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。

Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只回收堆内存中对象所占用的内存

所以为了确保一定能回收 try 块中打开的物理资源,异常处理机制提供了 finally 代码块,并且 Java 7 之后提供了自动资源管理。

try必须一个,catch非必需可多个,finally非必需可多个。执行顺序t-c-f.

 ===============================================

============================================

try-return---注意下面两种类型,基本类型和引用类型。

 private int testReturn1() {
 2         int i = 1;
 3         try {
 4             i++;
 5             System.out.println("try:" + i);
 6             return i;
 7         } catch (Exception e) {
 8             i++;
 9             System.out.println("catch:" + i);
10         } finally {
11             i++;
12             System.out.println("finally:" + i);
13         }
14         return i;
15     }
输出:try:2
finally:3
2

 private List<Integer> testReturn2() {
 2         List<Integer> list = new ArrayList<>();
 3         try {
 4             list.add(1);
 5             System.out.println("try:" + list);
 6             return list;
 7         } catch (Exception e) {
 8             list.add(2);
 9             System.out.println("catch:" + list);
10         } finally {
11             list.add(3);
12             System.out.println("finally:" + list);
13         }
14         return list;
15     }
输出:try:[1]
finally:[1, 3]
[1, 3]




二、catch中带有return
1     private int testReturn3() {
 2         int i = 1;
 3         try {
 4             i++;
 5             System.out.println("try:" + i);
 6             int x = i / 0 ;
 7         } catch (Exception e) {
 8             i++;
 9             System.out.println("catch:" + i);
10             return i;
11         } finally {
12             i++;
13             System.out.println("finally:" + i);
14         }
15         return i;
16     }
输出:try:2
catch:3
finally:4
3



三、finally中带有return
 1 private int testReturn4() {
 2         int i = 1;
 3         try {
 4             i++;
 5             System.out.println("try:" + i);
 6             return i;
 7         } catch (Exception e) {
 8             i++;
 9             System.out.println("catch:" + i);
10             return i;
11         } finally {
12             i++;
13             System.out.println("finally:" + i);
14             return i;
15         }
16     }
输出:try:2
finally:3
3
======================================
========================================================

Java 9增强的自动资源管理

自动关闭资源的 try 语句相当于包含了隐式的 finally 块(这个 finally 块用于关闭资源),因此这个 try 语句可以既没有 catch 块,也没有 finally 块。

java7之前:当程序使用 finally 块关闭资源时,程序会显得异常臃肿

  1. public static void main(String[] args) {
  2. FileInputStream fis = null;
  3. try {
  4. fis = new FileInputStream("a.txt");
  5. } catch (FileNotFoundException e) {
  6. e.printStackTrace();
  7. } finally {
  8. // 关闭磁盘文件,回收资源
  9. if (fis != null) {
  10. try {
  11. fis.close();
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }

java7开始:Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件,被称为自动资源管理(Automatic Resource Management)。该特性是在 try 语句上的扩展,主要释放不再需要的文件或其他资源。

当 try 代码块结束时,自动释放资源。不再需要显式的调用 close() 方法,该形式也称为“带资源的 try 语句”。

注意:

  1. try 语句中声明的资源被隐式声明为 final,资源的作用局限于带资源的 try 语句。
  2. 可以在一条 try 语句中声明或初始化多个资源,每个资源以;隔开即可。
  3. 需要关闭的资源必须实现了 AutoCloseable 或 Closeable 接口。

Closeable 是 AutoCloseable 的子接口,Closeable 接口里的 close() 方法声明抛出了 IOException,

因此它的实现类在实现 close() 方法时只能声明抛出 IOException 或其子类;

AutoCloseable 接口里的 close() 方法声明抛出了 Exception,

因此它的实现类在实现 close() 方法时可以声明抛出任何异常。

 

  1. public class AutoCloseTest {
  2. public static void main(String[] args) throws IOException {
  3. try (
  4. // 声明、初始化两个可关闭的资源
  5. // try语句会自动关闭这两个资源
  6. BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
  7. PrintStream ps = new PrintStream(new FileOutputStream("a.txt"))) {
  8. // 使用两个资源
  9. System.out.println(br.readLine());
  10. ps.println("C语言中文网");
  11. }
  12. }
  13. }

 

java9:不要求在 try 后的圆括号内声明并创建资源,只需要自动关闭的资源有 final 修饰或者是有效的 final 

Java 7 几乎把所有的“资源类”(包括文件 IO 的各种类、JDBC 编程的 Connection 和 Statement 等接口)进行了改写,改写后的资源类都实现了 AutoCloseable 或 Closeable 接口。

  1. public class AutoCloseTest {
  2. public static void main(String[] args) throws IOException {
  3. // 有final修饰的资源
  4. final BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
  5. // 没有显式使用final修饰,但只要不对该变量重新赋值,该变量就是有效的
  6. final PrintStream ps = new PrintStream(new FileOutputStream("a. txt"));
  7. // 只要将两个资源放在try后的圆括号内即可
  8. try (br; ps) {
  9. // 使用两个资源
  10. System.out.println(br.readLine());
  11. ps.println("C语言中文网");
  12. }
  13. }
  14. }

===============================================

==========================================================

throws 声明异常

throws Exception 1,Exception2,…{…}
使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;
如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。
JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。
方法---main---JVM,停止运行。-----------一直网上知道解决为止。
public void readFile() throws IOException,C,D... {}



子类不能大于父类,不然编译失败如下:
  1. public class OverrideThrows {
  2. public void test() throws IOException {
  3. FileInputStream fis = new FileInputStream("a.txt");
  4. }
  5. }
  6. class Sub extends OverrideThrows {
  7. // 子类方法声明抛出了比父类方法更大的异常
  8. // 所以下面方法出错
  9. public void test() throws Exception {
  10. }
  11. }

throw 拋出异常

ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类

如果不是就会编译失败:throw new String("拋出异常"); // String类不是Throwable类的子类

public class Throw {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名: ");
String username = input.next();
boolean con ;
try{
con = validateUserName(username);
if(con){
System.out.println("用户名输入正确");
}
}catch(IllegalArgumentException e){
System.out.println(e);
}
}
private static boolean validateUserName(String username) {
boolean flag =false;
if(username.length()>8){
for(int i=0;i<username.length();i++){
char ch = username.charAt(i);
if((ch>='0' &&ch<='9') ||(ch>='a'&&ch<='z')||(ch>='A'&& ch<='Z')){
flag=true;
}else{
flag=false;
throw new IllegalArgumentException("用户名只能由字母或数字组成");
}
}
}else{
throw new IllegalArgumentException("用户名必须大于8为");
}
return flag;
}
}

throws 与 throw区别:未必有异常,后者一定抛出了异常对象;

          作用在方法或类上,方法或类内;

          解决不了可以往上抛,只能自己解决。

====================================================================

==================================================

java7提供了多异常捕获,catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException e),

这样就不用写多个catch了.但是还是要遵循先子后父的规律。

 

==================

==============

自定义异常:

<class><自定义异常名><extends><Exception>
  1. public class MyException extends Exception {
  2. public MyException() {
  3. super();
  4. }
  5. public MyException(String str) {
  6. super(str);
  7. }
  8. }
  1. public class Test07 {
  2. public static void main(String[] args) {
  3. int age;
  4. Scanner input = new Scanner(System.in);
  5. System.out.println("请输入您的年龄:");
  6. try {
  7. age = input.nextInt(); // 获取年龄
  8. if(age < 0) {
  9. throw new MyException("您输入的年龄为负数!输入有误!");
  10. } else if(age > 100) {
  11. throw new MyException("您输入的年龄大于100!输入有误!");
  12. } else {
  13. System.out.println("您的年龄为:"+age);
  14. }
  15. } catch(InputMismatchException e1) {
  16. System.out.println("输入的年龄不是数字!");
  17. } catch(MyException e2) {
  18. System.out.println(e2.getMessage());
  19. }
  20. }
  21. }

 

============================

====================

写个用户名和密码验证

假设在某仓库管理系统的登录界面中需要输入用户名和密码,其中用户名只能由 6~10 位数字组成,密码只能有 6 位,任何不符合用户名或者密码要求的情况都视为异常,并且需要捕获并处理该异常。

  1. public class LoginException extends Exception {
  2. public LoginException() {
  3. super();
  4. }
  5. public LoginException(String msg) {
  6. super(msg);
  7. }
  8. }
  1. public static void main(String[] args) {
  2. Scanner input = new Scanner(System.in);
  3. System.out.println("用户名:");
  4. String username = input.next();
  5. System.out.println("密码:");
  6. String password = input.next();
  7. Test08 lt = new Test08 ();
  8. boolean con = lt.validateLogin(username,password); // 调用 validateLogin() 方法
  9. if (con) {
  10. System.out.println("登录成功!");
  11. }
  12. }
  1. public boolean validateLogin(String username,String pwd) {
  2. boolean con = false; // 用户名和密码是否正确
  3. boolean conUname = false; // 用户名格式是否正确
  4. try {
  5. if (username.length() >= 6 && username.length() <= 10) {
  6. for (int i = 0;i < username.length();i++) {
  7. char ch = username.charAt(i); // 获取每一个字符
  8. if (ch >= '0' && ch <= '9') { // 判断字符是否为0~9的数字
  9. conUname = true; // 设置 conUname 变量值为 true
  10. } else {
  11. // 如果字符不是0~9的数字,则拋出LoginException异常
  12. conUname = false;
  13. throw new LoginException("用户名中包含有非数字的字符!");
  14. }
  15. }
  16. } else {
  17. // 如果用户名长度不在6~10位之间,拋出异常
  18. throw new LoginException("用户名长度必须在6〜10位之间!");
  19. }
  20. if (conUname) { // 如果用户名格式正确,判断密码长度
  21. if (pwd.length() == 6) {
  22. // 如果密码长度等于6
  23. con=true; // 设置con变量的值为true,表示登录信息符合要求
  24. } else { // 如果密码长度不等于6,拋出异常
  25. con = false;
  26. throw new LoginException("密码长度必须为 6 位!");
  27. }
  28. }
  29. } catch(LoginException e) {
  30. // 捕获 LoginException 异常
  31. System.out.println(e.getMessage());
  32. }
  33. return con;
  34. }

 

=====================

==============================

Java完善除法运算的错误信息

  1. public class Compute {
  2. private int[] num = new int[2];
  3. public int[] getNum() {
  4. return num;
  5. }
  6. public void setNum(int[] num) {
  7. this.num = num;
  8. }
  9. }
  1. public class Test04 {
  2. public static void main(String[] args) {
  3. Compute c = new Compute();
  4. int array[] = c.getNum();
  5. int res = 0;
  6. String YorN = null;
  7. Scanner in = new Scanner(System.in);
  8. try {
  9. System.out.println("请输入第一个整数:");
  10. array[0] = in.nextInt();
  11. System.out.println("请输入第二个整数:");
  12. array[1] = in.nextInt();
  13. res = array[0] / array[1];
  14. System.out.println("是否保存结果请输入Y或者N");
  15. YorN = in.next();
  16. if (YorN.equals("Y")) {
  17. array[2] = res;
  18. }
  19. System.out.println(array[0] + "除以" + array[1] + "的结果是:" + res);
  20. }
  21. }
  22. }
  1. catch (ArrayIndexOutOfBoundsException e) {
  2. System.out.println("出现数组越界错误,下标过大或者过小。");
  3. } catch(ArithmeticException e) {
  4. System.out.println("出现算术运算错误,被除数不能为0。");
  5. } catch(InputMismatchException e) {
  6. System.out.println("输入的数据类型不匹配,只能输入数字。");
  7. } catch(Exception e) {
  8. System.out.println("发生未知错误。");
  9. }

 

 

=======================

====================

异常使用注意点:

1、不要自定义继承RuntimException,直接if判断就可以提高代码健壮性。

2、try-catch:不要直接Exception,而是要一个一个从小到大写出所有异常。

3、异常捕捉处理:一个一个写出来,不要直接用一个模板写出抛给用户提示。类似上面直接Exception

4、事务:a、try在事物中,要手动提交事物;b、在catch要使事物有效,必须抛出

5、异常与finally:比如io,要从里层到外层关闭---------------------------JDK 7 以上可以使用try-with-resources 方式。

6、finally有return:执行完finally的return,方法结束

7、抛出异常需要精确捕获,就像抛绣球也要看着人抛

8、程序员的基本修养 & NPE(现有代码中出现率较高)---nullpointexception

1.方法(接口)的返回值可以为 null ,但不推荐返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。

2、防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景。

a.查询数据库返回null ,包括null 对象和null 集合。

b.集合内元素有null 对象。

 

========================

=====================================

Java的异常跟踪栈

printStack();

xception in thread "main" Test.SelfException: 自定义异常信息
        at Test.PrintStackTraceTest.thirdMethod(PrintStackTraceTest.java:26)
        at Test.PrintStackTraceTest.secondMethod(PrintStackTraceTest.java:22)
        at Test.PrintStackTraceTest.firstMethod(PrintStackTraceTest.java:18)
        at Test.PrintStackTraceTest.main(PrintStackTraceTest.java:14)

 

 

 

 

Java.util.logging:JDK自带记录日志类

日志用来记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。

Logger.getGlobal().info("打印信息");

 

 Logger 的默认级别是 INFO,比 INFO 级别低的日志将不显示。Logger 的默认级别定义在 jre 安装目录的 lib 下面。

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO

所以在默认情况下,日志只显示前三个级别,对于所有的级别有下面几种记录方法:

logger.warning(message);
logger.fine(message);

或者指定级别

logger.log(Level.FINE, message);

 

  1. log.info("info");
  2. log.warning("warning");
  3. log.severe("server");

十一月 27, 2019 5:13:05 下午 Test.Test main
信息: info
十一月 27, 2019 5:13:05 下午 Test.Test main
警告: warning
十一月 27, 2019 5:13:05 下午 Test.Test main
严重: server

修改日志管理器配置

可以通过编辑配置文件来修改日志系统的各种属性。在默认情况下,配置文件存在于 jre 安装目录下“jre/lib/logging.properties”

java -Djava.util.logging.config.file = configFile MainClass

 .level=INFO

Test.Test.level=FINE

java.util.logging.ConsoleHandler.level=FINE

注意:在日志管理器配置的属性设置不是系统属性,因此,用-Dcom.mycompany.myapp.level=FINE启动应用程序不会对日志记录器产生任何影响。

posted @ 2020-02-28 10:13  不良小帅  阅读(251)  评论(0编辑  收藏  举报