File、IO类及反射总结

File类及IO流章节总结

1. File的实例化与常用方法

实例化代码:

File file = new File("hello.txt");//相对路径
File file = new File("C:\\practice\\hello.txt");//绝对路径
注意事项

1.无论该路径下是否存在文件或者目录,都不影响File对象的创建。

2.File创建的file对象不一定一定要存在,只有当要读该文件时,必须存在,当对该文件进行写入或其他相关操作时,若不存在则会自动创建该文件。

  • 相对路径与绝对路径(1)绝对路径:从盘符开始的路径,是一个完整的路径。(2)相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用
    • IDEA中,main中的文件的相对路径,是相对于"当前工程"
    • IDEA中,单元测试方法中的文件的相对路径,是相对于"当前module"
常用方法

1、获取文件和目录基本信息

  • public String getName() :获取名称
  • public String getPath() :获取路径
  • public String getAbsolutePath():获取绝对路径
  • public File getAbsoluteFile():获取绝对路径表示的文件
  • public String getParent():获取上层文件目录路径。若无,返回null
  • public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
  • public long lastModified() :获取最后一次的修改时间,毫秒值2、列出目录的下一级
  • public String[] list():返回一个String数组,表示该File目录中的所有子文件或目录。
  • public File[] listFiles():返回一个File数组,表示该File目录中的所有的子文件或目录
package com.atguigu.file;

import org.junit.Test;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;

public class DirListFiles {
    @Test
    public void test01() {
        File dir = new File("d:/atguigu");
        String[] subs = dir.list();
        for (String sub : subs) {
            System.out.println(sub);
        }
    }

}

2. IO流原理及流的分类

2.1 IO原理

IO流分为输入流和输出流,输入流是指将文件中的数据输入到程序运行的内存当中;输出流是指将程序运行内存中的数据输出到文件中。

  • 输入input:读取文件中的数据到程序(内存)中。
  • 输出output:将程序(内存)中的数据输出到文件中。
2.2 流的分类
  • 按数据的流向不同分为:输入流和输出流
    • 输入流:把数据从其他设备上读取到内存中的流,输入流对象以InputSteam和Reader结尾。
    • 输出流:把数据从内存中写出到其他设备上的流,输出流对象以OutputStream和Writer结尾。
  • 按操作数据单位的不同分为:字节流(8bit)和字符流(16bit)
    • 字节流:以字节为单位,读写数据的流。
      • 操纵字节的流对象以InputSream和OutputStream结尾,如:FileInputStream(字节输入流)、FileOutputStream(字节输出流)
    • 字符流:以字符为单位,读写数据的流。
      • 操作字符的流对象以Reader和Writer结尾,如:FileReader(字符输入流)、FileWriter(字符输出流)

(抽象基类)

输入流

输出流

字节流

InputStream

OutputStream

字符流

Reader

Writer

常用的节点流:

  • 文件流: FileInputStream、FileOutputStrean、FileReader、FileWriter
  • 字节/字符数组流: ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter
    • 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。

常用处理流:

  • 缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
    • 作用:增加缓冲功能,避免频繁读写硬盘,进而提升读写效率。
  • 转换流:InputStreamReader、OutputStreamReader
    • 作用:实现字节流和字符流之间的转换。
  • 对象流:ObjectInputStream、ObjectOutputStream
    • 作用:提供直接读写Java对象功能

①因为出现了流资源的调用,因此为了避免内存泄漏,需要使用try-catch-finally来处理异常,在finally中关系流资源。

②对于输入流来说,File类的对象一定要在物理磁盘上存在(文件存在),否则会报FileNotFoundException异常,如果传入的是一个目录,则会报IOException异常。

对于输出流来说,File类的对象可以是不存在的。

>如果File类的对象不存在,则在输出的过程中,会自动创建File类的对象。

>如果File类的对象存在:

(1)如果调用FileWriter(File file)或FileWriter(File file,false),输出时会覆盖File文件中已有的内容。

(2)如果调用FileWriter(File file,true)构造器,则会对现有已存在的文件末尾追加写出内容。

2.3 关于flush(刷新)

在Java中,flush() 方法是 java.io.Flushable 接口定义的一个方法,它通常用于强制将缓冲区中的数据写入目标设备,例如文件或网络连接。这个方法的作用是确保缓冲区中的数据被立即写出,而不是等到缓冲区满了或者关闭流时才进行写出。

因为内置缓冲区的原因,如果FileWriter不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush() 方法了。

  • flush() :刷新缓冲区,流对象可以继续使用。
  • close():先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

注意:即便是flush()方法写出了数据,操作的最后还是要调用close方法,释放系统资源。

2.4 处理流之一:缓冲流

缓冲流的主要功能是提高数据的读写速度,减少磁盘和程序之间的IO次数。

  • 缓冲流要“嵌套”在相应的输入/输出流之上,比如字符缓冲输入流的构造器参数是字符输入流(BufferedReader br = new BufferedReader(new FileWriter("hello.txt"))),根据数据操作单位可以把缓冲流分为:
    • 字节缓冲流:BufferedInputStream,BufferedOutputStream
    • 字符缓冲流:BufferedReader,BufferedWriter
  • 缓冲流的基本原理:在创建流对象时,内部会创建一个缓冲区数组(缺省使用8192个字节(8kb)的缓冲区),通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
2.4 处理流之二:转换流

在Java中,转换流(InputStreamReader 和 OutputStreamWriter)是用于字节流和字符流之间进行转换的桥梁。它们允许将字节流转换为字符流,以便更方便地处理字符数据。

具体来说:

  1. InputStreamReader: 它是Reader的子类,用于将字节输入流转换为字符输入流。它可以指定字符集,以便正确地将字节转换为字符。构造器
    • InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
    • InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流
  1. 举例:
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis,"utf-8");

上述代码将字节输入流fis转为了字符输入流,解码集为utf-8(因为Java中默认编码集为utf-8),因此解码集也要是utf-8.

  1. OutputStreamWriter: 它是Writer的子类,用于将字符输出流转换为字节输出流。同样,可以指定字符集,以便正确地将字符转换为字节。OutputStreamWriter
    • 转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
    • 构造器
      • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
      • OutputStreamWriter(OutputStream in,String charsetName): 创建一个指定字符集的字符流。
File file = new File("hello.txt");
FileWriterr fis = new FileWriter(file);
OutputStreamWriter osw = new OutputStreamWriter(fis,"GBK");

上述代码将字符输出流osw转为了字节输出流,编码集为GBK(从utf-8变为GBK)

使用转换流的情况包括:

  • 字符和字节的转换: 当你有一个字节流(例如FileInputStream)但需要处理字符数据时,可以使用InputStreamReader将字节流转换为字符流。同样,当你有一个字符输出流(例如FileWriter)但需要处理字节数据时,可以使用OutputStreamWriter将字符流转换为字节流。
  • 字符集处理: 在进行字符编码和解码时,转换流允许你指定字符集。这对于确保正确的字符转换是很重要的,特别是在涉及不同字符集的系统之间进行数据交换时。

转换流的理解

作用:转换流是字节与字符间的桥梁!

具体来说:将编码集为utf-8的文件改为编码集为GBK的文件

2.5 字符编码和字符集

编码与解码

计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码

字符编码(Character Encoding) : 就是一套自然语言的字符与二进制数之间的对应规则。

编码表:生活中文字和计算机中二进制的对应规则

乱码的情况:按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。

编码:字符(人能看懂的)--字节(人看不懂的)

解码:字节(人看不懂的)-->字符(人能看懂的)
2.6 处理流之三:对象流

在Java中,对象流是ObjectInputStream和ObjectOutputStream的实现,它们提供了一种在字节流中直接读取和写入对象的机制。对象流能够序列化(将对象转换为字节序列)和反序列化(将字节序列转换为对象)对象,从而方便对象在网络传输、持久化存储等场景中的处理。

  • 注意事项:可序列化的对象必须要实现Serializable接口,并且含有private static final long serialVersionUID

以下是关于对象流的一些要点:

  1. ObjectOutputStream: 用于将对象写入输出流,序列化对象。可以将对象直接写入文件、网络流等。
  2. ObjectInputStream: 用于从输入流中读取对象,反序列化对象。可以从文件、网络流等读取对象。

使用对象流的情况包括:

  • 对象的持久化: 当需要将对象的状态保存到文件或数据库中,或者通过网络传输对象时,可以使用对象流来将对象转换为字节流,并写入到文件或发送到远程位置。
  • 对象的传输: 在分布式系统中,对象流常用于在客户端和服务器之间传输对象。对象可以在网络上传输,而无需手动拆分和组装对象的各个属性。
  • 缓存对象: 在一些应用中,对象流还可以用于缓存对象,将对象写入字节流,然后在需要时从字节流中读取对象。这样可以减少对象的内存占用,特别是在内存有限的环境中。

示例代码:

javaimport java.io.*;

public class ObjectStreamExample {
    public static void main(String[] args) {
        // 序列化对象并写入文件
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"))) {
            MyClass obj = new MyClass("John Doe", 25);
            oos.writeObject(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化对象并读取文件
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"))) {
            MyClass loadedObj = (MyClass) ois.readObject();
            System.out.println("Name: " + loadedObj.getName() + ", Age: " + loadedObj.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class MyClass implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public MyClass(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

在上面的例子中,ObjectOutputStream 用于将一个MyClass对象序列化并写入文件,然后 ObjectInputStream 用于从文件中读取对象并反序列化。

对象类 MyClass 实现了 Serializable 接口,这是Java中标识对象可序列化的接口。

  • 何为对象序列化机制

对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

  • 序列化过程:用一个字节序列可以表示一个对象,该字节序列包含该对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
  • 反序列化过程:该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

网络编程

1. IP地址

IP地址:IP地址(Internet Protocol Address,互联网协议地址)是分配给网络中连接设备(如计算机、路由器等)的数字标识符。它是一种在互联网上唯一标识每个网络设备的方式,以便设备能够相互通信。IP地址是网络通信中的基本元素,它允许数据在网络上正确地定位和传递。

IP地址分类方式:
  • IPv4:它由32位的二进制数字组成,通常被分为4个字节,以点分十进制的形式表示,如 192.168.0.1。IPv4地址的32位长度限制了可用的唯一地址数量,这导致了IPv4地址枯竭的问题。
  • IPv6:它由128位二进制数组成,通常被分为16个字节,以冒号分隔的八组16位十六进制数字表示,如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,按保守方法估算IPv6实际可分配的地址,整个地球的每平方米面积上仍可分配1000多个地址,这样就解决了网络地址资源数量不够的问题。

2. 域名

Internet上的主机有两种方法表示地址:

  • 域名(hostName):www.baidu.com
  • IP地址(hostAddress):153.3.238.110

域名解析:因为IP地址数字不便于记忆,因此出现了域名。域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS,Domain Name System,域名系统)负责将域名转化成IP地址,这样才能和主机建立连接。

3. 端口号

端口号是一种用于标识网络中特定进程或服务的数字标识符。在计算机网络通信中,端口号是在主机上运行的应用程序与网络之间建立连接的一种方式。它与IP地址一起形成了网络通信中的目标地址,确保数据能够准确地被发送到目标应用程序或服务。端口号可以唯一标识设备中的进程(应用程序)。

端口号的范围是从0到65535,其中0到1023是被系统保留的一些特定端口,用于一些知名的服务,如HTTP(80端口)、HTTPS(443端口)、FTP(21端口)等。这些端口被称为"知名端口",通常用于一些标准化的网络服务。

端口号分为两种类型:TCP(Transmission Control Protocol)端口UDP(User Datagram Protocol)端口

  • TCP端口:用于支持TCP协议的应用程序。TCP是一种面向连接的协议,它提供可靠的、有序的数据传输。许多常见的应用程序,如网页浏览、电子邮件等,使用TCP协议进行通信。
  • UDP端口:用于支持UDP协议的应用程序。UDP是一种无连接的协议,它提供了更轻量级的数据传输,但不保证数据的可靠性和有序性,一些需要快速数据传输而可以容忍一些数据丢失的应用程序使用UDP协议。当计算机上的应用程序需要通过网络进行通信时,它会监听一个特定的端口号。在建立连接时,源主机将数

发送到目标主机的特定IP地址和端口号上。目标主机使用端口号来确定将数据交付给哪个应用程序或服务。

4. 网络编程API

4.1 InetAddress类

InetAddress类主要表示IP地址,有两个子类:Inet4Address、Inet6Address。

  • InetAddress 提供了如下几个常用的方法
    • public String getHostAddress() :返回 IP 地址字符串(以文本表现形式)
    • public String getHostName() :获取此 IP 地址的主机名
    • public boolean isReachable(int timeout):测试是否可以达到该地址
4.2 Socket类
  • 网络上具有唯一标识的IP地址和端口号组合在一起构成唯一能识别的标识符套接字(Socket)。
  • 利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。网络通信其实就是Socket间的通信。
  • 通信的两端都要有Socket,是两台机器间通信的端点。
  • Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
  • 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
  • Socket分类:
    • 流套接字(stream socket):使用TCP提供可依赖的字节流服务
      • ServerSocket:此类实现TCP服务器套接字。服务器套接字等待请求通过网络传入。
      • Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
    • 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
      • DatagramSocket:此类表示用来发送和接收UDP数据报包的套接字。

反射

1. 反射(Reflection)的概念

1.1 反射概述

Reflectionary(反射)是被视为动态语言的关键,反射机制允许程序在运行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射。

从内存加载上看反射:

针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class字节码文件。接着,我们使用
java.exe命令对指定的.class文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载(使用类的加载器)
到内存中(存放在方法区)。加载到内存中的.class文件对应的结构即为Class的一个实例。

1.2 反射相关的主要API

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
… …

2. 理解Class类并获取Class实例

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API:

  • java.lang.Class
  • java.lang.reflect.*

所以,Class对象是反射的根源。

2.1 理解Class

2.1.1 理论上

在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
2.1.2 内存结构上

说明:上图中字符串常量池在JDK6中存储在方法区;JDK7及以后,存储在堆空间。

2.2 获取Class类的实例(三种方法)

方法1:要求编译期间已知类型

前提:已知具体的类,通过类的Class属性获取,该方法最为安全可靠,程序性能最高。

例:

Class clazz  = String.class;

方法2:获取对象的运行时类型

前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象

例:

Person per = new Person();
Class clazz = per.getClass();

方法3:获取编译期间未知的类型

前提:必须已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFounException

例:

Class clazz = Class.forName("java.lang.String");

2.3 哪些类型可以有Class对象

所有的Java类型都有Class对象,如:

(1)class:外部类,成员(成员内部类、静态内部类),局部内部类,匿名内部类

(2)interface:接口

(3)[]:数组

(4)enum:枚举

(5)annotation:注解@interface

(6)primitive type:基本数据类型

(7)void

举例:

Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;

int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素类型与维度一样,就是同一个Class
System.out.println(c10 == c11);

2.4 Class类的常用方法

方法名

功能说明

static Class forName(String name)

返回指定类名 name 的 Class 对象

Object newInstance()

调用缺省构造函数,返回该Class对象的一个实例

getName()

返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称

Class getSuperClass()

返回当前Class对象的父类的Class对象

Class [] getInterfaces()

获取当前Class对象的接口

ClassLoader getClassLoader()

返回该类的类加载器

Class getSuperclass()

返回表示此Class所表示的实体的超类的Class

Constructor[] getConstructors()

返回一个包含某些Constructor对象的数组

Field[] getDeclaredFields()

返回Field对象的一个数组

Method getMethod(String name,Class … paramTypes)

返回一个Method对象,此对象的形参类型为paramType

举例:

String str = "test4.Person";
Class clazz = Class.forName(str);

Object obj = clazz.newInstance();

Field field = clazz.getField("name");
field.set(obj, "Peter");
Object name = field.get(obj);
System.out.println(name);

//注:test4.Person是test4包下的Person类

3. 反射的基本应用

3.1 调用指定的属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性的内容的操作。

步骤:

(1)获取运行时的类

方法1:Class clazz = Person.class;

方法2:Class clazz = Class.forName("com.java.practice._reflect.data.Person")

(2) 创建实例对象

(2.1)获取构造器:Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class); //getDeclaredConstructor(Object ... args)调用含参数的所有权限(包括private)构造器 //getConstructor(Object ... args)只能调用public权限的构造器

(2.2)访问权限设置:constructor.setAccessible(true); //如果构造器的权限修饰符不是public,那么需要设置构造器可访问

(2.3)创建对象:Person per = (Person) constructor.newInstance("Tom",23);

(3) 获取属性对象

Filed field = clazz.getDeclaredFiled("属性名");//getConstructor(String fieldName)只能调用public权限的属性

field.setAccessible(true);// //如果属性的权限修饰符不是public,那么需要设置属性可访问

(4) 设置指定对象per上此Filed属性的值

field.set(per,"属性值");

如果操作静态变量,那么实例对象可以省略,用null表示

(5) 取得指定对象per上此Filed的属性值

Object value = field.get(per);

如果操作静态变量,那么实例对象可以省略,用null表示

3.2 调用指定的方法

/*
     * 反射的应用3-2:调用指定的方法
     * */
    //private String showNation(String nation,int age)
    @Test
    public void test7() throws Exception {
        //创建运行时类的对象
        Class<Person> clazz = Person.class;
        Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        Person per = constructor.newInstance("Tom", 24);

        //1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法
        Method showMethod = clazz.getDeclaredMethod("showNation", String.class, int.class);

        //2. setAccessible(true):确保此方法是可访问的
        showMethod.setAccessible(true);

        //3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。
        //invoke()的返回值即为Method对应的方法的返回值
        //特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
        String str= (String) showMethod.invoke(per,"CHN",21);
        System.out.println(str);
    }

    //public static void showInfo()
    @Test
    public void test8() throws Exception{
        //创建运行时类
        Class<Person> clazz = Person.class;

        //1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法
        Method showInfoMethod = clazz.getDeclaredMethod("showInfo");

        //2.setAccessible(true):确保此方法是可访问的
        showInfoMethod.setAccessible(true);

        //3.通过Method实例调用invoke(Object obj,Object ... args),即为对Method对应的方法的调用。
        //invoke()的返回值即为Method对应的方法的返回值
        //特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
        Object showMethodReturn = showInfoMethod.invoke(Person.class);
        System.out.println(showMethodReturn);//null
    }
  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值