【Java文件流】你的“转换流、对象流、打印流、Properites”学习资料

在我学习Java的IO流过程中,有很多的类、每个类又有很多方法。经过我的几天经历,在看懂、听懂了别人的讲解后,一定要自己去敲出来,在IDE中运行出来,在一定程度上才算了解了这个东西。

尝试:

  • 牢牢把握、心中有数:类与接口之间的继承、实现关系;
  • 只要有继承、实现,方法就一定是通用的;
  • 心中清晰:有哪些类、接口,他们的主要功能是什么;

转换流

**转换流的功能:**将字符流和字节流,互相转换,并附加编码格式

  • InputStreamReader:字节到字符的桥梁。将字节流以特定的编码格式,转换为字符流。
  • OutputStreamWriter:字符到字节的桥梁。将字符流以特定的编码格式,转换为字节流。

结构体系

- java.lang.Object
  - java.io.Writer
    - java.io.OutputStreamWriter
      - java.io.FileWriter

- java.lang.Object
  - java.io.Reader
    - java.io.InputStreamReader
      - java.io.FileReader

FileWriter类和FileReader类:

  • 作为子类,仅作为操作字符文件的便捷类存在。
  • 当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。

OutputStreamWriter类

功能: 在将文本写入到磁盘文件的过程中,指定编码格式。

构造方法:

  • OutputStreamWriter(OutputStream out)
  • OutputStreamWriter(OutputStream out, String charsetName)

快速理解OutputSteamWriter:

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

public class Main {
	public static void main(String[] args) throws IOException {
		writerGBK(); //文件大小为4KB,因为GBK对中文是一个字2位Bytes
		writerUTF8(); //文件大小为6KB,因为UTF-8对中文是一个字3为Bytes
	}
	//将文本写入到磁盘文件中,以GBK编码形式写入
	public static void writerGBK() throws IOException {
		FileOutputStream fos = new FileOutputStream("C:\\users\\kyle\\desktop\\a.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos/*,"GBK"*/); //因为win10默认GBK编码,这里可以不用指定,默认使用系统编码
		osw.write("你好");
		osw.close();
	}
	//将文本写入到磁盘文件中,以UTF-8编码形式写入
	public static void writerUTF8() throws IOException {
		FileOutputStream fos = new FileOutputStream("C:\\users\\kyle\\desktop\\b.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
		osw.write("你好");
		osw.close();
	}
}

InputStreamReader

功能: 在将磁盘上的二进制文件读入到内存的过程中,指定解码格式。

快速理解InputStreamReader:

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

public class Main {
	public static void main(String[] args) throws IOException {
		readGBK(); //如果文件不是以GBK编码写入的,那么以GBK编码读取就会读出乱码
		readUTF8();
	}
	//从磁盘文件中读入内存,以GBK编码形式读入
	public static void readGBK() throws IOException {
		FileInputStream fis = new FileInputStream("C:\\users\\kyle\\desktop\\a.txt");
		InputStreamReader isr = new InputStreamReader(fis,"gbk");
		char[] ch = new char[1024];
		int len = isr.read(ch);
		System.out.println(new String(ch,0,len));
		isr.close();
	}
	//磁盘文件中读入内存,以UTF-8编码形式读入
	public static void readUTF8() throws IOException {
		FileInputStream fis = new FileInputStream("C:\\users\\kyle\\desktop\\a.txt");
		InputStreamReader isr = new InputStreamReader(fis,"utf-8");
		char[] ch = new char[1024];
		int len = isr.read(ch);
		System.out.println(new String(ch,0,len));
		isr.close();
	}
}

对象流

诞生背景:

  1. 当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了。
  2. 但是如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被重建并且拥有与程序上次运行时拥有的信息相同。可以使用对象的序列化。

对象流的两种类型:

  1. 对象的序列化:将内存中的对象直接写入到磁盘文件中。ObjectOutputStream
  2. 对象的反序列化:将磁盘文件中的数据转换为内存对象。ObjectInputStream

对象流框架:

  • ObjectOutput接口
    • ObjectOutputStream类:被写入的对象必须实现一个接口Serializable,该接口根本没有任何方法,直接“implements Serializable”即可。否则会抛出:NotSerializableException
  • ObjectInput接口
    • ObjectInputStream类:如果没有找到类的反序列文件,则抛出异常:ClassNotFountException

ObjectOutputStream

功能: 实现序列化,把对象写入到磁盘文件。

反序化的条件:

  1. 序列化时写入的文件;
  2. 被序列化类的class文件;注意被序列化类一定要单独成一个源文件,且保证该单独类的class没有丢失;

特性:

  1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口 。Serializable接口没有任何的方法,是一个标识接口而已。
  2. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
  3. 静态变量和方法不能被序列化

理解序列化:

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.FileOutputStream;

public class Main {
	public static void main(String[] args) throws IOException {
		writerObjectToFile();
	}
	public static void writerObjectToFile() throws IOException {
		String path = "C:\\users\\kyle\\desktop\\object.obj";
		FileOutputStream fos = new FileOutputStream(path);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		Person p = new Person("KYLE",20);
		oos.writeObject(p);
		oos.close();
	}
}

//被序列化类
class Person implements Serializable {
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String toString() {
		return "name: " + this.name + "; age: " + this.age;
	}
}

ObjectInputStream

功能: 实现反序列化,从磁盘文件读入到内存中。

反序化的条件:

  1. 序列化时写入的文件;
  2. 被序列化类的class文件;

特性:

  1. 对象的反序列化创建对象的时候并不会调用到构造方法。

反序列化使用示例

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

public class Test {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		readObjectFromFile();
	}
	public static void readObjectFromFile() throws IOException, ClassNotFoundException {
		String path = "C:\\users\\kyle\\desktop\\Object.obj";
		FileInputStream fis = new FileInputStream(path);
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		Object obj = ois.readObject();
		ois.close();
		System.out.println(obj);
	}
}

序列号问题

序列号(serialVersionUID)作用:
A. 标识一个序列化后的类,如果类再发生改变后序列化,相应的序列号也会发生改变。
B. serialVersionUID是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。

序列号问题的现象:

  1. 第一次序列化完毕,不对序列化类有任何修改,立即反序列化,是成功的
  2. 第一次序列化完毕,再对类做出修改,反序列化,则失败

产生序列号问题的原因(仔细阅读):

  1. 一个完整的类,状态为A

  2. 对其进行序列化,得到序列码为001
    a) 因为实现了Serializable接口,编译过程中,会有一个序列号在class文件中生成;
    b) 并且会把这个序列号写入到ObjectOutputStream对象所在的磁盘文件中;

  3. 这时对其反序列化,JVM会比较class文件中的序列号 和 ObjectOutputStream对象所写磁盘文件中 的序列号是否一致。一致,则反序列化成功。

  4. 这时,我修改了类的内容,使其变为了状态B

  5. 执行类的源文件编译使得类的class文件中的序列号变为了002而ObjectOutputStream对象所写磁盘文件中 的序列号依然是001

  6. 执行反序列化,两个序列号不一致,反序列化失败,抛出“InvalidClassException”异常。

如何解决: 即实现即时修改了源代码,也不重新计算序列号

  1. 做一个终身不变得序列号
  2. 在需要被序列化类中添加一行代码:“static final long serialVersionUID = 45645452L;”,等号的右边的数字可以自定义
  3. 这时编译器就不会给类计算序列号,而直接使用我们制定的序列号,从而避免了每次编译了都改变了序列号

打印流

功能: 向某种流容器中写入数据

打印流的两种类型:

  • PrintStream
  • PrintWriter

特性:

  1. 只能输出,不能输入。即此流不负责数据源,只负责数据目的
  2. 为其他输出流,添加功能
  3. 永远不会抛出IOException异常

类框架:

java.lang.Object				     java.lang.Object
 java.io.OutputStream				   java.io.Writer
  java.io.FilterOutputStream			 java.io.PrintWriter
   java.io.PrintStream

共性方法:

  1. void print(被打印内容); 打印完毕后不换行
  2. void println(被打印内容); 打印完毕后换行
  3. void write(被打印内容); Notice:print是完全安全原格式打印,write打印是走编码表,即先编码后打印

PrintWriter类使用示例:

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

public class Main {
	public static void main(String[] args) throws IOException {
		String path = "C:\\users\\kyle\\desktop\\a.txt";
		File file = new File(path);
		PrintWriter pw = new PrintWriter(file);
		pw.print(100);//不换行
		pw.println("hello"); //打印完后换行
		pw.print("kyle");
		pw.flush(); //字符流必须刷新,否则在文件中看不到
		pw.close();
	}
}

Properites类

类功能:

  1. 将软件中可变部分(操作、参数)的数据可以定义到一个文件中,方便以后更改,该文件称之为配置文件。
  2. Properites类就提供这个配置文件的读写操作。

类的本质:

  1. 是Hashtable的子类,map集合中的方法都可以用。
  2. 存储的是键值对,作用是持久化该键值对。键值都是字符串。

类特性:

  1. 该集合没有泛型。
  2. 有和流技术相结合的方法。

类框架:
在这里插入图片描述

构造方法:

  1. Properties() 创建一个无默认值的空属性列表。
  2. Properties(Properties defaults) 创建一个带有指定默认值的空属性列表。

类方法:

  1. 存入配置项:Object setProperty(String key, String value) 本质上是调用 Hashtable 的put方法。jdk1.6版本。

  2. 获取配置项:String getProperty(String key) 用指定的键在此属性列表中搜索属性。

  3. 将Properties类中的键存储到Set集合中:Set<String> stringPropertyNames() 类似于Map接口的方法keySet

  4. 使用方法:Properties类对象直接调用方法即可。

  5. 从配置文件加载
    a) void load(InputStream inStream) 从Properties类对象所指的流中加载属性列表(键和元素对),以字节流的形式。jdk1.6版本。
    b) void load(Reader reader) 从Properties类对象所指的流中加载属性列表(键和元素对),以字符流的形式。

  6. 写入到配置文件:
    a) void store(OutputStream out, String comments) 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。comments不要写中文。
    b) void store(Writer writer, String comments) 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。comments不要写中文。

快速理解Properties的使用方式:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class Main {
	public static void main(String[] args) throws IOException {
		storeProperties(); //写入到配置文件
		loadProperties(); //从配置文件加载
	}
	public static void storeProperties() throws IOException {
		Properties pro = new Properties();
		pro.setProperty("name", "kyle");
		pro.setProperty("age", "23");
		pro.setProperty("sey", "man");
		String path = "C:\\users\\kyle\\desktop\\pro.properties";
		
		FileOutputStream fos = new FileOutputStream(path); //FileWriter fw = new FileWriter(path);
		pro.store(fos, "Write you why change you config file. Notice:Not to write Chinese.");
		fos.close();
	}
	public static void loadProperties() throws IOException {
		String path = "C:\\users\\kyle\\desktop\\pro.properties";
		FileInputStream fos = new FileInputStream(path); //FileReader fr = new FileReader(path);
		Properties pro = new Properties();
		
		pro.load(fos); //文件中的流对象已经到Properties对象中
		fos.close();
		System.out.println(pro);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值