【从零开始的Java开发】1-5-1 异常:分类、异常处理、try-catch-finally、多重catch、finally与return、throws、throw关键字、自定义异常类、异常链

什么是异常

  • 异常就是程序的错误。
  • 错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误。
  • 在程序运行过程中,意外发生的情况,背离我们程序本身意图的表现,都可以理解为异常。

举个例子:这些都算是运行的错误——在编译时没有错误提示。
在这里插入图片描述

异常分类

在Java中,用Throwable和它的子类对异常进行描述。

Throwable分为ErrorException

  • Error:是程序中无法处理的错误,表示运行应用程序中较严重的问题——大多数错误与代码编写者执行的操作无关,主要是代码运行是Java虚拟机出现的问题,如:虚拟机错误VirtualMachineError,内存溢出OutOfMemoryError,线程死锁ThreadDeath
  • Exception:是程序本身可以处理的异常。异常处理通常指针对这种异常的处理。包括:非检查异常Unchecked Exception,检查异常Checked Exception

关于Error:它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况,对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况

关于Unchecked Exception:编译器不要求强制处理的异常,包含RuntimeException以及它的相关子类。如:

  • 空指针异常
  • 数组下标越界异常
  • 算数异常
  • 类型转换异常

关于Checked Exception:编译器要求在代码中处理的异常。是RuntimeException以及它的相关子类之外的异常。

思维导图
在这里插入图片描述

异常处理

在Java应用程序中,异常处理机制分为:

  • 抛出异常
  • 捕捉异常

关于捕获异常
对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同。
Java规定:

  • 对于可查异常必须捕捉、或者声明抛出
  • 允许忽略不可查的RuntimeException(含子类)和Error(含子类)

关于关键字trycatchfinallythrowthrows
在这里插入图片描述

try-catch-finally

try:用于捕获异常
catch:用于处理捕获到的异常
finally:无论是否发生异常,代码总能执行

**try块后可接零个或者多个catch块,如果没有catch块,必须跟一个finally块。**简言之,try必须跟catch或finally组合使用,不允许单独存在。

举例:要求:定义两个整,接收键盘用户输入,输出两数之商。

代码:

public class TryDemoOne {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		System.out.println("请输入两个整数:");
		Scanner input = new Scanner(System.in);
		int one = input.nextInt(), two = input.nextInt();
		System.out.println("商为" + one / two);
	}

}

奇葩输入1:12 0
则输出:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)

奇葩输入2:12 a
则输出:

Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:12)

接下来我们使用try-catch结构处理上述异常。

若有异常,catch会捕获它,然后输出其代码块内的内容;否则完成try内的操作。

public class TryDemoOne {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try {
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		} catch (Exception e) {
			System.out.println("程序出错啦!");
		}

		System.out.println("运算结束");

	}

}

输入:12 a
输出:

程序出错啦!
运算结束

输入:12 0
输出:

程序出错啦!
运算结束

如果我们想知道错误的类型和位置,可以在catch中添加语句:e.printStackTrace();
如:

public class TryDemoOne {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try {
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("程序出错啦!");
		}

		System.out.println("运算结束");

	}

}

那么输入12 0时,会输出:

java.lang.ArithmeticException: / by zero
程序出错啦!
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:14)
运算结束

ps:e.printStackTrace();的错误信息的输出位置相对随机,不用计较。

我们该怎么看异常信息呢?

at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)

为例,这是输入12 a的异常信息。

我们要从下往上看。

最后一行at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)表示:异常发生的最外层的表示是在TryDemoOne.java文件的第13行:
在这里插入图片描述

在这个位置它调用了Scanner.nextInt方法,在Scanner.java:2212,我们点击它,则跳转到Scanner.class
在这里插入图片描述
而这个位置调用了nextInt(defaultRadix),其实就是在Scanner.nextInt(Scanner.java:2258),点击它则跳转到:
在这里插入图片描述
到这里我们可以看到integerPattern(),大概能猜出来这是一个整数转换的方法,而a肯定不能转化为整数,所以会跳转到Scanner.next(Scanner.java:1594),即:throwFor(),异常抛出。
在这里插入图片描述
异常抛出的具体代码在Scanner.throwFor(Scanner.java:939),即:
在这里插入图片描述
所以,我们这里的异常是:InputMismatchException,会被catch所捕获。

这就是try-catch产生异常、捕获异常的整体流程。

关于finally:我们如果想要某个语句不管是否产生异常都一定要执行,可以在这个语句外套一个finally,如:

public class TryDemoOne {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try {
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("程序出错啦!");
		} finally {
			System.out.println("运算结束");
		}

	}

}

使用多重catch结构处理异常

如果针对不同的异常,有不同的处理方式,该怎么解决呢?
答:用多重catch解决

ps:

  • 多重catch时不能出现同类型异常
  • 安全起见,多重catch块的最后一个块中异常为Exception(不能放第一个,会报错)

代码如下:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try {
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		} catch (ArithmeticException e) {
			System.out.println("除数不能为0");
			e.printStackTrace();
		} catch (InputMismatchException e) {
			System.out.println("请输入整数");
			e.printStackTrace();
		} catch (Exception e) {
			System.out.println("其他异常");
			e.printStackTrace();
		} finally {
			System.out.println("运算结束");
		}

	}

测试输入:12 0
输出:

除数不能为0
java.lang.ArithmeticException: / by zero
运算结束
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:14)

测试输入:12 a

请输入整数
java.util.InputMismatchException
运算结束
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)

终止finally执行的方法

catchfinally不能脱离try独自出现。
一般情况下,不管finally一定会执行,有没有终止finally执行的方法呢?

答:有。System.exit(1);

在这里插入图片描述
在这里插入图片描述
则若catch的代码改成:

catch (ArithmeticException e) {
			System.exit(1);
			System.out.println("除数不能为0");
			e.printStackTrace();
		}

那么他会在System.exit(1);时终止程序的执行,就不会允许到finally了。

return关键字在异常处理中的作用

类如下:

public class Test {
	public static void main(String[] args) {
		int res=test();
		System.out.println("商为"+res);
	}
	
	public static int test() {
		try {
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			return one/two;
		} catch (ArithmeticException e) {			
			System.out.println("除数不能为0");
			return 0;		
		} catch (Exception e) {
			System.out.println("其他异常");
			e.printStackTrace();
		} finally {
			System.out.println("运算结束");
			return -10000;
		}
		
	}
}

测试输入为:2 1
输出:

运算结束
商为-10000

输入为:2 0
输出:

除数不能为0
运算结束
商为-10000

由此可见,在try-catch-finally中,三个语句都有return的情况下,永远都是返回finally中的return

也就是说,从语法的角度上finally块中带return是没问题的,但从逻辑的角度上,finally块中的return是一定会被返回的——这也就是为什么finally中有return就会有Warning:
在这里插入图片描述
如果把finally中的return删去,即:

public static int test() {
		try {
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			return one/two;
		} catch (ArithmeticException e) {
			System.out.println("除数不能为0");
			return 0;		
		} catch (Exception e) {
			System.out.println("其他异常");
			e.printStackTrace();
			return 0;
		} finally {
			System.out.println("运算结束");
		}
		
	}

再测试一下:
输入:2 1
输出:

运算结束
商为2

输入:2 0
输出:

除数不能为0
运算结束
商为0

这就正确了。

使用throws声明异常类型

可以通过throws声明将要抛出何种类型的异常,通过throw将产生的异常抛出。如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。

throws语句用在方法定义时声明该方法要抛出的异常类型

public void method() throws Exception 1,Exception 2{
	//可能产生异常的代码
}

当方法抛出异常列表中的异常时,方法将不对这些类型及其子类作处理,而抛向调用该方法的方法,由他去处理。

举个例子:
测试类代码如下:

public class TryDemo3 {
	public static void main(String[] args) {
		
		try {
			int res = test();
			System.out.println("商为" + res);
		} catch (ArithmeticException e) {
			// TODO Auto-generated catch block
			System.out.println("除数不允许为0");
			e.printStackTrace();
		}
	}

	//表示这个函数可能会抛出ArithmeticException这样的异常
	public static int test() throws ArithmeticException {

		System.out.println("请输入两个整数:");
		Scanner input = new Scanner(System.in);
		int one = input.nextInt(), two = input.nextInt();
		System.out.println("运算结束");
		return one / two;
	}
}

测试输入1:2 1
输出:

运算结束
商为2

测试输入2:2 0
输出:

运算结束
java.lang.ArithmeticException: / by zero
除数不允许为0
	at ExceptionProj/com.test.TryDemo3.test(TryDemo3.java:26)
	at ExceptionProj/com.test.TryDemo3.main(TryDemo3.java:10)

这段代码能处理别的异常吗?答案是不可以。
测试输入3:2 a
输出:这个异常是虚拟机在处理。

Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemo3.test(TryDemo3.java:24)
	at ExceptionProj/com.test.TryDemo3.main(TryDemo3.java:10)

解决方法1:throws后面接多个异常类型,用逗号分隔

public class TryDemo3 {
	public static void main(String[] args) {
		
		try {
			int res = test();
			System.out.println("商为" + res);
		} catch (ArithmeticException e) {
			// TODO Auto-generated catch block
			System.out.println("除数不允许为0");
			e.printStackTrace();
		}catch(InputMismatchException e) {
			System.out.println("输入必须为整数");
			e.printStackTrace();		
		}
	}

	//表示这个函数可能会抛出ArithmeticException这样的异常
	public static int test() throws ArithmeticException,InputMismatchException {

		System.out.println("请输入两个整数:");
		Scanner input = new Scanner(System.in);
		int one = input.nextInt(), two = input.nextInt();
		System.out.println("运算结束");
		return one / two;
	}
}

解决方法2:throws后接Exception

public class TryDemo3 {
	public static void main(String[] args) {

		try {
			int res = test();
			System.out.println("商为" + res);
		} catch (ArithmeticException e) {
			// TODO Auto-generated catch block
			System.out.println("除数不允许为0");
			e.printStackTrace();
		} catch (InputMismatchException e) {
			System.out.println("输入必须为整数");
			e.printStackTrace();
		} catch (Exception e) {
			System.out.println("其他错误");
			e.printStackTrace();
		}
	}

	public static int test() throws Exception {

		System.out.println("请输入两个整数:");
		Scanner input = new Scanner(System.in);
		int one = input.nextInt(), two = input.nextInt();
		System.out.println("运算结束");
		return one / two;
	}
}

那么问题来了,当test方法后接的异常为Exception时,我们在main方法中若是直接写一个test(),会有报错:
在这里插入图片描述
而test方法后接的异常为ArithmeticException时,就不会报错。为什么呢?

原因如下:ArithmeticException异常是非检查异常,所以编译器不会要求我们一定去改它,而Exception异常是父类,它包含检查异常,因此编译器会报错,要求我们去改掉它。
在这里插入图片描述

如果我们程序中可能产生的异常都是非检查异常,而我们希望编译器给我们报错的提示,改如何解决呢?
答:文档注释——/**

/**
	 * 
	 * @return
	 * @throws ArithmeticException
	 * @throws InputMismatchException
	 */

由图可见,写上了文档注释后,我们把鼠标放到test上,它会提示说可能有ArithmeticExceptionInputMismatchException这两种异常——尽管它不报错。
在这里插入图片描述

使用throw抛出异常对象

throw用来抛出一个异常,且抛出的只能够是可抛出类Throwable或者其子类的对象。

抛出异常的处理方式有两种:
方案1:自己抛出的异常自己处理——在throw外加trycatch块:

public void method(){
	try{
	//代码
	throw new 异常类型();
	}catch(异常类型 e){
	//对异常的处理代码
	}
}

方案2:我们在抛出异常的方法声明处通过throws关键字标识对应的异常类型——谁调用当前方法,谁处理它:

public void method() throws 异常类型{
	//代码段1
	throw new 异常类型();
}

异常处理可以:

  1. 规避可能出现的风险
  2. 完成一些程序的逻辑

举个例子:如何通过异常处理完成一些程序的逻辑。
假设我们有这样一个场景:一个旅馆要求18岁以下或80岁以上的人如果想办理入住,需要其亲友陪同。

方案1:通过try-catch包含throw语句——在方法内部自己抛出异常自己处理(在这里,18岁以下,80岁以上的人就是”异常“)

public class TryDemo4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		testAge();
	}

	// 描述酒店的入住规则:限定年龄,要求18岁以下或80岁以上的人需要其亲友陪同。
	public static void testAge() {

		try {
			System.out.println("请输入年龄");
			Scanner input = new Scanner(System.in);
			int age = input.nextInt();
			if (age < 18 || age > 80) {
				throw new Exception("要求18岁以下或80岁以上的人需要其亲友陪同");
			} else {
				System.out.println("欢迎入住!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}


测试输入:20
输出:欢迎入住!

测试输入:17
输出:

java.lang.Exception: 要求18岁以下或80岁以上的人需要其亲友陪同
	at ExceptionProj/com.test.TryDemo4.testAge(TryDemo4.java:20)
	at ExceptionProj/com.test.TryDemo4.main(TryDemo4.java:9)

在这里插入图片描述
方案2:通过throws在方法声明处抛出异常类型——谁调用谁处理——调用者可以自己处理,或继续向上抛出(由上层处理)

public class TryDemo4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			testAge();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 描述酒店的入住规则:限定年龄,要求18岁以下或80岁以上的人需要其亲友陪同。
	public static void testAge() throws Exception {

		System.out.println("请输入年龄");
		Scanner input = new Scanner(System.in);
		int age = input.nextInt();
		if (age < 18 || age > 80) {
			throw new Exception("要求18岁以下或80岁以上的人需要其亲友陪同");
		} else {
			System.out.println("欢迎入住!");
		}

	}
}

测试输入:20
输出:欢迎入住!

测试输入:17
输出:

java.lang.Exception: 要求18岁以下或80岁以上的人需要其亲友陪同
	at ExceptionProj/com.test.TryDemo4.testAge(TryDemo4.java:20)
	at ExceptionProj/com.test.TryDemo4.main(TryDemo4.java:9)

总结:throw抛出异常对象处理方案

  1. 通过try-catch包含throw语句——在方法内部自己抛出异常自己处理
  2. 通过throws在方法声明处抛出异常类型——谁调用谁处理——调用者可以自己处理,或继续向上抛出(由上层处理)

其他补充:throw语句抛出的异常,在用throws方法声明时,可以声明它的父类。但不能是子类,如:

这样是可以的,因为ThrowableException的父类,是匹配的。
在这里插入图片描述
这样是不行的,IOExceptionException的子类,范围小,不能描述Exception的相关信息。
在这里插入图片描述
ps:不建议抛出非检查异常,因为不会报错。

自定义异常类

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。也可以通过自定义异常描述特定业务产生的异常类型。
所谓自定义异常,就是定义一个类,去继承Throwable类或者它的子类。

对上面的案例,我们为18岁以下和80岁以上自定义一个异常类HotelAgeException

public class HotelAgeException extends Exception{
	public HotelAgeException() {
		super("要求18岁以下或80岁以上的人需要其亲友陪同");
	}
}

在测试类中调用它:

public class TryDemo4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			testAge();
		} catch (HotelAgeException e) {
			System.out.println(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 描述酒店的入住规则:限定年龄,要求18岁以下或80岁以上的人需要其亲友陪同。
	public static void testAge() throws HotelAgeException {

		System.out.println("请输入年龄");
		Scanner input = new Scanner(System.in);
		int age = input.nextInt();
		if (age < 18 || age > 80) {
			throw new HotelAgeException();
		} else {
			System.out.println("欢迎入住!");
		}

	}
}

测试输入:1
输出:要求18岁以下或80岁以上的人需要其亲友陪同

异常链

有时候我们会捕获一个异常后在抛出另一个异常。

代码:

public class TryDemo5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			test3();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void test1() throws HotelAgeException {
		throw new HotelAgeException();
	}

	public static void test2() throws Exception {
		try {
			test1();
		} catch (HotelAgeException e) {
			throw new Exception("我是新产生的异常1");
		}

	}

	public static void test3() throws Exception {
		try {
			test2();
		} catch (Exception e) {
			throw new Exception("我是新产生的异常2");
		}
	}
}

由此可见,这里的异常信息是丢失了的——由于新抛出了异常而导致异常信息丢失。有没有方法可以把异常保留下来呢?
API文档链接
找到lang包——找到Throwable类:可以用这个方法把 异常信息保留下来。
在这里插入图片描述
修改后的代码:

public class TryDemo5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			test3();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void test1() throws HotelAgeException {
		throw new HotelAgeException();
	}

	public static void test2() throws Exception {
		try {
			test1();
		} catch (HotelAgeException e) {
			throw new Exception("我是新产生的异常1",e);
		}

	}

	public static void test3() throws Exception {
		try {
			test2();
		} catch (Exception e) {
			throw new Exception("我是新产生的异常2",e);
		}
	}
}

输出:
由此可见,异常2由异常1产生,异常1由18岁以下80岁以上的异常产生。
在这里插入图片描述

这就是异常链。

  • 有时候我们会捕获一个异常后抛出另一个异常。
  • 顾名思义:将异常发生的原因一个传一个地串起来,即把底层的异常信息传到上层,这样逐层抛出。

总结

异常

在程序运行过程中,意外发生的情况,背离我们程序本身意图的表现,都可以理解为异常。
利用Java中的异常机制,我们可以更好地提升程序的健壮性。
在Java中,通过Throwable及其子类描述各种不同的异常类型。

在这里插入图片描述

异常处理

在Java应用程序中,异常处理机制为:抛出异常,捕捉异常。
通过5个关键字来实现:trycatchfinallythrowthrows

在这里插入图片描述
try-catch:
在这里插入图片描述
try-catch-finally:
异常类型匹配时:
在这里插入图片描述
异常类型不匹配时:
在这里插入图片描述
多重catch时:
在这里插入图片描述
有return时:finally执行完后才执行return
在这里插入图片描述

一些总结

  1. 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
  2. 在多重catch后,可以加一个catch(Exception e)来处理可能会遗漏 的异常
  3. 对于不确定的代码,也可以加上try-catch,处理潜在的异常
  4. 尽量去处理异常,切记只是简单的调用printStackTrace()去打印输出异常信息
  5. 具体如何处理异常,要根据不同的业务需求和异常类型去决定
  6. 尽量添加finally语句块去释放占用的资源

throw和throws

可以通过throws声明将要抛出何种类型的异常,通过throw将产生的异常抛出。
如果一个方法可能会产生异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。

throw

  • 当子类重写父类抛出异常的方法时,声明的异常必须是父类方法所声明异常的同类或子类

自定义异常

可以通过自定义异常描述特定业务产生的异常类型。
所谓自定义异常,就是定义一个类,去继承Throwable类或者它的子类。

异常链

  • 有时候我们会捕获一个异常后抛出另一个异常。
  • 顾名思义:将异常发生的原因一个传一个地串起来,即把底层的异常信息传到上层,这样逐层抛出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值