java学习笔记之文件IO流详解

JAVA的文件IO流分为两种, 字符流和字节流,其中字节流两个基类:InputStream,OutStream,字符流两个基类 Writer Reader, IO流用于操作数据,以操作文件为主。

首先先讲讲在文件操作上,如何选择流的问题。我们通过三个步骤来确定流的选取。

一,明确源和目的,源:输入流  InputStream    Reader         目的:输出流 OutputStream    Writer
二、明确操作的数据是否是纯文本,是:字符流,否:字节流
三、当体系明确后,在明确要使用哪个具体的对象



字符流


首先,我们先来讲讲IO流的字符流。字符流的基类是Writer和Reader,其子类有非常的多,今天我们就主要讲讲FileReader,FileWriter和BufferedReader,BufferedWriter。


1、FileReader,FileWriter。

FileReader是inputStreamReader(这是讲一个字节流按照编码集转换成字符流的类的)的子类,其默认使用的是GBK的编码。FileWriter是outputStreamWriter(此类和inputStreamReader是成对的存在,也是将字节流转换成字符流的类)的子类。它们的使用范例如下:

	//使用字符流读取一个文件的内容并写入到一个新的文件中去
	public static void copy_2() {
		FileReader rd = null;
		FileWriter wr = null;
		try {
			
			//拿到输入流
			rd = new FileReader(
					"F:\\j2eesoftware\\workspace\\IOTest\\src\\zifuliu\\WorkTest.java");
			//拿到输出流,并创建文件
			wr = new FileWriter("copy2.txt");
			char[] buf = new char[1024];
			int len = 0;
			while ((len = rd.read(buf)) != -1) {
				//将读到的流写入文件中去
				wr.write(buf, 0, len);
			}

		} catch (IOException e) {
			throw new RuntimeException("读写失败");
		} finally {
			if (rd != null)
				try {
					//关闭流
					rd.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if (wr != null)
				try {
					//关闭流
					wr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
在使用fileWriter的时候需要特别注意的是,如果使用构造方法wr = new FileWriter("copy2.txt"),会将之前写入的内容全部覆盖,即每次都是创建一个新的文件。如果不想覆盖原有内容而是在文件末端写入内容,应该调用构造方法wr = new FileWriter(file, append);最后一个参数为boolean型,如果为 true,则将数据写入文件末尾处,而不是写入文件开始处

2、BufferedReader,BufferedWriter

这两个类是为了更高效的操作字符流而存在的类。起构造方法中必须传入一个reader和writer,其使用方法如下:

//使用BufferedReader,BufferedWriter高效读取文件
	public static void copy() {
		BufferedReader bufrd = null;
		BufferedWriter bufwr = null;
		try {
			bufrd = new BufferedReader(
					new FileReader(
							"F:\\j2eesoftware\\workspace\\IOTest\\src\\zifuliu\\FileRe.java"));
			bufwr = new BufferedWriter(new FileWriter("copyBuf.txt"));
			String line = null;
			while ((line = bufrd.readLine()) != null) {
				bufwr.write(line);
				//使用bufferWriter时需要特别注意,如果是做聊天功能的时候,这端在写的时候,結尾的時候注意一定要写newLine(),不然另外一段的reader好像会卡主不动还是读不出内容(好像是这样的问题,如果有错误,请留言指正)
				bufwr.newLine();
				bufwr.flush();
			}

		} catch (Exception e) {
			throw new RuntimeException("读写失败");
		} finally {
			if (bufrd != null)
				try {
					bufrd.close();
				} catch (IOException e) {
					throw new RuntimeException("读关闭失败");
				}
			if (bufwr != null)
				try {
					bufwr.close();
				} catch (IOException e) {
					throw new RuntimeException("写关闭失败");
				}
		}
	}

3、inputStreamReader,outputStreamWriter

刚刚提到了这两个类,那么也顺便讲讲吧,这两个类的主要作用在于字节流转换成字符流的时候。

有时候,我们想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中,但是刚刚说了,FileWriter使用的是默认编码表GBK,所以,存储时,需要使用转换流,OutputStreamWriter, 该转换流对象接受一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream

OutputStreamWriter osw = new  OutputStreamWriter(new FileOutputStream("sss.txt"),"utf-8")

 我们可以再高效的封装一下
 BufferedWriter w = new BufferedWriter(osw);

后续操作参照以上。


字节流


字节流两个基类:InputStream,OutStream。这里我们主要讲解他们的子类FileInputStream、FileOutStream和ObjectInputStream、ObjectOutputStream。

FileInputStream 从文件系统中的某个文件中获取输入字节。哪些文件可用取决于主机环境。 

FileInputStream 用于读取诸如图像数据之类的原始字节流。

FilterOutputStream 是过滤输出流的所有类的超类。这些流位于已存在的输出流(基础 输出流)之上,它们将已存在的输出流作为其基本数据接收器,但可能直接传输数据或提供一些额外的功能。

FilterOutputStream 类本身只是简单地重写那些将所有请求传递给所包含输出流的 OutputStream 的所有方法。FilterOutputStream 的子类可进一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。 

下面以复制一个文件为例子,参考代码如下:

public static void main(String[] args) {
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try {
			//拿到流
			fis = new FileInputStream("E:\\moli.jpg");
			fos = new FileOutputStream("moli.jpg");

			byte[] buf = new byte[1024];
			int len = 0;
			while ((len = fis.read(buf)) != -1) {
				//读到多少写多少
				fos.write(buf, 0, len);
			}

		} catch (Exception e) {
			throw new RuntimeException("复制文件失败");
		} finally {
			try {
				//关闭流
				if (fis != null)
					fis.close();

			} catch (Exception e2) {
				throw new RuntimeException("关闭读失败");
			}
			try {
				//关闭流
				if (fos != null)
					fos.close();

			} catch (Exception e3) {
				throw new RuntimeException("关闭写失败");
			}

		}

	}

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久性存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。

ObjectInputStream 确保从流创建的图形中所有对象的类型与 Java 虚拟机中的显示的类相匹配。使用标准机制按需加载类。

只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过使用流中的文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。


参考代码如下(这里讲对象传入一个文件中,但是我们在实际开发中,应该是将对象以流的方式进行网络传输,这里仅供参考):

对象类:

import java.io.Serializable;

public class Person implements Serializable {
	String name;
	int age;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	
	public String toString()
	{
		return name+":"+age;
	}
	

}

实例代码:

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {

		writeobj();
		readobj();
		
	}

	private static void readobj() throws FileNotFoundException, IOException, ClassNotFoundException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
		Person p = (Person)ois.readObject();
		System.out.println(p.toString());
		ois.close();
	}

	private static void writeobj() throws FileNotFoundException, IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
		
		oos.writeObject(new Person("lisi",29));
		
		oos.close();
		
	}

有时候,我们在做网络通信的时候,似乎只能拿到字节流,那么,当用字节流作为传输对象的时候,如果出现中文,我们就需要对其进行编码操作。那么这个时候,就涉及到字符编码问题。常见的编码表在这里就不在多说。在这里只说说方法即可。

传输的时候,getBytes(String charsetName) ,收取解码的时候,new String(byte[] bytes, String charsetName) 即可。最常见的用法是在做web开发的时候,当用GET方法提交数据的时候,只能手动的去解决乱码问题,因为浏览器的解码编码为ISO8859-1,于是我们解决编码的时候就new String(data.getBytes("ISO8859-1"),"UTF-8");即可。 


到此,文件IO流已经讲解完毕。文件IO流主要操作对象就是文件,在JAVA中,文件操作对象为File类,在这里不再详细描述,相关操作请参考JDK文档即可。在这里在着重讲一个特殊的文件操作类

RandomAccessFile

此类的实例支持对随机存取文件的读取和写入。随机存取文件的行为类似存储在文件系统中的一个大型字节数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机存取文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。

先看该类的创建方法:

public RandomAccessFile(File file,
                        String mode)

在该类的创建方法中,有一个需要传入一个文件和一个mode

mode 参数指定用以打开文件的访问模式。允许的值及其含意为:

含意

"r"以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException
"rw"打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws"打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到基础存储设备。
"rwd"  打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到基础存储设备。
"rws""rwd" 模式的工作方式极其类似 FileChannel 类的 force(boolean) 方法,分别传递 truefalse 参数,除非它们始终应用于每个 I/O 操作,并因此通常更为高效。如果该文件位于本地存储设备上,那么当返回此类的一个方法的调用时,可以保证由该调用对此文件所做的所有更改均被写入该设备。这对确保在系统崩溃时不会丢失重要信息特别有用。如果该文件不在本地设备上,则无法提供这样的保证。

"rwd" 模式可用于减少执行的 I/O 操作数量。使用 "rwd" 仅要求更新要写入存储的文件的内容;使用 "rws" 要求更新要写入的文件内容及其元数据,这通常要求至少一个以上的低级别 I/O 操作。

如果存在安全管理器,则使用 file 参数的路径名作为其参数调用它的 checkRead 方法,以查看是否允许对该文件进行读取访问。如果该模式允许写入,那么还使用该路径参数调用该安全管理器的 checkWrite 方法,以查看是否允许对该文件进行写入访问。 

使用示例:

public static void main(String[] args) throws IOException {

		method_1();
		readFile();
		write();

	}

	public static void readFile() throws IOException {
		RandomAccessFile rr = new RandomAccessFile("random.txt", "r");
		// 调整对象中的指针,前后都可以
		rr.seek(8);

		byte[] buf = new byte[4];
		rr.read(buf);
		String s = new String(buf);
		out(s);
		int age = rr.readInt();
		out(age);

	}

	public static void method_1() throws IOException {
		RandomAccessFile ra = new RandomAccessFile("random.txt", "rw");
		ra.write("lisi".getBytes());
		ra.writeInt(68);
		ra.write("王五".getBytes());
		ra.writeInt(98);
		ra.close();
	}

	public static void write() throws IOException {
		RandomAccessFile ra = new RandomAccessFile("random.txt", "rw");
		ra.write("张三".getBytes());
		ra.writeInt(20);
		ra.close();
	}

	public static void out(Object obj) {
		System.out.println(obj);
	}


希望以上内容能够帮助到读到此文的人。谢谢。




















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值