初始java( 7 )10000字详解

简介: 初始java( 7 )10000字详解

一:异常

1.1引入异常

我们知道,java是一门面向对象的语言,在java中一切皆对象,在生活中,对象不可能是一直正常的,比如说一个灯泡,它正常的情况下会发光,但是当它出现异常的时候,是不是就不会发光了,同理的,人这个对象也会出现异常,比如说我们可能会感冒发烧,这些都是异常,那在生活中这些异常的情况在java中是如何体现的呢?

在java中,程序执行过程中发生的不正常的行为就称为异常,在java中,异常有很多种类型,每种类型都有对应的类进行描述,当异常发生时,它会中断程序的正常执行流程,并且可能导致程序崩溃。

1.2 异常的体系结构

Java中的异常体系结构是由一个类和多个子类组成的继承体系,它以java.lang.Throwable类为根,分为两个主要分支:ErrorException

  1. Error类表示严重的错误,通常是由于底层系统或资源故障引起的,程序无法处理或恢复。常见的错误类型有StackOverflowErrorOutOfMemoryError等,它们发生时很难进行处理,一般应用程序无法捕获和处理这些错误。
  2. Exception类表示可处理的异常情况,是对程序正常运行中可能发生的情况进行建模。Exception类有很多子类,可细分为两种类型:
  • 受检异常:也称为编译时异常,必须在方法的声明中显式声明可能抛出的受检异常,并使用try-catch块或throws子句进行处理。常见的受检异常有IOExceptionSQLException等。
  • 非受检异常:也称为运行时异常,是由程序错误或逻辑错误引起的异常,通常是可以通过代码优化避免的错误。在方法的声明中不需要显式地声明可能抛出的非受检异常,也不需要进行强制性的异常处理。常见的非受检异常有NullPointerExceptionArrayIndexOutOfBoundsException等。

下面是一个Java异常体系结构的简单示意图:

注意,此图只是简单的示意图,并不是完整的异常体系结构

其中运行时异常都直接或者间接的继承了RuntimeException这个类,IOException,CloneNotSupportedException,ClassNotFound,都是编译时异常(也称为受查异常)

注意:异常是指程序执行的时候出现的异常状况,编译时出现的语法性错误,不能称之为异常。例如将 System.out.println 拼写错了,写成了system.out.println. 此时编译过程中就会出错,但这不能称为异常

1.3异常的处理

在java中,对异常的处理要分为三个部分:

抛出异常 — 捕获异常 — 处理异常

1.3.1抛出异常

抛出异常:

  1. 自动抛出异常

在Java中,有一些异常是会自动被抛出的,即无需我们显式地使用throw关键字来抛出。这些异常主要包括:

  1. 空指针异常(NullPointerException):当我们对一个空对象调用其方法或访问其属性时,就会抛出该异常。
  2. 数组越界异常(ArrayIndexOutOfBoundsException):当我们试图访问数组中不存在的索引位置时,就会抛出该异常。
  3. 类型转换异常(ClassCastException):当我们试图将一个对象强制转换为不兼容的类型时,就会抛出该异常。
  4. 文件未找到异常(FileNotFoundException):当我们尝试打开一个不存在的文件时,就会抛出该异常。
  5. 输入输出异常(IOException):当处理输入输出流时,如果发生了读写错误或中断等情况,就会抛出该异常。

当程序发生异常的时候,会自动new一个异常对象,抛出异常。

举个例子:

int a = 10;
int b = 0;
int c = a / b;

在这段代码中就会有一个异常,此时相当于会执行这样一个语句: new ArithmeticException(“ /by zero”);

下面我来解释一下这句话的意思:

ArithmeticException是Java中的一个内置异常类,用于表示算术错误, new ArithmeticException()代表创建了一个 ArithmeticException对象,并且()放的是错误信息,在上述代码中,将"/by zero"作为错误信息封装在了这个异常对象之中

  1. 显式抛出异常

在Java中,可以使用 throw 关键字显式抛出异常。throw 关键字后面紧跟着要抛出的异常对象。通过抛出异常,可以在代码中指定出现异常的情况,并将控制权交给异常处理机制。

下面是一个简单的示例,演示如何使用 throw 关键字显式抛出异常:

public class Main {
    public static void main(String[] args) {
        int age = -1;
        if (age < 0) {
            throw new IllegalArgumentException("年龄不能为负数");
        }
    }
}

在上面的示例中,通过 throw 关键字显式抛出了一个 IllegalArgumentException 异常对象,并传递了一个错误信息 "年龄不能为负数"。当 age 的值小于 0 时,就会抛出该异常。

注意,使用 throw 关键字抛出的异常必须是 Throwable 类或其子类的实例。

1.3.2处理异常的两种方式

1.3.2.1 try-catch捕获异常
  1. 捕获和处理异常

在java中,我们可以使用try-catch语句来捕获异常。try块包含可能会抛出异常的代码,而catch块用于处理这些异常。

以下是一个捕获异常的基本语法结构:

try {
   // 代码块,可能会抛出异常
} catch (ExceptionType1 e1) {
   // 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {
   // 处理 ExceptionType2 类型的异常
} finally {
   // 可选,无论是否发生异常都会执行的代码
}

在try块中,我们放置可能会抛出异常的代码。如果在try块中的任何位置发生了异常,try块内剩余的代码将被跳过,而控制流将转移到与之匹配的catch块。

catch块用于捕获和处理特定类型的异常。我们可以编写多个catch块来捕获不同类型的异常,从而提供不同的处理逻辑。

在catch块中,我们可以使用异常参数(例如e1和e2)来访问抛出异常的详细信息,例如异常类型、消息等。我们可以使用这些信息进行错误处理、记录日志或其他操作。

最后,我们可以选择使用finally块来编写一些无论是否发生异常都会执行的代码。例如,我们可以在finally块中释放资源或进行一些清理操作。

下面是一个简单的示例,演示如何使用try-catch-finally来捕获和处理异常:

public class ExceptionExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Division by zero is not allowed.");
        } finally {
            System.out.println("End of program.");
        }
    }
    public static int divide(int num1, int num2) {
        return num1 / num2;
    }
}

在上面的示例中,我们尝试计算10除以0,这将导致算术异常(ArithmeticException)。在catch块中,我们捕获并处理该异常,输出一条自定义的错误消息。无论是否发生异常,finally块都会被执行,输出"End of program."。

1.3.2.2通过throws声明异常

在Java中,throws关键字用于声明一个方法可能会抛出的异常类型。当一个方法可能会抛出一个或多个具体的异常时,我们可以使用throws关键字来告知调用者可能需要处理这些异常。这种方式称为"方法抛出异常"。

throws的作用主要有两个方面:

  1. 方法签名中的throws:它允许我们在方法声明中指定该方法可以抛出的异常类型。这样一来,在调用该方法时,调用者就可以知道可能需要处理的异常。
  2. 异常传递:当一个方法抛出一个异常时,如果该方法没有捕获该异常并进行处理,那么该异常会被传递给调用该方法的方法,直到有某个方法捕获并处理该异常,或者直到达到程序的顶层(例如main方法),如果程序的顶层也没有处理该异常,那么该异常会导致程序的终止。

下面是一个关于throws关键字的代码示例:

public class Example {
    public static void main(String[] args) {
        try {
            divide(10, 0);
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        }
    }
    public static double divide(int dividend, int divisor) throws ArithmeticException {
        if (divisor == 0) {
            throw new ArithmeticException("除数不能为0");
        }
        return dividend / divisor;
    }
}

在上述代码中,divide方法可能会抛出ArithmeticException,在方法的声明中使用了throws关键字来指定该方法可能会抛出该异常。在main方法中调用divide方法时,使用try-catch块捕获并处理了可能抛出的异常。

通过使用throws关键字,我们可以在方法声明处指定可能抛出的异常,提供对异常的详细描述,并让调用者知道某个方法可能需要处理的异常,以便进行适当的异常处理。

1.3.2.3throws和try - catch

当处理异常时,throws 关键字和 try-catch 块传递了两种不同的态度。

使用 throws 关键字表示异常是方法的责任之一。它将异常的处理责任转移到方法的调用者。这种方法将异常类型列在方法的声明中,告诉调用者可能会抛出哪些异常。通过在方法签名中使用 throws 关键字,方法提供了一种标准的异常传递机制,使得方法调用者能够明确处理这些可能的异常。这种方式适用于对特定类型的异常进行统一处理,或者当无法处理异常时将其传递给上层调用者。

以下是一个使用 throws 关键字的方法示例:

public void readFile(String filePath) throws FileNotFoundException {
    // 尝试打开文件
    FileInputStream fileInputStream = new FileInputStream(filePath);
    // 读取文件内容
    // ...
}

在上面的示例中,readFile 方法声明可能会抛出 FileNotFoundException 异常。如果在方法内部发生了这个异常,方法不会处理它,而是将异常传递给调用者。

相比之下,try-catch 块体现了对异常的主动处理。它允许程序捕获并对可能发生的异常进行处理,而不是简单地将异常传递给上级调用者。通过使用 try-catch 块,程序可以在出现异常时执行特定的操作。

以下是一个使用 try-catch 块的示例,展示了对异常的处理:

public void divideNumbers(int a, int b) {
    try {
        int result = a / b;
        System.out.println(result);
    } catch (ArithmeticException e) {
        System.out.println("除数不能为零!");
    }
}

在上面的示例中,divideNumbers 方法试图进行除法运算,但是如果除数为零,就会抛出 ArithmeticException 异常。通过在 try-catch 块中捕获并处理这个异常,程序可以避免因除以零而导致程序崩溃,而是提供了一种恰当的处理方式。

1.3.2.4try - catch执行流程

当在Java中使用try-catch语句时,它的执行流程如下:

  1. 首先,程序执行到try块中的代码。try块是包含可能引发异常的代码块。
  2. 如果在try块中没有引发异常,程序将继续执行try块后面的代码,跳过catch块。这意味着,如果没有异常被抛出,catch块将不会执行。
  3. 如果在try块中引发了异常,程序将立即跳转到与异常类型匹配的catch块。Java将根据引发的异常类型,按顺序匹配到第一个合适的catch块。
  4. 当找到与引发的异常类型匹配的catch块时,该catch块中的代码将会执行。catch块是用于捕获和处理异常的代码块。
  5. 若没有找到匹配的catch块,则会发生异常传播,将异常一直向上抛,直到异常被捕获为止。
  6. catch块中,可以编写处理异常的逻辑,如输出错误消息、记录日志等。
  7. catch块执行完毕后,程序将继续执行try-catch语句块之后的代码。

下面是一个简单的代码示例来说明try-catch执行流程:

public class TryCatchExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);  // 在这里调用可能抛出异常的方法
            System.out.println("结果:" + result);
        } catch (ArithmeticException e) {
            System.out.println("除以零异常:" + e.getMessage());
        }
        System.out.println("程序继续执行");
    }
    public static int divide(int num1, int num2) {
        return num1 / num2;
    }
}

在上面的代码中,divide方法用于计算两个数字的商,如果除数为零,就会引发ArithmeticException异常。

当程序执行到try块中的divide方法调用时,如果除数为零,将引发ArithmeticException异常。此时,程序将跳转到匹配的catch块,输出错误消息:“除以零异常”。

最后,无论是否引发异常,程序都会执行try-catch语句块之后的代码,并输出:“程序继续执行”。

1.4 异常的处理流程

当程序发生异常时,Java的异常处理机制会按照以下流程进行处理:

  1. 异常产生:异常是在程序执行过程中由于错误或异常条件引起的。它可能是由Java运行时库中的异常,也可以是用户自定义的异常。
  2. 异常对象的创建:在异常产生时,会创建一个异常对象来表示异常情况。这个异常对象封装了异常的类型、信息和堆栈轨迹等相关信息,以便在后续的处理中使用。
  3. 异常抛出:一旦异常对象创建完成,它就会被抛出。异常对象会被传递给调用栈中的上层调用者,直到找到能够处理异常的代码块。
  4. 异常处理:当异常对象传递到能够处理异常的代码块时,异常处理机制会根据编写的异常处理逻辑进行处理。可以使用try-catch语句来捕获异常并提供相应的处理逻辑,或者使用throws关键字将异常继续向上抛出。
  5. 异常处理或终止程序运行:如果异常在调用栈中找不到能够处理它的代码块,异常会一直向上抛出,直到程序的顶层调用者。如果顶层调用者也无法处理异常,程序将终止运行,并打印异常的堆栈轨迹信息供调试使用。

1.5异常传播

在异常发生的情况下,Java会自动创建一个异常对象,其中包含有关异常的信息(如异常类型、消息和堆栈跟踪)。这个异常对象会从当前方法返回到调用栈的上一层方法,并由上一层方法来处理。

当某个方法无法处理异常时,它会将异常传递给调用它的方法,直到异常被合适的异常处理程序捕获或传递到调用栈的最高层。如果异常一直传递到最高层而没有被捕获,程序将终止并打印异常信息。

当一个异常在程序中被抛出时,它会在调用栈中向上传递,直到找到能够处理该异常的代码块或者到达JVM。这个过程被称为异常传播。

如果异常传播到了JVM,那么JVM会尝试查找能够处理该异常的代码块(异常处理程序)。如果找到了匹配的异常处理程序,那么程序会按照该处理程序的逻辑进行处理,然后继续执行。

但是,如果异常在异常传播过程中未被处理或者未找到匹配的异常处理程序,那么程序会停止运行,并打印出错误信息。这是因为在默认情况下,JVM会将未处理的异常作为致命错误(Fatal Error)来处理,终止程序的运行。

1.6 finally关键字

注意这是finally不是final

在Java中,finally是一个可选的代码块,它通常与try-catch语句一起使用。finally块中的代码总是会被执行,无论在try块中是否发生了异常,都会在try块或者catch块结束后执行。

finally块主要用于确保在任何情况下都会执行一些必要的清理工作,比如关闭打开的文件或者释放资源。即使在try块中发生了异常,finally块也会被执行。

下面是一个示例代码,展示了finally的使用方式:

try {
    // 可能会抛出异常的代码
    // ...
} catch (Exception e) {
    // 异常处理逻辑
    // ...
} finally {
    // 无论是否发生了异常,都会执行的代码
    // ...
}

在上述代码中,try块中包含可能会抛出异常的代码。如果异常被捕获到,则会执行对应的catch块中的代码进行异常处理。无论是否发生了异常,finally块中的代码都会被执行。

finally块具有以下几个特点:

  1. finally块中的代码会在任何情况下被执行,无论是否有异常抛出。
  2. finally块可以单独存在,不一定需要与try-catch一起使用。
  3. 如果在finally块中使用了return语句,它会覆盖掉try块或catch块中的return语句,返回finally块中的结果。

注意:finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。

finally块提供了一种确保必要的清理工作能够被执行的机制,无论是否发生异常都可以保证代码的完整性和正确性。

1.7进一步了解异常

一个异常被抛出后,只要在传递到JVM之前被捕获,程序就可以正常运行,即使异常被捕获了,在catch中的代码块不做异常处理也可以

但是,如果在 catch 块中不对异常进行处理,那么异常将会被忽略,可能会导致程序逻辑错误或无法预测的行为。

以下是一个示例代码,说明了异常捕获后不处理的情况:

public class ExceptionExample {
    public static void main(String[] args) {
        try {
            throwException();
        } catch (Exception e) {
            // 不处理异常,仍然可以继续执行
        }
        System.out.println("程序继续执行");
    }
    public static void throwException() throws Exception {
        throw new Exception("抛出异常");
    }
}

运行结果:程序继续执行

在 catch 块中,我们没有对异常进行处理,只是简单地留空。这意味着异常并没有被处理,但是忽略异常可能会导致难以排查的问题,并且可能破坏程序的正常逻辑,因此建议始终对捕获的异常进行适当处理。

注意:在try中,一次只能抛出一个异常,并不嫩一次抛出多个异常

如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误,下面是个代码示例:

public class ExceptionExample {
    public static void main(String[] args) {
        try {
            // 可能会抛出父类异常的代码
            throw new IOException();
        } catch (FileNotFoundException e) {
            System.out.println("FileNotFoundException: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("IOException: " + e.getMessage());
        }
    }
}

在上述代码中,我们先捕捉了FileNotFoundException异常,再捕捉IOException异常。由于FileNotFoundException是IOException的子类,所以如果将这两个catch块的顺序调换,编译器将会报错提示语法错误。正确的顺序是先捕捉子类异常,再捕捉父类异常。

当然我们也可以通过一个catch捕获所有的异常,即多个异常,一次捕获(不推荐),下面是代码示例:

public static void main(String[] args) {
  int[] arr = {1, 2, 3};
  try {
    System.out.println("before");
    arr = null;
    System.out.println(arr[100]);
    System.out.println("after");
 } catch (Exception e) {
    e.printStackTrace();
 }
  System.out.println("after try catch");
}

由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.

注意:catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象.

二:自定义异常类

在Java中,可以通过创建自定义异常类来处理特定的异常情况。自定义异常类是从Java内置的Exception派生而来的子类。

自定义异常类的命名应遵循一定的规范,一般采用以"Exception"结尾的命名方式,例如"CustomException"。通过自定义异常类,我们可以提供有关异常的详细信息,并根据需要执行适当的处理逻辑。

下面是一个简单的例子来说明如何创建和使用自定义异常类:

// 创建自定义异常类
class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}
// 使用自定义异常类
class Example {
    public void process(int value) throws CustomException {
        if (value < 0) {
            throw new CustomException("值不能为负数");
        }
    }
    public static void main(String[] args) {
        Example example = new Example();
        try {
            example.process(-1);
        } catch (CustomException e) {
            System.out.println(e.getMessage());
        }
    }
}

上面的代码中,我们创建了一个名为CustomException的自定义异常类,它继承自Java的Exception类。CustomException类有一个接受String类型参数的构造函数,super(message)的作用是调用Exception类的构造方法,将指定的错误消息传递给父类。通过构造函数中调用父类的构造函数来设置异常信息。

在Example类的process方法中,我们通过判断value的值是否小于0来抛出CustomException异常。在main方法中,我们通过try-catch块捕获了CustomException异常,并打印出异常信息。

自定义异常类的意义在于,它提供了一种更灵活和可读性更高的异常处理机制。通过自定义异常类,我们可以根据实际需求定义不同的异常类型,并提供相应的处理逻辑。这有助于代码的可维护性和可扩展性,提高了代码的质量和可读性。

注意事项

  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常.

我们在自定义一个异常类的时候,可以通过构造方法将String参数传递给父类的构造方法,以此来设置出现异常的原因

本章完

目录
相关文章
|
11月前
|
存储 SQL Java
初始java ( 3 )10000字详解
初始java ( 3 )10000字详解
43 0
|
3月前
|
Java
JAVA随机生成一个汉字的方法和代码
JAVA随机生成一个汉字的方法和代码
32 1
|
4月前
|
存储 Oracle 小程序
初始Java
初始Java
24 1
|
11月前
|
安全 Java 编译器
初始java ( 1 ) 6000字详解
初始java ( 1 ) 6000字详解
34 0
|
11月前
|
存储 安全 搜索推荐
初始java( 8 )15000字详解
初始java( 8 )15000字详解
54 0
|
11月前
|
存储 Java 编译器
java之十二 事 件 处 理
java之十二 事 件 处 理
52 0
java之十二 事 件 处 理
|
11月前
|
Java 程序员
初始java( 4 )7000字详解
初始java( 4 )7000字详解
27 0
|
11月前
|
存储 Java 编译器
初始java ( 2 )6000字详解
初始java ( 2 )6000字详解
30 0
|
11月前
|
Java 编译器
初始java( 5 )7000字详解
初始java( 5 )7000字详解
50 0