Java常见IO流整理及使用方法(二)之转换流,序列化,打印流

转换流

字符编码和字符集
字符编码
字符编码:计算机中存储的信息都是二进制数据,而我们看到的数字,英文,符号,汉字等都是二进制数转换之后的结果。
字符编码:就是一套自然语言的字符与二进制之间的对应规则
按照某种规则,将字符存储到计算机中。称为编码
编码:字符(能看懂的) -》 字节(看不懂的)
反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码
解码:字节(看不懂的) -》字符(能看懂的)
字符集:
也叫编码表,是一个系统支持的所有字符的集合,包括各国家文字,标点符号,图形符号,数字等
编码表:生活中文字和计算机中二进制的对应规则
计算机要准确的存储和失败各种字符集符号 需要进行字符编码,一套字符集必然至少有一套字符编码
常见字符集
1.ASCII码 ASCII编码
ASCII码 用于显示现代英语 , 控制字符(回车键,退格,换行符等) 和可显示字符(英文大小写字符 , 阿拉伯数字 西文字符)
基本ASCII码字符集,使用7位表示一个字符,共128个字符 。 第一位使用正负
(拓展ASCII字符使用8位表示一个字符共计256个字符 支持欧洲常用字符)

2.GBK字符集 GBK编码
GBK字符集 国标码 显示中文的一套字符集 两个字节存储一个中文

3.Unicode字符集等 UTF-8编码 UTF16编码 UTF32编码
Unicode字符集 万国码 包含所有国家的任意语言字符而设计的 3个字节存储一个中文
最常用的UTF-8编码 :是电子邮件,网页及其他存储或传送文字的应用中,优先采用的编码
当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们关心的
编码引出的问题
编码不匹配会出现乱码问题

public class Demo01 {
	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("E:\\java\\GBK.txt");
		int len = 0;
		while ((len = fr.read())!=-1) {
			System.out.print((char)len);//锘夸綘濂藉晩 产生乱码
		}
		fr.close();
	}
}	

1. OutputStreamWriter:转换输出流
是字符流通向字节流的桥梁:可以使用指定的字节charset 将要写入流中的字符编码成字节(编码)
java.io.OutputStreamWriter extends Writer
继承自父类的共性成员方法
1.void write(int c) 写一个字符
2.void write(char[] cbuf) 写入一个字符数组。
3.abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
4.void write(String str) 写一个字符串
5.void write(String str, int off, int len) 写一个字符串的一部分。
6.abstract void flush() 刷新该流的缓冲。
7.abstract void close() 关闭流,必须先刷新。
构造方法:
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的OutputStreamWriter。
参数:
OutputStream out:字节输出流。可以用来写转换之后的字节到文件中
String charsetName : 指定编码表名称 不区分大小写,可以是utf-8/UTF-8 GBK/gbk …
不指定 默认使用utf-8/UTF-8
String charsetName : 指定编码表名称 不区分大小写,可以是utf-8/UTF-8 GBK/gbk …
不指定 默认使用utf-8/UTF-8

使用步骤:
1.创建OutputStreamWriter对象,构造方法中传递字节输出流 和指定的表码表名称
2.使用OutputStreamWriter对象中的方法 write 把字符转换为字节 存储到缓冲区中(编码)
3.使用OutputStreamWriter对象中的方法 flush ,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4.资源释放

public class Demo03OutputStreamWriter {
	public static void main(String[] args) throws IOException {
		//1.创建OutputStreamWriter对象,构造方法中传递字节输出流 和指定的表码表名称
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E://java//utf.txt"),"GBK");
		//2.使用OutputStreamWriter对象中的方法 write 把字符转换为字节 存储到缓冲区中(编码)
		osw.write("你好");
		//3.使用OutputStreamWriter对象中的方法 flush ,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
		//4.资源释放
		osw.close();
	}
}

2. InputStreamReader 转换输入流
是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符(解码:把看不懂的变成能看懂的)
java.io.InputStreamReader extends Reader

继承自父类的方法:
1.int read() 读一个字符 并返回
2.int read(char[] cbuf) 一次读取多个字符,将字符读入数组
3.void close() 关闭流并释放与之相关联的任何系统资源。

构造方法:
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 创建一个使用指定字符集的InputStreamReader。
参数:
InputStream in:字符输入流,用来读取文件中保存的字节
String charsetName:指定编码表名称 不区分大小写,可以是utf-8/UTF-8 GBK/gbk …
不指定 默认使用utf-8/UTF-8

使用步骤:
1.创建InputStreamReader对象,构造方法中传递字节输入流,和会自动的编码表名称
2.使用InputStreamReader对象中的方法 read 读取文件
3.释放资源
注意事项:构造方法中指定的编码表 要和 文件的编码相同,否则会发生乱码

public class Demo02InputStreamReader {
	public static void main(String[] args) throws IOException {
		//1.创建InputStreamReader对象,构造方法中传递字节输入流,和会自动的编码表名称
		InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java\\GBK.txt"),"GBK");
		int len = 0;
		//2.使用InputStreamReader对象中的方法 read 读取文件
		while((len = isr.read()) != -1) {
			System.out.println((char)len);
		}
		//3.释放资源
		isr.close();
	}
}

案例:将GBK编码的文件 转换为UTF-8编码的文件
1.创建InputStreamReader对象 构造方法中传入字节输入流,和指定编码表名称
2.创建OutputStreamWriter对象 构造方法中传入字节输出流,和指定编码表名称
3.使用InputStreamReader对象中的read方法 读取文件
4.使用OutputStreamWriter对象中的write方法 把读取的数据写人到文件中
5.释放资源

public class Test01 {
	public static void main(String[] args) throws IOException {
		//1.创建InputStreamReader对象  构造方法中传入字节输入流,和指定编码表名称
		InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java\\GBK.txt"),"GBK");
		//2.创建OutputStreamWriter对象  构造方法中传入字节输出流,和指定编码表名称
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java\\utf.txt"),"utf-8");
		int len = 0;
		//3.使用InputStreamReader对象中的read方法  读取文件
		while((len = isr.read())!=-1) {
			//4.使用OutputStreamWriter对象中的write方法  把读取的数据写人到文件中
			osw.write(len);
		}
		//5.释放资源
		isr.close();
		osw.close();
	}
}

序列化

概述:java提供了一种对象 序列化 机制。用一个字节序列化可以表示一个对象,该字节序列包含 对象的数据,对象的类型,对象存储的属性等
字节序列写出文件之后,相当于文件中持久保存了一个对象的信息

	反之,该字节序列化还可以从文件中读取回来,重构对象,对他进行反序列化。

1 .ObjectOutputStream:对象的序列化流
java.io.ObjectOutputStream extends OutputStream
作用:把对象以流的方式写入到文件中保存

构造方法:
ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
参数:OutputStream out :字节输出流

特有的成员方法:
void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。

使用步骤:
1.创建一个ObjectOutputStream对象 构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject 把对象写入到文件中
3.释放资源
注意:
序列化和反序列的时候,会抛出 NotSerializableException:没有序列化异常,
类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化

Serializable:也叫标记型接口
要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出NotSerializableException 没有序列化异常
类似于:市场卖肉:肉上有一个蓝色章(检测合格)-》放心购买 -》买回来怎么吃 随意

public class Demo01 {
	public static void main(String[] args) throws IOException {
		//1.创建一个ObjectOutputStream对象  构造方法中传递字节输出流
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java\\person.txt"));
		oos.writeObject(new Person("小美女",18));
		oos.close(); // java.io.NotSerializableException: 未序列化异常
	}
}

2. ObjectInputStream:对象的反序列化流
java.io.ObjectInputStream extends InputStream
作用:把文件中保存的对象,以流的方式读取出来使用

构造方法:
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。
参数:InputStream in:字节输入流
特有的成员方法:
Object readObject() 从ObjectInputStream读取一个对象。

使用步骤:
1.创建一个ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法:readObject 读取保存对象的文件
3.释放资源
4.使用读取出来的对象
ClassNotFoundException: class文件找不到异常
当不存在对象的class文件时 抛出异常
反序列前提:
1.类必须实现:Serializable
2.必须存在对应的class文件

transient关键字
transient关键字:瞬态关键字 (如果不想某个成员变量被序列化 加上transient关键字即可)
被transient修饰的成员变量 不能被序列化
序列化出来的对象 值是默认值
static关键字 :静态关键字:静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
被static修饰的成员变量不能被序列化的,序列化的都是对象
序列化出来的对象 值是默认值

public class Demo02 {
	public static void main(String[] args) throws ClassNotFoundException, IOException {
		//1.创建一个ObjectInputStream对象,构造方法中传递字节输入流
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java\\person.txt"));
		//2.使用ObjectInputStream对象中的方法:readObject 读取保存对象的文件
		Object o =  ois.readObject();
		//3.释放资源
		ois.close();
		//4.使用读取出来的对象
		System.out.println(o);//Person [name=小美女, age=18]
		Person person  = (Person)o;
		System.out.println(person.getName()+person.getAge());//小美女18
	}
}

反序列化操作2:
InvalidClassException 异常
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。
但是, 强烈建议所有可序列化的类都明确声明serialVersionUID值,因为默认的serialVersionUID计算对类详细信息非常敏感,这可能会因编译器实现而异,
因此可能会在反InvalidClassException化期间导致InvalidClassException的InvalidClassException。
因此,为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值。 还强烈建议,
显式的serialVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无效。
为了防止我们更改对象属性后,出现异常,UID改变 我们可以自己在对象中声明UID值
static final long serialVersionUID = 42L;

案例:序列化集合:
当我们想在文件中保存多个对象的时候
可以把多个对象存储在集合中,
对集合进行序列化和反序列化
分析:
1.定义存储person对象的Arraylist集合
2.往Arraylist集合存储person对象
3.创建一个序列化流 objectOutputstream
4.使用objectOutputstream方法中的ObjectWrite 对集合进行序列化
5.创建一个反序列化:objectInputstream
6.使用objectInputstream对象中的方法 readobject 读取文件中保存的集合
7.把object类型的集合 转换为Arraylist
8.遍历集合
9.释放资源

public class Test01 {
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		//1.定义存储person对象的Arraylist集合
		ArrayList<Person> list = new ArrayList<>();
		//2.往Arraylist集合存储person对象
		list.add(new Person("张三", 18));
		list.add(new Person("李四", 20));
		list.add(new Person("王五", 17));
		list.add(new Person("牛二", 22));
		list.add(new Person("赵六", 23));
		//3.创建一个序列化流 objectOutputstream
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java\\person.txt"));
		//4.使用objectOutputstream方法中的ObjectWrite 对集合进行序列化
		oos.writeObject(list);
		//5.创建一个反序列化:objectInputstream
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java\\person.txt"));
		//6.使用objectInputstream对象中的方法 readobject 读取文件中保存的集合
		Object obj= ois.readObject();
		//7.把object类型的集合 转换为Arraylist
		ArrayList<Person> list2 = (ArrayList<Person>)obj;
		//8.遍历集合
		for (Person person : list2) {
			System.out.println(person);
		}
		//9.释放资源
		oos.close();
		ois.close();
	}
}

打印流

平时我们在控制台打印输出,是调用print和println方法完成的,这两个方法其实就是来自java.io.PrintStream 类。
这个类能够方便的打印各种数据类型的值,是一种便捷的输出方式

PrintStream打印流
java.io.PrintStream extends OutputStream
PrintStream特点:
1.只负责数据的输出,不负责数据的读取
2.与其他输出流不同, PrintStream从不抛出IOException
3.有特有的方法print println
void print(任意类型的值)
void println(任意类型的值并换行)

构造方法:
PrintStream(File file) 输出的目的地是一个文件
PrintStream(OutputStream out) 输出的目的地是一个字节输出流
PrintStream(String fileName) 输出的目的地是一个文件路径

继承自父类特有的方法:
void close() 关闭此输出流并释放与此流相关联的任何系统资源。
void flush() 刷新此输出流并强制任何缓冲的输出字节被写出。
void write(byte[] b) 将 b.length字节从指定的字节数组写入此输出流。
void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
abstract void write(int b) 将指定的字节写入此输出流。

注意:
如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表97-a
如果使用自己特有的方法print println写数据,写的数据原样输出

public class Demo01 {
	public static void main(String[] args) throws FileNotFoundException {
		//1.创建打印流对象,构造方法中绑定要输出的目的地
		PrintStream ps = new PrintStream("E:\\java\\x.txt");
		//2.如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表97-a
		//2.如果使用自己特有的方法print println写数据,写的数据原样输出
		ps.write(97); //a
		ps.println(97);//97
		//3.释放资源
		ps.close();
	}
}

可以改变输出语句的目的地(打印流的流向)

输出语句:默认在控制台输出
使用System.setOut方法改变输出语句的目的地改为参数传递的打印流的目的地
static void setOut(PrintStream out) 重新分配“标准”输出流。

public class Demo2 {
	public static void main(String[] args) throws FileNotFoundException {
		System.out.println("在控制台输出");//在控制台输出
		PrintStream ps = new PrintStream("E:\\java\\arm.txt");
		System.setOut(ps); //把输出语句的目的地改为打印流的目的地
		System.out.println("我在打印流的目的地输出");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值