第二章【一切都是对象】——java编程思想第四版[【Think in java】

1、为什么创建句柄/应用/指针的时候,要进行初始化?

在其他 Java 参考书里,还可看到有的人将其称作一个“引用”,甚至一个“指针”。可将这一情形想象成用遥控板(句柄)操纵电视机(对象)。只要握住这个遥控板,就相当于掌握了与电视机连接的通道。但一旦需要“换频道”或者“关小声音”,我们实际操纵的是遥控板(句柄),再由遥控板自己操纵电视机(对象)。如果要在房间里四处走走,并想保持对电视机的控制,那么手上拿着的是遥控板,而非电视机。此外,即使没有电视机,遥控板亦可独立存在。也就是说,只是由于拥有一个句柄,并不表示必须有一个对
象同它连接。所以如果想容纳一个词或句子,可创建一个 String句柄:

String s;

但这里创建的只是句柄,并不是对象。若此时向s 发送一条消息,就会获得一个错误(运行期)。这是由于s实际并未与任何东西连接(即“没有电视机”)。因此,一种更安全的做法是:创建一个句柄时,记住无论如何都进行初始化:

String s = "asdf";

String s = new String("asdf");

2、Java保存数据的六个地方

(1) 寄存器。

这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存
器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的
程序里找到寄存器存在的任何踪迹。
(2) 堆栈。

驻留于常规 RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆
栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存
方式,仅次于寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存
在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活
性,所以尽管有些Java 数据要保存在堆栈里——特别是对象句柄,但Java 对象并不放到其中。

(3) 堆。

一种常规用途的内存池(也在 RAM区域),其中保存了Java 对象。和堆栈不同,“内存堆”或
“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要
在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命
令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然
会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!
(4) 静态存储。

这儿的“静态”(Static)是指“位于固定位置”(尽管也在 RAM里)。程序运行期间,静
态存储的数据将随时等候调用。可用static 关键字指出一个对象的特定元素是静态的。但 Java 对象本身永
远都不会置入静态存储空间。
(5) 常数存储。

常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数
需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。
(6) 非RAM 存储。

若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。
其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给
另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对
于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复
成普通的、基于RAM的对象。Java 1.1 提供了对Lightweight persistence 的支持。未来的版本甚至可能提
供更完整的方案。

3、常见的基本数据类型简介

主类型 大小 最小值 最大值 封装器类型
boolean 1 位 - - Boolean
char 16位 Unicode 0 Unicode 2的 16次方-1 Character
byte 8位 -128 +127 Byte(注释①)
short 16 位 -2 的15 次方 +2的 15次方-1 Short(注释①)
int 32位 -2的 31次方 +2 的31 次方-1 Integer
long 64位 -2 的63 次方 +2的 63次方-1 Long
float 32 位 IEEE754 IEEE754 Float
double 64 位 IEEE754 IEEE754 Double
Void - - - Void(注释①)
①:到Java 1.1 才有,1.0 版没有。

 

3.1、高精度数字

Java 1.1 增加了两个类,用于进行高精度的计算:BigInteger和 BigDecimal。尽管它们大致可以划分为
“封装器”类型,但两者都没有对应的“主类型”。

这两个类都有自己特殊的“方法”,对应于我们针对主类型执行的操作。也就是说,能对int或 float做的
事情,对BigInteger 和BigDecimal 一样可以做。只是必须使用方法调用,不能使用运算符。此外,由于牵
涉更多,所以运算速度会慢一些。我们牺牲了速度,但换来了精度。
BigInteger支持任意精度的整数。也就是说,我们可精确表示任意大小的整数值,同时在运算过程中不会丢
失任何信息。
BigDecimal支持任意精度的定点数字。例如,可用它进行精确的币值计算。

3.2、基本类型的默认值

若某个主数据类型属于一个类成员那么即使不明确(显式)进行初始化,也可以保证它们获得一个默认
值。

主类型 默认值
Boolean false
Char '\u0000'(null)
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d

一旦将变量作为类成员使用,就要特别注意由 Java 分配的默认值。这样做可保证主类型的成员变量肯定得到
了初始化(C++不具备这一功能),可有效遏止多种相关的编程错误。
然而,这种保证却并不适用于“局部”变量——那些变量并非一个类的字段。所以,假若在一个函数定义中
写入下述代码:
int x;
那么x 会得到一些随机值(这与C 和C++是一样的),不会自动初始化成零

4、类的字段和方法的简述

定义一个类时(我们在 Java 里的全部工作就是定义类、制作那些类的对象以及将消息发给那些对象),可在
自己的类里设置两种类型的元素:数据成员(有时也叫“字段”)以及成员函数(通常叫“方法”)。其
中,数据成员是一种对象(通过它的句柄与其通信),可以为任何类型。它也可以是主类型(并不是句柄)
之一。如果是指向对象的一个句柄,则必须初始化那个句柄,用一种名为“构建器”(第4 章会对此详述)
的特殊函数将其与一个实际对象连接起来(就象早先看到的那样,使用new关键字)。但若是一种主类型,
则可在类定义位置直接初始化(正如后面会看到的那样,句柄亦可在定义位置初始化)

5、函数或方法的简介

方法的基本组成部分包括名字、自变量、返回类型以及主体。下面便是它最基本的形式:

返回类型 方法名( /* 自变量列表*/ ) {/* 方法主体 */}

6、package与import

package

Java 的设计者鼓励程序员反转使用自己的Internet 域名,因为它们肯定是独一
无二的。由于我的域名是BruceEckel.com,所以我的实用工具库就可命名为
com.bruceeckel.utility.foibles。反转了域名后,可将点号想象成子目录。
在Java 1.0 和 Java 1.1 中,域扩展名 com,edu,org,net 等都约定为大写形式。所以库的样子就变成:
COM.bruceeckel.utility.foibles。然而,在 Java 1.2的开发过程中,设计者发现这样做会造成一些问题。
所以目前的整个软件包都以小写字母为标准

Java 的这种特殊机制意味着所有文件都自动存在于自己的命名空间里。而且一个文件里的每个类都自动获得
一个独一无二的标识符(当然,一个文件里的类名必须是唯一的)

 

而import

大多数时候,我们直接采用来自标准Java 库的组件(部件)即可,它们是与编译器配套提供的。使用这些组
件时,没有必要关心冗长的保留域名;举个例子来说,只需象下面这样写一行代码即可:
import java.util.Vector;
它的作用是告诉编译器我们想使用 Java 的Vector 类。然而,util 包含了数量众多的类,我们有时希望使用
其中的几个,同时不想全部明确地声明它们。为达到这个目的,可使用“*”通配符。如下所示:
import java.util.*;
需导入一系列类时,采用的通常是这个办法。应尽量避免一个一个地导入类。

7、static关键字

通常,我们创建类时会指出那个类的对象的外观与行为。除非用new 创建那个类的一个对象,否则实际上并
未得到任何东西。只有执行了new 后,才会正式生成数据存储空间,并可使用相应的方法。
但在两种特殊的情形下,上述方法并不堪用。一种情形是只想用一个存储区域来保存一个特定的数据——无
论要创建多少个对象,甚至根本不创建对象。另一种情形是我们需要一个特殊的方法,它没有与这个类的任
何对象关联。也就是说,即使没有创建对象,也需要一个能调用的方法。为满足这两方面的要求,可使用
static(静态)关键字。一旦将什么东西设为static,数据或方法就不会同那个类的任何对象实例联系到一
起。所以尽管从未创建那个类的一个对象,仍能调用一个 static方法,或访问一些 static数据。而在这之前,对于非 static数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法。这是由于非
static数据和方法必须知道它们操作的具体对象。当然,在正式使用前,由于static方法不需要创建任何
对象,所以它们不可简单地调用其他那些成员,同时不引用一个已命名的对象,从而直接访问非 static成员
或方法(因为非static成员和方法必须同一个特定的对象关联到一起)。
有些面向对象的语言使用了“类数据”和“类方法”这两个术语。它们意味着数据和方法只是为作为一个整
体的类而存在的,并不是为那个类的任何特定对象。有时,您会在其他一些Java 书刊里发现这样的称呼。
为了将数据成员或方法设为static,只需在定义前置和这个关键字即可

有两个办法可引用一个 static变量。正如上面展示的那样,可通过一个对象命名它,如st2.i。亦可直接用
它的类名引用,而这在非静态成员里是行不通的(最好用这个办法引用static 变量,因为它强调了那个变量
的“静态”本质)

类似的逻辑也适用于静态方法。既可象对其他任何方法那样通过一个对象引用静态方法,亦可用特殊的语法
格式“类名.方法()”加以引用

8、命令执行单个class文件一闪而过的解决办法

在某些编程环境里,程序会在屏幕上一切而过,甚至没机会看到结果。可将下面这段代码置于main()的
末尾,用它暂停输出:

try {
Thread.currentThread().sleep(5 * 1000);
} catch(InterruptedException e) {}
}


它的作用是暂停输出5 秒钟。

9、默认每个类都会自动导入的类库java.lang,不需要通过import显示导入

因为一个特殊的类库会自动导入每个Java 文件:java.lang

10、类的入口main方法的简述

类里必须包含一个名为main()的方法,形式如下:
public static void main(String[] args) {
其中,关键字“public”意味着方法可由外部世界调用(第5 章会详细解释)。main()的自变量是包含了
String对象的一个数组。args 不会在本程序中用到,但需要在这个地方列出,因为它们保存了在命令行调用
的自变量

11、学会使用javadoc输出专业的注释文档

对于Java 语言,最体贴的一项设计就是它并没有打算让人们为了写程序而写程序——人们也需要考虑程序的
文档化问题。对于程序的文档化,最大的问题莫过于对文档的维护。若文档与代码分离,那么每次改变代码
后都要改变文档,这无疑会变成相当麻烦的一件事情。解决的方法看起来似乎很简单:将代码同文档“链
接”起来。为达到这个目的,最简单的方法是将所有内容都置于同一个文件。然而,为使一切都整齐划一,
还必须使用一种特殊的注释语法,以便标记出特殊的文档;另外还需要一个工具,用于提取这些注释,并按
有价值的形式将其展现出来。这些都是Java 必须做到的。
用于提取注释的工具叫作javadoc。它采用了部分来自Java 编译器的技术,查找我们置入程序的特殊注释标
记。它不仅提取由这些标记指示的信息,也将毗邻注释的类名或方法名提取出来。这样一来,我们就可用最
轻的工作量,生成十分专业的程序文档。
javadoc输出的是一个 HTML 文件,可用自己的Web 浏览器查看。该工具允许我们创建和管理单个源文件,并
生动生成有用的文档。由于有了jvadoc,所以我们能够用标准的方法创建文档。而且由于它非常方便,所以
我们能轻松获得所有Java 库的文档

具体语法:

所有javadoc命令都只能出现于“/**”注释中。但和平常一样,注释结束于一个“*/”。主要通过两种方式
来使用javadoc:嵌入的HTML,或使用“文档标记”。其中,“文档标记”(Doc tags)是一些以“@”开头
的命令,置于注释行的起始处(但前导的“*”会被忽略)。
有三种类型的注释文档,它们对应于位于注释后面的元素:类、变量或者方法。也就是说,一个类注释正好
位于一个类定义之前;变量注释正好位于变量定义之前;而一个方法定义正好位于一个方法定义的前面。如
下面这个简单的例子所示:
/** 一个类注释 */
public class docTest {
/** 一个变量注释 */
public int i;
/** 一个方法注释 */
public void f() {}
}
注意javadoc只能为 public(公共)和protected(受保护)成员处理注释文档。“private”(私有)和
“友好”(详见5 章)成员的注释会被忽略,我们看不到任何输出(也可以用-private标记包括private 成
员)。这样做是有道理的,因为只有public 和protected 成员才可在文件之外使用,这是客户程序员的希
望。然而,所有类注释都会包含到输出结果里。
上述代码的输出是一个 HTML 文件,它与其他Java 文档具有相同的标准格式

 

注意在文档注释中,位于一行最开头的星号会被javadoc 丢弃。同时丢弃的还有前导空格。javadoc 会对所
有内容进行格式化,使其与标准的文档外观相符。不要将<h1>或<hr>这样的标题当作嵌入 HTML 使用,因为
javadoc会插入自己的标题,我们给出的标题会与之冲撞。
所有类型的注释文档——类、变量和方法——都支持嵌入 HTML。

 

12、@see :引用其他类

所有三种类型的注释文档都可包含@see 标记,它允许我们引用其他类里的文档。对于这个标记,javadoc会
生成相应的 HTML,将其直接链接到其他文档。格式如下:
@see 类名
@see 完整类名
@see 完整类名#方法名
每一格式都会在生成的文档里自动加入一个超链接的“See Also”(参见)条目。注意 javadoc不会检查我
们指定的超链接,不会验证它们是否有效

 

13、类文档标记

随同嵌入HTML 和@see 引用,类文档还可以包括用于版本信息以及作者姓名的标记。类文档亦可用于“接
口”目的(本书后面会详细解释)。
1. @version
格式如下:
@version 版本信息
其中,“版本信息”代表任何适合作为版本说明的资料。若在 javadoc命令行使用了“-version”标记,就
会从生成的 HTML 文档里提取出版本信息。
2. @author
格式如下:
@author 作者信息
其中,“作者信息”包括您的姓名、电子函件地址或者其他任何适宜的资料。若在javadoc命令行使用了“-
author”标记,就会专门从生成的HTML 文档里提取出作者信息。
可为一系列作者使用多个这样的标记,但它们必须连续放置。全部作者信息会一起存入最终HTML 代码的单独
一个段落里

14、变量文档标记


变量文档只能包括嵌入的HTML 以及@see 引用

15、方法文档标记

除嵌入HTML 和@see 引用之外,方法还允许使用针对参数、返回值以及违例的文档标记。
1. @param
格式如下:

@param 参数名 说明
其中,“参数名”是指参数列表内的标识符,而“说明”代表一些可延续到后续行内的说明文字。一旦遇到
一个新文档标记,就认为前一个说明结束。可使用任意数量的说明,每个参数一个。
2. @return
格式如下:
@return 说明
其中,“说明”是指返回值的含义。它可延续到后面的行内。
3. @exception
有关“违例”(Exception)的详细情况,我们会在第 9 章讲述。简言之,它们是一些特殊的对象,若某个方
法失败,就可将它们“扔出”对象。调用一个方法时,尽管只有一个违例对象出现,但一些特殊的方法也许
能产生任意数量的、不同类型的违例。所有这些违例都需要说明。所以,违例标记的格式如下:
@exception

完整类名 说明
其中,“完整类名”明确指定了一个违例类的名字,它是在其他某个地方定义好的。而“说明”(同样可以
延续到下面的行)告诉我们为什么这种特殊类型的违例会在方法调用中出现。
4. @deprecated
这是Java 1.1 的新特性。该标记用于指出一些旧功能已由改进过的新功能取代。该标记的作用是建议用户不
必再使用一种特定的功能,因为未来改版时可能摒弃这一功能。若将一个方法标记为@deprecated,则使用该
方法时会收到编译器的警告。

 

16、有文档注释的代码实例:

下面还是我们的第一个 Java 程序,只不过已加入了完整的文档注释:

//: Property.java
import java.util.*;

/**
 * The first Thinking in Java example program. Lists system information on
 * current machine.
 * 
 * @author Bruce Eckel
 * @author http://www.BruceEckel.com
 * @version 1.0
 */
public class Property {
	/**
	 * Sole entry point to class & application
	 * 
	 * @param args
	 *            array of string arguments
	 * @return No return value
	 * @exception exceptions
	 *                No exceptions thrown
	 */
	public static void main(String[] args) {
		System.out.println(new Date());
		Properties p = System.getProperties();
		p.list(System.out);
		System.out.println("--- Memory Usage:");
		Runtime rt = Runtime.getRuntime();
		System.out.println("Total Memory = " + rt.totalMemory()
				+ " Free Memory = " + rt.freeMemory());
	}
} // /

第一行:
//: Property.java
采用了我自己的方法:将一个“:”作为特殊的记号,指出这是包含了源文件名字的一个注释行。最后一行也
用这样的一条注释结尾,它标志着源代码清单的结束。这样一来,可将代码从本书的正文中方便地提取出
来,并用一个编译器检查

 

17、编码样式:驼峰标识:

一个非正式的Java 编程标准是大写一个类名的首字母。若类名由几个单词构成,那么把它们紧靠到一起(也
就是说,不要用下划线来分隔名字)。此外,每个嵌入单词的首字母都采用大写形式。例如:
class AllTheColorsOfTheRainbow { // ...}
对于其他几乎所有内容:方法、字段(成员变量)以及对象句柄名称,可接受的样式与类样式差不多,只是
标识符的第一个字母采用小写。例如:

class AllTheColorsOfTheRainbow {
int anIntegerRepresentingColors;
void changeTheHueOfTheColor(int newHue) {
// ...
}
// ...
}


当然,要注意用户也必须键入所有这些长名字,而且不能输错。

18、最后,给出一个经典的java例子,看懂每个细节:

// Property.java
import java.util.*;

public class Property {
	public static void main(String[] args) {
		System.out.println(new Date());
		Properties p = System.getProperties();
		p.list(System.out);
		System.out.println("--- Memory Usage:");
		Runtime rt = Runtime.getRuntime();
		System.out.println("Total Memory = " + rt.totalMemory()
				+ " Free Memory = " + rt.freeMemory());
	}
}

19、代码详解

在每个程序文件的开头,都必须放置一个import 语句,导入那个文件的代码里要用到的所有额外的类。注意
我们说它们是“额外”的,因为一个特殊的类库会自动导入每个Java 文件:java.lang。启动您的 Web浏览
器,查看由 Sun提供的用户文档(如果尚未从 http://www.java.sun.com下载,或用其他方式安装了Java 文
档,请立即下载)。在 packages.html 文件里,可找到Java 配套提供的所有类库名称。请选择其中的
java.lang。在“Class Index”下面,可找到属于那个库的全部类的列表。由于 java.lang 默认进入每个
Java 代码文件,所以这些类在任何时候都可直接使用。在这个列表里,可发现System和 Runtime,我们在
Property.java 里用到了它们。java.lang 里没有列出 Date 类,所以必须导入另一个类库才能使用它。如果
不清楚一个特定的类在哪个类库里,或者想检视所有的类,可在Java 用户文档里选择“Class Hierarchy”
(类分级结构)。在Web浏览器中,虽然要花不短的时间来建立这个结构,但可清楚找到与Java 配套提供的
每一个类。随后,可用浏览器的“查找”(Find)功能搜索关键字“Date”。经这样处理后,可发现我们的
搜索目标以 java.util.Date的形式列出。我们终于知道它位于util 库里,所以必须导入 java.util.*;否
则便不能使用Date。
观察packages.html 文档最开头的部分(我已将其设为自己的默认起始页),请选择java.lang,再选
System。这时可看到 System 类有几个字段。若选择out,就可知道它是一个static PrintStream 对象。由
于它是“静态”的,所以不需要我们创建任何东西。out 对象肯定是 3,所以只需直接用它即可。我们能对这
个out 对象做的事情由它的类型决定:PrintStream。PrintStream 在说明文字中以一个超链接的形式列出,
这一点做得非常方便。所以假若单击那个链接,就可看到能够为PrintStream 调用的所有方法。方法的数量
不少,本书后面会详细介绍。就目前来说,我们感兴趣的只有 println()。它的意思是“把我给你的东西打
印到控制台,并用一个新行结束”。所以在任何Java 程序中,一旦要把某些内容打印到控制台,就可条件反
射地写上System.out.println("内容")。
类名与文件是一样的。若象现在这样创建一个独立的程序,文件中的一个类必须与文件同名(如果没这样
做,编译器会及时作出反应)。类里必须包含一个名为main()的方法,形式如下:
public static void main(String[] args) {
其中,关键字“public”意味着方法可由外部世界调用(第5 章会详细解释)。main()的自变量是包含了
String对象的一个数组。args 不会在本程序中用到,但需要在这个地方列出,因为它们保存了在命令行调用
的自变量。
程序的第一行非常有趣:
System.out.println(new Date());
请观察它的自变量:创建Date 对象唯一的目的就是将它的值发送给 println()。一旦这个语句执行完毕,
Date 就不再需要。随之而来的“垃圾收集器”会发现这一情况,并在任何可能的时候将其回收。事实上,我
们没太大的必要关心“清除”的细节。
第二行调用了System.getProperties()。若用 Web浏览器查看联机用户文档,就可知道 getProperties()是System类的一个 static方法。由于它是“静态”的,所以不必创建任何对象便可调用该方法。无论是否存
在该类的一个对象,static 方法随时都可使用。调用getProperties()时,它会将系统属性作为 Properties
类的一个对象生成(注意Properties 是“属性”的意思)。随后的的句柄保存在一个名为p 的 Properties
句柄里。在第三行,大家可看到Properties 对象有一个名为list()的方法,它将自己的全部内容都发给一
个我们作为自变量传递的PrintStream 对象。
main()的第四和第六行是典型的打印语句。注意为了打印多个 String值,用加号(+)分隔它们即可。然
而,也要在这里注意一些奇怪的事情。在String 对象中使用时,加号并不代表真正的“相加”。处理字串
时,我们通常不必考虑“+”的任何特殊含义。但是,Java 的 String类要受一种名为“运算符过载”的机制
的制约。也就是说,只有在随同String 对象使用时,加号才会产生与其他任何地方不同的表现。对于字串,
它的意思是“连接这两个字串”。
但事情到此并未结束。请观察下述语句:
System.out.println("Total Memory = "
+ rt.totalMemory()
+ " Free Memory = "
+ rt.freeMemory());
其中,totalMemory()和freeMemory()返回的是数值,并非String对象。如果将一个数值“加”到一个字串
身上,会发生什么情况呢?同我们一样,编译器也会意识到这个问题,并魔术般地调用一个方法,将那个数
值(int,float 等等)转换成字串。经这样处理后,它们当然能利用加号“加”到一起。这种“自动类型转
换”亦划入“运算符过载”处理的范畴。
许多Java 著作都在热烈地辩论“运算符过载”(C++的一项特性)是否有用。目前就是反对它的一个好例
子!然而,这最多只能算编译器(程序)的问题,而且只是对 String对象而言。对于自己编写的任何源代
码,都不可能使运算符“过载”。
通过为Runtime 类调用 getRuntime()方法,main()的第五行创建了一个 Runtime对象。返回的则是指向一个
Runtime对象的句柄。而且,我们不必关心它是一个静态对象,还是由 new命令创建的一个对象。这是由于
我们不必为清除工作负责,可以大模大样地使用对象。正如显示的那样,Runtime可告诉我们与内存使用有
关的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱的叹息

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

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

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

打赏作者

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

抵扣说明:

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

余额充值