中南林业科技大学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. 程序输出如下:
苹果,检查通过
炸药属于危险品!
炸药,被禁止!
西服,检查通过
硫酸属于危险品!
硫酸,被禁止!
手表,检查通过
硫磺属于危险品!
硫磺,被禁止!

【运行流程】


相关文章
|
6天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
33 17
|
1天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
19 4
|
2天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
9 2
|
6天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
9天前
|
Java
如何在 Java 中处理“Broken Pipe”异常
在Java中处理“Broken Pipe”异常,通常发生在网络通信中,如Socket编程时。该异常表示写入操作的另一端已关闭连接。解决方法包括:检查网络连接、设置超时、使用try-catch捕获异常并进行重试或关闭资源。
|
11天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
11天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
10天前
|
存储 Java 编译器
java wrapper是什么类
【10月更文挑战第16天】
18 3
|
11天前
|
存储 安全 Java
如何避免 Java 中的“ArrayStoreException”异常
在Java中,ArrayStoreException异常通常发生在尝试将不兼容的对象存储到泛型数组中时。为了避免这种异常,确保在操作数组时遵循以下几点:1. 使用泛型确保类型安全;2. 避免生类型(raw types)的使用;3. 在添加元素前进行类型检查。通过这些方法,可以有效防止 ArrayStoreException 的发生。
|
8天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?