文章的许多内容来源于:
- https://blog.csdn.net/Chianz632/article/details/79946851
- https://blog.csdn.net/jediael_lu/article/details/26813153
- https://www.cnblogs.com/liyanyan665/p/11198605.html
JAVA中可以分为两种流:字节流和字符流。如果细分的话有,可分为:4个文件流,4个缓冲流,2个转换流,2个打印流,2个序列化流,2个数据流。
文件流
FileInputStream //字节输入流
FileOutputStream //字节输出流
FileReader //字符输入流
FileWriter //字符输出流
缓冲流
BufferedInputStream //字节输入缓冲流
BufferedOutputStream //字节输出缓冲流
BufferedReader //字符输入缓冲流
BufferedWriter //字符输出缓冲流
序列化流
ObjectInputStream
ObjectOutputStream
转换流
(字符流) 将字节流转换为字符流
InputStreamReader
OutputStreamWriter
数据流
DataInputStream
DataOutputStream
打印流
PrintWriter
PrintStream
本文只讲文件流和序列化流的使用
一、字节流FileInputStream 和FileOutputStream
有人可能会好奇,为什么存储文件时用Output,而读取文件却时Input呢,其实这里的存储和读取时相对一内存而言的。
FileOutputStream
: 将内存中的数据存储到硬盘中。 所以是Output
FileInputStream:
读取硬盘中的数据到内存中,所以是Input
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Scanner reader = new Scanner(System.in); //从控制台读取
//写文件
File file=new File("wenjian.txt"); //文件名
FileOutputStream fos =new FileOutputStream(file,true); //true意思是 追加存储
System.out.println("请输入要写入的内容:");
String message=reader.next(); //从控制台读取字符
byte bytes[]=message.getBytes();
fos.write(bytes,0,bytes.length); //存入
fos.close(); //关闭流System
System.out.println("文件写入成功!");
//读文件
FileInputStream fis=new FileInputStream(file);
byte b[]=new byte[1024]; //每次做多读取1kb的数据
int temp=0;
while( (temp=fis.read(b))!=-1 ) { //将数据读取到缓冲区,并返回读取的字节数(temp) ,-1表示读取完了
System.out.println(new String(b,0,temp)); //String有将字节数组转换位String的构造方法
}
}
二、序列化流:ObjectInputStream 和 ObjectOutputStream
- 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存,这就需要用到
序列化流
。比如最常见的是Web服务器中的Session对象,当有10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中,说白了,就是能将一个2进制文件变成内存中的对象。 - JAVA的自定义实体类,是没有getByte()方法的,无法将对象直接转换成字节流,那么对象应该怎么进行传输?
在JAVA中,要实现这种机制,只要实现Serializable接口
就可以了,先看下面这个简单例子,serialVersionUID
稍后引出。 - 我们先定义一个简单的Person类,然后创建这个对象,最后序列化它到一个文件。
public class Person implements Serializable { //注意:这里用来Serializable接口
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/将对象序列化到一个文件中**/
public class WhySerialversionUID {
public static void main(String[] args) throws Exception {
Person person= new Person();
person.setName("jack");
File file = new File("E://jack.test"); //创建file
FileOutputStream fileoutStream =new FileOutputStream(file ); //为创建字节输出流对象
ObjectOutputStream oo = new ObjectOutputStream (fileoutStream ); //创建对象输出流对象
oo.writeObject(person);
oo.close();
/还原文件*/
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E://jack.test")));
Person person = (Person)ois.readObject();
String name= person.getName();
System.Out.Print("name is: "+name);
一切都那么顺利,但是如果在序列化之后,Person这个类发生了改变呢?比如,多了一个成员变量。之后,我们再去运行一下还原,就发现运行出错了。这是因为类只要一改边,那么类中的serialVersionUID就会改变,虽然类中我们没有指定serialVersionUID这个变量,但编译器会自动为我们添加一个。要杜绝这种错误,可以显示的添加一个serialVersionUID变量,这样当序列化存储之后,类即便改变了,serialVersionUID的值也不会变化,依然可以序列化还原。
/自己添加serialVersionUID变量**/
private static final long serialVersionUID = 1L;
class Person implements serialVersionUID{ } 后代码的左边会出现一个感叹号,点击一下系统即可自动生成唯一的serialVersionUID值。