字符输出流与序列化

1. BufferedWriter字符流

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了
创建对象

BufferedWriter(Writer out) 
          创建一个使用默认大小输出缓冲区的缓冲字符输出流

测试构造方法以及write方法

public static void main(String[] args) {
	try {
		// 1.创建FileOutputStream输出流,将数据输出到d.txt文件中
		//	true表示写出的数据将会追加文件的末尾, 而不是覆盖原来的数据
		Writer out = new FileWriter( "d:/demoout/d.txt", true );
		// 创建BufferedWriter缓冲区输出流, 输出FileWriter流中的数据
		BufferedWriter bufout = new BufferedWriter( out );
		// 2.开始往d.txt中写数据
		//输出单个字符
		bufout.write( 97 ); //a
		bufout.write( 98 ); //b
		bufout.write( 99 ); //c
		bufout.write( 100 );//d
		bufout.write( 101 );//e
		bufout.write( 13 );//回车
		bufout.write( 10 );//换行
		//输出字符数组
		bufout.write( "ABCDE\r\n".toCharArray() );
		//输出字符数组中的一部分,从下标1处(b)开始,输出后面的3个元素(BCD)
		bufout.write( "ABCDE\r\n".toCharArray(), 1, 3 );
		//输出字符串
		bufout.write( "hello\r\n" );
		//输出字符串中的一部分,从下标1处(e)开始,输出后面的3个字符(ell)
		bufout.write( "hello\r\n", 1, 3 );
		// 3.关闭流(释放资源)
		System.out.println( "成功将数据写入d.txt文件,关闭流..." );
		bufout.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
}

这里大家可以自行测试BufferedWriter输出流写出数据的效率

2.练习

2.1.字符写出流测试

把数据写出到指定文件中。如果文件不存在会自动创建,文件夹不存在会报错

package cn.tedu.out;
 
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
 
//这个类用来测试字符流输出数据
//总结:
//效率问题:BufferedWriter  > FileWriter,因为BufferedWriter底层为了一个char[],默认大小是8*1024字节相当于8K
//写出时,把数据填满数组后一把保存在磁盘中,比一个一个给要快,减少了程序和磁盘的交互次数,提高程序的执行效率。
public class Test2_Writer {
    public static void main(String[] args) {
       method();// 普通的字符流输出数据
       method2();// 高效的字符流输出数据
    }
 
    private static void method2() {
       Writer out = null;
       try {
           // 1,创建字符输出流对象
//         out = new BufferedWriter(new FileWriter("D:\\iotest\\a.txt"));//在覆盖数据
           out = new BufferedWriter(new FileWriter("D:\\iotest\\a.txt",true));//想追加
 
           // 2,开始写出数据
           out.write(90);
           out.write(91);
           out.write(92);
           out.write("io");
           out.write("test");
       } catch (IOException e) {
           e.printStackTrace();
       } finally {
           try {
              // 3,释放资源,放入finally块中,保证资源一定会被释放
              out.close();
           } catch (IOException e) {
              e.printStackTrace();
           }
       }
    }
 
    private static void method() {
       Writer out2 = null;// 声明变量,因为finally要用
       try {
           // 1,创建字符输出流对象
           // Writer out = new Writer();//报错,因为Writer是抽象类不能new
           // Writer out = new FileWriter(new File("D:\\iotest\\a.txt"));
 
           // out2 = new FileWriter("D:\\iotest\\a.txt");//默认就是覆盖数据的模式
 
           // 需求:保持原有数据,在原有数据的末尾处追加数据
           out2 = new FileWriter("D:\\iotest\\a.txt", true);// true表示要追加数据
 
           // 2,开始写出数据
           out2.write(100);
           out2.write("hello");
           out2.write("java");
       } catch (IOException e) {
           e.printStackTrace();
       } finally {
           try {
              // 3,释放资源,放入finally块中,保证资源一定会被释放
              out2.close();
           } catch (IOException e) {
              e.printStackTrace();
           }
       }
    }
}

2.2.文件复制

前面我们学习了如何通过[字节输入流]和[字符输入流]读取指定文件中的内容
也学习了如何通过[字节输出流]和[字符输出流]如何将数据输出(写入)到指定的文件中
而我们在日常生活中, 将计算机硬盘中的某一个文件(例如,d:/iotest/src/test.txt)复制到另一个位置(例如,d:/iotest/tar/test-副本.txt), 这其实就是文件的读取和写入!

  • 通过输入流先将源文件(test.txt)中的数据读取到Java程序中
  • 再将读取到Java程序中的数据写入到目标文件中(test-副本.txt)
    复制文件文件的代码如下所示
public class CopyFileDemo {
	public static void main(String[] args) {
		//1.定义变量, 存储源文件路径
		String source = "d:/iotest/src/test.txt";
		//2.定义变量, 存储目标文件路径
		String target = "d:/iotest/tar/text-副本.txt";
		try {
			//3.调用copyTextFile方法复制文本文件
			copyTextFile( source, target );
			System.out.println( source+" 文件复制成功!" );
		} catch (Exception e) {
			System.out.println( source+" 文件复制失败!" );
			e.printStackTrace();
		}
	}
	/*
	* 方法作用: 复制指定位置的文本文件到另一位置(只能复制文本文件)
	* source参数: 源文件路径(即,要复制的文件路径)
	* target参数: 目标文件路径(即,通过源文件复制的副本文件路径)
	*/
	public static void copyTextFile(String source, String target) throws Exception {
		//1.定义带缓冲区的字符输入流, 读取源文件中的数据
		BufferedReader in=new BufferedReader(new FileReader(source) );
		//2.定义带缓冲区的字符输出流, 将源文件中的数据写入到目标文件中
		BufferedWriter out=new BufferedWriter(new FileWriter(target ) );
		//3.从源文件中读取(in)数据, 将读取到的数据写入(out)到目标文件中
		int data;
		while( (data=in.read()) != -1 ) {
			//将读取到的数据写入到目标文件
			out.write( data );
		}
		//4.释放资源(关闭流)
		in.close();
		out.close();
	}
}

上面的copyTextFile只能复制文本文件,== 如果复制图片、音频、视频等二进制文件,即使复制成功了,复制后的文件也是无法正常打开的!==这是因为图片、音频、视频等文件中都是二进制(1和0)组成的, 如果按照字符去读取这些二进制, 再按照字符写出到目标文件中, 就会破坏从源文件的中读取到内容, 从而导致这些复制后的目标文件是无法打开的!
下面是支持复制所有文件的方法:(既可以复制文本又可以图片、音频、视频等二进制文件)

public class CopyFileDemo {
	public static void main(String[] args) {
		//1.定义变量, 存储源文件路径
		String source = "d:/iotest/src/meinv.jpg";
		//2.定义变量, 存储目标文件路径
		String target = "d:/iotest/tar/meinv-副本.jpg";
		try {
			//3.调用copyBinaryFile方法复制任意文件
			copyBinaryFile( source, target );
			System.out.println( source+" 文件复制成功!" );
		} catch (Exception e) {
			System.out.println( source+" 文件复制失败!" );
			e.printStackTrace();
		}
	}
	/*
	* 方法作用: 复制指定位置的文本文件到另一位置(可以复制任何文件)
	* source参数: 源文件路径(即,要复制的文件路径)
	* target参数: 目标文件路径(即,通过源文件复制的副本文件路径)
	*/
	public static void copyBinaryFile(String source, String target) throws Exception {
		//1.定义带缓冲区的字节输入流, 读取源文件中的数据
		BufferedInputStream in = new BufferedInputStream( new FileInputStream( source ) );
		//2.定义带缓冲区的字节输出流, 将源文件中的数据写入到目标文件中
		BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( target ) );
		//3.从源文件中读取(in)数据, 将读取到的数据写入(out)到目标文件中
		int data;
		while( (data=in.read()) != -1 ) {
			//将读取到的数据写入到目标文件
			out.write( data );
		}
		//4.释放资源(关闭流)
		in.close();
		out.close();
	}
}	

2.3.批量读写

上面的代码中, 在从源文件中读取数据时, 使用了带缓冲区的字节输入流(BufferedInputStream), 可以保证高效率的读取文件中的数据;在将读取到的数据输出到目标文件中时, 使用了带缓冲区的字节输出流(BufferedOutputStream), 保证了将读到的数据高效率的写出到目标文件中;
但有一个过程中没有使用缓冲区, 就是当把源文件中的数据读取到Java程序中后, 将要通过输出流将数据写出到目标文件中时, 这个过程是一个字节一个字节的去处理的, 没有使用字节数据, 如果此处也加上字节数组, 同样可以提高程序执行的效率!
将上面的代码进行改进如下

public class CopyFileDemo2 {
	public static void main(String[] args) {
		//1.定义变量, 存储源文件路径
		String source = "d:/iotest/src/xiaoquanquan.mp4";
		//2.定义变量, 存储目标文件路径
		String target = "d:/iotest/tar/xiaoquanquan-副本.mp4";
		try {
			//3.调用copyBinaryFile方法复制任意文件
			copyBinaryFile( source, target );
			System.out.println( source+" 文件复制成功!" );
		} catch (Exception e) {
			System.out.println( source+" 文件复制失败!" );
			e.printStackTrace();
		}
	}
	/*
	* 方法作用: 复制指定位置的文本文件到另一位置(可以复制任何文件)
	* source参数: 源文件路径(即,要复制的文件路径)
	* target参数: 目标文件路径(即,通过源文件复制的副本文件路径)
	*/
	public static void copyBinaryFile(String source, String target) throws Exception {
		//1.定义带缓冲区的字节输入流, 读取源文件中的数据
		BufferedInputStream in = new BufferedInputStream( new FileInputStream( source ) );
		//2.定义带缓冲区的字节输出流, 将源文件中的数据写入到目标文件中
		BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( target ) );
		//3.从源文件中读取(in)数据, 将读取到的数据写入(out)到目标文件中
		//定义一个字节数据, 将读进来的数据存入byte数组中, 再将byte数组写入到目标文件中
		byte[] buf = new byte[8192];
		int data;
		while( (data=in.read( buf )) != -1 ) {
			//将byte数组中的数据写入到目标文件中
			out.write( buf );
		}
		//4.释放资源(关闭流)
		in.close();
		out.close();
	}
	/*
	* 方法作用: 复制指定位置的文本文件到另一位置(只能复制文本文件)
	* source参数: 源文件路径(即,要复制的文件路径)
	* target参数: 目标文件路径(即,通过源文件复制的副本文件路径)
	*/
	public static void copyTextFile(String source, String target) throws Exception {
		//1.定义带缓冲区的字符输入流, 读取源文件中的数据
		BufferedReader in = new BufferedReader( new FileReader( source ) );
		//2.定义带缓冲区的字符输出流, 将源文件中的数据写入到目标文件中
		BufferedWriter out = new BufferedWriter( new FileWriter( target ) );
		//3.从源文件中读取(in)数据, 将读取到的数据写入(out)到目标文件中
		//定义一个字符数据, 将读进来的数据存入char数组中, 再将char数组写入到目标文件中
		char[] buf = new char[8192];
		int data;
		while( (data=in.read( buf )) != -1 ) {
			//将读取到的数据写入到目标文件
			out.write( buf );
		}
		//4.释放资源(关闭流)
		in.close();
		out.close();
	}
}

3.序列化 / 反序列化

3.1.1.1 概述

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
– 简单地说: 将内存中的Java对象转换为字节序列(二进制)的过程就是序列化。该字节序列中包含了对象的数据(比如成员变量,成员方法等)以及对象的类型信息。
– 将内存中的Java对象转成二进制文件可以存储在磁盘上, 或者转成流数据, 这样可以通过网络进行Java对象数据的传输。而【反序列化】:
– 将包含对象信息的字节序列(二进制)再转换为内存中的Java对象的过程就是反序列化。
实现序列化:
– 利用ObjectOutputStream字节输出流,将内存中的Java对象,按照固定格式转成字节序列输出
实现反序列化:
– 利用ObjectInputStream字节输入流,读取序列化的字节序列,将这些字节序列重新转换为Java对象

在这里插入图片描述

3.2. 应用场景

思考问题1:
内存中的Java对象在服务器关闭后, 随着内存的释放, 内存中的数据也会得到释放。而在服务器重启后也很难恢复这些数据;
解决方法:
如果要重启服务器(比如:重新部署应用程序), 在关闭服务器之前, 可以将内存中的Java对象通过序列化转换为文件存储在磁盘上, 在部署完应用后, 启动服务器, 此时再通过反序列化将磁盘上的文件转换为服务器内存中的Java对象。这样既重启了服务器, 实现了应用的重新部署, 也避免了重启服务器时内存中数据的丢失问题!
思考问题2:
一台服务器内存中的数据只能被当前1号服务器读取, 如果别的服务器也希望拿到这个数据呢?(比如分布式系统中)
解决方法:
可以将1号服务器中的Java对象通过序列化转成二进制流数据, 再通过网络传输到2号服务器中, 二号服务器接收到这些二进制数据后, 再通过反序列化将二进制数据转成内存中的Java对象即可!

4.序列化和反序列化的特点

如果一个对象需要序列化,该对象所属的类必须实现Serializable接口才可以被序列化
如果要进行序列化的对象所属的类没有实现该接口, 在进行序列化时, 将会抛出NotSerializableException异常

Student stu = new Student( "张三", 22 );
class Student extends... implements Serializable{ ... }
  • 如果对象中的个别属性不希望被序列化(比如一些敏感信息, 银行卡号, 密码), 可以使用static修饰,
    由于static修饰的成员属于类, 不会随着对象被序列化输出。
  • 不需要序列化的属性也可以被修饰为transient, 被transient修饰的属性只会存在于内存中, 而不会被序列化持久保存。
  • 每个被序列化的文件都会有一个唯一id, 如果没有手动添加, 编译器会根据类的定义信息计算产生一个版本号; 在反序列化时, 如果和序列化的版本号不一致时, 将无法完成反序列化。
  • 序列化和反序列化常用于不同服务器之间的数据传输,将Java对象序列化字节序列进行传输,另一方在接收后再进行反序列化, 将字节序列转成Java对象

5.ObjectOutputStream

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储

ObjectOutputStream(OutputStream out) 
          创建写入指定 OutputStreamObjectOutputStreamvoid writeObject(Object obj) 
          将指定的对象写入 ObjectOutputStream

测试ObjectOutputStream对Java对象进行序列化:

/*
* 测试使用ObjectOutputStream将Java对象序列化到磁盘的文件中
* 1) 定义学生类, 添加成员变量和成员方法(get,set),构造方法
* 2) 创建ObjectOutputStream流对象, 创建学生类的对象
* 3) 将学生对象转成字节序列(序列化)到磁盘文件中
*/
public class SerializableDemo {
	public static void main(String[] args) {
		try {
			// 1.创建ObjectOutputStream流对象, 用于序列化Java对象
			// 创建FileOutputStream流对象,将Java对象输出到指定的文件中
			FileOutputStream out = new FileOutputStream( "d:/iotest/ser/student.ser" );
			ObjectOutputStream objOut = new ObjectOutputStream( out );
			// 2.创建学生对象,将学生对象转成字节序列(序列化)到磁盘文件中
			Student stu = new Student( "张飞", 20 );
			objOut.writeObject( stu );
			// 3.关闭流(释放资源)
			objOut.close();
			System.out.println( "对象序列化成功!" );
		} catch (Exception e) {
			System.out.println( "对象序列化失败!" );
			e.printStackTrace();
		}
	}
}
/*
* 定义学生类
*/
class Student implements Serializable{
	//成员变量
	private String name;
	private int age;
	//构造方法
	public Student() {}
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	//get和set方法
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

6.ObjectInputStream

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectInputStream,用于读取序列化后的字节序列,将这些字节序列重新转换为Java对象

ObjectInputStream(InputStream in) 
          创建从指定 InputStream 读取的 ObjectInputStreamObject readObject()ObjectInputStream 读取对象,读取序列化数据

测试ObjectInputStream对Java对象进行反序列化

/*
* 测试使用ObjectInputStream将序列化后磁盘文件转成内存中的Java对象
* 1) 创建ObjectInputStream流对象
* 2) 将序列化后的磁盘文件转成Java对象
* 3) 测试Java对象
*/
public class SerializableDemo2 {
	public static void main(String[] args) {
		try {
			//1.创建ObjectInputStream流对象, 用于反序列化
			//创建FileInputStream流对象,用于反序列化(读取)指定文件中的内容
			FileInputStream in = new FileInputStream( "d:/iotest/ser/Student.ser" );
			ObjectInputStream objIn = new ObjectInputStream( in );
			// 2.将序列化后的磁盘文件转成Java对象
			Object obj = objIn.readObject();
			// 3.测试Java对象
			System.out.println( obj ); //cn.tedu.serializable.Student@b684286
			// 向下转型为Student类型
			Student stu = (Student)obj;
			System.out.println( stu.getName()+", "+stu.getAge() );
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

7.编码转换流介绍

输入/输出流体系中还提供了两个转换流, 这两个转换流用于实现将字节流转成字符流, 其中:

InputStreamReader负责将字节输入流转成字符输入流(转换时可以指定编码)
OutputStreamWriter负责将字节输出流转成字符输出流(转换时可以指定编码)

思考: 为什么没有提供将字符流转成字节流?
如果现在有一个字节流:

  • 若读写的文件是图片、音频、视频等二进制数据,只能使用字节流进行读写,也就不能转换!
  • 若读写的文件确定是文本内容,那么把字节流转成字符流来进行读写会更方便一些!
    在读取文本时, 字节流是一个字节一个字节读或写, 而字符流是一个字符一个字符读或写, 显然字符流效率更高!所以Java只提供了字节流转字符流, 没有提供字符流转字节流

7.1.java中常见字符

7.1.1ASCII

ASCII(American Standard Code for Information Interchange,美国信息互换标准编码)是基于罗马字母表的一套电脑编码系统,ASCII主要用于显示现代英语和其他西欧语言,它是最基础,也是最通用的单字节编码系统。
基本的ASCII字符集共有128个字符,字符值从0到127,其中32到126是可打印字符,占 1 bytes(字节),适用于不同地区的扩充的ASCII字符集,扩充字符的编码均为高位为1的8位代码,即128-255,也称为扩展ASCII码

7.1.2.GB2312

简体中文字符集(又称为GB2312-80),兼容ASCII,占 2 bytes(字节),由于我们汉字比较复杂,又不能和ASCII编码冲突,所以,中国制定了GB2312编码,并且需要两个字节
GB2312所收录的汉字已经覆盖99.75%的使用频率,基本满足了汉字的计算机处理需要。在中国大陆和新加坡获广泛使用。GB2312收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。其中包括6763个汉字

7.1.3.GBK

GB2312的扩展字符集,支持繁体字,兼容GB2312,占 2 bytes(字节),由于汉字太多,太复杂,GB2312无法处理所有的汉字,这就出现GBK字符集,它扩展了GB2312编码,加入了更多的汉字

7.1.4.Unicode

国际标准组织统一标准字符集,占 2 bytes,Unicode是一种在计算机上使用的字符编码。
Unicode是为了解决不同编码兼容性问题,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求

7.1.5.UTF-8

UTF-8是Unicode的其中一个使用方式。(UTF是Unicode Tranformation Format简写)
它的出现是为了提高Unicode编码的效率,根据不同符号自动选择编码的长短。
UTF-8使用可变长度字节来储存 Unicode字符 ,占 1-3 bytes,
例如:

  • ASCII字母(英文字母,数字,西欧语言)继续使用 1 字节储存,
  • 重音文字、希腊字母或西里尔字母等使用 2 字节来储存,
  • 而我们常用的汉字就要使用 3 字节来储存

7.2.工具类

OutputStreamWriter:是字节流通向字符流的桥梁
--OutputStreamWriter(OutputStream out, String charsetName)
--OutputStreamWriter(OutputStream out) 
InputStreamReader: 是字节流通向字符流的桥梁
--InputStreamReader(InputStream in)
--InputStreamReader(InputStream in,String charsetName)

7.3.InputStreamReader案例

InputStreamReader负责将字节输入流转成字符输入流(转换时可以指定编码)
InputStreamReader(InputStream in) 
	-- 创建一个 InputStreamReader 字符输入流对象, 该对象负责将 字节输入流 转成字符输入流
	-- 在将字节流转成字符流时, 默认使用系统默认字符集(win中, GBK)
InputStreamReader(InputStream in, String charsetName)
	-- 创建一个 InputStreamReader 字符输入流对象, 该对象负责将 字节输入流 转成字符输入流
	-- 在将字节流转成字符流时, 将会使用charsetName指定的字符集

下面以获取键盘输入为例来介绍转换流的用法。
Java使用System.in代表标准输入(即键盘输入),但这个标准输入流是InputStream类的实例,使用不太方便,而且键盘输入内容都是文本内容。
所以可以使用InputStreamReader将其转换成字符输入流,普通的Reader读取输入内容时依然不太方便,可以将普通的Reader再次包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容。如下程序所示

public static void main(String[] args) {
	//1.将System.in字节输入流转成Reader字符流
	Reader reader = new InputStreamReader(System.in);
	// 再将普通的Reader字符流转成带缓冲区功能的BufferReader流
	BufferedReader br = new BufferedReader( reader );
	try {
		//2.读取用户通过键盘输入的一行文本
		//String line1 = br.readLine();
		//String line2 = br.readLine();
		//System.out.println( line1 );
		//System.out.println( line2 );	
		//3.使用循环来逐行读取用户通过键盘输入的每一行文本
		String line;
		while( (line=br.readLine()) != null ) {
			System.out.println( "输入的内容为: "+line );
			//如果读取的内容为exit, 则结束循环
			if( line.equals( "exit" ) ) {
				break;
			}
		}
		//4.关闭流
		br.close();	
	} catch (Exception e) {
		e.printStackTrace();
	}
}

之所以将System.in字节流包装成BufferedReader,因为BufferedReader流具有缓冲功能,它可以一次读取一行文本——以换行符为标志,如果它没有读到换行符,则程序阻塞,等到读到换行符为止。运行上面程序可以发现这个特征,在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容

7.4.OutputStreamWriter案例

OutputStreamWriter负责将字节输出流转成字符输出流(转换时可以指定编码)
OutputStreamWriter(OutputStream out)
	-- 创建一个 OutputStreamWriter 字符输出流对象, 该对象负责将 字节输出流 转成字符输出流
	-- 在将字节流转成字符流时, 默认使用系统默认字符集(win中, GBK)
OutputStreamWriter(OutputStream out, String charsetName)  
	-- 创建一个 OutputStreamWriter 字符输出流对象, 该对象负责将 字节输出流 转成字符输出流
	-- 在将字节流转成字符流时, 将会使用charsetName指定的字符集

通过FileWriter字符输出流往文件中写数据时, 默认使用的是系统平台(GBK)
但如果要写入的文件中有数据,并且该文件使用的编码不是GBK, 而是utf-8, 那么此时将GBK编码的数据写入到utf-8的文件中, 就可能会出现乱码问题!
e.txt: 你好, utf-8
e.txt: 追加数据, 韩少云, FileWriter, GBK
下面测试FileWriter字符流写数据时可能出现的乱码问题!

public static void main(String[] args) {
	try {
		/* 
		* 通过FileWriter字符输出流往文件中写数据时, 默认使用的是系统平台码(GBK)
		* 但如果e.txt文件中有数据,并且该文件使用的编码不是GBK,而是utf-8
		*那么此时将GBK编码数据写入到utf-8的文件中,就可能会出现乱码问题!
		*/
		//1.创建FileWriter字符输出流对象,将数据写入到d:/demoout/e.txt文件中
		FileWriter writer=new FileWriter( "d:/demoout/e.txt", true );
		System.out.println( writer.getEncoding() );
		// 2.输出数据
		writer.write( "你好,马云\r\n" );
		// 3.关闭流
		writer.close();
		System.out.println( "写入成功!" );
	} catch (Exception e) {
		e.printStackTrace();
	}
}

将FileWriter字符流替换为FileOutputStream字节流, 再通过OutputStreamWriter将字节流转成字符流, 还可以指定编码, 修改后的代码如下

public static void main(String[] args) {
	try {
		// 1.创建FileOutputStream字节输出流对象, 将数据写入到 d:/demoout/e.txt 文件中
		FileOutputStream out = new FileOutputStream( "d:/demoout/e.txt", true );
		// 2.通过OutputStreamWriter将字节流转成字符流, 并可以指定编码
		OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
		//System.out.println( writer.getEncoding() );
		// 3.输出数据
		writer.write( "你好,马云!\r\n" );
		// 4.关闭流
		writer.close();
		System.out.println( "写入成功!" );
	} catch (Exception e) {
		e.printStackTrace();
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值