java字节流和字符流的操作

一。字节流的操作,通过复制一个MP3文件比较快慢
 

(1)。字节流的操作



package cneduzzuli;






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


public class IOUtil {
	/**
	 * 读取文件内容,按照16进制输出控制台
	 * @param fileName
	 * @throws IOException
	 */
	public static void printHex(String fileName) throws IOException{
	FileInputStream in = new FileInputStream(fileName);
	int b;
	int i = 1;
	while((b = in.read()) != -1) {
		if(b <= 0xf)
			System.out.print("0" + Integer.toHexString(b) + " ");
		else
			System.out.print(Integer.toHexString(b) + " ");
		if(i++ % 10 == 0)
			System.out.println();
	}
	in.close();
	}
	
	public static void printHexByteArray(String fileName)throws IOException{
		FileInputStream in = new FileInputStream(fileName);
		byte[] buf = new byte[2 * 1024];
//		/*从in中批量读取字节,放入buf字节数组
//		 * 从第0位置放, 最多放buf。length
//		 * in.read返回读到的字节个数
//		 */
		int bytes = in.read(buf, 0, buf.length);
		//一次性读完
		for(int i = 0, j = 1;i < bytes;i++) {
			if(buf[i] <= 0xf) {
				System.out.print("0");
			}
			System.out.print(Integer.toHexString(buf[i]) + " ");
			if(j++ % 10 == 0) {
				System.out.println();
			}
		}
		int bytes = 0;
		int j = 1;
		while((bytes = in.read(buf, 0, buf.length)) != -1) {
			for(int i= 0;i < bytes;i++) {
				System.out.print(Integer.toHexString(buf[i]) 
						+ " ");
				if(j++ % 10 == 0) {
					System.out.println();
				}
			}
		}
		in.close();
	}
	/**
	 * 字节批量读取
	 * @param srcFile
	 * @param destFile
	 * @throws IOException
	 */
	public static void copyFile(File srcFile, File destFile) throws IOException{
		if(!srcFile.exists()) {
			throw new IllegalArgumentException("文件不存在");
		}
		if(!srcFile.isFile()) {
			throw new IllegalArgumentException("不是文件");
		}
		FileInputStream in = new FileInputStream(srcFile);
		FileOutputStream out = new FileOutputStream(destFile);
		byte[] buf = new byte[5000 * 1024];
		int b;
		while((b = in.read(buf, 0, buf.length)) != -1) {
			out.write(buf, 0, b);
			out.close();//最好加上
		}
		in.close();
		out.close();
	}
	/**
	 * 缓冲区的读取
	 * @param srcFile
	 * @param destFile
	 * @throws IOException
	 */
	public static void copyByBuffer(File srcFile, File destFile) throws IOException{
		if(!srcFile.exists()) {
			throw new IllegalArgumentException("文件不存在");
		}
		if(!srcFile.isFile()) {
			throw new IllegalArgumentException("不是文件");
		}
		BufferedInputStream bis = new BufferedInputStream(
				new FileInputStream(srcFile));
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(destFile));
		int c;
		while((c = bis.read()) != -1) {
			bos.write(c);
			bos.flush();//刷新缓冲区
		}
		bis.close();
		bos.close();
	}
	/**
	 * 单字节读取
	 * @param srcFile
	 * @param destFile
	 * @throws IOException
	 */
	public static void copyByByte(File srcFile, File destFile)throws IOException{
		if(!srcFile.exists()) {
			throw new IllegalArgumentException("文件不存在");
		}
		if(!destFile.isFile()) {
			throw new IllegalArgumentException(destFile + "这不是文件");
		}
		FileInputStream in = new FileInputStream(srcFile);
		FileOutputStream out = new FileOutputStream(destFile);
		int  c;
		while((c = in.read()) != -1) {
			out.write(c);
			out.flush();
		}
		in.close();
		out.close();
	}
}




package cneduzzuli;






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


public class IOUtil {
	/**
	 * 读取文件内容,按照16进制输出控制台
	 * @param fileName
	 * @throws IOException
	 */
	public static void printHex(String fileName) throws IOException{
	FileInputStream in = new FileInputStream(fileName);
	int b;
	int i = 1;
	while((b = in.read()) != -1) {
		if(b <= 0xf)
			System.out.print("0" + Integer.toHexString(b) + " ");
		else
			System.out.print(Integer.toHexString(b) + " ");
		if(i++ % 10 == 0)
			System.out.println();
	}
	in.close();
	}
	
	public static void printHexByteArray(String fileName)throws IOException{
		FileInputStream in = new FileInputStream(fileName);
		byte[] buf = new byte[2 * 1024];
//		/*从in中批量读取字节,放入buf字节数组
//		 * 从第0位置放, 最多放buf。length
//		 * in.read返回读到的字节个数
//		 */
		int bytes = in.read(buf, 0, buf.length);
		//一次性读完
		for(int i = 0, j = 1;i < bytes;i++) {
			if(buf[i] <= 0xf) {
				System.out.print("0");
			}
			System.out.print(Integer.toHexString(buf[i]) + " ");
			if(j++ % 10 == 0) {
				System.out.println();
			}
		}
		int bytes = 0;
		int j = 1;
		while((bytes = in.read(buf, 0, buf.length)) != -1) {
			for(int i= 0;i < bytes;i++) {
				System.out.print(Integer.toHexString(buf[i]) 
						+ " ");
				if(j++ % 10 == 0) {
					System.out.println();
				}
			}
		}
		in.close();
	}
	/**
	 * 字节批量读取
	 * @param srcFile
	 * @param destFile
	 * @throws IOException
	 */
	public static void copyFile(File srcFile, File destFile) throws IOException{
		if(!srcFile.exists()) {
			throw new IllegalArgumentException("文件不存在");
		}
		if(!srcFile.isFile()) {
			throw new IllegalArgumentException("不是文件");
		}
		FileInputStream in = new FileInputStream(srcFile);
		FileOutputStream out = new FileOutputStream(destFile);
		byte[] buf = new byte[5000 * 1024];
		int b;
		while((b = in.read(buf, 0, buf.length)) != -1) {
			out.write(buf, 0, b);
			out.close();//最好加上
		}
		in.close();
		out.close();
	}
	/**
	 * 缓冲区的读取
	 * @param srcFile
	 * @param destFile
	 * @throws IOException
	 */
	public static void copyByBuffer(File srcFile, File destFile) throws IOException{
		if(!srcFile.exists()) {
			throw new IllegalArgumentException("文件不存在");
		}
		if(!srcFile.isFile()) {
			throw new IllegalArgumentException("不是文件");
		}
		BufferedInputStream bis = new BufferedInputStream(
				new FileInputStream(srcFile));
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(destFile));
		int c;
		while((c = bis.read()) != -1) {
			bos.write(c);
			bos.flush();//刷新缓冲区
		}
		bis.close();
		bos.close();
	}
	/**
	 * 单字节读取
	 * @param srcFile
	 * @param destFile
	 * @throws IOException
	 */
	public static void copyByByte(File srcFile, File destFile)throws IOException{
		if(!srcFile.exists()) {
			throw new IllegalArgumentException("文件不存在");
		}
		if(!destFile.isFile()) {
			throw new IllegalArgumentException(destFile + "这不是文件");
		}
		FileInputStream in = new FileInputStream(srcFile);
		FileOutputStream out = new FileOutputStream(destFile);
		int  c;
		while((c = in.read()) != -1) {
			out.write(c);
			out.flush();
		}
		in.close();
		out.close();
	}
}

(2)。这是执行完的输出
 

字节流为10毫秒,

缓冲区17622ms

单字节读取24834ms

2。

(1)DataInputStream所所所

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

公共类DosDemo { 

	public static void main(String [] args)throws IOException { 
		String file =“demo / dos.dat”; 
		DataOutputStream dos = new DataOutputStream(
				新的FileOutputStream(文件)); 
		dos.writeInt(10); 
		dos.writeInt(-10); 
		dos.writeLong(10升); 
		dos.writeUTF(“中国”); 
		dos.writeUTF(“中国”); 
		dos.writeChars(“中国”); 
		dos.close(); 
		IOUtil.printHex(文件); 
		
	} 

}
import java.io.FileOutputStream; 
import java.io.IOException; 

公共类DosDemo { 

	public static void main(String [] args)throws IOException { 
		String file =“demo / dos.dat”; 
		DataOutputStream dos = new DataOutputStream(
				新的FileOutputStream(文件)); 
		dos.writeInt(10); 
		dos.writeInt(-10); 
		dos.writeLong(10升); 
		dos.writeUTF(“中国”); 
		dos.writeUTF(“中国”); 
		dos.writeChars(“中国”); 
		dos.close(); 
		IOUtil.printHex(文件); 
		
	} 

}

相当于用水瓢把水从缸移到另一个缸

(2)

的的FileInputStream

import java.io.FileOutputStream; 
import java.io.IOException; 
公共类FileOutputStreamtest { 

	public static void main(String [] args)throws IOException { 
		
		//如果不存在,创建。如果存在,删除创建。如果为新的FileOutputStream(“demo / out.dat “,真正); 
		//为追加内容
		FileOutputStream out = new FileOutputStream(“demo / out.dat”); 
		out.write('A'); 
		out.write('B'); 
		int a = 10; 
		out.write(a >>> 24); //一次一个字节
		out.write(a >>> 16);
		out.write(a >>> 8);
		out.write(一);
		byte [] gbk =“中国”.getBytes(“gbk”);
		out.write(GBK);
		out.close();
		
		IOUtil.printHex( “演示/ out.dat”);
		
	}

}
import java.io.IOException; 
公共类FileOutputStreamtest { 

	public static void main(String [] args)throws IOException { 
		
		//如果不存在,创建。如果存在,删除创建。如果为新的FileOutputStream(“demo / out.dat “,真正); 
		//为追加内容
		FileOutputStream out = new FileOutputStream(“demo / out.dat”); 
		out.write('A'); 
		out.write('B'); 
		int a = 10; 
		out.write(a >>> 24); //一次一个字节
		out.write(a >>> 16);
		out.write(a >>> 8);
		out.write(一);
		byte [] gbk =“中国”.getBytes(“gbk”);
		out.write(GBK);
		out.close();
		
		IOUtil.printHex( “演示/ out.dat”);
		
	}

}

相当于一滴一滴把水移到另一个缸

(3)

byte [] bytes = new byte [200 * 1024];

public static void printHexByteArray(String fileName)throws IOException {
		FileInputStream in = new FileInputStream(fileName);
		byte [] buf = new byte [2 * 1024];
		/ *从以中批量读取字节,放入BUF字节数组
		 *从第0位置放,最多放buf.length
		 * in.read返回读到的字节个数
		 * /
// int bytes = in.read(buf,0,buf.length);
// //一次性读完
// for(int i = 0,j = 1; i <bytes; i ++){
// if(buf [i] <= 0xf){
// System.out.print(“0”);
//}
// System.out.print(Integer.toHexString(buf [i])+“”);
// if(j ++%10 == 0){
// System.out.println();
//}
//}
		int bytes = 0;
		int j = 1;
		while((bytes = in.read(buf,0,buf.length))!= -1){
			for(int i = 0; i <bytes; i ++){
				是System.out.print(Integer.toHexString(BUF [I]) 
						+“”);
				if(j ++%10 == 0){
					的System.out.println();
				}
			}
		}
		附寄();
	}
		FileInputStream in = new FileInputStream(fileName);
		byte [] buf = new byte [2 * 1024];
		/ *从以中批量读取字节,放入BUF字节数组
		 *从第0位置放,最多放buf.length
		 * in.read返回读到的字节个数
		 * /
// int bytes = in.read(buf,0,buf.length);
// //一次性读完
// for(int i = 0,j = 1; i <bytes; i ++){
// if(buf [i] <= 0xf){
// System.out.print(“0”);
//}
// System.out.print(Integer.toHexString(buf [i])+“”);
// if(j ++%10 == 0){
// System.out.println();
//}
//}
		int bytes = 0;
		int j = 1;
		while((bytes = in.read(buf,0,buf.length))!= -1){
			for(int i = 0; i <bytes; i ++){
				是System.out.print(Integer.toHexString(BUF [I]) 
						+“”);
				if(j ++%10 == 0){
					的System.out.println();
				}
			}
		}
		附寄();
	}

最牛逼,端着缸倒进去了

二,字符流

一次处理一个字符,底层依然是字节流

1.InputStreamReader:将字节字节转换为字符char流,按照编码解析
   OutputStreamWriter:将字符转换字节,按照编码编码

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

公共类IsrAndOswDemo {

	public static void main(String [] args)throws IOException {
		FileInputStream in = new FileInputStream(“d:\\ Desktop \\ Test.txt”);
		@SuppressWarnings( “资源”)
		InputStreamReader isr = new InputStreamReader(in,“gbk”); //“gbk”
		FileOutputStream out = new FileOutputStream(“D:\\ java \\ TEXTS \\ oneminuts \\ test.txt”);
		OutputStreamWriter osw = new OutputStreamWriter(out,“utf-8”); //“utf-8”编码要对应,不能与文件编码不匹配
		/ * int c;
		while((c = isr.read())!= -1){
			是System.out.print((char)的C);
		}
		* /
                //字节数组做中介
		char [] buffer = new char [8 * 1024];
		int c;
		//批量读取,放入buffer这个字符数组,从0位置放到buffer.length
		long start = System.currentTimeMillis();
		while((c = isr.read(buffer,0,buffer.length))!= -1){
// for(char ch:buffer){
// System.out.print(ch);
//}
// String s = new String(buffer,0,c);
// System.out.println(s);
			 osw.write(buffer,0,c);
			 osw.flush();
		}
		长结束= System.currentTimeMillis();
		System.out.println(end  -  start);
		附寄();
		out.close();
	}

}

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

公共类IsrAndOswDemo {

	public static void main(String [] args)throws IOException {
		FileInputStream in = new FileInputStream(“d:\\ Desktop \\ Test.txt”);
		@SuppressWarnings( “资源”)
		InputStreamReader isr = new InputStreamReader(in,“gbk”); //“gbk”
		FileOutputStream out = new FileOutputStream(“D:\\ java \\ TEXTS \\ oneminuts \\ test.txt”);
		OutputStreamWriter osw = new OutputStreamWriter(out,“utf-8”); //“utf-8”编码要对应,不能与文件编码不匹配
		/ * int c;
		while((c = isr.read())!= -1){
			是System.out.print((char)的C);
		}
		* /
                //字节数组做中介
		char [] buffer = new char [8 * 1024];
		int c;
		//批量读取,放入buffer这个字符数组,从0位置放到buffer.length
		long start = System.currentTimeMillis();
		while((c = isr.read(buffer,0,buffer.length))!= -1){
// for(char ch:buffer){
// System.out.print(ch);
//}
// String s = new String(buffer,0,c);
// System.out.println(s);
			 osw.write(buffer,0,c);
			 osw.flush();
		}
		长结束= System.currentTimeMillis();
		System.out.println(end  -  start);
		附寄();
		out.close();
	}

}

2.FileReader FileWriter

的FileReader为文件读操作,FileWriter的为写操作

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

公共类FileRAndWDemo {

	public static void main(String [] args)throws IOException {
		FileReader fr = new FileReader(“d:\\ Desktop \\ Test.txt”); //这里不支持加编码,FileInputReader支持编码
		FileWriter fw = new FileWriter(“d:\\ Desktop \\ Test1.txt”,true); // true。为追加,false或没有为覆盖
		char [] buffer = new char [2048];
		int c;
		while((c = fr.read(buffer,0,buffer.length))!= -1){
			 fw.write(buffer,0,c);
			 fw.flush();
		}
		fr.close();
		fw.close();
	}

}

import java.io.FileWriter;
import java.io.IOException;

公共类FileRAndWDemo {

	public static void main(String [] args)throws IOException {
		FileReader fr = new FileReader(“d:\\ Desktop \\ Test.txt”); //这里不支持加编码,FileInputReader支持编码
		FileWriter fw = new FileWriter(“d:\\ Desktop \\ Test1.txt”,true); // true。为追加,false或没有为覆盖
		char [] buffer = new char [2048];
		int c;
		while((c = fr.read(buffer,0,buffer.length))!= -1){
			 fw.write(buffer,0,c);
			 fw.flush();
		}
		fr.close();
		fw.close();
	}

}

3.字符流的过滤器

BufferedReader:readline ---->一次读一行

但是BufferedWriter / PrintWriter的---------一次写一行

newLine换行/ println换行

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

公共类BufferedAndReader {

	public static void main(String [] args)throws IOException {
		//
		BufferedReader br = new BufferedReader(
				新的InputStreamReader(
						新的FileInputStream(“d:\\ Desktop \\ Test.txt”)));
// BufferedWriter bw = new BufferedWriter(
//新的OutputStreamWriter(
// new FileOutputStream(“d:\\ Desktop \\ Test2.txt”)));
		//和BufferedWriter将功能一样
		PrintWriter pw = new PrintWriter(“d:\\ Desktop \\ Test1.txt”);
		//可以以OutStream为参数,是否自动刷新为参数
		// PrintWriter pw1 = new PrintWriter(new OutputStream out,boolean autoFlush);
		String str;
		while((str = br.readLine())!= null){//不能识别换行
			的System.out.println(STR);
// bw.write(str);
// bw.newLine(); //换行
// bw.flush();
			pw.println(STR); //有就换行,没有不换行
			pw.flush();
		}
// bw.close();
		pw.close();
		br.close();
	}

}

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

公共类BufferedAndReader {

	public static void main(String [] args)throws IOException {
		//
		BufferedReader br = new BufferedReader(
				新的InputStreamReader(
						新的FileInputStream(“d:\\ Desktop \\ Test.txt”)));
// BufferedWriter bw = new BufferedWriter(
//新的OutputStreamWriter(
// new FileOutputStream(“d:\\ Desktop \\ Test2.txt”)));
		//和BufferedWriter将功能一样
		PrintWriter pw = new PrintWriter(“d:\\ Desktop \\ Test1.txt”);
		//可以以OutStream为参数,是否自动刷新为参数
		// PrintWriter pw1 = new PrintWriter(new OutputStream out,boolean autoFlush);
		String str;
		while((str = br.readLine())!= null){//不能识别换行
			的System.out.println(STR);
// bw.write(str);
// bw.newLine(); //换行
// bw.flush();
			pw.println(STR); //有就换行,没有不换行
			pw.flush();
		}
// bw.close();
		pw.close();
		br.close();
	}

}

三,对象的序列化和反序列化骚操作

    4)transsient关键字

        private void writeObject(java.io.ObjectOutputStream s)
	        抛出java.io.IOException {
		s.defaultWriteObject(); //把JVM能默认序列化的元素执行序列化操作
		s.writeInt(年龄); //自己完成序列化
		
	}
	private void readObject(java.io.ObjectInputStream s)
	        抛出java.io.IOException,ClassNotFoundException {
		s.defaultReadObject(); //把JVM能默认反序列化的元素执行反序列化操作
		age = s.readInt(); //自己完成反序列化
	}
	        抛出java.io.IOException {
		s.defaultWriteObject(); //把JVM能默认序列化的元素执行序列化操作
		s.writeInt(年龄); //自己完成序列化
		
	}
	private void readObject(java.io.ObjectInputStream s)
	        抛出java.io.IOException,ClassNotFoundException {
		s.defaultReadObject(); //把JVM能默认反序列化的元素执行反序列化操作
		age = s.readInt(); //自己完成反序列化
	}

这是ArrayList的源码,因为底层实现是数组,为了使没有实例化的元素不实例化,从而节省网络流量

   private void writeObject(java.io.ObjectOutputStream s)
        抛出java.io.IOException {
        //写出元素数量和任何隐藏的东西
        int expectedModCount = modCount;
        s.defaultWriteObject();

        //将行为兼容性的容量写出为clone()
        s.writeInt(大小); //版本兼容性问题,可不用,为了低版本能反序列化高版本序列化的对象(新版本没有序列化长度)

        //以正确的顺序写出所有元素。
        for(int i = 0; i <size; i ++){
            s.writeObject(elementData中[1]); //只将实例化的元素序列化
        }

        if(modCount!= expectedModCount){
            抛出新的ConcurrentModificationException();
        }
    }

   
    private void readObject(java.io.ObjectInputStream s)
        抛出java.io.IOException,ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        //读取大小和隐藏的东西
        s.defaultReadObject();

        //读入容量
        s.readInt(); //被忽略

        if(size> 0){
            //如clone(),根据大小而不是容量分配数组
            int capacity = calculateCapacity(elementData,size);
            SharedSecrets.getJavaOISAccess()。checkArray(s,Object []。class,capacity);
            ensureCapacityInternal(大小);

            Object [] a = elementData;
            //以正确的顺序读入所有元素。
            for(int i = 0; 我<size; i ++){ 
                a [i] = s.readObject(); 
            } 
        } 
    }
        抛出java.io.IOException {
        //写出元素数量和任何隐藏的东西
        int expectedModCount = modCount;
        s.defaultWriteObject();

        //将行为兼容性的容量写出为clone()
        s.writeInt(大小); //版本兼容性问题,可不用,为了低版本能反序列化高版本序列化的对象(新版本没有序列化长度)

        //以正确的顺序写出所有元素。
        for(int i = 0; i <size; i ++){
            s.writeObject(elementData中[1]); //只将实例化的元素序列化
        }

        if(modCount!= expectedModCount){
            抛出新的ConcurrentModificationException();
        }
    }

   
    private void readObject(java.io.ObjectInputStream s)
        抛出java.io.IOException,ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        //读取大小和隐藏的东西
        s.defaultReadObject();

        //读入容量
        s.readInt(); //被忽略

        if(size> 0){
            //如clone(),根据大小而不是容量分配数组
            int capacity = calculateCapacity(elementData,size);
            SharedSecrets.getJavaOISAccess()。checkArray(s,Object []。class,capacity);
            ensureCapacityInternal(大小);

            Object [] a = elementData;
            //以正确的顺序读入所有元素。
            for(int i = 0; 我<size; i ++){ 
                a [i] = s.readObject(); 
            } 
        } 
    }

(5)父类子类的序列化

【1】,

父类实现了序列化,子类可以直接序列化

序列化时候显式递归调用构造函数

反序列化时没有显式得调用构造函数,但是不能认为不调用构造函数,在反序列化时从文件中可以得到父类的构造函数

故不用显示调用

【2】,

父类没有实现序列化,子类可以实现序列化以实现可序列化

序列化时,递归显式调用构造函数。

反序列化时,父类的超类没有实现序列化,父类实现了序列化,子类反序列化时都显式递归调用超类的构造函数

反序列化时,父类的超类和父类没有实现序列化,子类实现了序列化,没有实现序列化的都会显式调用构造函数

总结,反序列化时候,父类没有实现序列化的都会被显式调用构造函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值