图灵日记之java奇妙历险记--异常&&包装类&&泛型

慈云数据 6个月前 (05-28) 技术支持 60 0

目录

  • 异常概念与体系结构
    • 异常的分类
    • 异常的处理
      • 防御式编程
      • 异常的抛出
      • 异常的捕获
        • 异常声明throws
        • try-catch捕获并处理
        • 自定义异常类
        • 包装类
          • 基本数据类型及其对应包装类
          • 装箱和拆箱
          • 泛型
            • 泛型使用
              • 类型推导
              • 裸类型
                • 说明
                • 泛型的编译机制
                • 泛型的上界
                  • 语法

                    异常概念与体系结构

                    在java中,将程序执行过程中发生的不正常行为称为异常

                    异常的分类

                    异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为:

                    1. 编译时异常

                      在程序编译期间发生的异常,称为编译时异常,也称为受检查异常

                    2. 运行时异常

                      在程序执行期间发生的异常,.称为运行时异常也称为运行时异常

                      编译时出现的语法性错误,不能称为异常

                    异常的处理

                    防御式编程

                    错误在代码中是客观存在的,因此我们要让程序出现问题及时报错

                    1. 在检查前做好充分的检查:事前防御型

                      缺陷:正常流程和错误处理流程代码混在一起,代码显得乱

                    2. 先操作,出现问题再处理

                      优势:正常代码与错误代码分离开

                    try{
                    //可能报错的代码
                    }catch (捕获具体异常) {
                    }finally {
                    }
                    

                    在Java中,处理异常主要的5个关键字:throw,try,catch,final,throws

                    异常的抛出

                    如何抛出异常?

                    1. 程序触发
                    2. throw抛出异常

                      在Java中借助throw关键字,抛出指定的异常对象,手动抛出异常,一般用于抛出自定义的异常

                    throw new XXXException("异常原因");
                    

                    注意:

                    1. throw必须写在方法内部
                    2. 抛出的对象必须是Exception或Exception的子类对象
                    3. 如果抛出RunTimeException或其子类,则可以不处理,留给JVM处理
                    4. 抛出编译时异常要自己解决,否则无法通过编译
                    5. 异常一旦抛出,后面的代码将不会执行

                    异常的捕获

                    异常的捕获,也就是异常的处理方式,主要有两种,一种是try-catch捕获处理,另一种是异常声明throws

                    异常声明throws

                    使用在方法声明之后

                    告诉使用者这个方法会出现XX异常

                    如果一个方法内部存在一个编译时异常(受查异常),此时这个编译时异常一定要进行处理,目前的处理方式是在方法定义后面加上throws来声明该异常,该异常会留给JVM来处理

                    处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者.即当前方法不处理,提醒方法调用者处理异常

                    语法格式:
                    修饰符 返回值类型 方法名(参数列表) throws 异常类型1, 异常类型2... {
                    }
                    

                    注意:

                    1. throws必须在方法参数之后
                    2. 声明的异常必须是Exception或者Exception的子类
                    3. 方法内部如果抛出多个异常,throws之后必须跟多个异常类型,用逗号隔开,存在父子关系的,声明父类即可
                    4. 调用声明抛出异常的方法,调用者必须对该异常进行处理,或者继续使用throws抛出

                      throws抛出交给JVM处理,一旦交给JVM处理,程序就会异常终止

                    try-catch捕获并处理

                    throws并没有对异常进行处理,而是把异常报给了使用者,需要使用者进行处理.真正要对异常进行处理,要用try-catch

                            System.out.println("1");
                            System.out.println(1/0);
                            System.out.println("2");
                    

                    在这里插入图片描述

                    使用try-catch,catch里面的参数就是你要捕获的异常,如果捕获到了才会执行catch当中的内容

                            System.out.println("1");
                            try {
                                System.out.println(1/0);
                            } catch (ArithmeticException e) {
                                System.out.println("处理异常1");
                            }
                            System.out.println("2");
                    

                    在这里插入图片描述

                    如果存在多种异常,使用catch多个捕获

                    可以使用printStackTrace来具体追踪异常的位置

                            System.out.println("开始");
                            try {
                                int[] arr = new int[5];
                                System.out.println(arr[10]);
                            } catch (ArrayIndexOutOfBoundsException e) {
                                System.out.println("异常处理");
                                e.printStackTrace();
                            }
                            System.out.println("结束");
                    

                    在这里插入图片描述

                    打印的顺序好像不大对,printStackTrace的底层代码是使用其他的工具来打印的,可能运行时间不同,导致前后顺序不对

                            try {
                                int[] arr = new int[1];
                                System.out.println(arr[10]);
                            } catch (ArrayIndexOutOfBoundsException e) {
                                System.out.println("处理异常");
                            } catch (ArithmeticException e) {
                                System.out.println("处理异常");
                            }
                    

                    多次使用catch来捕获多个异常,也可以如下

                            try {
                                int[] arr = new int[1];
                                System.out.println(arr[10]);
                            } catch (ArrayIndexOutOfBoundsException |ArithmeticException e) {
                                System.out.println("处理异常");
                            }
                    

                    当在try中捕获到异常后,直接调转到catch,不会执行后面的代码,如下:

                            try {
                                int[] arr = null;
                                System.out.println(arr[0]);
                                System.out.println("abc");
                            } catch (NullPointerException e) {
                                System.out.println("处理异常");
                            }
                    

                    在这里插入图片描述

                    关于异常的处理方式

                    异常种类很多,取决于业务场景

                    对于比较严重问题(和钱相关的场景),让程序直接崩溃

                    对于不大严重的问题(大多数情形下),记录错误并通知

                    对于可能会回复的问题(和网络有关问题),可以尝试再次连接

                    注意

                    1. try内部抛出异常之后的代码不会再执行
                    2. 如果抛出异常与catch时类型不匹配,异常不会被捕获,也不会被处理,继续抛出,直到被JVM接收后中止程序
                    3. try可能抛出多个异常对象,必须使用多个catch来捕获

                      虽然能够捕获多个异常,但是同一时刻只能抛出一个异常

                            try {
                                int[] arr = null;
                                System.out.println(arr[0]);
                                System.out.println("abc");
                            } catch (Exception e) {
                                System.out.println("处理异常");
                            } catch (NullPointerException e) {
                                System.out.println("处理异常");
                            }
                    

                    不能使用父类来接受子类异常,会导致异常不精准

                    可以让父类在子类之后充当垫底的作用,但是放在子类之前子类将不会执行,因为子类能捕获的异常,父类一定能捕获

                            try {
                                int[] arr = null;
                                System.out.println(arr[0]);
                                System.out.println("abc");
                            } catch (NullPointerException e) {
                                System.out.println("处理异常");
                            } catch (Exception e) {
                                System.out.println("处理异常");
                            }
                    
                    public static void main(String[] args) {
                            System.out.println(mtd());
                        }
                        public static int mtd() {
                            try {
                                int[] arr = null;
                                System.out.println(arr[0]);
                            } catch (NullPointerException e) {
                                System.out.println("空指针异常");
                            } finally {
                                System.out.println("执行finally");
                            }
                            return 1;
                        }
                    

                    在这里插入图片描述

                        public static void main(String[] args) {
                            System.out.println(mtd());
                        }
                        public static int mtd() {
                            try {
                                int[] arr = new int[10];
                                System.out.println(arr[0]);
                            } catch (NullPointerException e) {
                                System.out.println("空指针异常");
                            } finally {
                                System.out.println("执行finally");
                            }
                            return 1;
                        }
                    

                    在这里插入图片描述

                    finally的代码无论是否捕获异常一定会被执行

                        public static void main(String[] args) {
                            System.out.println(mtd());
                        }
                        public static int mtd() {
                            try {
                                return 0;
                            } catch (NullPointerException e) {
                                System.out.println("空指针异常");
                            } finally {
                                System.out.println("执行finally");
                            }
                            return 1;
                        }
                    

                    在这里插入图片描述

                    即便try的内容中已经返回,但是仍旧会执行finally中的内容

                    所以finally里的内容一定会被执行

                    一般用来释放资源

                    语法格式

                    try{
                    }catch(捕获的异常) {
                    //try抛出的异常与捕获的异常一致或是try中抛出的异常的父类
                    }finally{//一定会被执行
                    }
                    //后续代码
                    //异常被捕获,异常处理,此处代码执行
                    //如果不活了,但是类型不匹配,就没有捕获到,此处不会执行
                    

                    异常处理流程:

                    程序先执行try中的代码

                    如果try中的代码出现异常,就会结束try中的代码,看和catch中的异常类型是否匹配

                    若未找到匹配类型,就会将异常向上传递给上层调用者

                    若找到则执行catch内容

                    无论是否匹配异常类型,finally内容都会执行

                    如果上层调用者也没有处理异常,就继续向上传递

                    一直到main方法也没有合适的代码处理异常就会交给JVM处理,程序终止

                    自定义异常类

                    如果要写一个自定义异常,一定要继承一个异常

                    extends Exception 编译时异常

                    extends RuntimeException 运行时异常

                    具体方式:

                    1. 自定义异常类,然后继承自Exception或者RunTimeException
                    2. 实现一个带有String类型参数的构造方法,参数函数:写出异常原因

                    包装类

                    在java里,因为基本类型不继承于Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型

                    基本数据类型及其对应包装类

                    基本数据类型 包装类

                    byte Byte

                    short Short

                    int Integer

                    long Long

                    float Float

                    double Double

                    char Character

                    boolean Boolean

                    装箱和拆箱

                    基本类型转变为包装类型

                        public static void main(String[] args) {
                             Integer a = 0;
                             int b = 1;
                             Integer c = b;
                            System.out.println(a);
                        }
                    
                        public static void main(String[] args) {
                            Integer a = 1;//装箱  自动装箱
                            
                            Integer b = Integer.valueOf(2);//显示装箱
                        }
                    

                    拆箱

                        public static void main(String[] args) {
                            Integer a = 1;
                            int b = a;//自动拆箱
                            int c = a.intValue();//显示拆箱
                            System.out.println(b);
                        }
                    
                        public static void main(String[] args) {
                            Integer a = 100;
                            Integer b = 100;
                            System.out.println(a==b);
                            Integer aa = 200;
                            Integer bb = 200;
                            System.out.println(aa==bb);
                        }
                    

                    在这里插入图片描述

                    再看valueOf源码

                    在这里插入图片描述

                    IntegerCache.low是-128

                    IntegerCache.high是127

                    当i处理[-128,127]中时,i是放进了一个数组,当超出这个范围是产生一个新的对象,产生新的地址,打印时比较的是地址所以返回false

                    泛型

                    泛型:适用于多种类型

                    针对代码而言就是对类型实现了参数化

                    泛型的主要目的:指定当前容器,要持有什么类型的对象.让编译器去检查

                    实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值?

                    class Generics {
                        public Object[] arr = new Object[10];
                        public void PushArray(int pos, Object o) {
                            arr[pos] = o;
                        }
                        public Object GetArray(int pos) {
                            return arr[pos];
                        }
                    }
                    

                    实现代码如上

                    任何类型都可以存储

                        public static void main(String[] args) {
                            Generics generics = new Generics();
                            generics.PushArray(0,1);
                            generics.PushArray(1,"abc");
                            String c = (String) generics.arr[1];
                            System.out.println(c);
                        }
                    

                    数组下标1的类型String需要强制类型转换

                     
                    public class Test {
                        public static void main(String[] args) {
                            Generics generics = new Generics();
                            generics.PushArray(0,1);
                            generics.PushArray(1,2);
                            Generics generics1 = new Generics();
                            generics1.PushArray(0,"abc");
                            generics1.PushArray(1,"efg");
                        }
                    }
                    class Generics {
                        public Object[] arr = new Object[10];
                        public void PushArray(int pos, T o) {
                            arr[pos] = o;
                        }
                        public T GetArray(int pos) {
                            return (T)arr[pos];
                        }
                    }
                    

                    泛型就是将类型进行传递

                    class 泛型类名称{
                    	//这里可以使用类型参数
                    }
                    class 泛型类名称 extends 继承类/* 这里可以使用类型参数 */ {
                      // 这里可以使用类型参数
                    }
                    

                    泛型使用

                    泛型类 变量名;//定义一个泛型类引用
                    new 泛型类(构造方法实参);//实例化一个泛型类对象
                    

                    泛型只能接收类,所有基本类型必须使用包装类

                    类型推导

                    当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写

                            Generics arr = new Generics();
                    

                    裸类型

                    说明

                    裸类型是一个泛型类但没有类型实参

                            Generics array = new Generics();
                    

                    小结

                    1. 泛型是将数据类型参数化,进行传递
                    2. 使用表示当前类是一个泛型类
                    3. 泛型目前的优点:数据类型参数化,编译时自动进行类型检查和转换

                    泛型的编译机制

                        public T[] arr = new T[10];
                    

                    不能new泛型类型数组

                    泛型是编译时期存在的,当程序运行起来,到JVM之后,不存在泛型这一概念了

                    泛型在编译的时候是如何编译的呢?

                    擦除机制.擦除成了Object

                    观察字节码文件

                    在这里插入图片描述

                    为什么不能实例化泛型类型数组?

                     public T[] array = (T[])new Object[10];
                     public T[] getArray() {
                        return array;
                     }
                    

                    替换后的方法:

                    public Object[] getArray() {
                      return array;
                    }
                    

                    返回的Object数组里面可能存放不同的数据类型,直接转给某一个类型的数组可能无法转换

                    泛型的上界

                    在定义泛型类时,有时需要传入的类型变量做一定的约束,可以通过类型边界来进行约束

                    语法

                    class 泛型类名称 {
                     ...
                    }
                    
                    public class Test {
                        public static void main(String[] args) {
                            Generics generics = new Generics();
                            Generics generics1 = new Generics();
                            Generics generics2 = new Generics();
                            
                            Generics generics3 = new Generics();//此处报错,String的父类不是Number
                        }
                    }
                    //T一定是Number或者Number的子类
                    class Generics {
                    }
                    
                    class Person {
                    }
                    class Student extends Person {
                    }
                    class Generics2 {
                    }
                    public class Test {
                        public static void main(String[] args) {
                            Generics2 generics21 = new Generics2();//Student的父类是Person
                            Generics2 generics22 = new Generics2();//是Person类
                            Generics2 generics23 = new Generics2();//报错,Integer的父类并非Person
                        }
                    }
                    

                    写一个泛型类,求数组最大值,如下

                    class Ag {
                        public T FindMax(T[] arr) {
                            T max = arr[0];
                            for (int i = 1; i  
                    

                    T一定是引用数据类型,引用数据类型比较大小要告诉编译器比较什么,上段代码中未指定

                    而且你的T 被擦除成了一个Object类,Object类中也没有比较的接口,不能比较,如下图

                    而你又要得到最大值的话,你的T类型一定要是可以比较的

                    在这里插入图片描述

                    所以:我们要如何让这个T一定可以进行比较呢?

                    在这里插入图片描述

                    进行这样修改,并不是说T一定要继承Comparable接口,意思是将来你指定的泛型类传入的参数一定是实现了Comparable接口

                    T一定是实现了Comparable接口的

                    Comparable接口有比较的方法

                    在这里插入图片描述

                    class Ag {
                        public T FindMax(T[] arr) {
                            T max = arr[0];
                            for (int i = 1; i  
                    
                    public class Test {
                        public static void main(String[] args) {
                            Ag ag = new Ag();//未报错Integer实现Comparable接口
                            Ag ag1 = new Ag();//报错,Person类未实现Comparable接口
                        }
                    }
                    class Person {
                    }
                    

                    在这里插入图片描述

                    public class Test {
                        public static void main(String[] args) {
                            Ag ag = new Ag();
                            Ag ag1 = new Ag();
                        }
                    }
                    class Person implements Comparable{
                        @Override
                        public int compareTo(Person o) {
                            return 0;
                        }
                    }
                    

                    当你的Person实现了Comparable接口后,就不会报错了

                        public static void main(String[] args) {
                            Ag ag = new Ag();
                            Integer[] integers = {1,2,3,4,5,6};
                            Integer res = ag.FindMax(integers);
                            System.out.println(res);
                        }
                    

                    在这里插入图片描述

                    public class Test {
                        public static void main(String[] args) {
                            Ag2 ag2 = new Ag2();
                            Integer[] integers = {1,2,3,4};
                            Integer res = ag2.FindMax(integers);
                            System.out.println(res);
                        }
                    }
                    class Ag2 {
                    //泛型方法
                        public T FindMax(T[] arr) {
                            T max = arr[0];
                            for (int i = 1; i  
                    

                    没有传入类型,编译器是如何识别类型进行比较得到最大值的?

                    类型推导:根据实参传值来推导此时的类型

                    在这里插入图片描述

                    这样写也可以,可以直接推导出类型

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon