黑马程序员-Java基础:IO

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


File类 + 异常机制 + IO流总结

一、异常
1、异常概述
在代码的编写过程中,往往不能够考虑到程序在运行时可能出现的所有状况,那么为了处理程序在运行期间可能出现的问题,就需要一个错误处理机制使得程序在出现意料之外的运行状况时可以进行恰当的处理。 
Java中提供了异常机制来帮助编程人员处理程序运行中可能出现的错误

2、异常的分类

分类:
问题: Throwable
严重问题:       Error
非严重问题: Exception
      编译期异常: 非 RuntimeException
      运行期异常: RuntimeException

3、异常处理

JVM对异常的处理方案:在控制台打印出具体的异常信息,并结束程序

处理方法:

a: 使用try...catch语句 二种:

第一种:

try {

可能出现问题的代码 ;

}catch(异常名称 变量名) {

针对问题的处理方案 ;

}

注意事项:

(1): try 中的代码越少越好

(2): catch中一定要做处理,哪怕是一条输出语句

第二种:

try {

可能出现问题的代码 ;

}catch(异常名称1 变量名1) {

针对问题的处理方案 ;

}catch(异常名称2 变量名2) {

针对问题的处理方案 ;

}
....

注意事项:

a: 如果可以明确异常,那么就不要使用大的进行接收

b: 如果多个catch中的异常是平级的关系,那么没有先后顺序的限制

c: 如果多个catch中存在子父类的继承关系,那么父类一定要放在最后

JDK1.7中对第二种情况的变换:

格式:

try {

可能出现问题的代码 ;

}catch(异常名称1 | 异常名称2 | ... 变量名) {

针对问题的处理方案 ;

}

注意事项: catch中不能存在子父类的继承关系

弊端:对多个异常的处理方案是一样的

4、编译期异常和运行期异常的区别

编译期异常: 必须对其进行处理,如果不处理程序就无法正常运行

运行期异常: 可以对其处理,也可以不处理

Throwable 中的几个方法:

a:getMessage(): 获取异常信息,返回字符串。

b:toString(): 获取异常类名和异常信息,返回字符串。

c:printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

5、throw和throws 的区别

throw:用在方法定义中 ,用来抛出异常 ,后面跟一个异常对象 ,而这个异常对象可以是运行期异常对象,也可以是编译期异常对象

throws:用在方法声明上 ,用来声明该方法可能会出现的问题 ,后面跟异常名称 ,可以是多个,但是多个异常之间需要使用","隔开

6、finally的特点

被finally控制的语句必须执行,前提: JVM不能退出

作用:释放资源

7、自定义异常

自定义异常:

定义一个类,然后让这个类继承 Exception 或者 RuntimeException 

如果继承自 Exception , 那么说明我们定义的这个类属于编译期异常

如果继承自 RuntimeException , 那么说明我们定义的这个异常类属于运行期异常

异常注意事项(针对编译期异常)

a:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)

b:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常

c:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws


二、File 类

File 类 : 是文件或者文件夹的抽象表示形式

构造方法:
public File(String fileName) : 那一个文件或者文件夹路径封装成一个File对象
public File (String parent , String child ) : 通过父文件路径和子文件或者文件夹路径构建一个File对象
public File (File parent , String child ) : 通过父文件和子文件或者文件夹路径构建一个File对象

成员方法:
public boolean createNewFile() : 创建一个文件
public boolean mkdir() : 创建一个文件夹,但是只能创建单级文件夹
public boolean mkdirs(): 可以用来创建多级文件夹

File类的删除功能:
public boolean delete():                        删除文件或者文件夹

File类的重命名功能:

public boolean renameTo(File dest):              把文件重命名为指定的文件路径

注意事项:

如果路径名相同,就是改名。

如果路径名不同,就是改名并剪切。

File类的判断功能:

public boolean isDirectory():         判断是否是目录
public boolean isFile(): 判断是否是文件
public boolean exists(): 判断是否存在
public boolean canRead(): 判断是否可读
public boolean canWrite(): 判断是否可写
public boolean isHidden(): 判断是否隐藏

File类的获取功能:
public String getAbsolutePath(): 获取绝对路径
public String getPath(): 获取相对路径
public String getName(): 获取名称
public long length(): 获取长度。字节数
public long lastModified(): 获取最后一次的修改时间,毫秒值
public String[] list(): 获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles(): 获取指定目录下的所有文件或者文件夹的File数组 


三、IO流

1、IO流概述

I: 输入  O: 输出 ,说这个输入和输出问题是相对于内存而言的.

2、分类:

a: 按照流向进行划分
  输入流
输出流

b: 按照操作数据的类型
字节流
字符流

字节流
字节输入流 InputStream   读
字节输出流 OutputStream   写
字符流
字符输入流 Reader   读
字符输出流 Writer   写

3、操作步骤:

IO流的操作步骤:

a: 创建io流对象

b: 调用方法进行读或者写

c: 释放资源

4、字节流常用方法

FileOutputStream 

构造方法:
public FileOutputStream(File file) ;
public FileOutputStream(String name) ;
public FileOutputStream(String name , boolean append) ; // 实现追加写
public FileOutputStream(File file , boolean append) ; // 实现追加写

写的成员方法:
public void write(int by) : 一次写一个字节
public void write(byte[] bytes) : 一次写一个字节数组
public void write(byte[] bytes , int off , int len ): 一次写一个字节数组中的一部分

在windows操作系统中的换行符是: "\r\n"

在linux操作系统中的换行是: "\n"

在mac操作系统中的换行是:   "\r"

FileInputStream

构造方法:
public FileInputStream(File file) ;
public FileInputStream(String name) ;

读取数据的方法:
public int read(): 一次读取一个字节
public int read(byte[] bytes) : 一次读取一个字节数组 , 返回值表示的意思是: 读取到的有效的字节个数

例3-1:字节流复制文件

public class CoypFileDemo2 {

	public static void main(String[] args) throws IOException {
		
		// 创建字节输入流和字节输出流对象
		FileInputStream fis = new FileInputStream("C:\\mv.jpg") ;
		FileOutputStream fos = new FileOutputStream("E:\\mv.jpg") ;
		
		//  一次读取一个字节数组复制文件
		byte[] bytes = new byte[1024] ;
		int len = 0 ;
		while((len = fis.read(bytes)) != -1){
			fos.write(bytes, 0, len) ;
		}
		
		// 释放资源
		fos.close() ;
		fis.close(); 
		
	}
}
5、字节高效流

高效的字节输入流和高效的字节输出流:

高效的字节输出流:BufferedOutputStream

构造方法:public BufferedOutputStream(OutputStream os) ;

高效的字节输入流:BufferedInputStream

构造方法:public BufferedInputStream(InputStream is) ;
例:复制文件总结

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 复制mp3文件
 */
public class CopyFileTest {
	
	public static void main(String[] args) throws IOException {
		
		
		
		// 复制文件
		copyFile_4() ;
	}
	
	/**
	 * 使用高效的字节输入流和高效的字节输出流一次读取一个字节数组复制文件
	 * 81
	 */
	private static void copyFile_4() throws IOException{
		
		// 创建对象
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\b.mp3")) ;
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\m.mp3")) ;
		
		// 一次读取一个字节数组复制文件
		byte[] bytes = new byte[1024] ;
		int len = 0 ;
		
		while((len = bis.read(bytes)) != -1){
			bos.write(bytes , 0 ,len) ;
		}
		
		// 释放资源
		bos.close() ;
		bis.close() ;
	}

	/**
	 * 使用高效的字节输入流和高效的字节输出流一次读取一个字节复制文件
	 * 495
	 */
	private static void copyFile_3() throws IOException  {
		
		// 创建对象
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\b.mp3")) ;
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\m.mp3")) ;
		
		// 一次读取一个字节复制文件
		int by = 0 ;
		while((by = bis.read()) != -1){
			bos.write(by) ;
		}
		
		// 释放资源
		bos.close() ;
		bis.close() ;
		
	}

	/**
	 * 使用基本的流一次读取一个字节数组复制文件
	 * 128
	 */
	private static void copyFile_2() throws IOException  {
		
		// 创建字节输出流和字节输入流对象
		FileInputStream fis = new FileInputStream("C:\\b.mp3") ;
		FileOutputStream fos = new FileOutputStream("E:\\m.mp3") ;
	
		// 一次读取一个字节数组复制文件
		byte[] bytes = new byte[1024] ;
		int len = 0 ;
		while((len = fis.read(bytes)) != -1){
			fos.write(bytes, 0, len) ;
		}
		
		// 释放资源
		fos.close() ;
		fis.close() ;

	}

	/**
	 * 使用基本的流一次读取一个字节复制文件
	 * @throws IOException 
	 * 78064
	 */
	private static void copyFile_1() throws IOException {
		
		// 创建字节输出流和字节输入流对象
		FileInputStream fis = new FileInputStream("C:\\b.mp3") ;
		FileOutputStream fos = new FileOutputStream("E:\\m.mp3") ;
		
		// 一次读取一个字节复制文件
		int by = 0 ;
		while((by = fis.read()) != -1){
			fos.write(by) ;
		}
		
		// 释放资源
		fos.close() ;
		fis.close() ;
		
	}

}

6、转换流

(1)字符流的由来

字符流的由来:为了方便的操作中文.java就提供了字符流

字符流 = 字节流 + 编码表

字符串中的编解码问题:

编码:就是把字符串转换成字节数组

public byte[] getBytes():使用平台默认的字符集进行编码
public byte[] getBytes(String charsetName):可以使用指定的字符集进行编码

解码:就是把字节数组转换成字符串
public String(byte[] bytes):使用平台默认的字符集进行解码
public String(byte[] bytes , String charsetName):可以使用指定的字符集进行解码

(2)转换流构造方法

转换流:

转换输入流(InputStreamReader)

构造方法:
public InputStreamReader(InputStream in);
public InputStreamReader(InputStream in , String charsetName);

成员方法:
public int read():一次读取一个字符
public int read(char[] chs):一次读取一个字符数组,返回值表示的意思是读取到的有效的字符个数

转换输出流(OutputStreamWriter)

构造方法:
public OutputStreamWriter(OutputStream out);
public OutputStreamWriter(OutputStream out , String charsetName) ;

成员方法:
public void write(int ch):一次写一个字符
public void write(char[] chs): 一次写一个字符数组
public void write(char[] chs , int off , int len):  一次写一个字符数组中的一部分
public void write(String line , int off , int len): 一次写一个字符串中的一部分

例:转换流复制文件

public class CopyFileDemo {
	
	public static void main(String[] args) throws IOException {
		
		copyFile_2() ;
		
	}
	
	/**
	 * 使用字符流一次读取一个字符数组复制文本文件
	 */
	private static void copyFile_2() throws IOException {
		
		// 创建字符输入流和字符输出流对象
		InputStreamReader isr = new InputStreamReader(new FileInputStream("InputStreamReaderDemo.java")) ;
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("copyFile2.java")) ;
		
		// 一次读取一个字符数组复制文本文件
		char[] chs = new char[1024] ;
		int len = 0 ;					// 记录读取到的有效的字符的个数
		while((len = isr.read(chs)) != -1){
			osw.write(chs, 0, len) ;
		}
		
		// 释放资源
		osw.close() ;
		isr.close() ;
		
	}

	/**
	 * 使用字符流一次读取一个字符复制文本文件
	 * @throws IOException 
	 */
	private static void copyFile_1() throws IOException {
		
		// 创建字符输入流和字符输出流对象
		File file = new File("InputStreamReaderDemo.java") ;
		File destFile = new File("copyFile.java") ;
		
		InputStreamReader isr = new InputStreamReader(new FileInputStream(file)) ;
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile)) ;
		
		// 一次读取一个字符复制文件
		int ch = 0 ;
		while((ch = isr.read()) != -1){
			osw.write(ch) ;
		}
		
		// 释放资源
		osw.close() ;
		isr.close() ;
		
	}

}
7、便捷类

(1)便捷类由来:由于转换流的名称太长使用起来不是很方便,并且在一般情况下我们也不指定编码表,所有java就提供了针对转换

流的便捷类。

转换流 便捷类

InputStreamReader FileReader

OutputStreamWriter FileWriter

(2)便捷类复制文件

private static void copyFile_2() throws IOException {
		// 创建便捷类的输入流和输出流对象
		FileReader fr = new FileReader("InputStreamReaderDemo.java") ;
		FileWriter fw = new FileWriter("fileWriter2.java") ;
		
		// 一次读取一个字符数组复制文件
		char[] chs = new char[1024] ;
		int len = 0 ;
		while((len = fr.read(chs)) != -1){
			fw.write(chs, 0, len) ;
		} 
		
		// 释放资源
		fw.close() ;
		fr.close() ;
	}

	/**
	 * 使用便捷类一次读取一个字符复制文本文件
	 * @throws IOException 
	 */
	private static void copyFile_1() throws IOException {
		
		// 创建便捷类的输入流和输出流对象
		/**
		 * public FileReader(String fileName);
		 * public FileReader(File file);
		 * 
		 * public FileWriter(String fileName) ;
		 * public FileWriter(File file);
		 */
		FileReader fr = new FileReader("InputStreamReaderDemo.java") ;
		FileWriter fw = new FileWriter("fileWriter.java") ;
		
		// 一次读取一个字符复制文本文件
		int ch = 0 ;
		while((ch = fr.read()) != -1){
			fw.write(ch) ;
		}
		
		// 释放资源
		fw.close() ;
		fr.close();
		
	}

}
8、高效字符流

高效的字符输入流(BufferedReader)

构造方法: public BufferedReader(Reader in) ;

特有的方法: public String readLine();一次读取一行

高效的字符输出流(BufferedWriter)

构造方法: public BufferedWriter(Writer w) ;

特有的方法: public void newLine() ; 写入换行符 

例子:高效字符流复制文件

public class CopyFileDemo {
	
	public static void main(String[] args) throws IOException {
		
		// 创建对象
		BufferedReader br = new BufferedReader(new FileReader("InputStreamReaderDemo.java")) ;
		BufferedWriter bw = new BufferedWriter(new FileWriter("buf_copy_file.java")) ;
		
		// 一次读取一行复制文件
		String line = null ;		// 记录读取到一行的数据
		while((line = br.readLine()) != null){
			bw.write(line) ;
			bw.newLine();
			bw.flush() ;
		}
		
		// 释放资源
		bw.close() ;
		br.close() ;
		
	}

}

三、IO特殊流

1、数据输入输出流

特点:可以操作基本的数据类型

数据输入流: DataInputStream

数据输出流: DataOutputStream

例:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 数据的输入和输出流
 * 数据输入流:		DataInputStream
 * 数据输出流:		DataOutputStream
 * 
 * 特点:
 * 		可以将基本的java 的数据类型写入到文件中
 */
public class DataStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
//		write() ;
		read() ;
		
	}
	
	/**
	 * 使用数据的输入流进行读取数据
	 */
	private static void read() throws IOException {
		
		// 创建对象
		/**
		 * public DataInputStream(InputStream in)
		 */
		DataInputStream dis = new DataInputStream(new FileInputStream("dataStream.txt")) ;
		
		// 读取数据
		int i = dis.readInt() ;
		char a = dis.readChar() ;
		String r = dis.readUTF() ;
		
		// 释放资源
		dis.close() ;
		
		// 输出
		System.out.println(i);
		System.out.println(a);
		System.out.println(r);
	}

	/**
	 * 使用数据输出流写入基本的数据类型
	 * @throws IOException 
	 */
	private static void write() throws IOException {
		
		/**
		 * public DataOutputStream(OutputStream out)
		 */
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("dataStream.txt")) ;
		
		// 写数据
		/**
		 * 写入基本的数据类型的数据
		 */
		dos.writeInt(100) ;
		dos.writeChar(97) ;
		dos.writeUTF("中国") ;
		
		// 释放资源
		dos.close() ;
		
	}

}

2、内存操作流

a:操作字节数组
ByteArrayOutputStream
ByteArrayInputStream

b:操作字符数组
CharArrayWriter
CharArrayReader

c:操作字符串
StringWriter
StringReader

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 内存操作流:
 * 		a:操作字节数组
			ByteArrayOutputStream
			ByteArrayInputStream
		b:操作字符数组
			CharArrayWriter
			CharArrayReader
		c:操作字符串
			StringWriter
			StringReader
 */
public class ByteArrayStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
		/**
		 * public ByteArrayOutputStream()创建一个新的 byte 数组输出流。
		 */
		ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
		
		// 写数据
		baos.write("nihao".getBytes()) ;
		
		// public byte[] toByteArray()创建一个新分配的 byte 数组。
		byte[] bytes = baos.toByteArray() ;
		
		// 我们把这个字节数组转换成字符串
//		System.out.println(new String(bytes));
		
		// public String toString(): 获取数据
//		String data = baos.toString() ;
//		System.out.println(data);
		
		System.out.println("--------------------------------------");
		
		/**
		 * public ByteArrayInputStream(byte[] buf)
		 */
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes) ;
		
		// 读取数据
		int by = 0 ;
		while((by = bais.read())  != -1){
			System.out.print((char)by);
		}
		
	}

}

3、打印流

字节打印流: PrintStream
字符打印流: PrintWriter

特点:
a: 只能操作目的地,不能操作数据源
b: 可以操作任意的数据类型
c: 如果启动了自动刷新,那么可以完成自动刷新 (println , printf , format)
d: 可以直接的对文件进行操作

public class CopyFileDemo {

	public static void main(String[] args) throws IOException {
		
		// 创建高效的字符输入流对象
		BufferedReader br = new BufferedReader(new FileReader("ByteArrayStreamDemo.java")) ;
		
		// 创建打印流对象
		PrintWriter pw = new PrintWriter(new FileWriter("copyFileWithPrintWriter.txt") , true) ;
		
		// 读取数据进行赋值文件
		// 一次读取一个文本行
		String line = null ;
		while((line = br.readLine()) != null) {
			pw.println(line) ;			// 完成了写数据和换行以及刷新
		}
		
		
		// 释放资源
		pw.close() ;
		br.close() ;
		
	}
	
}

4、标准输入输出流

在System类中存在两个静态的成员变量一个是in , 一个是out
public static final InputStream in ; 标准输入流 对应的设备是键盘
public static final PrintStream out ; 标准输出流 对应的设备是显示器

键盘录入数据的三种方式:
a: 使用Scanner
   Scanner sc = new Scanner(System.in) ;
b: 使用main方法
   java HelloWorld hello world java 
c: 使用BufferedReader进行模拟
   BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
对标准的输入流进行包装
   BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)) ;

5、序列化流和反序列化流

序列化: 就是把对象存储到文本文件中

反序列化:就是把文本文件中的对象还原成一个对象

如果我们想让一个对象可以被序列化, 那么该对象对应的类就需要实现Serializable接口.
序列化流:ObjectOutputStream
     public final void writeObject(Object obj);
反序列化流: ObjectInputStream
     public final Object readObject() ;

public class ObjectOutputStreamDemo {
	
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		
//		writeObject() ;
		readObject() ;
		
	}
	
	/**
	 * 使用反序列化流进行读取数据
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 * @throws FileNotFoundException 
	 */
	private static void readObject() throws IOException, ClassNotFoundException {
		
		/**
		 * public ObjectInputStream(InputStream in): 
		 */
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("objects.txt")) ;
		
		/**
		 * public final Object readObject(): 读取对象
		 */
		Object obj = ois.readObject() ;
		
		// 向下转型
//		Student s = (Student)obj ;
		
		// 输出
		System.out.println(obj);
//		System.out.println(s.getName() + "----" + s.getAge());
		
		// 释放资源
		ois.close() ;
	}

	/**
	 * 使用序列化流把对象存储到文本文件中
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	private static void writeObject() throws  IOException {
		
		/**
		 * public ObjectOutputStream(OutputStream out)
		 */
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("objects.txt")) ;
		
		// 创建一个对象
		Student s = new Student("小菲菲" , 23) ;
		
		// 写对象
		oos.writeObject(s) ;
		
		// 释放资源
		oos.close() ;
	}

}
6、Properties

Properties 类表示了一个持久的属性集。
Properties 可保存在流中或从流中加载。
属性列表中每个键及其对应值都是一个字符串。

public Object setProperty(String key,String value):添加元素
public String getProperty(String key): 根据键获取值
public Set<String> stringPropertyNames():获取所有的键对应的Set集合
public void load(Reader reader):可以把一个文本文件中的数据加载到一个集合中
public void store(Writer writer,String comments) : 可以把一个集合中的数据存储到文本文件中

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

/**
 * 我有一个文本文件,我知道数据是键值对形式的,但是不知道内容是什么。
	  请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其值为”100”
	  
	 分析:
	 	a: 创建一个Propertes对象
	 	b: 调用load方法加载文本文件中的数据到集合中
	 	c: 获取所有键对应的Set集合
	 	d: 判断这个Set集合中是否存在"lisi"这个键,如果存在就重新设置其值为100
	 	e: 把集合中的数据存储到文本文件中调用store方法
 */
public class PropertiesTest {
	
	public static void main(String[] args) throws IOException {
		
		// 创建一个Propertes对象
		Properties prop = new Properties() ;
		
		// 调用load方法加载文本文件中的数据到集合中
		prop.load(new FileReader("info.txt")) ;
		
		// 获取所有键对应的Set集合
		Set<String> names = prop.stringPropertyNames() ;
		
		// 判断这个Set集合中是否存在"lisi"这个键,如果存在就重新设置其值为100
		if(names.contains("lisi")){
			
			// 把值设置成100
			prop.setProperty("lisi", "100") ;
			
		}
		
		// 把集合中的数据存储到文本文件中调用store方法
		prop.store(new FileWriter("info.txt"), null) ;
		
	}

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值