Java学习 11.23(2)

Java第八天异常

1.1 数组工具类

封装:将重复代码写到函数中,需要使用的时候 通过函数调用形式让代码执行。
   
面向对象思想始于封装。

两种情况:1 类的封装[数据模型类封装  其他类封装 ]  2 工具类封装 [数据工具类封装   ArrayTool]
   
   
你能封装一个工具类 其他人也能,官方就封装了一个 Arrays
Arrays.equals()      判断俩数组是否完全一样
Arrays.sort()        数组排序
Arrays.toString()    将数组转换成格式化字符串
Arrays.copyOf()      克隆数组
Arrays.copyOfRange() 提取范围  前闭后开
Arrays.binarySearch()  查找出现的索引 没找到返回-1  

1.2 为什么需要异常


我们写过一个函数 getMax()  有问题

public class ArrayTool {

   public static int getMax(int[] arr){
       int max = arr[0];
       for(int i=1;i<arr.length;i++){
           if(arr[i]>max){
               max = arr[i];
          }
      }
       return max;
  }

}
当一个数组里面一个数据都没有的时候  就没有最大值一说了。
   @Test
   public void test(){

       int[] arr = {};
       int a = ArrayTool.getMax(arr);
       System.out.println(a);
   
  }

所以我们今后的代码 70%都是安全判断 30%代码是业务流程 所以我们获取最大值的函数也应该是 先判断是否能获取最大值
public static int getMax(int[] arr){
       
       if(arr.length == 0){
           return  -1;
      }
       
       int max = arr[0];
       for(int i=1;i<arr.length;i++){
           if(arr[i]>max){
               max = arr[i];
          }
      }
       return max;
  }

但是我们加了判断之后 发现了一个可怕的问题 条件成立该返回什么呢?
发现返回什么都不好使  返回0 -1 本身就有歧义  返回其他的类型又不匹配  所以说return什么都不行
   
所以java就造了一套新的返回形式  异常返回

我们以前说 有返回值的函数 一定要有一个可以执行的 return ,但是从今天开始 这句话就不要再说了  因为java对于返回值 有两种形式  
   
return 返回
throw  返回
   

1.3throw解决问题


public class ArrayTool {
   public static int getMax(int[] arr){
       if(arr.length == 0){
           throw new RuntimeException("?????? 数组为空");
      }
       int max = arr[0];
       for(int i=1;i<arr.length;i++){
           if(arr[i]>max){
               max = arr[i];
          }
      }
       return max;
  }
}

什么时候return  什么时候throw?
如果调用者传递的数据没有问题
   我们函数执行方能根据参数正常执行        那就正常return返回
   如果传递过来的数据有问题 导致无法正常执行 异常throw返回
public void test(){
   
       String  str = "hello world";
       char  a =  str.charAt(-50);
       System.out.println(a);
   
}    
public char charAt(int index) {
       if ((index < 0) || (index >= value.length)) {
           throw new StringIndexOutOfBoundsException(index);
      }
       return value[index];
}    

1.4 执行方抛出异常语法


throw 异常对象

1.5 异常的分类

 

Throwable 可抛出的
   Error    :错误,指的是程序内部出现问题,无法通过代码解决的 例如 OutOfMemoryError 内存超出错误,简单的说 就是内存不够了
            [说是代码无法解决 还是你代码的问题 例如 死循环]
   
   
   Exception:异常,程序传递数据的时候导致无法正常执行代码,通过异常表示。
         
             运行时异常  RuntimeException 和其子类 :程序运行起来之后才会出现的异常 例如:
   
             非运行时异常 RuntimeException分支以外的都称之为非运行时异常:代码写完就会出现 必须要解决 不解决无法执行
             例如 Class.forName("123123123");

我们需要记住分类的情况 还要记住常用的异常.
   
   
   
   
   



序号 异常名称 异常描述

1 java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

2 java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。

3 java.lang.SecurityException 安全性异常

4 java.lang.IllegalArgumentException 非法参数异常

5 java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常

6 java.lang.NegativeArraySizeException 数组长度为负异常

7 java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。



序号 异常名称 异常描述

1 IOException 操作输入流和输出流时可能出现的异常

2 EOFException 文件已结束异常

3 FileNotFoundException 文件未找到异常



序号 异常名称 异常描述

1 ClassCastException 类型转换异常类

2 ArrayStoreException 数组中包含不兼容的值抛出的异常

3 SQLException 操作数据库异常类

4 NoSuchFieldException 字段未找到异常

5 NoSuchMethodException 方法未找到抛出的异常

6 NumberFormatException 字符串转换为数字抛出的异常

7 StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常

8 IllegalAccessException 不允许访问某类异常

9 InstantiationException

当应用程序试图使用Class类中的newInstance()方法创建

一个类的实例,而指定的类对象无法被实例化时,抛出该异常

10 java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

1.6 调用方处理异常

A 异常转移(默认方案)

   当执行方抛出异常给调用方的时候 ,此时调用方可以使用异常转移的形式来处理异常。
   异常转移的语法:
   @Test
   public void test() throws ClassNotFoundException {  
       Class.forName("123123123");
  }
   在函数的参数后面添加一个 throws 关键字,后面写要转移的异常类。
   此时当前异常会转移给当前函数的调用者。  上层调用者需要去处理本次异常,如果上层一直选择转移,最后就会转移到JVM。JVM就会帮我们去处理异常。
   JVM处理异常的方式非常简单:终止当前程序 打印异常信息到控制台

异常转移可以转移多种异常
    @Test
   public void test() throws ClassNotFoundException, FileNotFoundException {

       Class.forName("asdf");

       new FileOutputStream("123");

  }
也可以转移一个总异常
   @Test
   public void test() throws Exception {

       Class.forName("asdf");

       new FileOutputStream("123");

  }

 

B 异常捕获

通过 try{}catch(Exception e){} 代码块进行异常捕获
   
@Test
public void test() {
       try {
           Class.forName("asdf");
      } catch (ClassNotFoundException e) {
           e.printStackTrace();
      }
  }

try{
   // 可能出现异常的代码
   
}catch(异常类型   名字){
   //出现异常之后的解决方案
}

1.7 catch中异常处理方案

  @Test
   public void test() {

       try {
           int  a = 0;
           int  b = 0;
           int  i = a/b;
           System.out.println(i);
      }catch (Exception e){
            /*1 打印异常跟踪栈信息*/
            e.printStackTrace();
            /*2 记录日志*/
            /*3 事务回滚*/
      }

       System.out.println("123123123");
  }

并且catch可以捕获多个异常:分类捕获
@Test
   public void test() {

       try {

           Class.forName("123");
           new FileOutputStream("123");

      } catch (ClassNotFoundException e) {
           e.printStackTrace();
      } catch (FileNotFoundException e) {
           e.printStackTrace();
      }

  }

但是不能把总异常写到前面
@Test
   public void test() {

       try {

           Class.forName("123");
           new FileOutputStream("123");

      } catch (Exception e){
           e.printStackTrace();
      } catch (ClassNotFoundException e) {
           e.printStackTrace();
      } catch (FileNotFoundException e) {
           e.printStackTrace();
      }

  }
因为异常的catch是从上往下依次查找执行的  所以我们捕获总异常一般写到最后用来兜底
@Test
   public void test() {

       try {

           Class.forName("123");
           new FileOutputStream("123");

           System.out.println("12313123123123");
           System.out.println("12313123123123");
           System.out.println("12313123123123");

      } catch (ClassNotFoundException e) {
           e.printStackTrace();
      } catch (FileNotFoundException e) {
           e.printStackTrace();
      } catch (Exception e){
           e.printStackTrace();
      }

  }
此时 我们的catch 是分类捕获 还是 只捕获总的呢?
分情况: 如果我们不一样的异常处理方案不一样 就需要分类捕获  如果所有的异常处理方案都一样 则直接捕获总的

 


1  封装函数
2  调用者传递参数可能非法
3  执行方 安全判断 有问题的异常返回  throw  异常对象
4  异常的分类  
   Throwable  Error  Exception : 运行时 非运行时

5  调用方处理异常
  A 异常转移 不处理默认走转移 转移方式 函数后面添加 throws 异常类名 可以写多个 如果一直转就到了JVM 就终止了 打印了
  B 异常捕获 通过try{}catch(){} try中写可能出现异常的代码  catch用来在try中代码出现异常之后 处理异常
       
6 catch 特点: 1 try中出现问题 则直接去catch中执行 接下来try的代码不执行了
              2 catch也不是精准制导 而是从上往下依次询问 所以总异常不能写到最上面 应在在最后兜底
              3 有的时候我们只写一个总的 因为我们不需要分类处理 统一处理的时候只捕获 Exception

1.8 finally代码块


我们之前讲过final关键字,代表最终的。今天是finally代码块 代表最终执行的代码。也就是说写到finally中的代码 铁定最后要执行

   @Test
   public void test() {

       try {
           int i = 1/0;
      }catch (Exception e){
           e.printStackTrace();
      }finally {
           // 以后我们关闭的代码写到这里面
           System.out.println("123");
      }
  }

面试题:
1 final finally 和  finalize 的区别?  
2 try catch  finally 哪些可以组合使用
   try-catch   try-catch-finally  try-finally
3 在try中有个return 请问finally中的代码还执行吗? return前执行还是return后执行?    
public class ArrayTool {
   public static  int  haha(){
       try {
           return  666;
      }finally {
           System.out.println("你好世界");
      }
  }
}

如果finally中也有个return 请求到底是哪个生效
public class ArrayTool {

   public static  int  haha(){
       try {
           return  666;
      }finally {
           return  777;
      }
  }
}

1.9 自定义异常



首先我们看一下 Throwable 源码
   public void printStackTrace() {
       printStackTrace(System.err);
  }
看一下Exception 源码
public class Exception extends Throwable {
   static final long serialVersionUID = -3387516993124229948L;
   public Exception() {
       super();
  }
   public Exception(String message) {
       super(message);
  }

   public Exception(String message, Throwable cause) {
       super(message, cause);
  }

   public Exception(Throwable cause) {
       super(cause);
  }
   protected Exception(String message, Throwable cause,
                       boolean enableSuppression,
                       boolean writableStackTrace) {
       super(message, cause, enableSuppression, writableStackTrace);
  }
}

public
class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException {
   private static final long serialVersionUID = -5116101128118950844L;

   /**
    * Constructs an <code>ArrayIndexOutOfBoundsException</code> with no
    * detail message.
    */
   public ArrayIndexOutOfBoundsException() {
       super();
  }

   /**
    * Constructs a new <code>ArrayIndexOutOfBoundsException</code>
    * class with an argument indicating the illegal index.
    *
    * @param   index   the illegal index.
    */
   public ArrayIndexOutOfBoundsException(int index) {
       super("Array index out of range: " + index);
  }

   /**
    * Constructs an <code>ArrayIndexOutOfBoundsException</code> class
    * with the specified detail message.
    *
    * @param   s   the detail message.
    */
   public ArrayIndexOutOfBoundsException(String s) {
       super(s);
  }
}


我们发现 整个集成体系中 子类基本上什么都没有写 全都是调用父类Throwable的函数  子类就写了个构造函数 构造函数还是调用父类的构造函数
发现Throwable的子类一点新东西都没有,那为什么还要建立这一套继承体系呢?
   
这样做的目的 为了有语义化
   
假如没有这些子类  此时所有的以异常只能Throwable表示,代码实现的角度将没有毛病,但是从语义的角度将 我们就很难区分此时的异常到底是 越界了?还是类找不到了?还是文件找不到了?

 


java的设想非常好 ,但是无法将全部的异常情景给罗列出来,例如 我现在遇到一个问题 账号不存在 ,没有一个异常能语义化表达这层
所以我们可以自定义异常

public class UsernameNotFoundException extends RuntimeException {
   private static final long serialVersionUID = -5116101128118950844L;

   /**
    * Constructs an <code>ArrayIndexOutOfBoundsException</code> with no
    * detail message.
    */
   public UsernameNotFoundException() {
       super();
  }
   /**
    * Constructs an <code>ArrayIndexOutOfBoundsException</code> class
    * with the specified detail message.
    *
    * @param   s   the detail message.
    */
   public UsernameNotFoundException(String s) {
       super(s);
  }
}

 

posted @ 2021-11-23 18:05  吴光熠  阅读(70)  评论(0)    收藏  举报