在我学习Java的IO流过程中,有很多的类、每个类又有很多方法。经过我的几天经历,在看懂、听懂了别人的讲解后,一定要自己去敲出来,在IDE中运行出来,在一定程度上才算了解了这个东西。
尝试:
- 牢牢把握、心中有数:类与接口之间的继承、实现关系;
- 只要有继承、实现,方法就一定是通用的;
- 心中清晰:有哪些类、接口,他们的主要功能是什么;
文章目录
- 转换流
- OutputStreamWriter类
- InputStreamReader
- 对象流
- ObjectOutputStream
- ObjectInputStream
- 序列号问题
- 打印流
- Properites类
转换流
**转换流的功能:**将字符流和字节流,互相转换
,并附加编码格式
。
- 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();
}
}
对象流
诞生背景:
- 当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了。
- 但是
如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被重建并且拥有与程序上次运行时拥有的信息相同
。可以使用对象的序列化。
对象流的两种类型:
- 对象的序列化:将内存中的对象直接写入到磁盘文件中。
ObjectOutputStream
。 - 对象的反序列化:将磁盘文件中的数据转换为内存对象。
ObjectInputStream
。
对象流框架:
ObjectOutput接口
- ObjectOutputStream类:被写入的对象必须实现一个接口Serializable,该接口根本没有任何方法,直接“implements Serializable”即可。否则会抛出:NotSerializableException
ObjectInput接口
- ObjectInputStream类:如果没有找到类的反序列文件,则抛出异常:ClassNotFountException
ObjectOutputStream
功能: 实现序列化,把对象写入到磁盘文件。
反序化的条件:
- 序列化时写入的文件;
- 被序列化类的class文件;
注意被序列化类一定要单独成一个源文件,且保证该单独类的class没有丢失;
特性:
- 如果对象需要被写出到文件上,那么
对象所属的类必须要实现Serializable接口 - 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
静态变量和方法不能被序列化
理解序列化:
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
功能: 实现反序列化,从磁盘文件读入到内存中。
反序化的条件:
- 序列化时写入的文件;
- 被序列化类的class文件;
特性:
- 对象的反序列化创建对象的时候并不会调用到构造方法。
反序列化使用示例
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这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
序列号问题的现象:
- 第一次序列化完毕,不对序列化类有任何修改,立即反序列化,是成功的
- 第一次序列化完毕,再对类做出修改,反序列化,则失败
产生序列号问题的原因(仔细阅读):
- 一个完整的类,状态为A
- 对其进行序列化,得到序列码为001
a) 因为实现了Serializable接口,编译过程中,会有一个序列号在class文件中
生成;
b) 并且会把这个序列号写入到ObjectOutputStream对象所在的磁盘文件
中; - 这时对其反序列化,JVM会比较class文件中的序列号 和 ObjectOutputStream对象所写磁盘文件中 的序列号
是否一致
。一致,则反序列化成功。 - 这时,我修改了类的内容,使其变为了状态B
- 执行类的源文件
编译
,使得类的class文件中的序列号变为了002
,而ObjectOutputStream对象所写磁盘文件中 的序列号依然是001
- 执行反序列化,两个
序列号不一致,反序列化失败
,抛出“InvalidClassException
”异常。
如何解决: 即实现即时修改了源代码,也不重新计算序列号
- 做一个终身不变得序列号
- 在需要
被序列化类中添加
一行代码:“static final long serialVersionUID = 45645452L;”,等号的右边的数字可以自定义 - 这时编译器就不会给类计算序列号,而直接使用我们制定的序列号,从而避免了每次编译了都改变了序列号
打印流
功能: 向某种流容器中写入数据
打印流的两种类型:
- PrintStream
- PrintWriter
特性:
- 只能输出,不能输入。即此流不负责数据源,只负责数据目的
- 为其他输出流,添加功能
- 永远不会抛出IOException异常
类框架:
java.lang.Object java.lang.Object
java.io.OutputStream java.io.Writer
java.io.FilterOutputStream java.io.PrintWriter
java.io.PrintStream
共性方法:
-
void print(被打印内容)
; 打印完毕后不换行 -
void println(被打印内容)
; 打印完毕后换行 -
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类
类功能:
- 将软件中可变部分(操作、参数)的数据可以定义到一个文件中,方便以后更改,该文件称之为配置文件。
- Properites类就提供这个配置文件的读写操作。
类的本质:
- 是Hashtable的子类,map集合中的方法都可以用。
- 存储的是键值对,作用是持久化该键值对。键值都是字符串。
类特性:
- 该集合没有泛型。
- 有和流技术相结合的方法。
类框架:
构造方法:
- Properties() 创建一个无默认值的空属性列表。
- Properties(Properties defaults) 创建一个带有指定默认值的空属性列表。
类方法:
- 存入配置项:
Object setProperty(String key, String value)
本质上是调用 Hashtable 的put方法。jdk1.6版本。 - 获取配置项:
String getProperty(String key)
用指定的键在此属性列表中搜索属性。 - 将Properties类中的键存储到Set集合中:
Set<String> stringPropertyNames()
类似于Map接口的方法keySet - 使用方法:Properties类对象直接调用方法即可。
- 从配置文件加载:
a)void load(InputStream inStream)
从Properties类对象所指的流中加载属性列表(键和元素对),以字节流的形式。jdk1.6版本。
b)void load(Reader reader)
从Properties类对象所指的流中加载属性列表(键和元素对),以字符流的形式。 - 写入到配置文件:
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);
}
}