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

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

实验9 内部类和异常类

9.1 实验目的

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

9.2 实验内容

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

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

  • 成员内部类是定义在外部类的成员位置上,并且没有static修饰
//Outer是外部类
public class Outer{
    //Inner是成员内部类
    class Inner{
    }
}
  • 可以直接访问外部类的所有成员,包括 private
  • 可以添加任意访问修饰符(public,protected,default默认,private),因为它的地位就是一个成员。
  • 作用域:和外部类的其他成员一样,为整个类体
  • 成员内部类访问外部类成员:直接访问
public class Outer { 
   private String name = "外部类";
   public class Inner{
       public void show(){
           //访问的外部类成员name
           System.out.println(name);     
       }
   }
}
  • 外部类访问成员内部类:创建对象再访问,且可以访问成员内部类的所有成员(包括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. 创建外部类对象再创建成员内部类
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. 在外部类中定义方法返回成员内部类实例对象
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();
    }
}
  • 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用 外部类名.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);
       }
   }
}

【核心代码】

编写 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();
    }
}

【运行流程】

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());
    }
}
  • ArithmeticException 数学运算异常
    当出现异常的运算条件时,抛出此异常。例如:
public class ExceptionTest {
    public static void main(String[] args) {
        // 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
        System.out.println(10/0);
    }
}
  • 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]);
    }
}
  • ClassCastException 类型转换异常
    当我们用子类对象去强制转换父类对象就会报错,同样会抛出此异常。例如:
public class ExceptionTest {
    public static void main(String[] args) {
        //所有类都默认直接或间接继承了str,Object是顶级父类
        Object object = new Object();
        //因此这是在把一个父类实例对象转为子类,会抛出异常ClassCastException
        String str = (String)object;
    }
}
  • NumberFormatException 数字格式不正确异常
    当应用程序试图将字符串转换成一种数值类型,但是该字符串不能转换为适当格式时,抛出该异常 --> 使用异常我们可以确保输入是满足条件的数字
public class ExceptionTest {
    public static void main(String[] args) {
        String str = "狐狸半面添";
        //parseInt方法:试图将str转为整型
        //但很明显,我们定义的 str 无法转为整型,因此抛出异常NumberFormatException
        int num = Integer.parseInt(str);
    }
}
  • 我们还可以查看一下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;
}

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代码块");
        }
    }
}
  • throws
    将发生的异常抛出,交给调用者来处理,最顶级的处理者就是 JVM 虚拟机
public class ExceptionTest {
    // throws Exception:发生异常时则抛出异常给该方法的调用者处理
    public static void main(String[] args) throws Exception {
        // 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
        System.out.println(10 / 0);
    }
}

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. 通过继承Exception类,编写一个DangerException类:该异常类有构造方法,该构造方法使用super调用父类构造方法,使用字符串:“属于危险品!”,对父类变量message进行初始化。
/**
 * 异常类
 */
public class DangerException extends Exception{
    public DangerException() {
        super("属于危险品!");
    }
}
  1. 编写商品类: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. 编写一个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. 编写主类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. 程序输出如下:
苹果,检查通过
炸药属于危险品!
炸药,被禁止!
西服,检查通过
硫酸属于危险品!
硫酸,被禁止!
手表,检查通过
硫磺属于危险品!
硫磺,被禁止!

【运行流程】


相关文章
|
1天前
|
网络协议 Java
Java中如何使用Socket类检测端口是否存在
Java中如何使用Socket类检测端口是否存在
12 4
|
1天前
|
存储 缓存 前端开发
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
10 3
|
1天前
|
存储 Java
Java的`java.io`包包含多种输入输出类
Java的`java.io`包包含多种输入输出类。此示例展示如何使用`FileInputStream`从`input.txt`读取数据。首先创建`FileInputStream`对象,接着分配一个`byte`数组存储流中的数据。通过`read()`方法读取数据,然后将字节数组转换为字符串打印。最后关闭输入流释放资源。`InputStream`是抽象类,此处使用其子类`FileInputStream`。其他子类如`ByteArrayInputStream`、`ObjectInputStream`和`BufferedInputStream`各有特定用途。
7 1
|
3天前
|
Java 开发者
Java中三种Set的实现类的用法和区别
Java中三种Set的实现类的用法和区别
|
Java 程序员 编译器
Java异常——throw、throws及自定义异常
Java异常——throw、throws及自定义异常
109 0
|
Java 编译器
Java中的异常(抛出异常、自定义异常等)
Java中的异常(抛出异常、自定义异常等)
225 1
|
存储 Java 编译器
【Java】认识异常及自定义异常
【Java】认识异常及自定义异常
【Java】认识异常及自定义异常