JavaIO、文件和序列化

IO流

在生活中我们需要从本地上传数据或者将数据存储的本地,或者在网络编程中的数据传输,这些操作都可以用IO流实现。

一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。

Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。

IO流结构

下图是一个描述输入流和输出流的类层次图:
在这里插入图片描述
IO流分类:

  • 按照⽅向分:
    • 输⼊流:从磁盘文件或网络流到Java程序中,用于读取数据。
    • 输出流:从Java程序流向磁盘文件或网络,用于写入数据。
  • 按照单位分:
    • 字节流:一般用于操作二进制数据,数据的单位是byte(视频、音频、图片、exe、dll等⾮⽂本类型的文件,文本也可以)。
    • 字符流:一般用于操作文本数据,数据的单位是char(txt、xml、html等⽂本类型的文件)。
  • 按照功能分:
    • 节点流(字节流)
    • 处理流(对节点流进⾏处理,⽣成其他类型的流)

1. 字节流

按照⽅向可以分为输⼊字节流InputStream和输出字节流OutputStream。

介绍里面最常用的 FileInputStream 和 FileOutputStream

FileInputStream

文件输入流FileInputStream,该流用于从文件或网络读取数据。

创建方法:

1.可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream("C:/java/hello");

2.也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);

常用方法:

int read() 							以字节为单位读取数据
int read(byte b[]) 					将数据存⼊ byte 类型的数组中,返回数组中有效数据的⻓度
int read(byte b[],int off,int len)	将数据存⼊ byte 数组的指定区间内,返回数组⻓度
byte[] readAllBytes() 				将所有数据存⼊ byte 数组并返回
int available() 					返回当前数据流未读取的数据个数
void close() 						关闭数据流

输入流案例:

public static void main(String[] args) {
	//申明InputStream
	InputStream in = null;
	try {
		//创建字节输入流
		in = new FileInputStream("stream.txt");
		//按照字符流的读取数据的方式,将字符数组替换为字节数组即可
		int len = -1;
		byte [] buff = new byte[1024];
		while((len = in.read(buff))!=-1) {
			//将字节数组转换为字符串
			String str = new String(buff,0,len);
			System.out.print(str);
		}
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		try {
			//关闭输入流
			if(in != null) {
				in.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

FileOutputStream

文件输出流FileInputStream,该流用于将数据输出到文件中或者传输到网络中。

方法:

void write(int b) 						以字节为单位输出数据
void write(byte b[])byte数组中的数据输出
void write(byte b[],int off,int len)byte数组中指定区间的数据输出
void close() 							关闭数据流
void flush() 							将缓冲流中的数据同步到输出流中

输出流案例:

public static void main(String[] args) {
	//申明流
	OutputStream out = null;
	try {
		//创建流
		//1 输出数据到这个文件的开头
		out = new FileOutputStream("out.txt");
		//2 输出数据到这个文件的末尾
		// out = new FileOutputStream("out.txt",true);
		//输出数据只能输出字节或者字节数组,不能输出字符串
		//将输出的内容首先转换为字节数组
		String str = "这是一句要输出到输出文件的内容";
		byte [] buff = str.getBytes();
		out.write(buff);
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
		try {
			if(out!=null){
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

字节缓冲流(高效流)

BufferedInputStream和BufferedOuputStream字节缓冲流的操作和字节流的操作就是完全一样。

缓冲就是在读写之前将数据先导入到缓冲内存中,后面读写就直接从缓冲中进行,减少了直接从磁盘读写的次数,从而提高读写效率。

我们使用缓冲流编写一个文件拷贝的流:

public static void main(String[] args) {
	InputStream in = null;
	OutputStream out = null;
	BufferedInputStream bufIn = null;
	BufferedOutputStream bufOut = null;
	try {
		in = new FileInputStream("sp.zip");
		bufIn = new BufferedInputStream(in);
		out = new FileOutputStream("sp1.zip");
		bufOut = new BufferedOutputStream(out);
		int len = -1;
		byte[] buff = new byte[1024];
		while ((len = bufIn.read(buff)) != -1) {
			out.write(buff, 0, len);
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		try {
		if(in!=null)
			in.close();
		if(bufIn!=null)
			bufIn.close();
		if(bufOut!=null)
			bufOut.close();
		if(out!=null)
			out.close();
		} catch (Exception e2) {
		// TODO: handle exception
		}
}

2. 字符流

按照⽅向可以分为输⼊字符流Reader 和输出字符流Writer ,Reader 和Writer 都是抽象类,

Reader 实现了Readable 接口,可以将数据以字符的形式读⼊到缓冲区

Writer实现了Appendable 接口,可以将 char 类型的数据读⼊到数据缓冲区

在这里插入图片描述 在这里插入图片描述

字节流转换为字符流

InputStreamReader和OutputStreamWriter这两个处理流都是字符流,专门用来将字节流转换为字符。

字符和字节:

  • 英⽂、数字、符号:1字节 = 1字符
  • 汉字:1 个字符 = 3 个字节

InputStreamReader

InputStreamReader 处理流的功能是将字节输⼊流转换为字符输入流

在这里插入图片描述

OutputStreamWriter

OutputStreamWriter的功能是将输出字节流转成输出字符流,与InputStreamReader 相对应的,将输⼊字节流转成输⼊字符流
在这里插入图片描述

FileReader

FileReader就是创建一个内存和文件之间的读取数据的管道,按字符读取流中数据

int read() 								读取字符,如果已经到达文件的末尾,就返回-1int read(char[] cbuf) 					将字符读入数组。 返回实际读取的字符个数,如果到达文件的末尾就返回-1int read(char[] cbuf, int off, int len) 将字符读入数组的一部分。返回读取字符个数,如果到达文件的末尾返回-1long skip(long n) 						跳过字符 
close()									关闭流

如果不知道字符的个数,或者文件特别庞大,不能一次性读取到内存中,如何处理?

考虑循环读取:
循环读取的思路:内存中准备一个固定长度的数组,每次循环的读取固定长度的数据到数组中,将数组中的数据进行
处理,再读取,如果到最后一个就可能无法填满整个数组,那么处理的时候,就只能处理数组的一部分。

public static void main(String[] args) throws IOException {
	FileReader fr = new FileReader("temp\\笔记.txt");
	//准备字符数组
	char [] buff = new char[100];
	//循环读取
	//定义一个变量,表示实际读取字符个数
	int len = -1;
	//fr.read是将数据读取到buff数组, 并且返回实际读取到的字符个数,并且赋值给len,然后判断len的值
	while((len = fr.read(buff)) != -1) {
		//说明肯定读取到了数据
		//将字符数组转换为字符串
		String str = new String(buff,0,len);
		System.out.print(str);
	}
	//关闭输入流
	fr.close();
}

文件拷贝:

public static void main(String[] args) throws IOException {
	FileReader fr = new FileReader("temp\\笔记.txt");
	FileWriter fw = new FileWriter("temp\\笔记2.txt");
	//准备循环读取数据
	char [] buff = new char[100];
	int len = -1;
	while((len = fr.read(buff)) != -1) {
		//将读取到的字符直接使用输出流输出到文件
		fw.write(buff, 0, len);
	}
	//关闭输入流
	fr.close();
	fw.close();
}

FileWriter

FileWriter 类按字符向流中写入数据

void write(char[] cbuf) 					写入一个字符数组。 
void write(char[] cbuf, int off, int len) 	写入字符数组的一部分。 
void write(int c) 							写一个字符 
void write(String str) 						写一个字符串 
void write(String str, int off, int len) 	写一个字符串的一部分
close()										关闭流

FileWriter的重载的构造方法

FileWriter(File file, boolean append)
FileWriter(String fileName, boolean append)

如果第二个参数是true ,则字节将写入文件的末尾而不是开头。如果不写默认是false则从文件的开头开始写数据会覆盖之前的数据。

字符缓冲流

BufferedReader和BufferedWriter这两个类也是Reader和Writer的实现类,

所以也拥有FileReader和FileWriter的所有的功能。

创建方式和缓冲字节流创建方式一样,需要包裹一个Reader或者Write。

缓冲字符流除过基本的操作之外,还有额外的方法:

缓冲输入流可以按行读取:
String readLine()	读一行文字。
缓冲输出流有一个产生新行的API :
void newLine()		写一行行分隔符。

使用缓冲字符流拷贝文件:

public static void main(String[] args) {
	Reader reader = null;
	Writer writer = null;
	BufferedReader bufReader = null;
	BufferedWriter bufWriter = null;
	try {
		//创建流
		reader = new FileReader("src.txt");
		bufReader = new BufferedReader(reader);
		writer = new FileWriter("target.txt"); //装饰模式
		bufWriter = new BufferedWriter(writer);
		//开始复制
		String line = null;
		while((line=bufReader.readLine())!=null) {//当读取不到数据的时候返回null
			bufWriter.write(line);
			//添加一个换行符
			bufWriter.newLine();
			//刷新缓冲区
			bufWriter.flush();
		}
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
		try {
			if(reader!=null)
			reader.close();
			if(bufReader!=null)
			bufReader.close();
			if(bufWriter!=null)
			bufWriter.close();
			if(writer!=null)
			writer.close();
		} catch (Exception e2) {
			e2.printStackTrace();
		}
	}
}

File类

File类主要用于文件和目录的创建、文件的查找和文件的删除等。

每一个File对象就代表磁盘上的一个具体的物理文件。我们可以通过File类的提供的API操作磁盘上的文件。

File类的方法:

String getName()			获取文件名或目录名
String getParent()			获取父路径名的路径名字符串,如果则返回 null。
File getParentFile()		获取父路径名的抽象路径名,如果返回 null。
String getPath()			获取路径名字符串。
boolean isAbsolute()		测试路径名是否为绝对路径名。
String getAbsolutePath()	获取绝对路径名字符串。
boolean exists()			测试文件或目录是否存在。
boolean isDirectory()		测试文件是否是一个目录。
boolean isFile()			测试文件是否是一个标准文件。
lastModified()				查询文件最后一次被修改的时间。
long length()				获取文件的长度(字节数)。
boolean delete()			删除文件或目录。
String[] list()				获取文件和目录的名称所组成字符串数组。
list(FilenameFilter filter)	获取file所表示的路径下的所有的文件或者文件夹的名称数组
File[] listFiles()			获取当前file代表的目录下的所有的文件或者文件夹对象数组。
boolean mkdir()				创建目录。
mkdirs()					创建目录,包括创建必需但不存在的父目录。
renameTo(File dest)			重新命名文件。
setLastModified(long time)	设置由文件或目录的最后一次修改时间。
setReadOnly()				标记此文件或目录只读
static File createTempFile(...) throws IOException创建一个新的空文件
int compareTo(File pathname)按字母顺序比较路径名。
boolean equals(Object obj)	测试此路径名与给定对象是否相等。

序列化和反序列化

什么是序列化和反序列化?.

我们为了方便数据的传输或者存储将数据转换成两外一种形式的过程称之为序列化。

我们将另外一种形式的数据转换为我们需要的形式的过程称之为反序列化。

具体的来说,它是处理对象流的一种机制,即可以很方便的保存内存中java对象的状态,同时也为了方便传输。

序列化实现需要两个流:
ObjectInputStream(对象输入流)和ObjectOutputStream(对象输出流)

对象输入流就是用来反序列化的,对象输出流就是用来序列化的

ObjectInputStream

反序列化就是使用ObjectInputStream将数据读出来,并且转换为对象。

读取方法:

Object readObject()

案例:

public static void main(String[] args) {
	InputStream in = null;
	ObjectInputStream objIn = null;
	try {
		in = new FileInputStream("ps.txt");
		objIn = new ObjectInputStream(in);
		//读取对象 反序列化
		Object obj = objIn.readObject();
		if(obj instanceof Person) {
			Person p = (Person)obj;
			System.out.println(p);
		}
		obj = objIn.readObject();
		if(obj instanceof Person) {
			Person p = (Person)obj;
			System.out.println(p);
		}
		obj = objIn.readObject();
		if(obj instanceof Person) {
			Person p = (Person)obj;
			System.out.println(p);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
	//关闭所有
	}
}

ObjectOutputStream

序列化就是使用ObjectOutputStream将流以对象的方式持久存储到本地或者网上

存储对象方法:

void writeObject(Object obj)

案例:

先准备一个类:
如果希望一个类的对象是可以被序列化的,则这个类必须实现接口java.io.Serializable,这个接口并没有任何方法,只
是纯粹的表这个当前这个类对象可以被序列化。

public class Person implements java.io.Serializable {
	private String name;
	private int age;
	public Person() {
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
}
public static void main(String[] args) {
	Person p1 = new Person("张三",18);
	Person p2 = new Person("李四",18);
	Person p3 = new Person("王五",18);
	//所谓序列化p1就是将这个p1对象以二进制的形式写入到文件中
	OutputStream out = null;
	ObjectOutputStream objectOut = null;
	try {
		out = new FileOutputStream("ps.txt",true);
		objectOut = new ObjectOutputStream(out);
		//写对象
		objectOut.writeObject(p1);
		objectOut.writeObject(p2);
		objectOut.writeObject(p3);
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
	//关闭流
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值