初识Java 13-1 异常

news2024/11/24 6:02:38

目录

概念

基本的异常

捕获异常

try块

异常处理程序(catch)

创建自己的异常

异常说明(及检查型异常)

捕获任何异常

多重捕捉

栈轨迹

重新抛出异常

异常链


本笔记参考自: 《On Java 中文版》


        Java的基本哲学之一:写得不好的代码无法运行。

        错误恢复机制对Java尤为重要,因为Java的一个主要目标就是创建程序组件供他人使用。捕获错误的理想时机是在编译时,也就是在程序员试图运行程序之前。但不是所有错误都能在编译时被发现。其他的问题需要在运行时通过其他方式解决。

        Java使用异常提供了一个一致的错误报告模型。从而使组件可以将问题可靠地传达给客户代码。

    在Java中,异常处理的目标是减少当前的代码量(但在之后会了解到,异常处理也会变相增加一些代码)。

概念

        C语言,乃至一些早期的编程语言往往有多种错误处理机制,但这类机制通常是通过约定建立的,而不是作为编程语言的一部分。这些机制会要求程序员检测错误条件。但我们发现,若每次调用一个方法,都要去彻底检查错误,我们的代码就会变得难以阅读。

        Java沿用了C++的解决方案:结束原本自由散漫的错误处理方式,并强制实施正规手段。

        当“异常”(exception)出现时,若当前的上下文没有提供足够的信息来解决这个问题,我们就需要停止我们的动作,并将问题交给更上层的上下文进行决策。

        异常降低了错误处理代码的复杂性。它使得我们不再需要在方法调用的地方检测错误,因为异常会保证有人会捕获它。

    在理想的情况下,我们只在一个地方处理问题,就是在所谓的异常处理程序中。

基本的异常

||| 异常情形:是指阻止当前方法或作用域继续执行的问题。这些问题在当前上下文章没有必要的信息进行处理。

        与异常情形相对的是普通问题,即指在当前上下文中有足够信息,我们能够以某种方式解决的问题。

        当抛出一个异常时,会发生几件事情:

  1. 异常对象被创建(使用new创建,并放在堆上)。
  2. 当前执行路径停止,指向这个异常对象的引用被从上下文中抛出。
  3. 异常处理机制接管控制,并开始寻找可以继续执行这个程序的适当位置

在这里,适当位置指的就是“异常处理程序”,这一程序的作用是从问题中恢复,使程序能够尝试另一条路径,或是继续执行。

        抛出异常:通过创建一个表示信息的对象,并将其“抛”出当前上下文,我们可以将关于这个错误的信息发送给一个更大的上下文。这就是抛出异常。例如,下面的语句将会检测引用,若其为空,则抛出异常:

if(t == null)
    throw new NullPointerException();

这个被抛出的异常会在其他地方被处理掉。

    我们可以把异常看作是一个内置的“撤销”系统。因为通过异常,我们可以在程序中小心地设置各种恢复点。若发生了不好的事情,异常不允许程序沿正常的路径继续执行。

异常参数

        异常和Java中的其他任何对象一样,都是通过new在堆上创建异常(分配空间,调用构造器)。所有标准异常类都有两个构造器:①无参构造器;②接受一个String参数的构造器,这一参数用于在异常中放置相关信息。

throw new NullPointerException("t == null");

        throw关键字有一些特别之处。上述的这个异常对象实际上是从方法中“返回”的,尽管它的类型通常不会是我们设计让这个方法返回的。通过抛出异常,我们可以退出当前的方法或是作用域(并且获取一个异常对象)。

        但是,和正常的方法返回不同,从异常中返回所到达的地方将会是一个适当的异常处理程序,这与异常被抛出的位置可能距离很远(若调用堆栈,会发现中间叠加了许多层)。

        所有的异常类型存在一个根类:Throwable

我们通常会为每一种不同类型的错误抛出一个不同的异常类。关于错误的信息即包含在异常对象中,也隐含在异常对象的名字中(但通常,异常对象仅有的信息就是异常类的名字)。

捕获异常

||| 被守护区域:一段可能会产生异常的代码,其后会跟着处理这些异常的代码。

try

        若我们正处于一个方法中,这个方法或其调用的方法抛出了异常,那么该方法就会在抛出异常的过程中退出。

        若不希望退出当前方法,就需要使用一个特殊的块捕获这个异常。这个块被称为try块,它是跟在try关键字后的普通作用域:

try {
    // 内部放置可能产生异常的代码
}

使用异常处理,我们可以把所有内容放置在一个try块中,并且在这里捕获所有异常。这种做法在一定程度上化简了代码,保证代码的正常执行不会因为过于复杂的错误检测而受干扰。


异常处理程序(catch

        被抛出的异常会在异常处理程序这被处理(每个异常都可以有自己的异常处理程序)。这一程序紧跟在try块之后,使用catch关键字表示:

try {
    // 可能引发异常的代码
} catch(Type1 id1) {
    // 处理Type1类型的异常
} catch(Type2 id2) {
    // 处理Type2类型的异常
} catch(Type3 id3) {
    // 处理Type3类型的异常
}

// ...

每个catch子句(异常处理程序)都只接受一个特定类型的参数。在这里,标识符(如id1等)可以在处理程序中进行使用(尽管有时我们并不使用标识符,但它们必须存在)。

        异常处理程序必须紧跟在try块后面。若异常被抛出,异常处理机制会去查找参数与异常类型相匹配的第一个处理程序(只有匹配的catch子句会执行,一旦子句完成,对处理程序的搜索就会停止)。

    在try块中,不同的方法调用可能产生同样的异常,但我们只需要一个针对这一异常的处理程序。

终止与恢复

        在异常处理理论中,存在着两种基本模型。Java支持的是终止模型,我们会假设错误过于严重,以至于无法返回(也不会想返回)异常发生的地方。

        另一种是恢复模型。它表示通过异常处理程序,我们可以修正一些情况,然后再重新尝试出现问题的方法,并假定第二次可以成功。

    恢复模型的问题在于其所导致的耦合:处理程序需要知道异常的抛出位置(并且要包含特定于抛出位置的非通用代码)。这会使代码难以编写和维护。

        若想在java中应用恢复模型,可以选择在遇到错误时不抛出异常,而是调用某个能处理这个问题的程序。或是把try块放入一个循环中,不断进入循环,知道出现一个令人满意的结果。

创建自己的异常

        Java的异常体系无法预测我们可能遇见的所有错误,所以我们可以创建自己的异常,以此来表示自己的代码可能遇到的特殊问题。

        要创建异常类,可以继承现有的异常类,最好的继承对象是与我们定义的新异常含义接近的(但这不太可能)。Java提供的异常通过具有两个构造器:无参的带有一个String参数的。使用无参构造器,那么最简单的自定义异常类几乎不需要任何代码:

【例子:创建最简单的异常类】

class SimpleException extends Exception {
    // 编译器自动创建无参构造器,并且会自动调用基类的无参构造器
}

public class InheritingExceptions {
    public void f() throws SimpleException {
        System.out.println("从f()中抛出异常SimpleException");
        throw new SimpleException();
    }

    public static void main(String[] args) {
        InheritingExceptions sed =
                new InheritingExceptions();
        try {
            sed.f();
        } catch (SimpleException e) {
            System.out.println("捕获异常SimpleException");
        }
    }
}

        程序执行的结果是:

        在上述代码中,编译器创建了一个(异常类的)无参构造器,并且自动(隐式地)调用了其基类的无参构造器。从中可以看出,对一个异常类而言,最重要的就是其的类名

    上述示例将结果打印到了控制台上。除此之外,还可以使用System.err将错误发生到标准错误流。这通常会是个更好的选择,因为System.out可以被重定向。

【例子:带有String参数的异常类构造器】

class MyException extends Exception {
    MyException() {
    }

    MyException(String msg) {
        super(msg); // 显式调用了基类的带有String参数的构造器
    }
}

public class FullConstructors {
    public static void f() throws MyException {
        System.out.println("从f()中抛出异常MyException");
        throw new MyException();
    }

    public static void g() throws MyException {
        System.out.println("从g()中抛出异常MyException");
        throw new MyException("来自g()");
    }

    public static void main(String[] args) {
        try {
            f();
        } catch (MyException e) {
            e.printStackTrace(System.out);
        }

        try {
            g();
        } catch (MyException e) {
            e.printStackTrace(System.out);
        }
    }
}

        程序执行的结果是:

        上述例子的处理程序调用了ThrowableException类就是从它继承而来的)的一个方法:printStackTrace()。这一方法会输出到达异常发生点的方法调用序列的信息。例子中将这些信息发生给了System.out,并自动被捕获和打印在了输出中。若调用printStackTrace()的默认版本:

e.printStackTrace();

那么这些信息将会进入标准错误流。

异常与日志记录

        可以使用java.util.logging工具将输出记录到日志中:

【例子:将异常纪录到日志中】

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

class LoggingException extends Exception {
    private static Logger logger =
            Logger.getLogger("LoggingException");

    LoggingException() {
        StringWriter trace = new StringWriter();
        printStackTrace(new PrintWriter(trace)); // 获取栈轨迹
        logger.severe(trace.toString()); // 进行日志写入的一个简单方式
    }
}

public class LoggingExceptions {
    public static void main(String[] args) {
        try {
            throw new LoggingException();
        } catch (LoggingException e) {
            System.err.println("捕获异常" + e);
        }
    }
}

        程序执行的结果是:

        通过语句

static Logger.getLogger("LoggingException");

我们可以创建一个与String参数“LoggingException”(这一错误通常是错误相关的包和类的名字)关联的Logger对象,这个对象会被发生给System.err

        printStackTrace()的默认版本不会生成String,这里通过StringWriterPrintWriter的组合,将printStackTrace()的结果通过toString调用出来(作为一个String)。

        LoggingException可以做到在异常中自动工作。但更常见的状况是其被用于捕获他人的异常,并记入到日志中。因此,我们必须在异常处理程序中生成日志信息

【例子:在异常处理程序这生成日志信息】

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

public class LogggingException2 {
    private static Logger logger =
            Logger.getLogger("LoggingExceptions2");

    static void logException(Exception e) {
        StringWriter trace = new StringWriter();
        e.printStackTrace(new PrintWriter(trace));
        logger.severe(trace.toString());
    }

    public static void main(String[] args) {
        try {
            throw new NullPointerException();
        } catch (NullPointerException e) {
            logException(e);
        }
    }
}

        程序执行的结果是:

        若是创建自己的异常,还可以添加更多的构造器和成员。

【例子:进一步创建异常】

class MyException2 extends Exception {
    private int x;

    MyException2() {
    }

    MyException2(String msg) {
        super(msg);
    }

    MyException2(String msg, int x) {
        super(msg);
        this.x = x;
    }

    public int val() {
        return x;
    }

    @Override
    public String getMessage() {
        return "详细信息:" + x + " " + super.getMessage();
    }
}

public class ExtraFeatures extends Exception {
    public static void f() throws MyException2 {
        System.out.println("从f()抛出异常MyException2");
        throw new MyException2();
    }

    public static void g() throws MyException2 {
        System.out.println("从g()抛出异常MyException2");
        throw new MyException2("来自g()");
    }

    public static void h() throws MyException2 {
        System.out.println("从h()抛出异常MyException2");
        throw new MyException2("来自h()", 47);
    }

    public static void main(String[] args) {
        try {
            f();
        } catch (MyException2 e) {
            e.printStackTrace(System.out);
        }

        try {
            g();
        } catch (MyException2 e) {
            e.printStackTrace(System.out);
        }

        try {
            h();
        } catch (MyException2 e) {
            e.printStackTrace(System.out);
        }
    }
}

        程序执行的结果是:

        异常只是另一种对象。尽管异常类可以像一个普通类一样被装饰,但使用这些包的客户程序员可能只会想要寻找被抛出的异常,因此大多装饰会失去意义。

异常说明(及检查型异常)

        Java鼓励人们将其方法中可能抛出的异常告知调用该方法的客户程序员(因为客户程序员不一定能够接触到库的源代码),这样客户程序员就可以解决它们。这就是异常说明,它是方法声明的组成部分,出现在参数列表之后。

        这种说明会使用额外的关键字throws,其后跟着可能被抛出的异常的列表:

void f() throws FirstException, SecondException, ThirdException{ // ...

        若不使用这一异常说明:

void f() { // ...

则意味着这个方法不会抛出异常(除了从RuntimeException继承来的异常,这种异常可以从任何地方抛出,而不需要异常说明)。

        异常说明必须与实际情况匹配,若方法引发了异常(且方法本身没有处理该异常),此时编译器会给我们两个选择:①处理这个异常;②使用异常说明,指出这个异常可以从该方法中抛出。

    在实际操作中,我们可以声明会抛出某个异常,而实际上不这么做。编译器认为这是合法的,并会要求使用该方法的用户按照方法会抛出这个异常的情况进行处理。通过这种方式,可以为预订的异常占个位置。

        这种在编译时被检测并强制实施的异常叫做检查型异常

捕获任何异常

         异常类型有一个基类Exception:

可以对Exception进行捕获,这样就可以创建一个可以捕获任何异常的处理程序。

catch(Exception e) {
    System.out.println("捕获一个异常");
}

    也存在其他的基本异常,不过Exception与编程活动更为相关。

        因为这种捕获对所有的异常有效,因此需要把它放在处理程序列表的最后,以避免它在其他处理程序前面捕获了异常。

        Exception是对程序员而言很重要的异常类的基类,因此它不会带有过多的具体信息,但这一点可以通过调用其基类Throwable的方法进行弥补。

String getMessage() // 获取详细信息
String getLocalizeMessage() // 获取针对特定区域调整过的信息

        另外,toString()方法会包含Throwable的简短描述和一些详细的信息(如果有)。

        下列展示的三个方法会展示Throwable及其调用栈轨迹。调用栈会显示把我们带到异常抛出电的方法调用序列

// 打印到标准错误流
void printStackTrace()

// 打印到我们选择的流
void printStackTrace(PrintStream)
void printStackTrace(java.io.PrintWriter)

        还有一个方法fillInStackTrace(),这个方法可以记录当前的Throwable对象的栈帧的当前状态信息,多用于重新抛出错误或异常。

        此为,Object类提供了一些有用的方法:getClass()会返回表示这个对象的类的Class对象。也可以查询这个Class对象的名字,getName()获得的结果包含了包信息,而getSimpleName()获得的结果只包含了类名。

【例子:Exception的基本方法】

public class ExceptionMethods {
    public static void main(String[] args) {
        try {
            throw new Exception("一个异常");
        } catch (Exception e) {
            System.out.println("捕获一个异常");

            System.out.println(
                    "getMessage(): " + e.getMessage());
            System.out.println(
                    "getLocalizeMessage(): " + e.getLocalizedMessage());

            System.out.println("toString(): " + e.toString());

            System.out.println("printStackTrace():");
            e.printStackTrace(System.out);
        }
    }
}

        程序执行的结果是:

        上述的每个方法都提供了比前一个方法更多的信息。实际上,每一个方法都是前一个方法的超集

多重捕捉

        若想以同样的方式处理一组异常,且这组异常有一个共同的基类,那么捕获这个基类即可。但若它们没有共同的基类,在Java 7之前,必须为每一个异常写一个catch子句。

        Java 7提供了多重捕捉处理程序,允许我们在一个catch子句中使用“|”操作符连接不同类型的异常:

【例子:多重捕捉】

class EBase1 extends Exception {}
class Except1 extends EBase1 { }

class EBase2 extends Exception { }
class Except2 extends EBase1 { }

class EBase3 extends Exception { }
class Except3 extends EBase1 { }

class EBase4 extends Exception { }
class Except4 extends EBase1 { }

public class MultiCatch {
    void x() throws Except1, Except2, Except3, Except4 {}
    void process() {}

    void f() {
        try {
            x();
        } catch (Except1 | Except2 | Except3 | Except4 e) {
            process();
        }
    }
}

        通过这种多重捕捉的机制,就可以将需要集中处理的异常集合起来,减少代码的重复,使代码更加清晰。


栈轨迹

        printStackTrace()提供信息,而getStackTrace()可以直接访问这些信息。

如上图所示,getStackTrace()的返回值是一个由栈轨迹元素组成的数组,其中的每个元素都表示一个栈帧。下标为0的元素是栈顶,其中存储的是序列中的最后一个方法调用(就是这个Throwable被创建和抛出的位置)。

【例子:访问栈轨迹】

public class WhoCalled {
    static void f() {
        // 生成一个异常,用来填充栈轨迹
        try {
            throw new Exception();
        } catch (Exception e) {
            for (StackTraceElement ste : e.getStackTrace())
                System.out.println(ste.getMethodName());
        }
    }

    static void g(){
        f();
    }

    static void h(){
        g();
    }

    public static void main(String[] args) {
        f();
        System.out.println("=========");
        g();
        System.out.println("=========");
        h();
    }
}

        程序执行的结果是:

        也可以直接打印StackTraceElement,以获取更多信息。


重新抛出异常

        有时会需要重新抛出刚捕获的异常,尤其是使用Exception捕获任何异常的时候。

catch(Exception e) {
    System.out.println("抛出一个异常");
    throw e; // 通过已有的引用进行抛出
}

        重新抛出一个异常,会导致其进入邻近的更上层上下文中的异常处理程序。并且同样会忽略同一个try块后面的catch子句。另外,关于这个异常对象的所有信息都会被保留,以保证处理程序对信息的提取。

        若重新抛出当前异常,printStackTrace()打印的关于异常的信息,仍会是原来异常抛出点的信息,而不是重新抛出异常的地方的信息。为了加入新的栈轨迹信息,就需要使用fillInStackTrace()

        这一方法所返回的Throwable对象,是通过将当前栈的信息塞到原本的异常对象中创建的。

【例子:fillInStackTrace()的使用例】

public class Rethrowing {
    public static void f() throws Exception {
        System.out.println("异常产生于f()");
        throw new Exception("从f()中抛出异常");
    }

    public static void g() throws Exception {
        try {
            f();
        } catch (Exception e) {
            System.out.println("在g()中,调用e.printStackTrace(): ");
            e.printStackTrace(System.out);
            throw e;
        }
    }

    public static void h() throws Exception {
        try {
            f();
        } catch (Exception e) {
            System.out.println("在h()中,调用e.printStackTrace(): ");
            e.printStackTrace(System.out);
            throw e;
        }
    }

    public static void main(String[] args) {
        try {
            g();
        } catch (Exception e) {
            System.out.println("在main()中,调用e.printStackTrace():");
            e.printStackTrace(System.out);
            System.out.println();
        }

        try {
            h();
        } catch (Exception e) {
            System.out.println("在main()中,调用e.printStackTrace():");
            e.printStackTrace(System.out);
            System.out.println();
        }
    }
}

        程序执行的结果是:

        由输出可知,fillInStackTrace()被调用的那一行,成为了异常的新起点。

        也可以抛出一个与所捕获的异常不同的异常,这同样可以做到类似于fillInStackTrace()的效果。重新抛出会导致异常的原始调用点的信息丢失,只剩下与新的throw相关的信息。

【例子:重新抛出不同的异常】

class OneException extends Exception {
    OneException(String s) {
        super(s);
    }
}

class TwoException extends Exception {
    TwoException(String s) {
        super(s);
    }
}

public class RethrowNew {
    public static void f() throws OneException {
        System.out.println("异常产生于f()");
        throw new OneException("从f()中抛出");
    }

    public static void main(String[] args) {
        try {
            try {
                f();
            } catch (OneException e) {
                System.out.println("在内层的try中捕获,e.printStackTrace():");
                e.printStackTrace(System.out);
                throw new TwoException("从内部的try中抛出");
            }
        } catch (TwoException e) {
            System.out.println("在内层的try中捕获,e.printStackTrace():");
            e.printStackTrace(System.out);
        }
    }
}

        程序执行的结果是:

    异常也是通过new在堆上创建的对象,因此它们也会被垃圾收集器自动清理。

精准地重新抛出异常

        在Java 7之前,若我们捕获了一个异常,那么我们只能重新抛出这个异常。这会导致代码中出现不精准的问题。例如:

【例子:Java 7之前不允许的精准抛出异常】

class BaseException extends Exception {
}

class DerivedException extends BaseException { 
}

public class PreciseRethrow {
    void catcher() throws DerivedException {
        try {
            throw new DerivedException();
        } catch (BaseException e) { // 捕获了父类
            throw e;
        }
    }
}

        这种代码在Java 7之前是不被允许的。因为catch捕获了一个BaseException,所以编译器会强制我们声明catcher() throws BaseException,尽管其抛出的是一个更具体的DerivedException

        从Java 7开始,这种代码可以被编译了,这很有用。


异常链

        有时我们会需要捕获一个异常并抛出另一个,并且仍然保留原始异常的信息,这被称为异常链

        在Java 1.4之后,Throwable多了包含cause对象的构造器:

这个cause就是原始的异常,尽管我们会创建和抛出一个新的异常,但原本的栈轨迹依旧可以通过cause进行跟踪。

       在Throwable的子类中,有三种基本的异常类提供了这个带cause参数的构造器,它们分别是Error(JVM用它报告系统错误)、ExceptionRuntimeException

    除上述三个基本的异常类外,大部分异常没有支持cause参数的构造器。这种情况下就需要使用initCase(),这一方法适用于所有的Throwable的子类。

【例子:异常链】

class DynamicFieldsException extends Exception {
}

public class DynamicFields {
    private Object[][] fields;

    public DynamicFields(int initialSize) {
        fields = new Object[initialSize][2];
        for (int i = 0; i < initialSize; i++)
            fields[i] = new Object[]{null, null};
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        for (Object[] obj : fields) {
            result.append(obj[0]);
            result.append(": ");
            result.append(obj[1]);
            result.append("\n");
        }

        return result.toString();
    }

    private int hasField(String id) {
        for (int i = 0; i < fields.length; i++)
            if (id.equals(fields[i][0]))
                return i;
        return -1;
    }

    private int getFieldNumber(String id)
            throws NoSuchFieldException {
        int fieldNum = hasField(id);
        if (fieldNum == -1) // 若不存在指定数据项
            throw new NoSuchFieldException();

        return fieldNum;
    }

    private int makeField(String id) {
        for (int i = 0; i < fields.length; i++)
            if (fields[i][0] == null) {
                fields[i][0] = id;
                return i;
            }

        // 若不存在空的数据项,则添加一个:
        Object[][] tmp = new Object[fields.length + 1][2];
        for (int i = 0; i < fields.length; i++)
            tmp[i] = fields[i];

        for (int i = fields.length; i < tmp.length; i++)
            tmp[i] = new Object[]{null, null};
        fields = tmp;

        //完成fields扩展后,递归调用
        return makeField(id);
    }

    public Object getField(String id)
            throws NoSuchFieldException {
        return fields[getFieldNumber(id)][1];
    }

    public Object setField(String id, Object value)
            throws DynamicFieldsException {
        if (value == null) {
            // DynamicFieldsException没有支持cause的构造器
            // 所以需要使用initCause()方法
            DynamicFieldsException dfe = new DynamicFieldsException();
            dfe.initCause(new NullPointerException());
            throw dfe;
        }

        int fieldNumber = hasField(id);
        if (fieldNumber == -1)
            fieldNumber = makeField(id);
        Object result = null;
        try {
            result = getField(id); // 处理getField()可能抛出的异常
        } catch (NoSuchFieldException e) {
            // 若getField()引发异常,就需要处理它
            // 将其转为RuntimeException()进行抛出
            // RuntimeException()有可以接受cause的构造器
            throw new RuntimeException(e);
        }
        fields[fieldNumber][1] = value;
        return result;
    }

    public static void main(String[] args) {
        DynamicFields df = new DynamicFields(3);
        System.out.println(df);

        try {
            df.setField("d", "为d赋予一个值");
            df.setField("number", 47);
            df.setField("number2", 48);
            System.out.println(df);

            df.setField("d", "赋予d一个新的值");
            df.setField("number3", 11);
            System.out.println("df: " + df);
            System.out.println("df.getField(\"d\"): "
                    + df.getField("d"));

            Object field = df.setField("d", null); // 引发异常
        } catch (NoSuchFieldException
                 | DynamicFieldsException e) {
            e.printStackTrace(System.out);
        }
    }
}

        程序执行的结果是:

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

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

相关文章

【Kotlin精简】第1章 基础类型

1 Kotlin基础类型 Kotlin中&#xff0c;我们可以调用任何变量的成员函数和属性&#xff0c;从这个角度来说&#xff0c;一切皆对象。某些类型可以有特殊的内部表现。例如&#xff1a;数字、字符和布尔型在运行时可以表现为基础类型&#xff08;primitivetypes&#xff09;。 …

九章云极DataCanvas公司完成D1轮融资!

近日&#xff0c;九章云极DataCanvas公司完成总融资额3亿元D1轮融资。中国电子集团旗下中电智慧基金、华民投、中国太平旗下太平创新、浙江东方旗下东方嘉富等央国企旗下投资机构&#xff0c;以及卓源资本等专注人工智能赛道的知名财务投资机构参与本轮融资。 九章云极DataCan…

dataframe保存excel格式比csv格式小很多很多

问题描述&#xff1a; 一个3万行的数据保存成csv大概10个G&#xff0c;但保存成excel格式只有100多M 原因分析&#xff1a; 因为xlsx 实际上就是 zip 压缩包&#xff0c;同时&#xff0c;如果有大量重复的数据&#xff0c;XLSX 会提取文本值&#xff0c;将其存储在查找表中&…

APP 备案公钥、签名 MD5获取方法。

一、IOS 系统获取 Bundle ID、平台公钥、签名 MD5 值的指引 获取 Bundle ID&#xff1a;使用 APP 对应的 IOS 开发者账号登录 Developer 控制台&#xff0c;找到下图标识符&#xff08;英文&#xff09;&#xff0c;单击进入 Certificates,Identifiers&Profiles 页面。 在…

【QT5-程序控制电源-[GPIB-USB-HS]-SCPI协议-上位机-基础样例【2】】

【QT5-程序控制电源-[GPIB-USB-HS]-SCPI协议-上位机-基础样例【2】】 1、前言2、实验环境3、自我总结1、基础了解仪器控制-熟悉仪器2、连接SCPI协议3、了解GPIB-USB-HS4、软件调试-代码编写 4、熟悉协议-SCPI协议5、实验过程-熟悉软件&#xff08;1&#xff09;去官网NI&#x…

电视盒子哪个牌子最好?经销商分享热门电视盒子排名

作为数码产品经销商&#xff0c;我对电视盒子这块非常了解&#xff0c;最近看到很多人因为不懂电视盒子哪个牌子最好而踩雷&#xff0c;我根据店内的销量情况整理了电视盒子排名&#xff0c;近期想买电视盒子不想花冤枉钱可以从下面五款电视盒子中入手。 TOP 1泰捷WEBOX 40S电视…

STM32CubeIDE修改文件编码格式(解决代码注释出现乱码问题)

文章目录 问题描述解决方法STM32CubeIDE介绍 问题描述 在使用STM32CubeIDE时&#xff0c;从外部导进来的几个文件后&#xff0c;发现注释都是乱码的&#xff0c;如下图所示&#xff1a; 所以为了方便的查看注释&#xff0c;必需将乱码显示正常的中文显示&#xff1b;记录一下修…

Mysql--内置函数

字符串函数 1、拼接字符串 concat(str1,str2...) select concat(12,34,abccc) select CONCAT(name,的家乡是,hometown) from students 2、包含字符个数 length(abc) 注&#xff1a;一个中文占3个字符&#xff0c;一个字母或数字占1个字符 3、截取字符串 left(str,len)返回字…

vue3使用element plus的时候组件显示的是英文

问题截图 这是因为国际化导致的 解决代码 import zhCn from "element-plus/es/locale/lang/zh-cn"; 或者 import zhCn from "element-plus/lib/locale/lang/zh-cn";const localezhCn<el-config-provider :locale"locale"><el-date-pic…

一文彻底搞懂性能测试

目录 性能测试概念 性能测试需要有指标 性能测试需要有模型 性能测试要有方案 性能测试中要有监控 性能测试要有预定的条件 性能测试中要有场景 性能测试中要有分析调优 性能测试肯定要有结果报告 性能场景TPS和响应时间 理解TPS、QPS、RT、吞吐量这些性能指标 对这…

使用雷电模拟器安装安卓APK

下载安装器 雷电安卓模拟器-手游模拟器安卓版_android手机模拟器电脑版_雷电模拟器官网 运行安装安装APK

QML(21)——Layout中的width, height设置技巧

目录 效果展示保持组件界面原始大小组件size固定&#xff0c;spacing自适应组件size自适应&#xff0c;spacing固定 使用技巧总结优先级需要固定size的自定义组件(button)需要自适应size的组件 界面结构主界面 main.qml功能界面 LayoutSpacing.qml自定义组件 BasicComponent .q…

企业服务器租用对性能有什么要求呢?

企业租用服务器租用首要的是稳定&#xff0c;其次是安全&#xff0c;稳定是为了让企业的工作能够顺利进行&#xff0c;只有性能稳定的服务器才能保证网站之类的正常工作&#xff0c;就让小编带大家看一看有什么要求吧&#xff01; 服务器简单介绍。服务器是在网络上为其它客户机…

紧贴墙面运动的文字

效果展示 CSS 知识点 clip-path 属性的运用视觉错觉运用 实现页面基础结构 <section><!-- 右侧文字 部分 --><div class"skew1"><h2 class"layer">Corner Text</h2></div><!-- 左侧文字 部分 --><div cla…

Transformer预测 | Pytorch实现基于mmTransformer多模态运动预测(堆叠Transformer)

文章目录 文章概述程序设计参考资料文章概述 Transformer预测 | Pytorch实现基于mmTransformer多模态运动预测(堆叠Transformer) 程序设计 Initialize virtual environment: conda create -n mmTrans python=3.7# -*- coding: utf-8 -*- import argparse import os

PCA和SVD数据降维

PCA&#xff08;Principal Component Analysis&#xff09; 是一种常见的数据分析方式&#xff0c;常用于高维数据的降维&#xff0c;可用于提取数据的主要特征分量。 最大可分性 基向量乘原始矩阵会将矩阵映射到这个基向量空间中&#xff0c;如果基的数量少于向量本身的维数…

2023年【金属非金属矿山(地下矿山)安全管理人员】考试题库及金属非金属矿山(地下矿山)安全管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年金属非金属矿山&#xff08;地下矿山&#xff09;安全管理人员考试题库为正在备考金属非金属矿山&#xff08;地下矿山&#xff09;安全管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的金属非…

webstorm自定义文件模板(Vue + Scss)

最终效果如下&#xff1a; 具体配置如下&#xff1a; 新增文件代码如下&#xff1a; <!--* Description: ${COMPONENT_NAME} 页面* Author: mhf* Date: ${DATE} --> <template><div>${COMPONENT_NAME} </div> </template><script&g…

一文教你如何快速备考云计算HCIE 3.0 !

大家好&#xff0c;在誉天实验辅导老师的耐心帮助下&#xff0c;本人在9月21日的云计算HCIE 3.0考试已顺利通过&#xff0c;很高兴有这个机会给大家分享备考的经历&#xff0c;希望对还在备考的同学能有一定的帮助。 备考准备 在云计算HCIE3.0的课程学习结束之后&#xff0c;就…

IntelliJ IDEA失焦自动重启服务的解决方法

IDEA 热部署特性 热部署&#xff0c;即应用正属于运行状态时&#xff0c;我们对应用源码进行了修改更新&#xff0c;在不重新启动应用的情况下&#xff0c;可以能够自动的把更新的内容重新进行编译并部署到服务器上&#xff0c;使修改立即生效。 现象 在使用 IntelliJ IDEA运…