聊聊Java中的异常

news2024/9/22 1:41:21

什么是异常

关于Java的异常,我们认为符合大致分为以下几种情况:

  1. 程序逻辑运行结果不符合预期。
  2. 程序执行时抛出各种exception。
  3. 因为各种原因导致服务崩溃。

Java异常类体系结构如下图所示:

在这里插入图片描述

Exception和Error的区别

Exception

Exception或者Exception自类的异常对象是可以进行捕获和处理的,例如下面这段算术异常,我们就可以手动捕获并打印处理掉:

 try {
            for (int i = 0; i < 30_0000; i++) {

                int num = 10 / 0;
                System.out.println("Fnum:" + num);
            }

        } catch (Exception e) {
            System.out.println("outerTryCatch 报错");
        }

Exception包含受检异常和非受检异常,他们区别是:

  1. 受检异常的方法在调用时需要抛出的潜在风险进行处理,要么捕获,要么向上抛。
  2. 非受检异常即运行时才可能知晓的异常,该异常可由开发人员结合业务场景确定是否捕获。

关于受检异常和非受检异常我们后文给出示例。

Error

这种错误一般都是OOM、Java虚拟机运行错误(Virtual MachineError)、或者类定义错误(NoClassDefFoundError),基本都是服务崩溃,无法捕获处理,只能由开发人员在发现问题后通过日志定位异常代码修复重启才能保证服务正常运行。

受检异常和非受检异常

受检异常

受检异常一般在调用时,用户就需要对其进行处理,如果不处理则不会通过编译。

例如FileInputStream,如果我们没有对其构造方法抛出的错误(即受检异常)进行处理,我们是无法通过编译的。

在这里插入图片描述

查看该构造方法定义,可以FileInputStream受检的异常FileNotFoundException已经定义在方法签名上。

public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }

非受检异常

非受检异常一般是运行时异常,常见的有空指针异常、非法参数异常、算数异常、数组越界等,这类异常可以按需进行捕获处理。
例如下面这段代码就会抛出算术异常,我们即使没有使用try代码块,代码也能通过编译并运行(直到报错)。

 int num = 10 / 0;
System.out.println("Fnum:" + num);

throw与throws的区别

它们唯一的区别就在定义的位置:

  1. throws放在函数上,throw放在函数内。
  2. throws可以跟多个错误类,throw只能跟单个异常对象

异常常用方法和使用示例

Throwable常用方法

getMessage方法

返回异常的错误的简要信息,它不会打印异常堆栈,只是打印异常的原因,例如这段代码,它的打印结果就是/ by zero,并不会输出运行报错的具体调用堆栈信息。

try {
            for (int i = 0; i < 30_0000; i++) {

                int num = 10 / 0;
                System.out.println("Fnum:" + num);
            }

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
toString

我们在catch模块调用异常类的toString方法,它会返回异常发生时的异常信息,同样不会打印堆栈结果,所以对于只需要看到异常信息的话,可以使用toString。

catch (Exception e) {
            System.out.println(e.toString());
        }

输出结果:

java.lang.ArithmeticException: / by zero
getLocalizedMessage方法

返回异常本地化信息。

catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
        }

输出结果:

/ by zero
printStackTrace方法

在控制台上打印堆栈追踪信息,我们可以详细看到异常的调用堆栈信息和错误原因。

 catch (Exception e) {
           e.printStackTrace();
        }

输出结果:

java.lang.ArithmeticException: / by zero
	at com.sharkChili.base.Main.outerTryCatch(Main.java:17)
	at com.sharkChili.base.Main.main(Main.java:7)

受检异常示例

我们自定义一个受检异常

public class ArithmeticException extends Exception {
    @Override
    public String getMessage() {
        return "自定义算术异常";
    }
}

编写一个除法的函数

private int calculate(int  number,int divNum) throws ArithmeticException {
        if (divNum==0){
            throw new ArithmeticException();
        }
        return number / divNum;
    }

测试代码如下,因为是受检异常,所以运行时需要手动捕获处理一下。

@Test
    public void calculateTest(){
        int number=20;
        try {
            int result = calculate(number,0);
            System.out.println(result);
        } catch (ArithmeticException e) {
            logger.error("calculateTest计算失败,请求参数:[{}],错误原因[{}]",number,e.getMessage(),e);
        }
    }

输出结果:

[main] ERROR com.guide.exception.ExceptionTest - calculateTest计算失败,请求参数:[20],错误原因[自定义算术异常]

try-catch运行原理

try-catch感知和捕获异常的工作过程如下:

  1. try块代码执行报错若有catch模块catch则会对其进行捕获处理。
  2. 根据捕获到的异常创建一个异常对象。
  3. 用户根据这个异常对象进行进一步处理。

例如我们上文捕获了ArithmeticException ,那么catch (ArithmeticException e) 实质上会做一个Exception e = new ArithmeticException()的动作,进而按照用户的想法进行错误捕获逻辑处理。

异常使用注意事项

多异常捕获处理技巧

对于多异常需要捕获处理时,我们建议符合以下三大原则:

  1. 有几个异常就处理几个异常,如果无法处理就抛出。
  2. 父类exception放在最下方。
  3. 多异常建议使用|进行归类整理。

如下所示,我们自定义一个自定义错误函数

private int calculate(int  number,int divNum) throws ArithmeticException,FileNotFoundException, UnknownHostException, IOException {
        if (divNum==0){
            throw new ArithmeticException();
        }
        return number / divNum;
    }

假定UnknownHostException 是用户配置问题,我们无法处理,那么就抛出,其他错误一一捕获,所以我们的代码可能是这样。

@Test
    public void calculateTest(){
        int number=20;

        int result = 0;
        try {
            result = calculate(number,0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e){

        }catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(result);

    }

实际在为了让代码更加整洁高效。生成的catch块也只是一个公共的代码块,所以我们最终的代码应该是下面这个样子

@Test
    public void calculateTest()throws UnknownHostException{
        int number=20;

        int result = 0;
        try {
            result = calculate(number,0);
        } catch (ArithmeticException|FileNotFoundException e) {
            logger.error("calculateTest执行报错,用户执行出错或者文件数据获取失败,请求参数[{}],错误信息[{}]",number,e.getMessage(),e);
        } catch (IOException e) {
            logger.error("calculateTest执行报错,文件操作异常,请求参数[{}],错误信息[{}]",number,e.getMessage(),e);
        }catch (Exception e){
            logger.error("calculateTest执行报错,请求参数[{}],错误信息[{}]",number,e.getMessage(),e);

        }
        System.out.println(result);

    }

注意,使用|运算符之后e会变为final变量,用户无法改变引用的指向(这个似乎对我们没有说明影响)

在这里插入图片描述

特殊的异常对象 RuntimeException

使用runtime异常类时,在函数内throw则函数上不用throws,编译可以正常通过。如下代码,即使没有throws ArithmeticException(ArithmeticException为runtime的子类),编译照样通过,代码可以正常运行,直到报错。

class Demo{
	int div(int a,int b)throws Exception//throws ArithmeticException{
		
		if(b<0)
			throw new Exception("出现了除数为负数了");
		if(b==0)
			throw new ArithmeticException("被零除啦");
		return a/b;
	}
}

RuntimeException可以编译通过且不用捕获的原因也很简单,设计者认为运行时异常是特定情况下运行时错误,是否处理需要让开发人员自行判断。

finally关键字

简介

finally无论异常是否执行,在try块结束后必定会运行的,需要注意的是如果程序出现异常,finally中有return语句的话,catch块的return将没有任何作用,代码如下所示

public static int func() {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            return 2;
        } finally {
            return 1;
        }
    }

使用场景

finally最常用于资源释放:

//读取文本文件的内容
        Scanner scanner = null;
        try {
            scanner = new Scanner(new File("D://read.txt"));
            while (scanner.hasNext()) {
                System.out.println(scanner.nextLine());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }

补充一句,对于资源释放,我们现在更简易使用try-with-resources代码简洁易读。上述那段关闭流的代码十分冗长,可读性十分差劲,对于继承CloseableAutoCloseable的类都可以使用以下语法完成资源加载和释放

 try (Scanner scanner = new Scanner(new File("D://read.txt"))) {
            while (scanner.hasNext()) {
                System.out.println(scanner.nextLine());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

finally 中的代码一定会执行吗

不一定,如下代码所示,当虚拟机执行退出的话,finally是不会被执行的

@Test
    public void finallyNoRun() {
        try {
            System.out.println("try code run.....");
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally run...");
        }

    
    }

输出结果如下,可以看到finally方法并没有被运行到。

try code run.....

异常使用注意事项

不要在finnally中使用return

函数执行的try块返回值会被缓存的本地变量中,当finally进行return操作就会覆盖这个本地变量。


    public void finallyReturnTest() {
        System.out.println(finallyReturnFun());
      
    }

    private String finallyReturnFun() {
        try {
            int ten = 10;
            return "tryRetunVal";
        } catch (Exception e) {
            System.out.println("报错了。。。。");
        } finally {
            return "finallyReturnVal";
        }

    }

输出结果为finallyReturnVal,因为finally优先于try块中的代码,当finally进行return操作就会覆盖这个本地变量。


finallyReturnVal
        

异常不处理就抛出

捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请
将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

自定义一个异常

public class MyIllegalArgumentException extends Exception {

    public MyIllegalArgumentException(String msg) {
        super(msg);
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }
}

测试代码


    public void checkTest() {
        String param = null;
        try {
            check(null);
        } catch (MyIllegalArgumentException e) {

            logger.info("参数校验异常,请求参数[{}],错误信息[{}]", param, e.getMessage(), e);
    
        }
    }


    private void check(String str) throws MyIllegalArgumentException {
        if (str == null || str.length() <= 0) {
            throw new MyIllegalArgumentException("字符串不可为空");
        }
    }

输出结果:

[main] INFO com.guide.exception.ExceptionTest - 参数校验异常,请求参数[null],错误信息[字符串不可为空]

不要用异常控制流程

不要使用try块语句控制业务执行流程,原因如下:

  1. try-catch阻止JVM试图进行的优化,所以当我们要使用try块时,使用的粒度尽可能要小一些。
  2. 现代标准遍历模式并不会导致冗余检查,所以我们无需为了避免越界检查而使用try块解决问题。

错误示例,不仅可读性差,而且性能不佳。

public static void stackPopByCatch() {
        long start = System.currentTimeMillis();
        try {
            //插入1000w个元素
            Stack stack = new Stack();
            for (int i = 0; i < 1000_0000; i++) {
                stack.push(i);
            }


            //使用pop抛出的异常判断出栈是否结束
            while (true) {
                try {
                    stack.pop();
                } catch (Exception e) {
                    System.out.println("出栈结束");
                    break;
                }
            }
        } catch (Exception e) {

        }


        long end = System.currentTimeMillis();
        System.out.println("使用try进行异常捕获,执行时间:" + (end - start));


        start = System.currentTimeMillis();
        Stack stack2 = new Stack();
        for (int i = 0; i < 1000_0000; i++) {
            stack2.push(i);

        }

        //使用for循环控制代码流程
        int size = stack2.size();

        for (int i = 0; i < size; i++) {
            stack2.pop();
        }
        end = System.currentTimeMillis();
        System.out.println("使用逻辑进行出栈操作,执行时间:" + (end - start));
    }

输出结果,可以看到用try块控制业务流程性能很差:

出栈结束
使用try进行异常捕获,执行时间:2613
使用逻辑进行出栈操作,执行时间:1481

规范异常日志打印

  1. 不要使用JSON工具,因为某些get方法可能会抛出异常。
  2. 记录参数,错误信息,堆栈信息。

    public void logShowTest() {
        Map inputParam = new JSONObject().fluentPut("key", "value");
        try {
            logShow(inputParam);
        } catch (ArithmeticException e) {
            logger.error("inputParam:{} ,errorMessage:{}", inputParam.toString(), e.getMessage(), e);
        
        }
    }


    private int logShow(Map inputParam) throws ArithmeticException {
        int zero = 0;
        if (zero==0){
           throw new ArithmeticException();
        }
        return 19 / zero;
    }

输出结果:


[main] ERROR com.guide.exception.ExceptionTest - inputParam:{"key":"value"} ,errorMessage:自定义算术异常
com.guide.exception.ArithmeticException: 自定义算术异常
	at com.guide.exception.ExceptionTest.logShow(ExceptionTest.java:166)

避免频繁抛出和捕获异常

如下代码所示,可以看到频繁抛出和捕获对象是非常耗时的,所以我们不建议使用异常来作为处理逻辑,我们完全可以和前端协商好错误码从而避免没必要的性能开销

private int testTimes;

    public ExceptionTest(int testTimes) {
        this.testTimes = testTimes;
    }

    public void newObject() {
        long l = System.currentTimeMillis();
        for (int i = 0; i < testTimes; i++) {
            new Object();
        }
        System.out.println("建立对象:" + (System.currentTimeMillis() - l));
    }

    public void newException() {
        long l = System.currentTimeMillis();
        for (int i = 0; i < testTimes; i++) {
            new Exception();
        }
        System.out.println("建立异常对象:" + (System.currentTimeMillis() - l));
    }

    public void catchException() {
        long l = System.currentTimeMillis();
        for (int i = 0; i < testTimes; i++) {
            try {
                throw new Exception();
            } catch (Exception e) {
            }
        }
        System.out.println("建立、抛出并接住异常对象:" + (System.currentTimeMillis() - l));
    }

    public void catchObj() {
        long l = System.currentTimeMillis();
        for (int i = 0; i < testTimes; i++) {
            try {
                new Object();
            } catch (Exception e) {
            }
        }
        System.out.println("建立,普通对象并catch:" + (System.currentTimeMillis() - l));
    }

    public static void main(String[] args) {
        ExceptionTest test = new ExceptionTest(100_0000);
        test.newObject();
        test.newException();
        test.catchException();
        test.catchObj();
     
    }

输出结果:


输出结果
建立对象:3
建立异常对象:484
建立、抛出并接住异常对象:539
建立,普通对象并catch3

尽可能在for循环外捕获异常

上文提到try-catch时会捕获并创建异常对象,所以如果在for循环内频繁捕获异常会创建大量的异常对象:

public static void main(String[] args) {
        outerTryCatch();
        innerTryCatch();

    }

	//for 外部捕获异常
    private static void outerTryCatch() {
        long memory = Runtime.getRuntime().freeMemory();
        try {
            for (int i = 0; i < 30_0000; i++) {

                int num = 10 / 0;
                System.out.println("Fnum:" + num);
            }

        } catch (Exception e) {
        }
        long useMemory = (memory - Runtime.getRuntime().freeMemory()) / 1024 / 1024;

        System.out.println("cost memory:" + useMemory + "M");
    }

	//for 内部捕获异常
    private static void innerTryCatch() {
        long memory = Runtime.getRuntime().freeMemory();

        for (int i = 0; i < 30_0000; i++) {
            try {
                int num = 10 / 0;
                System.out.println("num:" + num);
            } catch (Exception e) {
            }
        }
        long useMemory = (memory - Runtime.getRuntime().freeMemory()) / 1024 / 1024;

        System.out.println("cost memory:" + useMemory + "M");
    }

输出结果如下,可以看到for循环内部捕获异常消耗了22M的堆内存。原因很简单,for外部捕获异常时,会直接终止for循环,而在for循环内部捕获异常仅结束本次循环的,所以如果for循环频繁报错,那么在内部捕获异常尽可能创建大量的异常对象。

cost memory:0M
cost memory:22M

参考文献

Java基础常见面试题总结(下):https://javaguide.cn/java/basis/java-basic-questions-03.html#项目相关

Effective Java中文版(第3版):https://book.douban.com/subject/30412517/

阿里巴巴Java开发手册:https://book.douban.com/subject/27605355/

Java核心技术·卷 I(原书第11版):https://book.douban.com/subject/34898994/

Java 基础 - 异常机制详解:https://www.pdai.tech/md/java/basic/java-basic-x-exception.html#异常是否耗时为什么会耗时

面试官问我 ,try catch 应该在 for 循环里面还是外面?:https://mp.weixin.qq.com/s/TQgBSYAhs_kUaScw7occDg

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

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

相关文章

有关爬虫http/https的请求与响应

简介 HTTP协议&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;&#xff1a;是一种发布和接收 HTML页面的方法。 HTTPS&#xff08;Hypertext Transfer Protocol over Secure Socket Layer&#xff09;简单讲是HTTP的安全版&#xff0c;在HTT…

【亲测】获取百度智能云access_token并存储,百度智能云access_token有效期

百度智能云服务内置很多api接口&#xff08;文字识别&#xff0c;企业信息识别&#xff0c;等&#xff09;&#xff0c;所有百度智能云自带的接口都会用到百度的access_token 第一步&#xff1a;登录百度智能云管理中心 第二步&#xff1a;创建账户&#xff0c;完整身份认证 …

Binder IPC通讯流程 摘要

一次完整的 Binder IPC 通信过程通常是这样&#xff1a; 首先 Binder 驱动在内核空间创建一个数据接收缓存区&#xff1b;接着在内核空间开辟一块内核缓存区&#xff0c;建立内核缓存区和内核中数据接收缓存区之间的映射关系&#xff0c;以及内核中数据接收缓存区和接收进程用…

Leetcode2048. 下一个更大的数值平衡数

Every day a Leetcode 题目来源&#xff1a;2048. 下一个更大的数值平衡数 解法1&#xff1a;枚举 这种题不能想复杂了&#xff0c;枚举大法好。 代码&#xff1a; /** lc appleetcode.cn id2048 langcpp** [2048] 下一个更大的数值平衡数*/// lc codestart class Soluti…

中医电子处方管理系统软件,中医配方模板一键生成软件操作教程

一、前言&#xff1a; 在中医开电子处方时&#xff0c;如果能够使用配方模板功能&#xff0c;则可以节省很多时间。使用配方模板一键导入&#xff0c;几秒即可完成开单。 下面就以佳易王电子处方管理系统软件V17.1版本为例说明&#xff0c;其他版本可以参考&#xff0c;软件下…

SpringBoot整合Lucene实现全文检索【详细步骤】【附源码】

笑小枫的专属目录 1. 项目背景2. 什么是Lucene3. 引入依赖&#xff0c;配置索引3.1 引入Lucene依赖和分词器依赖3.2 表结构和数据准备3.3 创建索引3.4 修改索引3.5删除索引 4. 数据检索4.1 基础搜索4.2 一个关键词&#xff0c;在多个字段里面搜索4.3 搜索结果高亮显示4.4 分页检…

商业智能BI和数据可视化的区别

现在市场上有非常多的商业智能BI产品&#xff0c;几乎都在着重宣传其数据可视化功能的强大&#xff0c;给人造成一种商业智能BI就是数据可视化的印象。事实上商业智能BI并不等于数据可视化。要探究商业智能BI和数据可视化的区别&#xff0c;我们先要分别弄清楚这两个概念。 1、…

Java 基础学习(十)包装类、异常

1 包装类 1.1 包装类概述 1.1.1 什么是包装类 在进行类型转换时&#xff0c;有一种特殊的转换&#xff1a;将 int 这样的基本数据类型转换为对象&#xff0c;如下图所示&#xff1a; 所有基本类型都有一个与之对应的类&#xff0c;即包装类&#xff08;wrapper&#xff09;。…

VS2022配置C++ 20解决import std报错

C 20新特征支持用import std来导入std模块&#xff0c;如下&#xff1a; 配置时主要有两个步骤&#xff1a; &#xff08;1&#xff09;项目--属性--常规--C语言标准--预览 - 最新 C 工作草案中的功能 (/std:clatest) 注意选择ISO C20 标准 (/std:c20)也不能正常使用&#xf…

STM32的基本定时器注意点

本文介绍了STM32基本定时器3个重要的寄存器PSC、ARR、CNT&#xff0c;以及缓冲机制和计数细节。 基本定时器的框图 预分频器寄存器(TIMx_PSC)可以在运行过程中修改它的数值&#xff0c;新的预分频数值将在下一个更新事件时起作用。因为更新事件发生时&#xff0c;会把 TIMx_PS…

PHP在线SEO文章伪原创同义词交换工具源码

源码介绍 PHP在线SEO文章伪原创同义词交换工具源码 支持关键词提交 独立后台 1.支持文章在线伪原创功能 2.支持关键字交换预览 3.有独立背景 4.支持访客提交关键词(后台可以审核用户提交的关键词) 5.完全开源&#xff0c;支持二次开发 使用php语言独立开发utf-8编码 适合工具…

Java入门学习笔记一

一、Java语言环境搭建 1、JAVA语言的跨平台原理 1.1、什么是跨平台性&#xff1f; 跨平台就是说&#xff0c;同一个软件可以在不同的操作系统&#xff08;例如&#xff1a;Windows、Linux、mad&#xff09;上执行&#xff0c;而不需要对软件做任务处理。即通过Java语言编写的…

一次失败的群晖(Synology)NAS 硬盘更换扩容

对更换硬盘和扩容的时间知道要比较长&#xff0c;但是没有想到要这么长&#xff0c;同时还比较困难的获得更新的数据。 既然闲着也是闲着&#xff0c;并且每天都会查看下状态&#xff0c;所以就想着干脆记录下了。 需要有心理准备就是扩容和存储池的修复时间的单位不是分钟&a…

【Hadoop】

Hadoop是一个开源的分布式离线数据处理框架&#xff0c;底层是用Java语言编写的&#xff0c;包含了HDFS、MapReduce、Yarn三大部分。 组件配置文件启动进程备注Hadoop HDFS需修改需启动 NameNode(NN)作为主节点 DataNode(DN)作为从节点 SecondaryNameNode(SNN)主节点辅助分…

Appium自动化常用adb操作封装

一、前置说明 在Appium自动化中&#xff0c;经常需要使用adb命令与设备进行交互&#xff0c;所以有必要把常用的adb操作封装成一个类 二、代码实现 import os import platform import re import subprocessfrom common import path from common.exception import AndroidSDK…

数据结构从入门到入土——初识泛型

目录 一&#xff0c;包装类 1.基本数据类型和对应的包装类 2.装箱和拆箱 3.自动装箱和自动拆箱 二&#xff0c;什么是泛型&#xff1f; 三&#xff0c;引出泛型 语法 四&#xff0c;泛型类的使用 1.语法 2.类型推导(Type Inference) 五&#xff0c;裸类型(Raw Type) …

Axure中动态面板使用及轮播图多种登录方式左侧导航栏之案列

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《产品经理如何画泳道图&流程图》 ⛺️ 越努力 &#xff0c;越幸运 目录 一、轮播图简介 1、什么是轮播图 2、轮播图有什么作用 3、轮播图有什么特点 4、轮播图适应范围 5、…

C语言—每日选择题—Day41

第一题 1. 有以下程序段&#xff1a; char *p, *q; p (char *)malloc(sizeof(char) * 20); q p; scanf("%s %s", p, q); printf("%s %s\n", p, q); 若从键盘输入&#xff1a;abc def↙&#xff0c;则输出结果是&#xff08; &#xff09; A&#xff1a;d…

GDPU 数据结构 天码行空14

实验十四 查找算法的实现 一、【实验目的】 1、掌握顺序排序&#xff0c;二叉排序树的基本概念 2、掌握顺序排序&#xff0c;二叉排序树的基本算法&#xff08;查找算法、插入算法、删除算法&#xff09; 3、理解并掌握二叉排序数查找的平均查找长度。 二、【实验内容】 …

IIS + Axios 跨域设置

1、服务器端设置IIS &#xff08;web.config) 即可&#xff0c;不需要对django settings.py做配置&#xff08;python manage.py runserver 才需要settings.py配置跨域&#xff0c;IIS在iis上配&#xff09; 网站根目录的web.config中加上这段&#xff1a; <httpProtocol&…