当前位置: 首页 > news >正文

【Java】基础入门 (十六)--- 异常

1.异常

1.1 异常概述

       异常是指程序在运行过程中出现的非正常的情况,如用户输入错误、除数为零、文件不存在、数组下标越界等。由于异常情况再程序运行过程中是难以避免的,一个良好的应用程序除了满足基本功能要求外,还应具备预见并处理可能发生的各种异常情况。因此,在开发中需要充分考虑各种意外情况,以提高程序的容错能力。异常处理事一种技术,用于处理这种异常情况。

       我们阅读下面的代码,通过这段代码来认识异常。 我们调用一个方法时,经常一不小心就出异常了,然后在控制台打印一些异常信息。其实打印的这些异常信息,就叫做异常。

public class ExceptionTest01 {public static void main(String[] args) {System.out.println(Integer.valueOf("abc"));}
}

       运行之后会出现如下的页面:

在这里插入图片描述

       因为在设计这个方法的时候,在方法中对调用者传递的参数进行校验,如果校验数据不合法,就会将异常信息封装成一个对象并抛出,JVM接收到异常对象之后,就把异常打印了。

public static Integer valueOf(String s) throws NumberFormatException {return Integer.valueOf(parseInt(s, 10));
}

       因为写代码时经常会出现问题,Java的设计者们早就为我们写好了很多个异常类,来描述不同场景下的问题。而有些类是有共性的所以就有了异常的继承体系。

1.1.1 异常的体系

在这里插入图片描述

所有异常类都是Throwable类的子类,它派生出两个子类, Error 类和 Exception 类:

       (1) Error 类:表示程序无法恢复的严重错误或者恢复起来比较麻烦的错误,例如内存溢出、动态链接失败、虚拟机错误等。应用程序不应该主动抛出这种类型的错误,通常由虚拟机自动抛出。如果出现这种错误,最好的处理方式是让程序安全退出。在进行程序设计时,我们更应关注Exception类。
       (2)Exception类:由Java应用程序抛出和处理的非严重错误,例如文件未找到、网络连接问题、算术错误(如除以零)、数组越界、加载不存在的类、对空对象进行操作、类型转换异常等。Exception类的不同子类对应不同类型的异常。Exception类又可分为两大类异常:

  • 不受检异常:也称为unchecked异常,包括RuntimeException及其所有子类。对这类异常并不要求强制进行处理,例如算术异常ArithmeticException等。
  • 受检异常:也称为checked异常,指除了不受检异常外,其他继承自Exception类的异常。对这类异常要求在代码中进行显式处理。

Java提供了多种异常类,下表列举了一些常见的异常类及其用途:

在这里插入图片描述

1.2 Java异常处理机制

1.2.1 异常处理

       Java的异常处理机制类似于人们对可能发生的意外情况进行预先处理的方式。在程序执行过程中,如果发生了异常,程序会按照预定的处理方式对异常进行处理。处理完异常之后,程序会继续执行。如果异常没有被处理,程序将会终止运行。

       Java的异常处理机制依靠以下 5 个关键字:trycatchfinallythrowthrows。这些关键字提供了两种异常处理方式。

(1)使用try、catch、finally来捕获和处理异常:

  • try 块中包含可能会抛出异常的代码。
  • catch 块中用于捕获并处理指定类型的异常。
  • finally 块中的代码无论是否发生异常都会被执行,通常用于释放资源或清理操作。
package swp.kaifamiao.codes.Java.d0830;/*** {class description}** @author SWP* @version 1.0.0*/
public class Main {public static void main(String[] args) {try {int i = 1, j = 0, res;System.out.println("begin");res = i / j;System.out.println("end");}catch (ArithmeticException e){System.out.println("caught");e.printStackTrace();}finally {System.out.println("finally");}System.out.println("over");}
}

运行效果:

在这里插入图片描述

(2)使用throw、throws 来抛出异常:

       ① 使用throws声明抛出异常

       try-catch-finally 处理的是在方法内部发生的异常,在方法内部直接捕获并处理。如果在一个方法体内抛出了异常,并希望调用者能够及时地捕获异常,Java语言中通过关键字throws声明某个方法可能抛出的各种异常,以通知调用者。throws 可以同时声明多个异常,之间用逗号隔开。

package swp.kaifamiao.codes.Java.d0830;import java.util.InputMismatchException;
import java.util.Scanner;/*** {class description}** @author SWP* @version 1.0.0*/
public class Demo01 {public static void main(String[] args) {try {divide();}catch (InputMismatchException e){System.out.println("除数和被除数必须都是整数");}catch (ArithmeticException e){System.out.println("除数不能为零");}catch (Exception e){System.out.println("其他异常" + e.getMessage());}finally {System.out.println("感谢使用本程序");}System.out.println("程序结束");}/**通过throws声明抛出设计时异常*/public static void divide() throws Exception{Scanner input = new Scanner(System.in);System.out.println("计算开始");int i, j, res;System.out.println("请输入被除数");i = input.nextInt();System.out.println("请输入除数");j = input.nextInt();res = i / j;System.out.println(i + "/" + j + "=" + res);System.out.println("计算结束");}
}

       ②使用throw声明抛出异常

       除了系统自动抛出异常外,在编程过程中,有些问题是系统无法自动发现并解决的,如年龄不在正常范围之内,性别输入的不是“男”或“女”等,此时需要程序员而不是系统来自行抛出异常,把问题提交给调用者去解决。在Java语言中,可以使用throw关键字来自行抛出异常。

throw new Exception("message")
  • 如果 throw 语句抛出的异常是 Checked 异常,则该 throw 语句要么处于 try 块⾥,显式捕获该异常,要么放在⼀个带 throws 声明抛出的⽅法中,即把该异常交给该⽅法的调⽤者处理;
  • 如果 throw 语句抛出的异常是 Runtime 异常,则该语句⽆须放在 try 块⾥,也⽆须放在带 throws 声明抛出的⽅法中;程序既可以显式使⽤ try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该⽅法调⽤者处理。

自行抛出Runtime 异常比自行抛出Checked 异常的灵活性更好。同样,抛出 Checked 异常则可以让编译器提醒程序员必须处理该异常。

1.2.2 自定义异常

       当JDK中的异常类型不能满足程序需求时,可以自定义异常类,使用自定义异常类一般有以下几个步骤:
       (1)定义异常类,并继承ExceptionRuntimeException
       (2)编写异常类的构造方法,向父类构造方法传入异常描述信息,并继承父类的其他实现方法;
       (3)实例化自定义异常对象,并在程序中使用throw抛出。

举例:实现以下需求

需求:写一个saveAge(int age)方法,在方法中对参数age进行判断,如果age<0或者>=150就认为年龄不
合法,如果年龄不合法,就给调用者抛出一个年龄非法异常。

分析:Java的API中是没有年龄非常这个异常的,所以我们可以自定义一个异常类,用来表示年龄非法异常,然后再方法中抛出自定义异常即可。


自定义一个AgeIllegalException异常类:

package swp.kaifamiao.codes.Java.d0830;/*** {class description}* 自定义异常类 AgeIllegalException* 继承 Exception 类* 用于表示年龄不合法的异常* @author SWP* @version 1.0.0*/
public class AgeIllegalException extends Exception {public AgeIllegalException() {}public AgeIllegalException(String message) {super(message);}
}
package swp.kaifamiao.codes.Java.d0830;/*** {class description}* 异常测试类 ExceptionTest* 用于测试自定义异常 AgeIllegalException* 当年龄不合法时抛出 AgeIllegalException 异常* 并在 main 方法中捕获并处理该异常* @author SWP* @version 1.0.0*/
public class ExceptionTest {public static void main(String[] args) {try {saveAge(225);System.out.println("saveAge2底层执行是成功的!");} catch (AgeIllegalException e) {e.printStackTrace();System.out.println("saveAge2底层执行是出现bug的!");}}// 在方法中对age进行判断,不合法则抛出AgeIllegalExceptionpublic static void saveAge(int age) throws AgeIllegalException {if (age > 0 && age < 150) {System.out.println("年龄被成功保存: " + age);} else {// 用一个异常对象封装这个问题// throw 抛出去这个异常对象throw new AgeIllegalException("age is illegal, your age is " + age);}}
}

运行效果:

在这里插入图片描述


1.2.3 异常链

异常链(Exception Chaining)是指在异常处理过程中,将当前的异常作为原因(cause)链接到另一个异常上。通过异常链,可以追踪异常发生的完整路径,并提供更多的上下文信息。

(1)定义testOne,testTwo,testThree方法,testTwo对testOne抛出的异常进行捕获,testThree对testTwo抛出的异常进行捕获:

package swp.kaifamiao.codes.Java.d0830;/*** {class description}** @author SWP* @version 1.0.0*/
public class TryDemoFive {public static void main(String[] args) {try {testThree();} catch (Exception e) {// 打印完整的异常信息e.printStackTrace();}}public static void testOne() throws MyException {throw new MyException("我是一个异常");}public static void testTwo() throws Exception {try {testOne();} catch (Exception e) {// 在新抛出的异常中添加原来的异常信息throw new Exception("我是新产生的异常1", e);}}public static void testThree() throws Exception {try {testTwo();} catch (Exception e) {// 在要抛出的对象中使用 initCause() 方法,添加上一个产生异常的信息Exception e2 = new Exception("我是新产生的异常2");e2.initCause(e);throw e2;}}
}

(2)定义MyException 类:

package swp.kaifamiao.codes.Java.d0830;/*** {class description}** @author SWP* @version 1.0.0*/
public class MyException extends Exception{public MyException(String message) {super(message);}
}

(3)运行效果:

在这里插入图片描述

2.思考

2.1 什么是异常?

	异常是指程序在运行过程中出现的非正常的情况,如用户输入错误、除数为零、文件不存在、数组下标越界等。

2.2 什么是运行时异常?

运行时异常(Runtime Exception)是指在程序运行期间可能抛出的异常,它们属于非受检异常(Unchecked Exception)。与受检异常(Checked Exception)相比,运行时异常在编译期不需要强制处理或声明。运行时异常通常表示程序的逻辑错误或者错误的使用方式,例如数组越界、空指针引用等。这些异常通常是由程序员编码时的错误导致的,但在编译时却无法确定是否会发生异常。

2.3 如何处理异常?

(1) 使用try、catch、finally来捕获和处理异常;
(2) 使用throw、throws 来抛出异常

2.4 什么是checked异常?什么是unchecked异常?

(1) 受检异常是在编译时强制要求处理的异常,需要使用 try-catch 块捕获并处理,或者在方法签名中使用 throws 关键字声明异常。
(2) 非受检异常是由程序逻辑错误或错误的使用方式引起的异常,在编译时不需要强制要求处理,但仍可以选择性地进行捕获和处理。

2.5 构造方法可以throws异常吗?对子类有影响吗?有什么影响?

可以在构造方法中抛出异常,与其他方法一样,构造方法也可以声明受检异常并通过 throws 关键字将其抛出。
如果在父类的构造方法中声明了受检异常,那么所有继承自该类的子类构造方法必须显示地处理这些异常或者在它们的方法签名中使用 throws 关键字将其抛出。否则编译会报错。

2.6 throw和throws的区别?

(1) 作⽤不同:throw⽤于程序员⾃⾏产⽣并抛出异常,throws⽤于声明该⽅法内抛出了异常。
(2) 使⽤位置不同:throw位于⽅法体内部,可以作为单独的语句使⽤;throws必须跟在⽅法参数列表的后⾯,不能单独使⽤。
(3) 内容不同:throw抛出⼀个异常对象,只能是⼀个;throws后⾯跟异常类,可以跟多个。

2.7 能否自己throw一个Error?

可以使用 throw 关键字抛出一个 Error 对象。在Java中,Error 是 Throwable 类的子类,它表示严重的错误和异常情况,通常由Java虚拟机(JVM)或底层系统引起,例如 OutOfMemoryError、StackOverflowError 等。与 Exception 不同,Error 通常表示不可恢复的错误或系统级故障,它们是无法预料和处理的,一般不建议程序员捕获和处理 Error。通常情况下,Error 会导致程序中止执行。

以下是一个示例,演示如何抛出一个 Error:

public class Example {public static void main(String[] args) {throw new Error("This is an error.");}
}

需要注意的是,抛出 Error 可能会导致程序异常终止,并且不应该被常规的异常处理机制所捕获和处理。因此,在编写代码时,通常不建议自己抛出 Error,除非确实具有特殊的需求或深入了解它们的影响。一般的异常处理应该针对 Exception 及其子类。

2.8 假如throw里面有return语句,catch里面有return语句,finally里面也有return语句,为什么最后返回的是finally里面的return语句?

在 Java 中,finally 块中的 return 语句会覆盖之前的 try 或 catch 块中的 return 语句,并决定最终的返回值。这是因为无论在 try、catch 或者 finally 块中执行了哪个 return 语句,都会直接结束整个方法并返回对应的值。根据 Java 语言规范,finally 块中的 return 语句会在方法返回之前执行,以确保在方法返回之前可以进行一些必要的清理工作。所以,在 finally 块中使用 return 语句将决定最终的返回值。

2.9 为啥要自定义异常?如何自定义异常?

当JDK中的异常类型不能满足程序需求时,可以自定义异常类,自定义异常在编程中非常有用,它能够提供更加准确和具体的异常信息,并能够满足特定业务需求。通过自定义异常,可以将代码中可能发生的异常情况进行分类和处理,使代码更加清晰、可读性更高,并且有助于调试和错误处理。
(1)定义异常类,并继承`Exception` 或`RuntimeException`;
(2)编写异常类的构造方法,向父类构造方法传入异常描述信息,并继承父类的其他实现方法;
(3)实例化自定义异常对象,并在程序中使用throw抛出。

相关文章:

【Java】基础入门 (十六)--- 异常

1.异常 1.1 异常概述 异常是指程序在运行过程中出现的非正常的情况&#xff0c;如用户输入错误、除数为零、文件不存在、数组下标越界等。由于异常情况再程序运行过程中是难以避免的&#xff0c;一个良好的应用程序除了满足基本功能要求外&#xff0c;还应具备预见并处理可能发…...

[javaWeb]Socket网络编程

网络编程&#xff1a;写一个应用程序,让这个程序可以使用网络通信。这里就需要调用传输层提供的 api。 Socket套接字 传输层提供协议&#xff0c;主要是两个: UDP和TCP 提供了两套不同的 api&#xff0c;这api也叫做socket api。 UDP和 TCP 特点对比&#xff1a; UDP: 无连…...

<MySon car=“宝马“ :money=“money“></MySon>有没有冒号

为什么car"宝马"没有&#xff1a; 但是 :money"money"就有&#xff1a; <script setup> import {ref} from vue import MySon from /components/MySon.vueconst money ref(100) </script><template><h3>father</h3><My…...

netty(三):NIO——多线程优化

NIO多线程优化 使用Boss线程来处理accepct事件使用Worker线程来处理读写事件&#xff0c;可以创建多个worker线程 package com.review;import lombok.extern.slf4j.Slf4j;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; impor…...

Linux操作系统--linux概述

1.Linux概述 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff08;OS&#xff09;。简单的说就是一种操作系统。在日常中常见的操作系统有一下三种: 2.linux起源和背景 (1).linux的诞生 linux操作系统是由李纳斯托瓦兹&#xf…...

数组中出现次数超过一半的数字

⭐️ 题目描述 &#x1f31f; OJ链接&#xff1a;数组中出现次数超过一半的数字 思路&#xff1a; 采用投票计数的方式&#xff0c;我们可以把每个数字都看成一次投票并且计数&#xff0c;那么最后剩下来的就是数组中数字出现次数最多的那一个。比如 { 1,2,3,2,2,2,5,4,2 } &a…...

网络优化工程师,你真的了解吗?

一、5G网络优化工程师到底是什么&#xff1f; 5G&#xff0c;就是我们通常所说的第五代移动通信标准&#xff0c;属于目前最热门的新技术趋势。随着2019年5G技术进入正式的商用阶段&#xff0c;拥有广阔的发展前景&#xff0c;备受瞩目。“5G工程师”这个词是一个概念词&#x…...

git 的常用命令

git是一个版本管理器&#xff0c;是程序员必备工具之一&#xff0c;其主分为三个区&#xff1a; 工作区&#xff1a; 暂存区&#xff1a; 仓库&#xff1a; 通过保持软件版本&#xff0c;分支&#xff0c;合并&#xff0c;等多种版本操作&#xff0c;使软件能在自己想要的版本…...

linux如何拷贝文件,删除多余的一级目录,用*号代替所有文件

加上*&#xff0c;代表目录下的所有文件 mv /home/user/dir1/dir1/* /home/user/dir1/可以使用mv命令的通配符来去掉一层目录。 例如&#xff0c;假设有一个名为/home/user/dir1/dir2/file.txt的文件&#xff0c;要将它移动到/home/user/dir2/目录下并去掉dir1目录&#xff0…...

springboot使用properties

一、方式1&#xff1a; 1.1.配置类&#xff1a; package cn.zyq.stater.config;import cn.zyq.stater.bean.User4; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework…...

Android中获取手机SIM卡的各种信息

通过以下工具类方法可以获取到手机SIM的各种信息数据&#xff01;&#xff01;&#xff01; package com.utils; import android.telephony.TelephonyManager; import com.baidu.platform.comapi.map.E; import org.json.JSONArray; import org.json.JSONObject; import java.…...

matlab 根据索引提取点云

目录 一、语法二、说明三、名称-值对应参数1、输入参数2、输出参数四、代码示例五、结果展示六、参考链接一、语法 ptCloudOut = select(ptCloud,indices) ptCloudOut = select(ptCloud,row,column...

蓝芯、四川邦辰面试(部分)

蓝芯 HTTP请求经过MQ异步处理后&#xff0c;怎样返回结果呢&#xff1f;grpc比起spring cloud的优缺点&#xff1f; 四川邦辰 SkyWalking的埋点具体是怎么操作的&#xff1f;newBing: SkyWalking支持两种埋点方式&#xff1a;自动埋点和手动埋点。自动埋点是指通过SkyWalking…...

openCV实战-系列教程13:文档扫描OCR识别下(图像轮廓/模版匹配)项目实战、源码解读

&#x1f9e1;&#x1f49b;&#x1f49a;&#x1f499;&#x1f49c;OpenCV实战系列总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 上篇内容&#xff1a; openCV实战-系列教程11&#xff1a;文档扫描OCR识别上&am…...

SpringBootWeb案例 Part 4

3. 修改员工 需求&#xff1a;修改员工信息 在进行修改员工信息的时候&#xff0c;我们首先先要根据员工的ID查询员工的信息用于页面回显展示&#xff0c;然后用户修改员工数据之后&#xff0c;点击保存按钮&#xff0c;就可以将修改的数据提交到服务端&#xff0c;保存到数据…...

什么是ChatGPT水印,ChatGPT生成的内容如何不被检测出来,原理什么?

太长不看版 1. 什么是ChatGPT水印&#xff1f; ChatGPT水印是AI以伪随机方式生成的独特tokens序列。该序列用来作为水印&#xff0c;以区分AI生成内容和人类原创内容。 2. 如何规避ChatGPT水印&#xff1f; 一种规避方法是使用其他AI模型改写ChatGPT生成的文本。这会破坏水…...

Android 6.0 Settings中添加虚拟键开关

添加系统默认键值 b/frameworks/base/packages/SettingsProvider/res/values/defaults.xml-212,4 212,7 <!-- Default for Settings.Secure.NFC_PAYMENT_COMPONENT --><string name"def_nfc_payment_component"></string><!--mh.modify 2019060…...

Yolov8小目标检测(12):动态稀疏注意力BiFormer | CVPR 2023

💡💡💡本文改进:动态稀疏注意力,cvpr2023。 BiFormer | 亲测在红外弱小目标检测涨点,map@0.5 从0.755提升至0.758 💡💡💡Yolo小目标检测,独家首发创新(原创),适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提供每一步步骤和源码,带你轻松实现小…...

C# VS调试技巧

一.按照条件调试步骤 ①在需要代码执行的行打断点 ②触发此断点&#xff0c;让代码执行到此处 ③鼠标滑至在断点处&#xff0c;点击设置 ④设置断点条件&#xff0c;如下图所示 二、多线程调试技巧 ①在需要代码执行的行打断点 ②触发此断点&#xff0c;让代码执行到此处…...

VS的调试技巧

Visual Studiohttps://visualstudio.microsoft.com/zh-hans/vs/ 目录 1、什么是调试&#xff1f; 2、debug和release 3、调试 3.1、环境 3.2、 快捷键 3.2.1、F10和F11 3.2.2、ctrlF5 3.2.3、F5与F9 3.2.3.1、条件断点 3.3、监视和内存观察 3.3.1、监视 3.3.2、内存 …...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

Axure 下拉框联动

实现选省、选完省之后选对应省份下的市区...