IO流详解

IO流

IO流分类

  • 以内存为参照物
    - 往内存中去,输入流,读
    - 从内存中出,输出流,写
  • 读取方式不同
    - 按照字节读取数据,一次读取一个字节byte,相当于8个二进制位。这类流的读取方式是万能的,什么类型的文件都可以读取:文本文件、图片、声音文件、视频
    - 按照字符读取数据,一次读取一个字符,这种流是为了方便读取普通文本而存在的。这种流不能读取图片、声音、视频等文件,只能读取纯文本文件【word文档不可以,因为word有格式】

例:txt文件的内容:o中国o
【在Windows系统中:字母占一个字节、汉字占两个字节】 按照字节流读取:第一次:读取“o”;第二次:读取“中”字符的一半;第三次:读取“中”字符的另外一半。 按照字符流读取:第一次:读取“o”;第二次读取字符“中”。

  • 四大家族:以“Stream”结尾的都是字节流;以“reader”“writer”结尾的都是字符流

  • 所有的流都实现了Java.io.Closeable接口,都有close()方法。流毕竟是一个管道,联通内存与硬盘,用完之后一定要关闭。

    • InputStream 字节输入流
    • OutputStream 字节输出流
    • Reader 字符输入流
    • Writer 字符输出流
  • 所有的输出流都是可刷新的,都有flush()方法。实现了java.io.Flushable接口。输出流在最终输出之后,一定记得flush()刷新,这个刷新表示管道当中剩余的数据强行输出完(清空管道),刷新的作用就是清空管道。
    在这里插入图片描述在这里插入图片描述

FileInputStream

输入流框架

在这里插入图片描述

package Advance.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * java.io.FileInputStream:
 * 		文件字节输入流,万能
 * 		构建输入流框架
 * */
public class FileInputStreamTest01 {
	public static void main(String[] args)  {
		//创建字节输入流对象
			//文件路径:E:\Javatest 里面有一个temp.txt文档
			//编译器会自动把 \ 变成 \\ ,因为java中 \ 表示转义
		
			//FileInputStream fis = new FileInputStream("E:\\Javatest\\temp.txt");  //处理异常
			
			/*  //处理异常
			try {
				FileInputStream fis = new FileInputStream("E:\\Javatest\\temp.txt");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}*/
			
			//关闭流:无论程序执行如何,最后程序都需要关闭——finally
			//将创建的流放到try,catch语句块外面
			FileInputStream fis  = null;
			try { 
				 fis = new FileInputStream("E:\\Javatest\\temp.txt");
				 
				 //读取信息
				 int readData = fis.read();	  //该方法是读取到的字节  读取到的字节本身 a的ASCII码
				 System.out.println(readData);		//读取该文档的第一个字节
				 
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {	//处理读取信息时的异常
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				//finally语句块可以确保流一定关闭
				if(fis!=null) {   //关闭流的前提是:流不为空  :避免空指针异常
					try {
						fis.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}
		}
	}
}

相对路径

  • 相对路径是从当前所在位置开始作为起点开始找。
  • 编译器的默认当前路径:工程Project是当前的根
    在这里插入图片描述在这里插入图片描述

读取过程详解

在这里插入图片描述

package Advance.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamTest03 {
	public static void main(String[] args) {
		//创建字节流
		//处理异常
		//关闭流
		FileInputStream fis =null;
		try {
			//设置相对路径 :相对路径是从当前所在位置开始作为起点开始找
			//编译器的默认当前路径:工程Project是当前的根
			
			 fis = new FileInputStream("src/source/temp.txt");
			 
			 //读取信息: 采用byte数组 一次读取多个字节 最多读取数组.length个字节
			/*
			 * byte[] bytes = new byte[4];		//一次最多读取4个字节
			 *  int readCount = fis.read(bytes);   //读取到的字节数量,不是字节本身
			 System.out.println("第一次读取到的字节数——"+readCount);		//4
			 //此时内存的byte数组里面存有数据,转为String类型
			 //String(bytes);
			 System.out.println(new String(bytes));		//abcd
			 
			 readCount = fis.read(bytes);
			 System.out.println("第二次读取到的字节数——"+readCount);		//2
			 System.out.println(new String(bytes));		//efcd
			 //程序期望输出整个文件内容
			//byte数组转String,从下标0开始,到下标readCount结束
			 System.out.println(new String(bytes,0,readCount));		
			 
			 readCount = fis.read(bytes);
			 System.out.println("第三次读取到的字节数——"+readCount);		//-1
			 System.out.println(new String(bytes,0,readCount));		//没有内容
			 */
			 
			 //读取信息: 采用byte数组 一次读取多个字节 最多读取数组.length个字节
			 byte[] bytes = new byte[4];		//一次最多读取4个字节
			 int readCount =0;
			 while((readCount =fis.read(bytes))!=-1) {
				 System.out.print(new String(bytes,0,readCount));
			 }
			 
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(fis !=null) {		//流不为空,避免空指针异常
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
}

在这里插入图片描述

available()

  • int available(); 返回流当中剩余没有读到的字节数量
  • 当数据没有读取的时候,调用该方法,获得的是总字节数量,那么在建立byte数组时,可以设置数组长度来匹配文件字节数。但是这种方法不适合大的文件,因为byte数组不能太大。
package Advance.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
 * FileInputStream其他的常用方法
 * 		int available();   返回流当中剩余没有读到的字节数量
 * 		long skip(long n); 跳过几个字节不读
 * 
 * */
public class FileInputStreamTest04 {
	public static void main(String[] args) {
		//创建流
		//处理异常
		//关闭流
		FileInputStream fis =null;
		try {
			fis = new FileInputStream("src/source/temp.txt");
			
			System.out.println("总字节数——"+fis.available());
			//读取信息 
			//读取一个字节
			int readByte = fis.read();
			//剩余多少字节
			System.out.println("剩下字节数——"+fis.available());
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(fis!=null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

skip(long n)

  • long skip(long n); 跳过几个字节不读

FileOutputStream

  • 文件字节输出流,负责写数据

输入流框架

在这里插入图片描述

  • 在文件中写入字符串时,使用到字符串转byte方法:getBytes();
    在这里插入图片描述
  • 在java编译器相对路径写入信息时,如果指定的文件不存在,系统会先新建一个文件,再进行写入。当写在根目录下时,发现目录栏没有新建该文件,此时刷新一下就可以。
    在这里插入图片描述

FileReader

  • 文件字符输入流:只能读取普通文本
  • 读取文本时,比较方便、快捷
  • FileReader使用的是char数组
package Advance.io;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest01 {
	public static void main(String[] args) {
		//创建字符流
		FileReader fr = null;
			try {
				fr= new FileReader("src/source/tempChar.txt");
				//读取信息
				char[] c = new char[4];	//一次读取4个字符
				int readCount =0;
				while((readCount=fr.read(c))!=-1) {
					//下面两行代码,输出结果一致。笔者没有弄懂
					//按理来说,如果最后一个数组存入的数据不满数组的长度,那么readCount 到之后的数组
					//之内存储的应该是上一次数组存储的元素
					//那么,输出c时,上一次的数组元素依旧会输出;但是输出从0到readcount的长度的数组则不会
					//但是这两行代码输出结果一样
					System.out.print(c);
					//System.out.print(new String(c,0,readCount));
				}
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	finally {
				//关闭流
				if(fr !=null) {
					try {
						fr.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
	}
}

FileWriter

  • 字符输出流
    - 在FileOutputStream中,写信息时,需要进行String转换为byte类型,再将byte数组写进去。而在FileWriter中的writer方法可以直接接收字符串,方便程序的写入
    在这里插入图片描述

文件复制

  • 文件复制原理:想让文件从D盘移到C盘,需要利用内存作中介,内存一边从D盘读取,一边从C盘写出。
    在这里插入图片描述

FileInputStream 、FileOutputStream

在这里插入图片描述

package Advance.io;

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

//使用FileInputStream和FlieOutputStream完成文件的拷贝
	//拷贝的过程是一边读,一边写
public class Copy01 {
	public static void main(String[] args) {
		//创建流
		FileInputStream fis = null;
		FileOutputStream fos =null;
		
		//处理异常
		try {
			fis = new FileInputStream("H:\\temp.txt");
			fos= new FileOutputStream("E:\\temp.txt");
			
			//核心:边读边写
			byte[] bytes =new byte[1024*1024];	//1mb  一次最多拷贝1MB
			int readCount = 0;
			while((readCount=fis.read(bytes))!=-1) {
				fos.write(bytes,0,readCount);
			}
			
			//刷新
			fos.flush();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			//分开处理异常
			//一起处理,当一个异常,会影响另外一个关闭
			if(fis!=null) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(fos!=null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
	}
}

FileReader 、FileWriter

package Advance.io;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 使用FileReader、FileWriter进行拷贝
 * */
public class Copy02 {
	public static void main(String[] args) {
		//建立
		FileReader fr = null;
		FileWriter fw = null;
		
		try {
			fr =new FileReader("H:\\tempChar.txt");
			fw= new FileWriter("E:\\tempChar.txt");
			
			//复制
			int readCount =0;
			char[] c = new char[1024*512];		//char是两个字节 1MB
			while((readCount =fr.read(c))!=-1 ){
				fw.write(c,0,readCount);
			}
			//刷新
			fw.flush();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(fr!=null) {
				try {
					fr.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(fw!=null) {
				try {
					fw.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}	
	}
}

缓冲流

BufferedReader

  • 带有缓冲区的字符输入流
  • 使用这个流的时候不需要自定义char数组、byte数组。自带缓冲
  • 由于Buffered构造方法需要传入一个reader类型的参数,所以在创建Buffered时候,需要创建一个reader类型的参数,传入是参数对应的流属于结点流。当需要Buffered处理非Reader类型的数据时,需要采用格式转换,转换为reader类型的数据,再传入Buffered。
  • BufferReader优点:
    • 读取一行文字:readLine();
      在这里插入图片描述在这里插入图片描述
package Advance.io;

import java.io.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStreamReader;

public class BufferedReaderTest01 {
	public static void main(String[] args) throws Exception {
		
		FileReader reader =new FileReader("src/Advance/io/Copy02.java");
		//当一个流的构造方法需要一个流,那么传入的这个流称为节点流
		//外部负责包装的流:包装流、处理流
		//FileReader:节点流   ; BufferedReader :处理流。
		BufferedReader br = new BufferedReader(reader);
		
		//读取信息
		//使用readLine 读取一行信息
		String s =null;
		while((s=br.readLine())!=null) {
			System.out.println(s);
		}
		
		
		//关闭流
		//对应包装流来说,只需要关闭最外层的流就可以,里面的结点流会自动关闭
		br.close();
	
		System.out.println("===============================");
		//BUfferReader需要传入一个Reader类型的参数
		//当需要传入的数据属于字节流时,需要使用转换
		FileInputStream fis = new FileInputStream("src/Advance/io/Copy01.java");
		//转换类型
		InputStreamReader isr= new InputStreamReader(fis);
		BufferedReader bfr = new BufferedReader(isr);
		
		//读取信息......
		
		bfr.close();
	}
}

数据专属流

DataOutputStream

  • 这个流可以将数据连同数据类型一并写入文件。该文件不是普通文档,使用记事本打不开。
    在这里插入图片描述

DataInputDtream

  • DataOutputStream写入的文件,只能使用DataInputDtream读取,并且读取的时候需要提前知道写入的顺序。读的顺序需要和写的顺序一致,才能正常的取出数据。加密!!
  • 在这里插入图片描述

标准输出流

在这里插入图片描述

日志框架log 输出方向

package Advance.io;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamTest01 {
	public static void main(String[] args) throws Exception {
		PrintStream ps =System.out;
		ps.print("hello world!");
		
		//标准输出流不需要手动关闭
		
		/**
		 * System类使用过的方法
		 * System.gc();		运行垃圾回收器。
		 *	System.currentTimeMillis()		 返回以毫秒为单位的当前时间。
		 *	System.exit(status);		终止当前正在运行的 Java 虚拟机。
		 *	System.arraycopy(src, srcPos, dest, destPos, length);	 从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。
		 * 
		 * */
		
		//标准输出流更改输出方向
		//标准输出流不再指向控制台,指向log文件
		PrintStream printStream = new PrintStream(new FileOutputStream("log"));
		//修改输出方向,输出方向修改为log文件
		System.setOut(printStream);			//重新分配“标准”输出流。
		System.out.println("hello world");
		System.out.println("hello java");
		
	}
}

File

  • File :文件和目录路径名的抽象表达形式
  • 一个File可能对应的是目录,也可能对应的是文件
  • File类和四大家族没有关系,所以File类不能完成文件的读和写
  • File常用方法:
    • File(String pathname) 构造方法: 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
    • boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
    • String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
    • String getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
    • File getParentFile() 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。
    • String getName() 返回由此抽象路径名表示的文件或目录的名称。
    • File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
import java.io.File;

public class FileTest01 {
	public static void main(String[] args) throws Exception{
		//创建一个File对象
		File f1 = new File("E:\\Javatest\\file");
		
		//判断指定file是否存在
		System.out.println(f1.exists());
		
		//如果指定file不存在,
		if(!f1.exists()) {
				//以文件的形式创建出来
			//f1.createNewFile();
				//以目录形式创建出来
			f1.mkdir();
		}
		
		File f2 = new File("h:\\file");
		//获取文件的父路径
		String parentPath = f2.getParent();
		System.out.println(parentPath);
		
		//获取绝对路径
		System.out.println("获取的绝对路径——"+f2.getParentFile().getAbsolutePath());
		//获得文件名
		System.out.println("文件名"+f1.getName());
		
		//判断file是否是一个目录
		System.out.println(f1.isDirectory());
		
		//判断file是否是一个文件
		System.out.println(f1.isFile());
		
		//获得文件最后一次修改时间
		long haomiao = f1.lastModified();		//毫秒是从1970年到现在的总时间
			//转换日期
		Date time = new Date(haomiao);
			//日期格式话
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
		String strtime = sdf.format(time);
		System.out.println(strtime);
		
		//获取文件大小
		System.out.println(f1.length());
		
		//获取当前目录下的所有子文件
		File[] files = f1.listFiles();
		for(File f :files) {
			System.out.println(f);
		}
	}
}

目录拷贝

在这里插入图片描述

package homework;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 将E:\Javatest文件夹的东西拷贝到H盘
 * 		FileInputStream 
 * 		FileOutputDtream
 *		File
 * */

public class FileCopy {
	public static void main(String[] args) {
		//拷贝源
		File srcFile = new File("E:\\Javatest\\file");
		//拷贝目标
		File desFile = new File("H:\\");	
		//调用方法拷贝
		copyDir(srcFile,desFile);
	}
	
	
	/**
	 * 拷贝目录方法
	 * @param srcFile :拷贝源
	 * @param desFile	:拷贝目标
	 */
	private static void copyDir(File srcFile, File desFile) {
			//递归停止条件:如果是文件的话,递归结束
		if(srcFile.isFile()) {
			//确定是文件,进行拷贝:但是拷贝时是递归到了最后一层,将文件拷贝到其他盘时
			//也需要建立对应的路径
			//建立完路径之后,相当于在目的准备好了房子 ,下一步就是搬文件
						//FileInputStream FileOutputStream
						FileInputStream fs =null;
						FileOutputStream fos =null;
						try {
							fs =new FileInputStream(srcFile);
							//System.out.println("========"+desFile.getAbsolutePath()+srcFile.getAbsolutePath().substring(12));
							fos= new FileOutputStream(desFile.getAbsolutePath()+srcFile.getAbsolutePath().substring(12));
							
							//拷贝
							int readCount =0;
							byte[] bytes = new byte[1024*1024];
							while((readCount=fs.read(bytes))!=-1) {
								fos.write(bytes,0,readCount);
							}
							
							//刷新
							fos.flush();
						} catch (FileNotFoundException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}finally {
							if(fs!=null) {
								try {
									fs.close();
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
							if(fos!=null) {
								try {
									fos.close();
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
						}
						
			return;
		}
		//获取源下面的子目录
		File[] srcFiles = srcFile.listFiles();
		//System.out.println(srcFiles.length);
		
		
		for(File f : srcFiles) {	//取出源文件夹中的子文件			
								//如果File是文件夹的话,在目标目录新建对应目录
								if(f.isDirectory()) {
									//System.out.println("获取文件的绝对路径——"+f.getAbsoluteFile());
									//	E:\Javatest\file\a		源目录
									//	H:\file							目标目录
									//实际上,拷贝就是将目标文件夹放到目标地,那么拷贝完成后
									//新的拷贝后的路径,就是目标地+目标文件
									String srcDir = f.getAbsolutePath();
									//System.out.println(srcDir.substring(12));		//file\a  截取字符
									String desDir =desFile.getAbsolutePath()+srcDir.substring(12);
									System.out.println(desDir);
									
									//新建
									File newFile = new File(desDir);
									if(!newFile.exists()) {
										newFile.mkdirs();
									}
								}
			
			//递归
			copyDir(f,desFile);
		}
	}
}

对象流 ObjectOutputStream ObjectInputStream

  • 对象的序列化 反序列化 。
  • 参与序列化和反序列化的对象必须实现Serializable接口。否则出现NotSerializableException异常。Serializable接口只是一个标志接口,这个接口没有代码,起到了标识作用,java虚拟机看到这个类实现了这个接口之后,会为该类自动生成一个序列化版本号
    在这里插入图片描述在这里插入图片描述
package Advance.io.bean;

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class ObjectOutputStreamTest02 {
	public static void main(String[] args) throws Exception{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/source/students"));
		//反序列化 读
		Object obj = ois.readObject();
		
		//反序列化一个学生对象,调用学生对象的toString方法
		System.out.println(obj);
		
		ois.close();
	}
}

  • 当存储多个对象反序列话、序列话时,可以使用集合。
  • 当对于对象的某个属性,不希望它序列化、反序列化时,添加transient关键字
    在这里插入图片描述在这里插入图片描述
  • 在序列化一次之后【字节码文件】,再次对代码进行修改【生成新的字节码文件】,反序列化会出现异常——序列化版本号的作用!
    • 优点: java语言中进行类的区分时,先根据类名进行区分,如果类名一样,再依靠序列化版本号进行区分。——不同的开发人员编写的类名一致时、内容不同时,这时序列化版本号就发挥作用了,对于java虚拟机来说,当两个类都实现了Serialiable接口后,就具备了默认的版本号,两个同名类的版本号不一致,就可以区分出来。
    • 缺陷:自动化生成版本号,一旦代码确定生成版本号,不可更改。一旦修改,必定会重新编译,此时生成全新的序列化版本号,java虚拟机会认为是一个全新的类。
    • 最终建议:序列化版本号手写赋值,不建议自动生成
      在这里插入图片描述

IO、Properties

  • Io文件的读和写;Properties是一个map集合,Key和Value都是String类型,key重复时,会异常报错;不要写中文
  • 当value对应的是类的路径是,采用,而不是反斜杠。
  • 无需更改代码就可以获得动态信息。在编程时,经常更改的数据,可以单独写到一个文档中,使用程序动态读取,将来只需要更改这个文件的内容,java代码不需要更改,不需要重写编译,服务器也不需要重启,就可以拿到动态信息。类似于以上机制的文件被称为配置文件
配置文件的格式为:
	key=value
	key=value
	这种配置文件被称为属性配置文件。
java中规范要求:属性配置文件建议以properties结尾,非强制要求。
在属性配置文件中,key重复时,会异常报错;使用“#”进行注释

在这里插入图片描述

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值