• 中南林业科技大学Java实验报告九:内部类和异常类


    实验9 内部类和异常类

    9.1 实验目的

    1. 了解内部类的作用;
    2. 掌握内部类的使用;
    3. 掌握外部类和内部类的访问原则;
    4. 掌握自定义异常类;
    5. 掌握Java异常处理机制;

    9.2 实验内容

    9.2.1 编写一个Java程序,定义一个外部类和一个内部类,并在外部类中访问内部类的成员变量和方法。

    【前提引入-成员内部类】

    • 成员内部类是定义在外部类的成员位置上,并且没有static修饰

      //Outer是外部类
      public class Outer{
          //Inner是成员内部类
          class Inner{
              
          }
          
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 可以直接访问外部类的所有成员,包括 private

    • 可以添加任意访问修饰符(public,protected,default默认,private),因为它的地位就是一个成员

    • 作用域:和外部类的其他成员一样,为整个类体

    • 成员内部类访问外部类成员:直接访问

      public class Outer { 
         private String name = "外部类";
          
         public class Inner{
             
             public void show(){
                 //访问的外部类成员name
                 System.out.println(name);     
             }
              
         }
         
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    • 外部类访问成员内部类:创建对象再访问,且可以访问成员内部类的所有成员(包括private)

      public class Outer {
      
         public class Inner{
             //设置为私有
              private String name = "内部类";
         }
      
         public void show(){
             //外部类访问内部类成员
             
             //1. 先创建内部类
             Inner inner = new Inner();
             //2. 再访问内部类成员
             System.out.println(inner.name);
         }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    • 外部其他类访问成员内部类的两种方式:

      1. 创建外部类对象再创建成员内部类

        public class Outer {
        
           public class Inner{
        
           }
        }
        
        class MyTest{
            public static void main(String[] args) {
                Outer outer = new Outer();
                //创建内部类
                Outer.Inner inner1 = outer.new Inner();
                
                //也可以合做一句来写
                Outer.Inner inner2 = new Outer().new Inner();
        
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
      2. 在外部类中定义方法返回成员内部类实例对象

        public class Outer {
        
           public class Inner{
        
           }
        
           public Inner createInnerInstance() {
               return new Inner();
           }
        }
        
        class MyTest{
            public static void main(String[] args) {
                Outer outer = new Outer();
                Outer.Inner innerInstance1 = outer.createInnerInstance();
                
                //上述两句等价于:
                Outer.Inner innerInstance2 = new Outer().createInnerInstance();
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
    • 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用 外部类名.this.成员 来访问外部类成员。

      public class Outer {
         private String name = "外部类";
         
         public class Inner{
             private String name = "成员内部类";
             public void show() {
                 //就近原则 -> 访问内部类,输出:成员内部类
                 System.out.println(name);
                 
                 //访问外部类成员变量name,输出:外部类
                 System.out.println(Outer.this.name);
             }
         }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    【核心代码】

    编写 Outer 类:

    public class Outer {
        /**
         * 成员内部类:Inner
         */
        class Inner {
            /**
             * 内部类的成员变量
             */
            public String name = "普通内部类";
    
            /**
             * 内部类的成员方法
             */
            public void show() {
                System.out.println("====调用Inner成员方法====");
            }
        }
    
        /**
         * 测试外部类调用内部类的成员变量和成员方法
         */
        public void show() {
            Inner inner = new Inner();
            System.out.println("调用inner成员变量:" + inner.name);
            inner.show();
        }
    
        /**
         * 主启动类
         * @param args 参数
         */
        public static void main(String[] args) {
            new Outer().show();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    【运行流程】

    image-20221101180649162

    9.2.2 车站检查危险品的设备,如果发现危险品会发出警告。编程模拟设备发现危险品的情况。

    【前提引入-异常简单说明】

    1️⃣ 什么是异常

    java语言中,将程序执行中发生的不正常情况称为"异常"。

    ⚠️开发过程中的语法错误和逻辑错误不是异常

    2️⃣异常分类

    • Error
      1. Java虚拟机无法解决的严重问题。
      2. 如:JVM系统内部错误,资源耗尽等严重情况。
      3. 比如:栈溢出StackOverFlowError内存不足Out of Memory
      4. Error是严重错误,程序会崩溃!
    • Exception
      1. 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
      2. 例如空指针访问,试图读取不存在的文件,网络连接终端等等。
      3. Exception分为两大类:
        • 运行时异常:程序运行时发生的异常
        • 编译时异常:编译时编译器检查出的异常

    3️⃣ 注意事项

    • 运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。
    • 对于运行时异常,可以不做处理,因为这类异常很普遍,若全部处理可能会对程序的可读性与运行效率造成影响。
    • 编译时异常,是编译器要求程序员必须处理的异常,否则javac编译无法通过。
    • 运行时异常有时候也可以作为我们的业务逻辑的一部分。

    4️⃣ 常见的运行时异常

    • NullPointerException 空指针异常

      当应用程序试图在需要对象的地方使用null时,抛出该异常。例如:

      public class ExceptionTest {
      
          public static void main(String[] args) {
              //声明为null,未实例化
              String str = null;
              //调用str的toString方法,则会抛出NullPointerException异常
              System.out.println(str.toString());
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • ArithmeticException 数学运算异常

      当出现异常的运算条件时,抛出此异常。例如:

      public class ExceptionTest {
      
          public static void main(String[] args) {
              // 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
              System.out.println(10/0);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • ArrayIndexOutOfBoundsException 数组下标越界异常

      用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。例如:

      public class ExceptionTest {
      
          public static void main(String[] args) {
              // arr的数组长度为3,则索引范围是 [0,2]
              int[] arr = {1,2,3};
              // 索引为3,不在 [0,2] 范围内,因此抛出异常ArrayIndexOutOfBoundsException
              System.out.println(arr[3]);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • ClassCastException 类型转换异常

      当我们用子类对象去强制转换父类对象就会报错,同样会抛出此异常。例如:

      public class ExceptionTest {
      
          public static void main(String[] args) {
              //所有类都默认直接或间接继承了str,Object是顶级父类
              Object object = new Object();
              //因此这是在把一个父类实例对象转为子类,会抛出异常ClassCastException
              String str = (String)object;
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • NumberFormatException 数字格式不正确异常

      当应用程序试图将字符串转换成一种数值类型,但是该字符串不能转换为适当格式时,抛出该异常 --> 使用异常我们可以确保输入是满足条件的数字

      public class ExceptionTest {
      
          public static void main(String[] args) {
              String str = "狐狸半面添";
              //parseInt方法:试图将str转为整型
              //但很明显,我们定义的 str 无法转为整型,因此抛出异常NumberFormatException
              int num = Integer.parseInt(str);
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      我们还可以查看一下Integer类的parseInt方法源码,看下是否当类型不符合时真的抛出此异常:

      在这段源码中应当是可以看到大量地方提到了 throw new NumberFormatException()

      public static int parseInt(String s, int radix) throws NumberFormatException{
          /*
               * WARNING: This method may be invoked early during VM initialization
               * before IntegerCache is initialized. Care must be taken to not use
               * the valueOf method.
               */
      
          if (s == null) {
              throw new NumberFormatException("null");
          }
      
          if (radix < Character.MIN_RADIX) {
              throw new NumberFormatException("radix " + radix +
                                              " less than Character.MIN_RADIX");
          }
      
          if (radix > Character.MAX_RADIX) {
              throw new NumberFormatException("radix " + radix +
                                              " greater than Character.MAX_RADIX");
          }
      
          int result = 0;
          boolean negative = false;
          int i = 0, len = s.length();
          int limit = -Integer.MAX_VALUE;
          int multmin;
          int digit;
      
          if (len > 0) {
              char firstChar = s.charAt(0);
              if (firstChar < '0') { // Possible leading "+" or "-"
                  if (firstChar == '-') {
                      negative = true;
                      limit = Integer.MIN_VALUE;
                  } else if (firstChar != '+')
                      throw NumberFormatException.forInputString(s);
      
                  if (len == 1) // Cannot have lone "+" or "-"
                      throw NumberFormatException.forInputString(s);
                  i++;
              }
              multmin = limit / radix;
              while (i < len) {
                  // Accumulating negatively avoids surprises near MAX_VALUE
                  digit = Character.digit(s.charAt(i++),radix);
                  if (digit < 0) {
                      throw NumberFormatException.forInputString(s);
                  }
                  if (result < multmin) {
                      throw NumberFormatException.forInputString(s);
                  }
                  result *= radix;
                  if (result < limit + digit) {
                      throw NumberFormatException.forInputString(s);
                  }
                  result -= digit;
              }
          } else {
              throw NumberFormatException.forInputString(s);
          }
          return negative ? result : -result;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62

    5️⃣ 异常处理

    异常处理就是当异常发生时,对异常的处理方式。

    • try-catch-finally

      程序员在代码中捕获发生的异常,自行处理

      public class ExceptionTest {
      
          public static void main(String[] args) {
              // 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
              try {
                  System.out.println(10/0);
              } catch (Exception e) {
                  System.out.println("这里是用来捕获发生的异常");
                  //创建一个异常类,并抛出该异常给方法的调用者来处理
                  throw new RuntimeException(e);
              }finally {
                  System.out.println("这里通常是进行资源释放,也可以不写finally代码块");
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
    • throws

      将发生的异常抛出,交给调用者来处理,最顶级的处理者就是 JVM 虚拟机

      public class ExceptionTest {
      
          // throws Exception:发生异常时则抛出异常给该方法的调用者处理
          public static void main(String[] args) throws Exception {
              // 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
              System.out.println(10 / 0);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    6️⃣ 自定义异常类

    • 基本概念

    • 自定义异常类的步骤

      1. 定义一个类:必须继承 Excpetion类RuntimeExcpetion类

      2. 如果继承 Exception,属于编译异常;

        如果继承 RuntimeException,属于运行异常。

      3. 一般情况下爱,我们自定义异常是继承 RuntimeExcpetion,即把自定义异常做成运行时异常类,好处是可以使用默认的处理机制——throws方式处理

      4. 自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。

    • 示例演示:

      public class MyException extends RuntimeException{
          public MyException() {
      
          }
          public MyException(String message) {
              super(message);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    【题目要求与核心代码】

    1. 通过继承Exception类,编写一个DangerException类:该异常类有构造方法,该构造方法使用super调用父类构造方法,使用字符串:“属于危险品!”,对父类变量message进行初始化。

      /**
       * 异常类
       */
      public class DangerException extends Exception{
          public DangerException() {
              super("属于危险品!");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    2. 编写商品类:Goods,该类包含以下成员:

      • 私有的name属性(String类型),表示商品名称。
      • 私有的isDanger属性(boolean型),表示商品是否为危险品,如果为危险品,则值为true,否则为fales。
      • 分别为两个私有变量编写set和get方法
      /**
       * 商品类
       */
      public class Goods {
          /**
           * 商品名称
           */
          private String name;
          /**
           * 表示该商品是否为危险品,默认初始化为 false
           * true:为危险品
           * false:不是危险品
           */
          private boolean isDanger;
      
          public Goods(String name){
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public boolean isDanger() {
              return isDanger;
          }
      
          public void setDanger(boolean danger) {
              isDanger = danger;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
    3. 编写一个Machine类,该类的方法checkBag(Goods goods)。当发现参数goods是危险品时,即:goods的isDanger属性为true时,该方法抛出DangerException异常的对象。

      import java.util.ArrayList;
      import java.util.Arrays;
      
      /**
       * 定义工具类,扫描是否为危险品
       */
      public class Machine {
          /**
           * 危险商品名集合
           */
          public final static ArrayList<String> DangerGoods = new ArrayList<String>(Arrays.asList("炸药","硫酸","硫磺"));
      
          /**
           * 检查商品是否为危险品
           *
           * @param goods 检查的商品
           */
          public static void checkBag(Goods goods) throws DangerException {
              if(DangerGoods.contains(goods.getName())){
                  throw new DangerException();
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    4. 编写主类Check,在其main方法中创建创建商品对象,并使用Machine对象检查商品。

      /**
       * 主类:进行检查
       */
      public class Check {
          public static void main(String args[]) {
              //商品名称
              String[] name = {"苹果", "炸药", "西服", "硫酸", "手表", "硫磺"};
      
              Goods[] goods = new Goods[name.length];
      
              //创建商品对象
              for (int i = 0; i < name.length; i++) {
                  goods[i] = new Goods(name[i]);
              }
              //检查商品
              for (int i = 0; i < name.length; i++) {
                  try {
                      Machine.checkBag(goods[i]);
                      System.out.println(goods[i].getName() + ",检查通过");
                  } catch (DangerException e) {
                      System.out.println(goods[i].getName() + e.getMessage());
                      System.out.println(goods[i].getName() + ",被禁止!");
                  }
                  //换行分割
                  System.out.println();
              }
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
    5. 程序输出如下:

      苹果,检查通过
      
      炸药属于危险品!
      炸药,被禁止!
      
      西服,检查通过
      
      硫酸属于危险品!
      硫酸,被禁止!
      
      手表,检查通过
      
      硫磺属于危险品!
      硫磺,被禁止!
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

    【运行流程】

    image-20221101193208763

  • 相关阅读:
    10. IO框架
    一些常见的必须会的谭浩强基本代码大全也是常考的应试是没问题的
    Pytorch面试题整理(2023.09.10)
    【SpringMVC】工作流程
    CAD最常用的快捷键大全来啦
    Nginx之QPS限制模块解读
    代码随想录二刷 Day23
    基于springboot的家政系统毕业设计源码201524
    数据结构——线性表
    《博弈论》— 人生何处不博弈
  • 原文地址:https://blog.csdn.net/qq_62982856/article/details/127715777