Java—— 流(Stream)、文件(File)和IO

参考于:https://blog.csdn.net/qq_22063697/article/details/52137369 
版权声明:本文为博主原创文章,转载请附上博文链接!


一.  什么是 IO

Java中 I/O 操作主要是指使用 Java 进行输入,输出操作。 Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。

Java 的 I/O 流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。

二.  数据流的基本概念

基本流:一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。

输入流:程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道。

输出流:程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

为什么设计成数据流呢?

InputStream不关心数据源来自何种设备(键盘,文件,网络) 
OutputStream不关心数据的目的是何种设备(键盘,文件,网络)

采用数据流的目的就是使得输出输入独立于设备。
 

三.  I/O体系结构

在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable.

Java I/O主要包括如下几个层次,包含三个部分:

1.流式部分――IO的主体部分;

2.非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;

3.其他类–文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

 

1.  流式部分

Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。

a.  对文件进行操作

FileInputStream(字节输入流),FileOutputStream(字节输出流),FileReader(字符输入流),FileWriter(字符输出流)

b. 对管道进行操作

PipedInputStream(字节输入流),PipedOutputStream(字节输出流),PipedReader(字符输入流),PipedWriter(字符输出流)

PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。

c.  字节/字符数组

ByteArrayInputStream,ByteArrayOutputStream,CharArrayReader,CharArrayWriter是在内存中开辟了一个字节或字符数组。

d.  Buffered缓冲流

BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,是带缓冲区的处理流。

缓冲区的作用的主要目的是:避免每次和硬盘打交道,提高数据访问的效率。


e.  转化流

InputStreamReader/OutputStreamWriter,把字节转化成字符。

f.  数据流

DataInputStream,DataOutputStream。

因为平时若是我们输出一个8个字节的long类型或4个字节的float类型,那怎么办呢?

可以一个字节一个字节输出,也可以把转换成字符串输出,但是这样转换费时间,若是直接输出该多好啊,因此这个数据流就解决了我们输出数据类型的困难。

数据流可以直接输出float类型或long类型,提高了数据读写的效率。

g.  打印流

printStream,printWriter,一般是打印到控制台,可以进行控制打印的地方。

h.  对象流

ObjectInputStream,ObjectOutputStream,把封装的对象直接输出,而不是一个个在转换成字符串再输出。

i.  序列化流

SequenceInputStream。

对象序列化:把对象直接转换成二进制,写入介质中。
【注】

使用对象流需要实现Serializable接口,否则会报错。

而若用transient关键字修饰成员变量,不写入该成员变量,若是引用类型的成员变量为null,值类型的成员变量为0.

 

2.  非流式部分主要类

File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。

RandomAccessFile(随机文件操作):它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。

File类:

在Java语言的java.io包中,由File类提供了描述文件和目录的操作与管理方法。 

但File类不是InputStream、OutputStream或Reader、Writer的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。

作用:File类主要用于命名文件、查询文件属性和处理文件目录。

a.  File类

共提供了四个不同的构造函数,以不同的参数形式灵活地接收文件和目录名信息。构造函数:

1)File (String pathname)

通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。

File  f1=new File("FileTest1.txt"); //创建文件对象f1,f1所指的文件是在当前目录下创建的FileTest1.txt

2)File(URI uri)

通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。

3)File (String parent , String child)

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

// 注意:D:\\dir1目录事先必须存在,否则异常
File f2=new  File(“D:\\dir1","FileTest2.txt");

4)File (File parent , String child)

通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。

File  f4=new File("E:\\dir3");
File  f5=new File(f4,"FileTest5.txt");  //在如果 E:\\dir3目录不存在则需要先使用f4.mkdir()先创建


一个对应于某磁盘文件或目录的File对象一经创建, 就可以通过调用它的方法来获得文件或目录的属性。

 

序号方法描述
1public String getName()
返回由此抽象路径名表示的文件或目录的名称。
2public String getPath()
将此抽象路径名转换为一个路径名字符串。
3public String getAbsolutePath()
返回抽象路径名的绝对路径名字符串。
4public boolean exists()
测试此抽象路径名表示的文件或目录是否存在。
5public boolean isDirectory()
测试此抽象路径名表示的文件是否是一个目录。
6public boolean isFile()
测试此抽象路径名表示的文件是否是一个标准文件。
7public long length()
返回由此抽象路径名表示的文件的长度。
8public boolean delete()
 删除此抽象路径名表示的文件或目录。
9public String[] list()
返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。
10public File[] listFiles()
  返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。
11public boolean mkdir()
创建此抽象路径名指定的目录。
12public boolean mkdirs()
创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
13public boolean renameTo(File dest)
 重新命名此抽象路径名表示的文件。
14public boolean equals(Object obj)
测试此抽象路径名与给定对象是否相等。
15public String toString()
 返回此抽象路径名的路径名字符串。

 

【例】输出一个目录中的所有文件名(目录可能是多级目录,如a目录中有b、c目录。。。)

package  com.zth;

import java.io.File;
import java.io.IOException;

class FileList{
    public static void listDir( String dir)  throws IOException{
        File file = new File(dir);
        // 传进来的可能不是一个路径
        if(!file.isDirectory()){
            throw new IOException(dir + "不是目录");
        }
        // 传进来的可能是一个错误的路径
        if(file == null){
            throw new IOException("没有此路径");
        }
        File[] files = file.listFiles();

        for(File f:files){
            // 有可能是一个多级目录,递归调用
            if(f.isDirectory()){
                listDir(f.getAbsolutePath());
            }else{
                // 是文件就直接输出该文件的绝对路径
                System.out.println(f.getAbsolutePath());
            }
        }
    }
}

public class Test{
    public static void main(String[] args) throws  IOException{
        FileList.listDir("E:/aa");
    }
    
}

执行结果:

E:\aa\a.txt
E:\aa\b.txt
E:\aa\cc\a.txt
E:\aa\cc\b.txt

四.   常用流类

1.  InputStream 抽象类

InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 
继承自InputStream 的流都是向程序中输入数据的,且数据单位为字节(8bit)

InputStream是输入字节数据用的类,所以InputStream类提供了3种重载的read方法.Inputstream类中的常用方法: 
   
  (1) public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。 
   
  (2) public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。
   
  (3) public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。 
   
  (4) public int available( ):返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用, 
   
  (5) public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取 
   
  (6) public int close( ) :我们在使用完后,必须对我们打开的流进行关闭.

主要的子类

 1) FileInputStream:把一个文件作为InputStream,实现对文件的读取操作     

 2) ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用 

 3) StringBufferInputStream:把一个String对象作为InputStream 

 4) PipedInputStream:实现了pipe的概念,主要在线程中使用 

 5) SequenceInputStream:把多个InputStream合并为一个InputStream 
 

2.  OutputStream抽象类

OutputStream提供了3个write方法来做数据的输出,这个是和InputStream是相对应的。 
   
  (1) public void write(byte b[ ]):将参数b中的字节写到输出流。 
   
  (2)public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。 
   
  (3) public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。 
   
  (4)public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。 
   
  (5) public void close( ) : 关闭输出流并释放与流相关的系统资源。

主要的子类

 1) ByteArrayOutputStream:把信息存入内存中的一个缓冲区中 

 2) FileOutputStream:把信息存入文件中 

 3) PipedOutputStream:实现了pipe的概念,主要在线程中使用 

 4) SequenceOutputStream:把多个OutStream合并为一个OutStream 

注:

流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。

3、FileInputStream文件输入流

FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组,byte数组的元素有多少个,就读入多少个字节。

在将整个文件读取完成或写入完毕的过程中,这么一个byte数组通常被当作缓冲区,因为这么一个byte数组通常扮演承接数据的中间角色。

作用:以文件作为数据输入源的数据流。或者说是打开文件,从文件读数据到内存的类。

使用方法:

FileInputStream fis = new FileInputStream(“E:\a.txt”);

或 FileInputStream fis = new FileInputStream(“E:/a.txt”);

当然也可以传一个 File ,它还有好多个构造器。

 

4.  FileOutputStream文件输出流

FileOutputStream类用来处理以文件作为数据输出目的数据流;一个表示文件名的字符串,也可以是File或FileDescriptor对象。

作用:用来处理以文件作为数据输出目的数据流;或者说是从内存区读数据到文件

创建文件流的方式:

1)FileOutputStream(File file) 
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
 

2)FileOutputStream(File file, boolean append) 
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 append表示内容是否追加

3)FileOutputStream(FileDescriptor fdObj) 
创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一 
个到文件系统中的某个实际文件的现有连接。

4)FileOutputStream(String name) 
创建一个向具有指定名称的文件中写入数据的输出文件流。 
例:FileOutputStream out=new FileOutputStream(“d:/myjava/write.txt “);

5)FileOutputStream(String name, boolean append) 
创建一个向具有指定 name 的文件中写入数据的输出文件流。 append表示内容是否追加

【注】

(1)文件中写数据时,若文件已经存在,则覆盖存在的文件;

(2)在读/写操作结束时,应调用close方法关闭流。
 

5、缓冲输入输出流 BufferedInputStream/ BufferedOutputStream(包装流)

BufferedInputStream:当向缓冲流写入数据时候,数据先写到缓冲区,待缓冲区写满后,系统一次性将数据发送给输出设备。

BufferedOutputStream :当从向缓冲流读取数据时候,系统先从缓冲区读出数据,待缓冲区为空时,系统再从输入设备读取数据到缓冲区。

a、将文件读入内存:

将BufferedInputStream与FileInputStream相接

FileInputStream in=new FileInputStream( “file1.txt “);

BufferedInputStream bin=new BufferedInputStream(in);

b、将内存写入文件:

将BufferedOutputStream与 FileOutputStream相接

FileOutputStreamout=new FileOutputStream(“file2.txt”);

BufferedOutputStream bin=new BufferedInputStream(out);

c、键盘输入流读到内存 
将BufferedReader与标准的数据流相接

InputStreamReader sin=new InputStreamReader (System.in) ;

BufferedReader bin=new BufferedReader(sin);
 

6、Writer/Reader字符流

a、Reader抽象类

用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

子类:

(1) 用指定字符数组作为参数:CharArrayReader(char[]) 

 将字符数组作为输入流:CharArrayReader(char[], int, int) 
读取字符串,构造函数如下: public StringReader(String s); 

2) CharArrayReader:与ByteArrayInputStream对应 

3) StringReader : 与StringBufferInputStream对应 

4) InputStreamReader 
从输入流读取字节,在将它们转换成字符:Public inputstreamReader(inputstream is); 

5) FilterReader: 允许过滤字符流 
protected filterReader(Reader r); 

6) BufferReader :接受Reader对象作为参数,并对其添加字符缓冲器,使用readline()方法可以读取一行。 
Public BufferReader(Reader r); 

主要方法:

  (1)  public int read() throws IOException; //读取一个字符,返回值为读取的字符 


 (2)  public int read(char cbuf[]) throws IOException; /*读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量*/ 

  (3)  public abstract int read(char cbuf[],int off,int len) throws IOException; /*读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现*/ 

 

b、 Writer抽象类

写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

子类:

1) FileWrite: 与FileOutputStream对应  

  将字符类型数据写入文件,使用缺省字符编码和缓冲器大小。 
  Public FileWrite(file f);

2)  chararrayWrite:与ByteArrayOutputStream对应 ,将字符缓冲器用作输出。 
   Public CharArrayWrite(); 
   
3) PrintWrite:生成格式化输出 
   public PrintWriter(outputstream os); 
   
4) filterWriter:用于写入过滤字符流 
   protected FilterWriter(Writer w); 
   
5) PipedWriter:与PipedOutputStream对应

6) StringWriter:无与之对应的以字节为导向的stream  

主要方法:

  (1)  public void write(int c) throws IOException; //将整型值c的低16位写入输出流 
  
  (2)  public void write(char cbuf[]) throws IOException; //将字符数组cbuf[]写入输出流 
  
  (3)  public abstract void write(char cbuf[],int off,int len) throws IOException; //将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流 
  
  (4)  public void write(String str) throws IOException; //将字符串str中的字符写入输出流 
  
  (5)  public void write(String str,int off,int len) throws IOException; //将字符串str 中从索引off开始处的len个字符写入输出流 
  
  (6)  flush( ) //刷空输出流,并输出所有被缓存的字节。 
  
  (7)close()    关闭流 public abstract void close() throws IOException
 

五.  Java IO 的一般使用原则

一)按数据来源(去向)分类:

1 、是文件: FileInputStream, FileOutputStream, ( 字节流 )FileReader, FileWriter( 字符 )

2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream( 字节流 )

3 、是 Char[]: CharArrayReader, CharArrayWriter( 字符流 )

4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 字节流 )StringReader, StringWriter( 字符流 )

5 、网络数据流: InputStream, OutputStream,( 字节流 ) Reader, Writer( 字符流 )
 

二)按是否格式化输出分:

1 、要格式化输出: PrintStream, PrintWriter

三)按是否要缓冲分:

1 、要缓冲: BufferedInputStream, BufferedOutputStream,( 字节流 ) BufferedReader, BufferedWriter( 字符流 )

四)按数据格式分:

1 、二进制格式(只要不能确定是纯文本的,比如图片、音频、视频) : InputStream, OutputStream 及其所有带 Stream 结尾的子类

2 、纯文本格式(含纯英文与汉字或其他编码方式); Reader, Writer 及其所有带 Reader, Writer 的子类

五)按输入输出分:

1 、输入: Reader, InputStream 类型的子类

2 、输出: Writer, OutputStream 类型的子类

六)特殊需要:

1 、从 Stream 到 Reader,Writer 的转换类: InputStreamReader, OutputStreamWriter

2 、对象输入输出: ObjectInputStream, ObjectOutputStream

3 、进程间通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter

4 、合并输入: SequenceInputStream

5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
 

六.  标准输入输出

1.  控制台输入

a.  读取控制台输入

Java 的控制台输入由 System.in 完成。

为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。

下面是创建 BufferedReader 的基本语法:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。

b.  从控制台读取多字符输入

从 BufferedReader 对象读取一个字符要使用 read() 方法,它的语法如下:

int read( ) throws IOException

每次调用 read() 方法,它从输入流读取一个字符并把该字符作为整数值返回。 当流结束的时候返回 -1。该方法抛出 IOException。

package com.zth;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class Demo0 {
    public static void main(String[] args) throws IOException {
        Reader in = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(in);
        
        System.out.println("输入字符,输入q结束");
        char c;

        do{
            c = (char)br.read();
            System.out.println(c);
        }while(c != 'q');
    }
    
}

执行结果:

输入字符,输入q结束
hello
h
e
l
l
o


q
q

c.  从控制台读取字符串

从标准输入读取一个字符串需要使用 BufferedReader 的 readLine() 方法。

它的一般格式是:

String readLine( ) throws IOException

package com.zth;

        import java.io.BufferedReader;
        import java.io.IOException;
        import java.io.InputStreamReader;
        import java.io.Reader;

public class Demo0 {
    public static void main(String[] args) throws IOException {
        Reader in = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(in);

        System.out.println("Enter lines of text and enter end to quit");
        String str;

        do{
            str = br.readLine();
            System.out.println(str);
        }while(!"end".equals(str));
    }

}

执行结果:

Enter lines of text and enter end to quit
123
123
abc
abc
纵有红颜,百生千劫
纵有红颜,百生千劫
end
end

2. 控制台输出

控制台的输出由 print( ) 和 println() 完成。这些方法都由类 PrintStream 定义,System.out 是该类对象的一个引用。

PrintStream 继承了 OutputStream类,并且实现了方法 write()。这样,write() 也可以用来往控制台写操作。

PrintStream 定义 write() 的最简单格式如下所示:

void write(int byteval)

该方法将 byteval 的低八位字节写到流中。

 

七.  实例

1.  过滤文件后缀名

有时需要列出目录下指定类型的文件,例如:. java、. txt 等扩展名的文件。

【例】找出“e:/aa”下的所有 .java 文件

package com.zth;

import java.io.File;
import java.io.IOException;

public class Demo0{
    public static void main(String[] args) throws Exception{
        listFile("e:/aa");
    }

    public static void listFile(String str) throws  Exception{
        File file = new File(str);
        if(! file.isDirectory()){
            throw new IOException("不是目录");
        }
        if(file == null){
            throw new IOException("没有此路径");
        }

        File[] files = file.listFiles();
        for(File f:files){
            if(f.isDirectory()){
                listFile(f.toString());
            }else{
                if(f.toString().endsWith(".java")){
                    System.out.println(f.getAbsolutePath());
                }
            }
        }
    }
    
}

2.  从键盘输入、输出到显示器

package com.zth;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/**
 * @author 时光·漫步
 * 起点:键盘;终点:屏幕
 */
public class Test1 {
    public static void main(String[] args) throws Exception{
        OutputStreamWriter osw = new OutputStreamWriter(System.out);
        BufferedWriter bw = new BufferedWriter(osw);

        InputStreamReader in = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(in);

        System.out.println("Enter lines of text and Enter 'end' to quit");

        String str;

        do{
            str = br.readLine();
            if(str.equals("end")){
                break;
            }
            bw.write(str);
            bw.newLine();
        }while(true);

        br.close();
        bw.close();
    }
}

3.  从键盘输入到文件

package com.zth;

import java.io.*;

/**
 * 起点:键盘;终点:文件
 * @author 时光·漫步
 */
public class Test2 {
   public static void main(String[] args) throws IOException {
       InputStreamReader in = new InputStreamReader(System.in);
       BufferedReader br = new BufferedReader(in);

       Writer out = new FileWriter("e:/1.txt");
       BufferedWriter bw = new BufferedWriter(out);

       String str ;
       System.out.println("Enter lines of text and Enter 'end' to quit");

       do{
           str = br.readLine();
           if(str.equals("end")){
              break;
           }
           bw.write(str);
           bw.newLine();
       }while(true);
       System.out.println("Input over");
       br.close();
       bw.close();
   }

}

4. 从文件读入,输出到显示器

package com.zth;

import java.io.*;

/**
 * @author 时光·漫步
 *  起点:文件;终点:屏幕
 */
public class Test3 {
    public static void main(String[] args) throws IOException {
        Reader in = new FileReader("E:/2.txt");
        BufferedReader br = new BufferedReader(in);

        OutputStreamWriter osw = new OutputStreamWriter(System.out);
        BufferedWriter bw = new BufferedWriter(osw);

        String str ;

        do{
            str = br.readLine();
            if(str == null){
                break;
            }
            bw.write(str);
            bw.newLine();
        }while(true);

        br.close();
        bw.close();
    }

}

5. 从一个文件读入到另一个文件(文件复制)

package com.zth;

import java.io.*;

/**
 * @author 时光·漫步
 * 起点:文件;终点:文件
 */
public class Test4 {
    public static void main(String[] args) throws IOException {
        System.out.println("Start");

        Reader in = new FileReader("E:/1.txt");
        Writer out = new FileWriter("E:/2.txt");

        char [] arr = new char[100];
        int n;

        while((n = in.read(arr)) > 0){
            out.write(arr,0,n);
        }

        in.close();
        out.close();

        System.out.println("Over");

    }

}

 

  • 16
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值