【java进阶08:异常】finally关键字、自定义异常类、用户业务作业、军队武器作业

  1. java中的异常处理机制

    • 异常在java中以类和对象的形式存在,那么异常的继承结构是怎样的?我们可以使用UML图来描述以下继承结构

      画UML图的工具:Rational Rose、starUML等

      Object下有Throwable(可抛出的)

      Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)

      Exception下有两个分支:

      ​ Exception的直接子类:编译时异常。(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常)

      ​ RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管)

    • 编译时异常和运行时异常,都是发生在运行阶段,编译阶段异常是不会发生的。编译时异常因为什么而得名?

      ​ 因为编译时异常必须在编译(编写 )阶段预先处理,如果不处理编译器报错,因此得名。 所有异常都是在运行时发生的。因为只有程序运行阶段才可以new对象。

      ​ 因为异常的发生就是new对象

    • 编译时异常和运行时异常有什么区别?

      • 编译时异常一般发生的概率较高。如:

        你看到外面下雨了,倾盆大雨。出门前会预料:如果不打伞,很可能会生病(生病是一种异常)。而且这个异常发生的概率很高,所以我们出门前要那一把伞。“拿一把伞”就是对“生病异常”发生之前的一种处理方式。

        对于一些发生概率较高的异常,需要再运行之前对其进行预处理。

      • 运行时异常一般发生的概率较低。如:

        小明走在大街上,可能会被天上的飞机轮子砸到。被飞机轮子砸到也算是一种异常。但是这种异常发生概率较低。再出门值卡吗你没必要提前对这种发生概率较低的异常进行预处理。如果预处理这种异常,将“活的很累”。

      假设你在出门之前,把能够发生的异常都预先处理,你这个人会更加的安全,但是你这个人会活得很累。

      假设java中没有对异常进行划分,没有分为编译时异常和运行时异常,所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果?

      ​ 首先,如果这样,程序肯定是绝对的安全的。但是程序员编写程序太累,到处都是处理异常的代码。

    • 编译时异常还有其他名字:

      • 受检异常:CheckedException
      • 受控异常
    • 运行时异常还有其他名字:

      • 未受检异常:UnCheckedException
      • 非受控异常
    • 再次强调:所有异常都是发生在运行阶段的。

    • java语言中对异常的处理包括两种方式:

      • 第一种方式:在方法生命的位置上,使用throws关键字,抛给上一级。

        谁调用我,就抛给谁。抛给上一级。

      • 第二种方式:使用try…catch语句进行异常的捕捉。

        这件事发生了,谁也不知道,因为我抓住了

      如:

      ​ 我是某集团的一个销售员,因为我的失误,导致公司损失了1000元,”损失1000元“这就可以看作是一个异常发生了。我有两种处理方式。

      ​ 第一种方式:我把这件事告诉我的领导【异常上抛】

      ​ 第二种方式:我自己掏腰包把这个钱补上了。【异常的捕捉】

      张三 - - - -> 李四 - - - - > 王五 - - - - > CEO

      思考:

      ​ 异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。

    • 注意:java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果:终止java程序的执行。

  2. 什么是UML?有什么用?

    UML是一种统一建模语言。一种图标式语言(画图的)。

    UML不是只有java中使用。只要是面向对象的编程语言,都有UML。一般画UML图的都是软件架构师或者是系统分析师,这些级别的人使用的。软件设计人员使用UML。

    在UML图中可以描述类和类之间的关系、程序执行的流程、对象的状态等。

    盖大楼和软件开发一样,一个道理。

    ​ 盖大楼前,建筑师先画图纸。图纸上的一个一个符号都是标准符号,这个图纸画完,只要是搞建筑的都能看懂,因为这个图纸上标注的这些符号都是一种“标准语言”

    在java软件开发中,软件分析师/设计师负责设计类,java软件开发人员必须要能看懂。

  3. 异常的继承结构图【java进阶08:异常】finally关键字、自定义异常类、用户业务作业、军队武器作业_第1张图片

  4. 什么是异常,java提供异常处理机制有什么用?

    /*
        1、什么是异常,java提供异常处理机制有什么用?
            以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。
            java是很完善的语言,提供了异常的处理方式,以下程序执行过程中出现了不正常的情况,java把该异常信息供程序员参考。
            程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮。
    
            什么是异常:程序执行过程中的不正常情况
            异常的作用:增强程序的健壮性。
    
        2、以下程序执行控制台出现了:
            Exception in thread "main" java.lang.ArithmeticException: / by zero
    	        at ExceptionText01.main(ExceptionText01.java:11)
            这个信息别我们成为:异常信息,这个信息是JVM打印的。
     */
    public class ExceptionText01 {
        public static void main(String[]   args) {
            int a = 10;
            int b = 0;
            int c = a / b;  //实际上JVM在执行到此处的时候,会new对象:new ArithmeticException("/ by zero");
            //并且JVM将new的异常对象抛出,打印输出信息到控制台了
            System.out.println(a+"/"+b+"="+c);
    
            //此处运行也会创建一个:ArithmeticException类型的异常对象
            //System.out.println(100/0);
    
            //观察到异常信息之后,对程序进行修改,更加健壮。
    /*        int a = 10;
            int b = 2;
            if(b == 0){
                System.out.println("除数不能为0");
                return;
            }
            //程序执行到此处表示除数一定不是0
            int c = a / b;
            System.out.println(a+"/"+b+"="+c);*/
    
        }
    }
    
    
  5. java中异常是以什么形式存在的?

    /*
        java语言中异常是以什么形式存在的呢?
            1、异常在java中以类的形式存在,每一个异常类都可以创建异常对象。
    
            2、异常在对应的现实生活这是怎样的?
                火灾(异常类):
                    2008年8月8日   小明家着火了(异常对象)
                    2008年8月9日   小刚家着火了(异常对象)
                    2008年9月8日   小红家着火了(异常对象)
    
                类是: 模板
                对象是:实际存在的个体
    
                钱包丢了(异常类):
                    2008年1月1日,小明的钱包丢了(异常对象)
                    2008年1月9日,小芳的钱包丢了(异常对象)
                    ......
     */
    public class ExceptionText02 {
        public static void main(String[] args) {
            //通过异常类“实例化”异常对象
            NumberFormatException nfe = new NumberFormatException("数字格式化异常");
            System.out.println(nfe);//java.lang.NumberFormatException: 数字格式化异常
    
            //通过异常类“实例化”异常对象
            NullPointerException npe = new NullPointerException("空指针发生了");
            System.out.println(npe.toString());//java.lang.NullPointerException: 空指针发生了
    
        }
    }
    
    
  6. 运行时异常编写程序时可以不处理

    //运行时异常编写程序时可以不处理
    public class ExceptionText03 {
        public static void main(String[] args) {
            /*
                程序执行到此处发生了ArithmeticException异常,底层new了一个ArithmeticException异常对象,然后抛出了
                由于是main方法调用了100/0,所以这个异常ArithmeticException抛给了main方法,main方法没有处理
                将这个异常自动抛给了JVM。JVM最终终止程序的执行。
                ArithmeticException 继承 RuntimeException ,属于运行时异常。在编写阶段不需要对这种异常进行预先的处理。
             */
            System.out.println(100/0);
            //这里的hello world 没有输出,没有执行。
            System.out.println("hello world");
        }
    }
    
    
  7. 编译时异常必须处理

    /*
        以下代码报错的原因是什么?
            因为doSome()方法声明位置上使用了:throws ClassNotFoundException
            而ClassNotFoundException是编译时异常。必须编写代码时处理,如不处理,编译报错
     */
    public class ExceptionText04 {
        public static void main(String[] args) {
            //main方法中调用doSome()方法。因为doSome()方法声明位置上有:throws ClassNotFoundException
            //我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
            // 如果不处理,编译器报错.报错信息:Unhandled exception: java.lang.ClassNotFoundException
            //doSome();
    
    
        }
    
        /**
         * doSome方法在方法声明的位置上使用了:throws ClassNotFoundException
         * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常
         * 叫做:类没找到异常。这个异常直接父类是:Exception,所以ClassNotFoundException属于编译时异常。
         * @throws ClassNotFoundException
         */
        public static void doSome() throws ClassNotFoundException{
            System.out.println("doSome!!!!");
        }
    }
    
    
  8. 异常的处理方式有两种

    public class ExceptionText05 {
        //第一种处理方式:在方法声明的位置上继续使用:throws,来完成异常的继续上抛。抛给调用者
        //上抛类似于推卸责任。(继续把异常传递给调用者)
    /*    public static void main(String[] args) throws ClassNotFoundException {
            doSome();
        }*/
    
        //第二种处理方式:try..catch进行捕捉
        //捕捉等于把异常拦下了,异常真正解决了!(调用者是不知道的)
        public static void main(String[] args) {
            try{
                doSome();
            }catch (ClassNotFoundException e){
                e.printStackTrace();
            }
    
        }
        public static void doSome() throws ClassNotFoundException{
            System.out.println("doSome!!!!");
        }
    }
    
    

    注意事项

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /*
        处理异常的第一种方式:在方法声明的位置上使用throws关键字抛出,谁调用这个方法,我就抛给谁。抛给调用者来处理。
            在方法声明的位置上使用throws关键字抛出,谁调用我这个方法,我就抛给谁,抛给调用者来处理.
            这种处理异常的态度:上报.
        处理异常的第二种方式:
            使用try..catch语句对异常进行捕捉,这个异常不会上报,自己把这个事情解决了。异常抛出到此为止,不再上抛了。
    
        注意:
            只要异常没有捕捉,采用上抛的方式,此方法的后续代码不会执行
            另外需要注意:try语句块中的某一行出现异常,后面的代码不会执行
            try..catch捕捉异常之后,后续的代码可以执行.
    
        在以后的开发中,处理编译时异常,应该上抛还是应该捕捉呢?
            如果希望调用者来处理,选择throws上抛。
            其他情况使用捕捉的方式
    
     */
    public class ExceptionText06 {
        /*
            一般不建议在main方法上使用throws,因为这个异常如果真的发生了,一定会抛给JVM,JVM只有终止
            异常处理机制的作用是增强程序的健壮性。怎么能做到,异常发生了也不会影响程序的执行。
            所以一般main方法中的异常建议使用try..catch进行捕捉。main就不要继续上抛了。
         */
        //    public static void main(String[] args) throws FileNotFoundException {
        public static void main(String[] args) {
            //100/0是算术异常,这个异常是运行时异常,你在编译阶段,可以处理,也可以不处理。编译器不管
            //System.out.println(100/0);//不处理编译器也不管
            //处理以下也可以
    /*        try{
                System.out.println(100/0);
            }catch(ArithmeticException e){
                System.out.println("算术异常了!!!");
            }*/
    
    
    
            System.out.println("main begin");
            try {
                //try 尝试
                m1();
                //以上代码出现异常,直接进入catch语句进行捕捉
                System.out.println("helloworld");
            } catch (FileNotFoundException e) {//catch后面的好像一个形参
                //这个分支中可以使用e引用,e引用保存的的内存地址是那个new出的异常对象的内存地址.
                //catch是捕捉异常之后走的分支。
                //在catch分支中干什么?处理异常
                System.out.println("文件不存在,可能路径错误,也可能该文件被删除了");
                System.out.println(e.toString());
            }
    
            //try..catch把异常抓住之后,这里的代码会继续执行
            System.out.println("main over");
        }
        public static void m1() throws FileNotFoundException {
            System.out.println("m1 begin");
            m2();
            //以上代码出现异常,这里也不会执行.
            System.out.println("m1 over");
        }
    
        //    private static void m2() throws ClassCastException {
        // 抛别的不行,抛ClassCastException说明你还是没有对FileNotFoundException进行处理
    
        //    private static void m2() throws IOException {
        //抛FIleNotFoundException的父对象IOException,这样是可以的,因为IOException包括FileNotFoundException
    
        //    private static void m2() throws Exception {
        //这样也可以,因为Exception包括所有的异常.
    
        //    private static void m2() throws ClassCastException,FileNotFoundException {
        //throws也可以写多个异常,可以使用逗号隔开
    
        private static void m2() throws FileNotFoundException {
            System.out.println("m2 begin");
            //编译报错的原因是:m3()方法声明位置上有:throws FileNotFoundException
            //我们在这里调用m3()方法没有对异常进行预处理,所以编译报错
            m3();
            //以上如果出现异常,下面的代码是不会执行的
            System.out.println("m2 over");
        }
    
        private static void m3() throws FileNotFoundException {
            //调用SUN jdk中的某个类的构造方法
            //这个类还没接触过,后期IO流的时候就知道了。我们只是借助这个类学习一下异常处理机制。
            //创建一个输入流对象,该流指向一个文件。
            /*
                编译报错的原因是什么?
                    第一:这里调用了一个构造方法:FileInputStream(String name)
                    第二:这个构造方法的生命位置上有:throws FileNotFoundException
                    第三:通过类的继承结构可以看到:FileNotFoundException父类是IOException,IOException的父类是Exception
                    最终得知:FileNotFoundException是编译时异常。
    
                    错误原因:编译时异常要求程序员编写程序阶段必须对他进行处理,不处理编译器就报错
             */
            //new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\dd.txt");
            //我们采用第一种处理方式:在方法声明的位置上使用throws继续上抛。
            //一个方法当中的代码出现异常之后,如果上报的话,此方法结束.
            new FileInputStream("D:\\360Downloads\\Softwaress\\漏洞补丁目录\\dd.txt");
    
            System.out.println("如果new FIleInputStream出现异常,则这里的代码也不会执行");
        }
    
    }
    
    
  9. 深入try…catch

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /*
        深入try..catch
            1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型
            2、catch可以写多个。建议catch的时候,精确的一个一个处理,这样有利于程序的调试。
            3、catch写多个的时候,从上到小,必须遵守从小到大。
     */
    public class ExceptionText07 {
        public static void main(String[] args) {
    /*        try {
                FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt");
                System.out.println("以上出现异常,此处无法执行");
            } catch (FileNotFoundException e) {
                System.out.println("文件不存在");
            }
            System.out.println("helloworld");*/
    
    /*        try {
                FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt");
            } catch (IOException e) {//多态:IOException e = new FileNotException();
                System.out.println("文件不存在");
            }*/
    
            //不够精确
    /*        try {
                //创建输入流
                FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt");
                //读文件
                fis.read();
            } catch (Exception e) {//所有的异常都走这个分支.
                System.out.println("文件不存在");
            }*/
    
    /*        try {
                //创建输入流
                FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt");
                //读文件
                fis.read();
            } catch (FileNotFoundException e) {
                System.out.println("文件不存在");
            }catch (IOException e){
                System.out.println("读文件报错了");
            }*/
    
            //编译报错:Exception 'java.io.FileNotFoundException' has already been caught
            // 已经被捕捉过了,因为IOException是FIleNotFoundException的父类型。
    /*        try {
                //创建输入流
                FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt");
                //读文件
                fis.read();
            }catch (IOException e){
                System.out.println("读文件报错了");
            } catch (FileNotFoundException e) {
                System.out.println("文件不存在");
            }*/
    
            /*
                JDK8的新特性:
                    catch中的异常类型中间可以采用“或(|)”的方式
    
             */
            try {
                //创建输入流
                FileInputStream fis = new FileInputStream("D:\\360Downloads\\Software\\漏洞补丁目录\\ddd.txt");
                //进行数学运算
                System.out.println(100/0);//这个异常是运行时异常,编写程序时可以处理,也可以不处理
            }catch (FileNotFoundException | ArithmeticException | NullPointerException e) {
                System.out.println("文件不存在?算术异常?空指针异常?都有可能!");
            }
    
        }
    
    }
    
    
  10. 异常对象的两个非常重要的方法

    /*
        异常对象有两个非常重要的方法:
            获取异常简单的描述信息:
                String msg = exception.getMessage();
            打印异常追踪的堆栈信息:
                exception.printStackTrace();
     */
    public class ExceptionText08 {
        public static void main(String[] args) {
            //这里只是为了测试getMessage()方法和printStackTrace()方法
            //这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
            NullPointerException npe = new NullPointerException("空指针异常对象npe");
    
            //获取异常简单描述信息:这个信息实际上就是构造方法上的String参数。
            String msg = npe.getMessage();
            System.out.println(msg);
    
            //打印异常堆栈信息。java后台打印异常堆栈追踪信息处理信息的时候,采用了异步线程的方式打印的
            npe.printStackTrace();
            for (int i = 0; i <500 ; i++) {
                System.out.println(i);
            }
            System.out.println("helloworld");
        }
    }
    
    

    如何查看异常的追踪信息

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    /*
        异常对象的两个方法:
            String msg = e.getMessage();
            e.printStackTrace       (一般使用这个)
    
        我们以后查看异常的追踪信息,应该怎么看,可以快速的调试程序呢?
            异常信息追踪信息,从上往下一行一行看。但是要注意的是:SUN写的代码就不用看了(看包名就知道是自己写的还是SUN写的)
            主要问题出现在自己编写的代码上面。
     */
    public class ExceptionText09 {
        public static void main(String[] args) {
            try {
                m1();
            } catch (FileNotFoundException e) {
                //获取异常的简单描述信息
                System.out.println(e.getMessage());
                //打印异常堆栈追踪信息!!!
                //在实际的开发中,建议使用这个,养成好习惯。这行代码要写上,不然出问题了都不知道
                e.printStackTrace();
                /*
                    java.io.FileNotFoundException: D:\360驱动大师目录\下载保存目录\SeachDownload\eee.txt (系统找不到指定的文件。)
                        at java.io.FileInputStream.open0(Native Method)
                        at java.io.FileInputStream.open(FileInputStream.java:195)
                        at java.io.FileInputStream.(FileInputStream.java:138)
                        at java.io.FileInputStream.(FileInputStream.java:93)
                        at ExceptionText09.m3(ExceptionText09.java:47)
                        at ExceptionText09.m2(ExceptionText09.java:43)
                        at ExceptionText09.m1(ExceptionText09.java:39)
                        at ExceptionText09.main(ExceptionText09.java:16)
                        因为47行出问题导致43行出问题,43行出问题导致39行出问题,49行出问题导致16行出问题,应该先看47行,47行是代码错误根源
                 */
    
                //这里的程序不耽误执行,很健壮(服务器不会因为遇到异常而宕机)
                System.out.println("helloworld");
            }
        }
    
        private static void m1() throws FileNotFoundException {
            m2();
        }
    
        private static void m2() throws FileNotFoundException {
            m3();
        }
    
        private static void m3() throws FileNotFoundException {
            new FileInputStream("D:\\360驱动大师目录\\下载保存目录\\SeachDownload\\eee.txt");
        }
    }
    
    
  11. 关于try…catch的finally子句

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.lang.reflect.Parameter;
    
    /*
        关于try..catch中的finally子句:
            1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try..catch语句块中的代码出现了异常
                finally子句必须和try一起出现,不能单独编写
    
            2、finally语句通常使用在哪些情况下?
                通常在finally语句块中完成资源的释放/关闭,因为finally中的代码有保障。
                即使try语句块中的代码出现异常,finally中的代码也会执行。
    
     */
    public class ExceptionText10 {
        public static void main(String[] args) {
            FileInputStream fis = null;
            try {
                //创建输入流对象
                 fis = new FileInputStream("D:\\360驱动大师目录\\下载保存目录\\SeachDownload\\ee.txt");
                //开始读文件....
    
                String s = null;
                //这里一定会出现空指针异常
                s.toString();
                System.out.println("helloworld");
    
                //流使用完需要关闭,因为流是占用资源的
                //即使以上程序出现异常,流也必须关闭。放在这里可能关闭不了
                fis.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (NullPointerException e){
                e.printStackTrace();
            } catch (IOException e){
                e.printStackTrace();
            } finally {
                System.out.println("hello paoke");
                //流的关闭放在这里比较保险,finally中的代码是一定会执行的。即使try中的代码出现了异常,也可以执行。
                //当fis为空时不执行
                if(fis != null) {
                    try {
                        //close()方法有异常,采用捕捉的方式
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
            }
            System.out.println("hello kity");
    
        }
    
    }
    
    
  12. try和finally,可以没有catch。

    以及try中如果有return,finally怎么执行

    //finally语句:放在finally语句块中的代码是一定会执行的。
    public class ExceptionText11 {
        public static void main(String[] args) {
            /*
                try和finally,没有catch可以吗?可以
                    try不能单独使用
                    try finally可以联合使用
                以下代码的执行顺序:
                    先执行try..
                    在执行finally...
                    最后执行return(return语句只要执行,方法必然结束)
             */
            try{
                System.out.println("try....");
                return;
            }finally {
                //fianlly中的语句会执行,能执行到
                System.out.println("finally");
            }
    
    
            //Unreachable statement。这里不能写语句,因为这个代码是无法执行到的
            //System.out.println("helloworld");
        }
    }
    
    

    使用System.exit(0);退出JVM时,finally…

    //finally
    public class ExceptionText12 {
        public static void main(String[] args) {
            try{
                System.out.println("try....");
                //退出JVM
                System.exit(0);//退出JVM之后,finally语句中的代码就不会执行了。
            }finally {
                System.out.println("finally");
            }
        }
    }
    
    
  13. finally面试题

    //finally面试题
    public class ExceptionText13 {
        public static void main(String[] args) {
            int result = m();
            System.out.println(result);//100
        }
    
        /*
            java语法规则(有一些规则是不能破坏的,一旦这么说了,必须这么做):
                java中有一条这样的规则:
                    方法体中的代码必须遵循自上而下的顺序因此逐行执行(亘古不变的 语法)
                java中还有一条这样的语法规则:
                    return语句一旦执行,整个方法必须结束。(更古不变的语法)
         */
        private static int m() {
            int i =100;
            try{
                //这行代码出现在int i = 100; 的下面,所以最终结果必须返回100
                //return语句还必须保证是最后执行的,一旦执行,整个方法结束。
                return i;
            }finally {
                i++;
            }
        }
    }
    
    /*
    反编译之后的结果
        public static int m(){
            int i = 100;
            int j = i;
            i++;
            return j;
        }
     */
    
    
  14. final、finally、finalize有什么区别?

    /*final 、finally 、finalize有什么区别?
        final 关键字
            final修饰的类无法继承
            final修饰的方法无法覆盖
            final修饰的变量不能重新赋值
    
        finally 关键字
            finally与try一起联合使用
            finally语句块中的代码是一定会执行的
    
        finalize    标识符
            是一个Object类中的方法名
            这个方法是垃圾回收器GC负责调用的。
    
     */
    public class ExceptionText14 {
        public static void main(String[] args) {
            //final是一个关键字
            final int i = 100;
    
            //finally也是一个关键字,和try联合使用,使用在异常处理机制中,在finally语句块中的代码是一定会执行的
            try{
    
            }finally{
                System.out.println("finally");
            }
    
            //finalize()是Object类中的一个方法,作为方法名出线。所以finalize是标识符
    
    
        }
    }
    
    
  15. 如何自己定义异常?

    异常类

    /*
        1、SUN提供的JDK内置的异常肯定是不够用的。在实际的开发中,有很多业务,这些业务出现异常之后,JDK中都是没有的
            和业务挂钩的,这些异常类我们可以自己去定义
    
        2、java中怎么自定义异常呢?
            两步:
                第一步:编写一个类继承Exception或RuntimeException。
                第二步:提供两个构造方法,一个无参的,一个带有String参数的。
            死记硬背
    
     */
    
    public class MyException extends Exception{//编译时异常:发生概率比较高的时候定义为编译时异常
        public MyException(){
    
        }
    
        public MyException(String s){
            super(s);
        }
    
    }
    //public class MyExceptio exception RuntimeException{   //运行时异常:发生概率比较低的定义为运行时异常比较好
    //
    //}
    
    

    创建异常对象

    public class ExceptionText15 {
        public static void main(String[] args) {
            //创建异常对象(只是new对象,并没有手动抛出)
            MyException e = new MyException("用户名不能为空");
    
            //打印异常堆栈信息
            e.printStackTrace();
    
            //获取异常简单描述信息
            String msg = e.getMessage();
            System.out.println(msg);
        }
    }
    
    
  16. 对之前的数组模拟栈存储进行改进,使用异常进行错误的输出

    改进后的栈类

    public class MyStack {
    
        private Object[] elements ;
    
        private int index = -1;
    
        public MyStack() {
    
            this.elements = new Object[10];
        }
    
        public MyStack(int i) {
            this.elements = new Object[i];
        }
    
        public Object[] getElements() {
            return elements;
        }
        public void setElements(Object[] elements) {
            this.elements = elements;
        }
    
    
        //压栈
        public void push(Object object) throws MyStackOperationException {
            if(this.index >= this.elements.length-1){
                //学了异常之后对这个进行改进,不再使用输出语句,太low了
                            /*System.out.println("栈已满,压栈失败");
                            return;*/
    
                /*//改进:
                //创建异常对象
                MyStackOperationException e = new MyStackOperationException("栈已满,压栈失败");
                //手动将异常抛出去
                throw e;    //这里不能使用try捕捉,自己new了一个异常,自己捕捉,没有意义。栈已满的消息需要传递出去*/
    
                //合并
                throw new MyStackOperationException("栈已满,压栈失败");
            }
            elements[++index] = object;
            System.out.println("元素  "+object+"  压栈成功,栈帧此时指向"+index);
    
        }
        //弹栈
        public void pop() throws MyStackOperationException {
            if(index <= -1){
                /*System.out.println("栈已空,弹栈失败");
                return;*/
                throw new MyStackOperationException("栈已空,弹栈失败");
            }
    
            System.out.println("元素  " + elements[index] +"  弹栈成功,此时栈帧指向:"+ --index);
        }
    
    }
    
    

    自定义的内存类

    /**
     * 栈操作异常:自定义异常
     */
    public class MyStackOperationException extends Exception{//编译时异常
        public MyStackOperationException(){
    
        }
        public MyStackOperationException(String s){
            super(s);
        }
    }
    

    测试类

    //对改良之后的栈进行测试
    //注意:最后这个例子,是异常最重要的案例,必须掌握,自定义异常在实际开发中的作用
    public class ExceptionText16 {
        public static void main(String[] args) {
            //创建栈对象
            MyStack stack = new MyStack();
    
            //压栈、这里不能再上抛了,因为JVM发现异常之后,就会终止程序。
            try {
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                stack.push(new Object());
                //这里栈满了
                stack.push(new Object());
    
            } catch (MyStackOperationException e) {
                //输出异常的简单信息
                System.out.println(e.getMessage());
                //打印异常信息
                //e.printStackTrace();
            }
    
            //弹栈
            try {
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                stack.pop();
                //这里栈空了
                stack.pop();
            } catch (MyStackOperationException e) {
                //输出异常的简单信息
                System.out.println(e.getMessage());
                //打印异常信息
                //e.printStackTrace();
            }
        }
    }
    
    
  17. 方法重写时,重写后的方法不能比之前的方法抛出更多的异常

    /*
        之前在讲方法覆盖时,遗留的问题:
            重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少。
     */
    public class ExceptionText17 {
        public static void main(String[] args) {
    
        }
    }
    
    class Animal{
        public void doSome(){
    
        }
    
        public void doOther()throws Exception{
    
        }
    
    }
    
    class Cat extends Animal{
        //编译报错
        //public void doSome() throws Exception{}
    
        //编译通过
        //public void doOther(){}
    
        //编译通过
        //public void doOther() throws Exception{}
    
        //编译通过
        public void doOther() throws NullPointerException{
    
        }
    }
    
    /*
        总结异常中的关键字:
            异常捕捉:
                try
                catch
                finally
    
            throws  在方法声明位置上使用,表示上报异常信息给调用者
            throw   手动抛出异常
     */
    
  18. 相关作业

    编写程序模拟用户注册:
    1、程序开始执行时,提示用户输入“用户名”和“密码”信息。
    2、输入信息之后,后台java程序模拟用户注册。
    3、注册时用户名要求长度在[6-14]之间,小于或者大于都表示异常。
    
    注意:
    	完成注册的方法放到一个单独的类中。
    	异常类自定义即可。
    
    	class UserService {
    		public void register(String username,String password){
    			//这个方法中完成注册!
    		}
    	}
    
    	编写main方法,在main方法中接收用户输入的信息,在main方法
    	中调用UserService的register方法完成注册。
    

    用户业务类

    package homework;
    //用户业务类:处理用户相关的业务:例如登录、注册等功能
    public class UserService {
        /**
         * 用户注册
         * @param username  用户名
         * @param password  密码
         * @throws IllegalNameException 当用户名为null,或者用户名长度<6,或者长度>14时,出现该异常
         */
        public void register(String username , String password) throws IllegalNameException {
            //引用等于null的这个判断最好放到所有条件的最前面
            //if(username == null || username.length() < 6 || username.length() >14 ){}
    
            //再分享一个经验:  写成null ==username 比 username == null 要好。有时候 少一个=就成了:username = null,这种与我们想的不一样,但是这样写不会报错,采用前一种方法就比较保险
            //                写成"abc".equals(username) 比 username.equals("abc") 要好
            //if( null == username || username.length() < 6 || username.length() >14 ){}
    
            if( null == username || username.length() < 6 || username.length() >14 ){
                throw new IllegalNameException("用户名长度必须在[6-14]之间");
            }
            //程序能执行到这里,说明用户名没有问题
            System.out.println("欢迎["+username+"]");
    
    
        }
    }
    
    

    异常类

    package homework;
    
    public class IllegalNameException extends Exception{
        public IllegalNameException(){
    
        }
        public IllegalNameException(String s){
            super(s);
        }
    }
    
    

    测试类

    package homework;
    
    public class Text {
        public static void main(String[] args) {
            UserService us = new UserService();
            try {
                us.register("abcdef","123");
            } catch (IllegalNameException e) {
                e.printStackTrace();
            }
        }
    }
    
    
  19. 作业2

     开放型题目,随意发挥:
    	写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的所有武器),
    	该类还提供一个构造方法,在构造方法里通过传一个int类型的参数来限定该类所能拥有的最大武器数量,
    	并用这一大小来初始化数组w。
    
    	该类还提供一个方法addWeapon(Weapon wa),表示把参数wa所代表的武器加入到数组w中。
    	在这个类中还定义两个方法attackAll()让w数组中的所有武器攻击;
    	以及moveAll()让w数组中的所有可移动的武器移动。 
    
    	写一个主方法去测试以上程序。 
    
    	提示:
    		Weapon是一个父类。应该有很多子武器。
    		这些子武器应该有一些是可移动的,有一些
    		是可攻击的。
    

    可移动接口

    package arrayarmy;
    
    //可移动的接口
    public interface Moveable {
        //移动行为
        void move();
    }
    
    

    可攻击接口

    package arrayarmy;
    //可射击的接口
    public interface Shootable {
        //射击行为
        void shoot();
    }
    
    

    武器类

    package arrayarmy;
    
    //所有武器的父类
    public class Weapon {
        //武器的名字
        String name ;
        public Weapon(String name){
            this.name = name;
        }
    
        //重写toString方法
        @Override
        public String toString() {
            return name;
        }
    }
    
    

    坦克类

    package arrayarmy;
    
    /**
     * 坦克是一个武器,可移动,可攻击
     */
    public class TanK extends Weapon implements Moveable,Shootable{
    
    
        public TanK(String name) {
            super(name);
        }
    
        @Override
        public void move() {
            System.out.println("坦克移动");
        }
    
        @Override
        public void shoot() {
            System.out.println("坦克攻击");
        }
    }
    
    

    高射炮类

    package arrayarmy;
    
    //高射炮是一个武器,但是不能移动,只能射击。
    public class GaoShePao extends Weapon implements Shootable{
    
        public GaoShePao(String name) {
            super(name);
        }
    
        @Override
        public void shoot() {
            System.out.println("高射炮开炮");
        }
    }
    
    

    战斗机类

    package arrayarmy;
    
    //战斗机:是武器,可移动、可攻击。
    public class Fighter extends Weapon implements Shootable,Moveable{
        public Fighter(String name) {
            super(name);
        }
    
        @Override
        public void move() {
            System.out.println("战斗机起飞");
        }
    
        @Override
        public void shoot() {
            System.out.println("战斗机开炮");
        }
    }
    
    

    物资飞机类

    package arrayarmy;
    
    //物资飞机:只能运输物资
    public class WuZiFeiJi extends Weapon implements Moveable{
        public WuZiFeiJi(String name) {
            super(name);
        }
    
        @Override
        public void move() {
            System.out.println("物资飞机运输物资");
        }
    }
    
    

    添加武器异常类

    package arrayarmy;
    
    public class AddWeaponException extends Exception{
        public AddWeaponException(){
    
        }
        public AddWeaponException(String s){
            super(s);
        }
    }
    
    

    军队类

    package arrayarmy;
    /*
     开放型题目,随意发挥:
    	写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的所有武器),
    	该类还提供一个构造方法,在构造方法里通过传一个int类型的参数来限定该类所能拥有的最大武器数量,
    	并用这一大小来初始化数组w。
    
    	该类还提供一个方法addWeapon(Weapon wa),表示把参数wa所代表的武器加入到数组w中。
    	在这个类中还定义两个方法attackAll()让w数组中的所有武器攻击;
    	以及moveAll()让w数组中的所有可移动的武器移动。
    
    	写一个主方法去测试以上程序。
    
    	提示:
    		Weapon是一个父类。应该有很多子武器。
    		这些子武器应该有一些是可移动的,有一些
    		是可攻击的。
     */
    //军队
    //类在强制类型转换过程中,如果是类转换成接口类型。那么类和接口之间不需要存在继承关系,也可以转换,java语法中允许
    
    public class Army {
        //武器数组
        Weapon[] weapons;
    
        /**
         * 创建军队的构造方法
         * @param count 武器数量
         */
        public Army(int count){
            //动态初始化数组中的每一个元素默认值都是null,武器数组是有了,但是武器数组中没有放武器
            this.weapons =  new Weapon[count];
        }
    
        /**
         * 将武器加入数组weapons中
         * @param weapon    要放入数组的武器
         */
        public void addWeapon(Weapon weapon) throws AddWeaponException {
            for(int i = 0 ; i < weapons.length ; i++){
                if(null == weapons[i]){
                    weapons[i] = weapon;
                    System.out.println(weapon+"入队");
                    return;
                }
            }
                //程序如果执行到这里,说明武器没有添加成功。
                throw new AddWeaponException("武器库已满,无法放入武器");
        }
    
        /**
         * 所有可攻击的武器攻击
         */
        public void attackAll(){
            //遍历数组
            for(int i = 0 ; i < weapons.length ; i++){
                //Weapon是所有武器的父类,但是需要调用的是子类武器的方法,所以这里使用向下转型
                //看weapons[i]是否具有可攻击的方法,如果有,转型----> 调用该方法
                if ( weapons[i] instanceof Shootable){
                    Shootable shootable = (Shootable)weapons[i];
                    shootable.shoot();
                }
            }
    
        }
    
        /**
         * 所有可移动的武器移动
         */
        public void moveAll(){
            //遍历数组
            for(int i = 0 ; i < weapons.length ; i++){
                if( weapons[i] instanceof Moveable){
                    Moveable moveable =(Moveable)weapons[i];
                    moveable.move();
                }
            }
        }
    
    
    }
    
    

    测试类

    package arrayarmy;
    
    public class Text {
        public static void main(String[] args) {
            //构建一个军队army
            Army army  = new Army(4);   //这个军队的武器库里只能存放四个武器
            //创建武器对象
            Fighter fighter = new Fighter("战斗机");
            GaoShePao gaoShePao = new GaoShePao("高射炮");
            TanK tanK = new TanK("坦克");
            WuZiFeiJi wuZiFeiJi = new WuZiFeiJi("物资飞机");
            WuZiFeiJi wuZiFeiJi2 = new WuZiFeiJi("物资飞机2");
    
            //添加武器
            try{
                army.addWeapon(fighter);
                army.addWeapon(gaoShePao);
                army.addWeapon(tanK);
                army.addWeapon(wuZiFeiJi);
                army.addWeapon(wuZiFeiJi2);
            }catch(AddWeaponException e){
                System.out.println(e.getMessage());
            }
    
    
            //让所有可移动的移动
            army.moveAll();
            //让所有可攻击的攻击
            army.attackAll();
        }
    }

你可能感兴趣的:(java,java,jvm,uml)