精通Java异常机制,写出高质量代码

news2024/11/15 21:52:32

作为一名Java开发人员,异常处理是一个无法回避的话题。无论你是初学者还是老手,精通异常处理对于写出高质量、可维护的代码至关重要。今天,我将与大家分享关于Java异常处理的一切,助你在代码质量的道路上突飞猛进!


一、什么是异常?

在我们深入探讨之前,先让我们理解一下什么是异常。异常(Exception)是指程序在执行过程中发生的不正常情况,如文件未找到、网络连接中断、数组越界等。如果不做任何处理,程序将终止运行。


二、Java异常体系结构

Java异常体系结构是Java语言中处理程序错误和异常情况的核心机制。异常是程序运行时发生的异常事件,它们可以由JVM自动抛出,也可以由程序显式抛出。Java异常体系结构基于类继承,所有的异常类都继承自Throwable类。


在这里插入图片描述


Java异常体系结构的主要组成部分

  • Throwable类

    • 所有异常或错误的超类。
    • 有两个主要的子类:ErrorException
    • Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。
  • Error类

    • Error 类及其子类:程序中无法处理的错误,表示编译时和系统错误(如OutOfMemoryErrorStackOverflowError),通常是不可查的(即程序无法预见和恢复的)。

    • 这些错误通常表示JVM运行时出现了严重问题,此类错误发生时,JVM 将终止线程。

    • 这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们不应该实现任何新的Error子类!

  • Exception类

    • 表示程序可以捕捉和处理的异常情况。
    • 有两个主要的子类:运行时异常(RuntimeException)和非运行时异常(checked exceptions)。
  • 运行时异常(RuntimeException类)

    • RuntimeException类及其子类异常,表示由于程序中的错误而导致的异常,比如NullPointerExceptionIndexOutOfBoundsException等。
    • 这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。但通常是由编程错误导致的,应该从逻辑角度尽可能避免这类异常的发生。
    • 运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
  • 非运行时异常(Checked Exceptions)

    • 是RuntimeException以外的异常,类型上都属于Exception类及其子类,表示程序运行中可能出现的异常情况,但这些异常是可查的(checked)。

    • 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过,例如IOExceptionSQLException`等。


三、Java异常处理机制

1、异常关键字
  • try块

    • 用于监听,将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。

    • 可以有多个catch块来捕获不同类型的异常。

  • catch块

    • 捕获并处理try块中抛出的异常。
    • 可以有多个catch块来处理不同类型的异常。
  • finally块

    • 无论是否发生异常,都会执行的代码块。
    • 通常用于释放资源,如关闭文件流、数据库连接等。
    • 只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
  • throw关键字

    • 显式抛出一个异常。
  • throws关键字

    • 用在方法签名中,用于声明该方法可能抛出的异常。

2、try-catch-finally 执行的顺序
  • 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句。

  • 当try捕获到异常,catch语句块里没有处理此异常的情况:

    • 当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行。
  • 当try捕获到异常,catch语句块里有处理此异常的情况:

    • 在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句。

在这里插入图片描述


示例代码:

public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            // 尝试执行可能抛出异常的代码
            methodThatMayThrowException();
        } catch (IOException e) {
            // 处理IOException
            e.printStackTrace();
        } catch (Exception e) {
            // 处理其他类型的异常
            e.printStackTrace();
        } finally {
            // 无论是否发生异常,都会执行的代码
            System.out.println("Finally block executed.");
        }
    }

    public static void methodThatMayThrowException() throws IOException {
        // 抛出一个IOException
        throw new IOException("An I/O error occurred.");
    }
}

在这个示例中,methodThatMayThrowException方法声明可能会抛出IOException,因此在main方法中,我们需要用try-catch块来捕获并处理这个异常。finally块确保无论是否发生异常,都会执行一些清理工作。

通过这种异常体系结构,Java提供了一种强大的方式来处理程序中的错误和异常情况,使得程序更加健壮和易于维护。


3、异常的申明(throws)

在Java编程语言中,throws 关键字用于在方法声明中指定该方法可能会抛出的异常类型。当一个方法可能会抛出某种类型的异常,并且调用者需要处理这些异常时,方法声明中会使用throws关键字来告知调用者。


(1)、何时使用throws

  • 方法内部无法处理异常:如果方法内部的代码可能会抛出异常,并且该异常无法在方法内部被处理(例如,通过try-catch块),则需要使用throws关键字来声明。

  • 强制调用者处理异常:使用throws关键字可以强制调用者必须处理这些异常,要么通过捕获它们,要么通过进一步向上抛出。


(2)、语法

throws关键字的基本语法如下:

public returnType methodName(parameterList) throws ExceptionType1, ExceptionType2, ... {
    // 方法体
}

Throws抛出异常的规则:

  • 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
  • 必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。
  • 仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
  • 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

(3)、案例

假设有一个方法divide,它可能会因为除数为0而抛出ArithmeticException

public class Calculator {
    public int divide(int numerator, int denominator) throws ArithmeticException {
        if (denominator == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        return numerator / denominator;
    }
}

在这个例子中,divide方法声明了它可能会抛出ArithmeticException。这意味着任何调用divide方法的代码都必须处理这个异常,要么通过捕获它,要么通过在调用方法的声明中使用throws关键字。


(4)、注意事项

  • 异常链:当在catch块中抛出新的异常时,可以通过throw new ExceptionType(e)将原始异常作为新异常的cause来传递,这样可以保留原始异常的信息。
  • 检查型异常 vs. 非检查型异常:Java中的异常分为两类,检查型异常(checked exceptions)和非检查型异常(unchecked exceptions)。检查型异常是那些在编译时需要被处理的异常,而非检查型异常(如RuntimeException及其子类)则不需要。
  • 避免过度使用:过度使用throws关键字可能会导致代码难以阅读和维护。如果可能,尽量在方法内部处理异常,而不是将其抛出。
  • 文档化异常:在方法的文档注释中,应该详细说明哪些异常会被抛出,以及在什么情况下会抛出这些异常。

通过使用throws关键字,Java程序员能够创建更加健壮和易于维护的代码,因为它强制调用者考虑异常处理策略。


4、异常链

异常链是一种异常处理机制,它允许一个异常(称为“原因异常”或“原始异常”)被另一个异常(称为“包装异常”或“新异常”)所包装。这样做的主要目的是保留原始异常的信息,同时提供额外的上下文信息或更具体的异常类型。


(1)、为什么使用异常链?
  • 保留原始异常信息:当捕获一个异常并抛出一个新的异常时,原始异常的信息可能会丢失。通过异常链,原始异常可以作为新异常的cause被保留。

  • 提供上下文信息:新异常可以提供额外的上下文信息,这有助于调试和错误处理。

  • 使用更具体的异常类型:有时,捕获的异常可能不够具体,不足以表达问题的本质。通过抛出一个更具体的异常,可以提供更清晰的错误信息。


(2)、如何创建异常链

在Java中,创建异常链通常通过两种方式:

  • 使用构造函数:Java的异常类提供了接受一个Throwable参数的构造函数,该参数将作为新异常的cause。
try {
    // 可能会抛出异常的代码
} catch (SomeException e) {
    throw new AnotherException("Additional information", e);
}
  • 使用initCause方法:如果需要在构造异常之后设置cause,可以使用initCause方法。
try {
    // 可能会抛出异常的代码
} catch (SomeException e) {
    AnotherException anotherException = new AnotherException("Additional information");
    anotherException.initCause(e);
    throw anotherException;
}

(3)、使用异常链案例

假设有一个方法processFile,它尝试读取一个文件,并可能抛出FileNotFoundException。为了提供更多的上下文信息,我们可以创建一个包装异常:

public void processFile(String filename) throws FileProcessingException {
    try {
        // 尝试读取文件
        readFile(filename);
    } catch (FileNotFoundException e) {
        throw new FileProcessingException("Error processing file: " + filename, e);
    }
}

// FileProcessingException是一个自定义的异常类
public class FileProcessingException extends Exception {
    public FileProcessingException(String message, Throwable cause) {
        super(message, cause);
    }
}

在这个例子中,如果readFile方法抛出FileNotFoundException,我们捕获它并抛出一个新的FileProcessingException,同时将FileNotFoundException作为cause传递给FileProcessingException的构造函数。


(4)、访问原始异常

要访问异常链中的原始异常,可以使用Throwable.getCause()方法:

try {
    processFile("somefile.txt");
} catch (FileProcessingException e) {
    Throwable cause = e.getCause();
    if (cause instanceof FileNotFoundException) {
        // 处理FileNotFoundException
    }
}

通过这种方式,即使异常被包装,我们仍然可以访问原始异常的信息,这对于调试和错误处理非常有用。


(5)、注意事项
  • 不要滥用异常链:异常链应该在确实需要保留原始异常信息时使用。如果原始异常的信息对于错误处理没有帮助,或者新异常已经足够表达问题,就没有必要创建异常链。
  • 保持异常链的清晰:在创建异常链时,确保新异常能够清晰地表达问题,并且与原始异常有直接的关联。
  • 避免循环引用:在设置异常链时,要避免创建循环引用,这会导致StackOverflowError

通过合理使用异常链,可以创建更加健壮和易于维护的代码,同时提供更清晰的错误信息和调试信息。


5、自定义异常

除了Java内置的异常类型,我们还可以根据需要自定义异常类型。通常继承Exception或RuntimeException即可。

public class MyException extends Exception {
    public MyException() {
        super();
    }
    public MyException(String message) {
        super(message);
    }
}

使用时:

if(x < 0) {
    throw new MyException("x不能为负数");
}

四、常用的异常

在Java中提供了一些异常用来描述经常发生的错误,对于这些异常,有的需要程序员进行捕获处理或声明抛出,有的是由Java虚拟机自动进行捕获处理。Java中常见的异常类:

  • RuntimeException
    • java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
    • java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。
    • java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
    • java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
    • java.lang.NegativeArraySizeException 数组长度为负异常
    • java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常
    • java.lang.SecurityException 安全性异常
    • java.lang.IllegalArgumentException 非法参数异常
  • IOException
    • IOException:操作输入流和输出流时可能出现的异常。
    • EOFException 文件已结束异常
    • FileNotFoundException 文件未找到异常
  • 其他
    • ClassCastException 类型转换异常类
    • ArrayStoreException 数组中包含不兼容的值抛出的异常
    • SQLException 操作数据库异常类
    • NoSuchFieldException 字段未找到异常
    • NoSuchMethodException 方法未找到抛出的异常
    • NumberFormatException 字符串转换为数字抛出的异常
    • StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
    • IllegalAccessException 不允许访问某类异常
    • InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常

五、异常的最佳实践


1、尽量不要过度捕获异常 - 只捕获你知道如何处理的异常

异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流。

主要原因有三点:

  • 异常机制的设计初衷是用于不正常的情况,所以很少会会JVM实现试图对它们的性能进行优化。所以,创建、抛出和捕获异常的开销是很昂贵的。
  • 把代码放在try-catch中返回阻止了JVM实现本来可能要执行的某些特定的优化。
  • 对数组进行遍历的标准模式并不会导致冗余的检查,有些现代的JVM实现会将它们优化掉。

比如,在解析字符串形式的数字时,可能存在数字格式错误,不得通过catch Exception来实现:

  • 代码1
if (obj != null) {
  //...
}
  • 代码2
try { 
  obj.method(); 
} catch (NullPointerException e) {
  //...
}

2、及时释放资源 - 在finally块中释放资源或者使用 try-with-resource 语句

当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错误就是在try块的最后关闭资源。

  • 错误案例
public void doNotCloseResourceInTry() {
    FileInputStream inputStream = null;
    try {
        File file = new File("./tmp.txt");
        inputStream = new FileInputStream(file);
        // use the inputStream to read a file
        // do NOT do this
        inputStream.close();
    } catch (FileNotFoundException e) {
        log.error(e);
    } catch (IOException e) {
        log.error(e);
    }
}

以上代码问题在于:

只有没有异常抛出的时候,这段代码才可以正常工作。try 代码块内代码会正常执行,并且资源可以正常关闭。但是,使用 try 代码块是有原因的,一般调用一个或多个可能抛出异常的方法,这意味着代码可能不会执行到 try 代码块的最后部分。结果就是,你并没有关闭资源。

因此,应该把清理工作的代码放到 finally 里,或者使用 try-with-resource 特性。

  • 正确方法一:使用 finally 代码块

finally 代码块总是会被执行。不管 try 代码块成功执行之后还是你在 catch 代码块中处理完异常后都会执行。

因此你可以确保清理了所有打开的资源。

public void closeResourceInFinally() {
    FileInputStream inputStream = null;
    try {
        File file = new File("./tmp.txt");
        inputStream = new FileInputStream(file);
        // use the inputStream to read a file
    } catch (FileNotFoundException e) {
        log.error(e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error(e);
            }
        }
    }
}
  • 正确方法二:Java 7 的 try-with-resource 语法

如果资源实现了 AutoCloseable 接口,可以使用这个语法。大多数的 Java 标准资源都继承了这个接口。

当在 try 子句中打开资源,资源会在 try 代码块执行后或异常处理后自动关闭。

public void automaticallyCloseResource() {
    File file = new File("./tmp.txt");
    try (FileInputStream inputStream = new FileInputStream(file);) {
        // use the inputStream to read a file
    } catch (FileNotFoundException e) {
        log.error(e);
    } catch (IOException e) {
        log.error(e);
    }
}

3、不要在finally块中使用return

try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。

这是一个反面例子:

private int x = 0;
public int checkReturn() {
    try {
        // x等于1,此处不返回
        return ++x;
    } finally {
        // 返回的结果是2
        return ++x;
    }
}

4、优先捕获最具体的异常

通常情况下,只有匹配异常的第一个 catch 块会被执行。

如下例子:

public void catchMostSpecificExceptionFirst() {
    try {
        doSomething("A message");
    } catch (IllegalArgumentException  e) {
        log.error(e);
    } catch (NumberFormatException e) {
        log.error(e)
    }
}

如果首先捕获 IllegalArgumentException ,则永远不会到达应该处理更具体的 NumberFormatException 的 catch 块,因为它是 IllegalArgumentException 的子类。

因此,总是优先捕获最具体的异常类,并将不太具体的 catch 块添加到列表的末尾。

如下:

第一个 catch 块处理所有 NumberFormatException 异常,第二个处理所有非 NumberFormatException 异常的IllegalArgumentException 异常。

public void catchMostSpecificExceptionFirst() {
    try {
        doSomething("A message");
    } catch (NumberFormatException  e) {
        log.error(e);
    } catch (IllegalArgumentException e) {
        log.error(e)
    }
}

5、不要捕获 Throwable 类

Throwable 是所有异常和错误的超类。你可以在 catch 子句中使用它,但是你永远不应该这样做!

如果在 catch 子句中使用 Throwable ,它不仅会捕获所有异常,也将捕获所有的错误。JVM 抛出错误,指出不应该由应用程序处理的严重问题。

典型的例子是 OutOfMemoryError 或者 StackOverflowError 。两者都是由应用程序控制之外的情况引起的,无法处理。

所以,最好不要捕获 Throwable ,除非你确定自己处于一种特殊的情况下能够处理错误。

public void doNotCatchThrowable() {
    try {
        // do something
    } catch (Throwable t) {
        // don't do this!
    }
}

6、不要忽略异常

很多时候,开发者很有自信不会抛出异常,因此写了一个catch块,但是没有做任何处理或者记录日志。

public void doNotIgnoreExceptions() {
    try {
        // do something
    } catch (NumberFormatException e) {
        // this will never happen
    }
}

但现实是经常会出现无法预料的异常,或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。

合理的做法是至少要记录异常的信息。

public void logAnException() {
    try {
        // do something
    } catch (NumberFormatException e) {
        log.error("This should never happen: " + e); // see this line
    }
}

7 、不要记录并抛出异常

这可能是本文中最常被忽略的最佳实践。

可以发现很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑。如下:

try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
    throw e;
}

这个处理逻辑看着是合理的。但这经常会给同一个异常输出多条日志。如下:

17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"
Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.(Long.java:965)
at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)
at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)

如上所示,后面的日志也没有附加更有用的信息。如果想要提供更加有用的信息,那么可以将异常包装为自定义异常。

public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e);
    }
}

因此,仅仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理。


8、包装异常时不要抛弃原始的异常

捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。 在你这样做时,请确保将原始异常设置为原因(注:参考下方代码 NumberFormatException e 中的原始异常 e )。Exception 类提供了特殊的构造函数方法,它接受一个 Throwable 作为参数。否则,你将会丢失堆栈跟踪和原始异常的消息,这将会使分析导致异常的异常事件变得困难。

public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e);
    }
}

9、不要使用异常控制程序的流程

不应该使用异常控制应用的执行流程,例如,本应该使用if语句进行条件判断的情况下,你却使用异常处理,这是非常不好的习惯,会严重影响应用的性能。


六、结语

本文涵盖了异常处理的方方面面,但Java的异常处理机制还有很多值得探讨的地方。例如,各种异常处理模式的利弊、JDK9中新增的reactive streams操作符如何影响异常处理、如何实现优雅的异常处理等等。如果你对上述任何话题感兴趣,欢迎在评论区留言,我们下期再聊!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1714180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【RSGIS数据资源】1981-2021年中国陆地生态系统蒸腾蒸散比数据集

文章目录 摘要基本信息数据结构和内容采集方法信息数据处理方法与数据质量 摘要 本数据集涵盖了中国陆地生态系统蒸腾蒸散比&#xff08;T/ET&#xff09;、蒸腾&#xff08;T&#xff09;及蒸散&#xff08;ET&#xff09;三组数据。基于模型-数据融合方法&#xff0c;集成PT…

在window中使用HTTP服务器获取kali的文件

文章目录 一、在window中使用HTTP服务器获取kali的文件1、疑问2、执行条件3、成功读取 一、在window中使用HTTP服务器获取kali的文件 1、疑问 有时候kali上面有的文件想传入window但是发现不允许这样操作那怎么办呢&#xff1f;特别是在一些限制工具的比赛中想把kali的文件传…

杨校老师课题之基于Idea的SSM实训项目案例开发之在线手机商城开发(一)【非常适合初学者】

1.前期配置 2.开发涉及技术栈和工具 2.1 技术栈 后端: SSM前端&#xff1a;Html、CSS、BootStrap(官方定义好的CSS样式)数据库: MySQL 2.2 开发环境(工具) 进行本次开发&#xff0c;需要具备如下环境: JDK a. JDK8.0/1.8 b. 注意&#xff1a; 没有JDK是无法运行IdeaIDEA a. …

GPT-4o:重塑人机交互的未来

一个愿意伫立在巨人肩膀上的农民...... 一、推出 在人工智能&#xff08;AI&#xff09;领域&#xff0c;自然语言处理&#xff08;NLP&#xff09;技术一直被视为连接人类与机器的桥梁。近年来&#xff0c;随着深度学习技术的快速发展&#xff0c;NLP领域迎来了前所未有的变革…

SQL问题的常用信息收集命令及解决思路 |OceanBase应用实践

面对SQL问题&#xff0c;大家的常用的分析思路是&#xff1a; 一、问题是否源于SQL本身&#xff1f;是的话需进行SQL调优。 二、SQL语句本身无误&#xff0c;但执行效果并未达到我们的预期效果。 检查当前的服务器负载状况&#xff0c;例如CPU利用率、内存占用、IO读写等关键…

【服务器部署篇】Linux下Node.js的安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0c;产…

二叉树链式结构的前序_中序_后续_层序遍历【详细图解】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;LiUEEEEE                        …

unity中的常用属性修饰符

unity中的常用属性修饰符 一、前言二、常用修饰符三、结语 一、前言 在做unity开发编辑脚本的时候经常会用到属性修饰符&#xff0c;使开发调试更加便捷。初学者见过最多的莫过于[Header("标题文本")]了吧&#xff0c;除此之外其实还有很多&#xff0c;这篇文章列举说…

「异步魔法:Python数据库交互的革命」(二)

哈喽&#xff0c;我是阿佑&#xff0c;上篇文章带领了大家跨入的异步魔法的大门——Python数据库交互&#xff0c;一场魔法与技术的奇幻之旅&#xff01; 从基础概念到DB-API&#xff0c;再到ORM的高级魔法&#xff0c;我们一步步揭开了数据库操作的神秘面纱。SQLAlchemy和Djan…

[Linux系统编程] 静态库与动态库

一.库的概念 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统载入内存执行。库有两种&#xff1a;静态库&#xff08;.a、.lib&#xff09;和动态库&#xff08;.so、.dll&#xff09;。…

[leetcode hot 150]第一百九十一题,位1的个数

题目&#xff1a; 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中设置位的个数&#xff08;也被称为汉明重量&#xff09;。 这道题比较简单&#xff0c;直接对最后一位进行与1的与操作&#xff0c;然…

JVM之垃圾判断的详细解析

垃圾判断 垃圾介绍 垃圾&#xff1a;如果一个或多个对象没有任何的引用指向它了&#xff0c;那么这个对象现在就是垃圾 作用&#xff1a;释放没用的对象&#xff0c;清除内存里的记录碎片&#xff0c;碎片整理将所占用的堆内存移到堆的一端&#xff0c;以便 JVM 将整理出的内…

microk8s 报错tls: failed to verify certificate: x509:

问题&#xff1a; ssh命令出现如下图所示 输入任何microk8s的容器命令几乎都是x509报错 kubectl get pods -ALL 原因&#xff1a; 证书过期 相关文档&#xff1a; MicroK8s - 服务和端口 Microk8S v1.24 - refresh-certs 似乎无法刷新证书 问题 #3241 规范/microk8s Git…

Mybatis进阶——动态SQL(1)

目录 一、 <if> 标签 二、<trim> 标签 三、<where> 标签 四、<set> 标签 五、<foreach> 标签 六、<include> 标签 动态SQL 是Mybatis的强大特性之一&#xff0c;能够完成不同条件下的不同SQL拼接&#xff0c;可以参考官方文档&#…

端到端目标检测 |从DETR 到 GroundingDINO

文章目录 一&#xff0c;DETR1. 简介2. 亮点3. 细节4. 总结一下 二&#xff0c;GroundingDINOGrounding DINO的整体流程Grounding DINO的目标函数 一&#xff0c;DETR 之前的目标检测框架&#xff0c;需要很多的人工干预&#xff0c;很多的先验知识&#xff0c;而且可能还需要…

AppInventor2 表格布局的外面的黑框怎么去掉?

问&#xff1a;表格布局的外面的黑框怎么去掉啊&#xff1f; 答&#xff1a;这个黑框是界面设计的布局位置示意&#xff0c;实际 App 测试时并没有框。 来源&#xff1a;AppInventor2 表格布局的外面的黑框怎么去掉&#xff1f; - App应用开发 - 清泛IT社区&#xff0c;为创新…

SQL查询电商数据案例

包括&#xff0c;Python连接数据库到模拟电商数据库&#xff0c;到sql场景查询 1,Python连接SQL数据库 以下是使用Python连接MySQL数据库并进行操作的示例代码&#xff1a; import random import time import pymysql# 定义名字数据 xing ["王", "李",…

uni-app学习完结

昨天空余一天&#xff0c;并未写记录&#xff0c;是昨天属于项目完结&#xff0c;这里把最后的打包上线等这里说下。 打包成微信小程序 打包成微信小程序&#xff0c;这需要再微信公众平台里面&#xff0c;进行登陆和设置。这里说下&#xff0c;注册的后&#xff0c;选择需要…

文心智能体平台丨创建你的四六级学习小助手

引言 在人工智能飞速发展的今天&#xff0c;我们迎来了文心智能体平台。该平台集成了最先进的人工智能技术&#xff0c;旨在为用户提供个性化、高效的学习辅助服务。今天&#xff0c;我们将向大家介绍如何利用文心智能体平台&#xff0c;创建一个专属于你的四六级学习小助手。…

zabbix客户端启用ping脚本 , 采集结果返回服务端

1.Zabbix-agent配置 (1) 查看自定义配置的目录位置 # more /etc/zabbix/zabbix_agentd.conf (2) 将配置的脚本放在指定的目录下 # cd /etc/zabbix/zabbix_agentd.d # vi get_ping.conf UserParameter=get_ping[*], /bin/ping -c 1 -W 1 $1 &> /dev/null &a…