关闭

输入输出流回头看

279人阅读 评论(0) 收藏 举报
分类:

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">最近用到了输入输出流,感觉自己对这块不熟悉,于是回头看看,总结一下,记录下来,方便自己,方便他人。(以下内容参考某机构课件整理)</span>

目录:

1,file类简介。

2,路径分隔符。

3,绝对路径与相对路径

4,io流简介

4.1 字节流

4.2 字节缓冲流

4.3 字符流

4.3.1 Reader

4.3.2 Writer


1,file类简介。
File类中常用的方法:
创建:
createNewFile()在指定位置创建一个空文件,成功就返回true,如果已存在就不创建然后返回false
mkdir() 在指定位置创建目录,这只会创建最后一级目录,如果上级目录不存在就抛异常。
mkdirs() 在指定位置创建目录,这会创建路径中所有不存在的目录。
renameTo(File dest)重命名文件或文件夹,也可以操作非空的文件夹,文件不同时相当于文件的剪切,剪切时候不能操作非空的文件夹。移动/重命名成功则返回true,失败则返回false。
删除:
delete() 删除文件或一个空文件夹,如果是文件夹且不为空,则不能删除,成功返回true,失败返回false。
deleteOnExit()在虚拟机终止时,请求删除此抽象路径名表示的文件或目录,保证程序异常时创建的临时文件也可以被删除

判断:
exists() 文件或文件夹是否存在。
isFile() 是否是一个文件,如果不存在,则始终为false。
isDirectory() 是否是一个目录,如果不存在,则始终为false。
isHidden() 是否是一个隐藏的文件或是否是隐藏的目录。
isAbsolute() 测试此抽象路径名是否为绝对路径名。
获取:
getName() 获取文件或文件夹的名称,不包含上级路径。
getPath()       返回绝对路径,可以是相对路径,但是目录要指定
getAbsolutePath()获取文件的绝对路径,与文件是否存在没关系
length() 获取文件的大小(字节数),如果文件不存在则返回0L,如果是文件夹也返回0L。
getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回null。
lastModified()获取最后一次被修改的时间。
文件夹相关:
staic File[] listRoots()列出所有的根目录(Window中就是所有系统的盘符)
list() 返回目录下的文件或者目录名,包含隐藏文件。对于文件这样操作会返回null。
list(FilenameFilter filter)返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null。
listFiles() 返回目录下的文件或者目录对象(File类实例),包含隐藏文件。对于文件这样操作会返回null。
listFiles(FilenameFilter filter)返回指定当前目录中符合过滤条件的子文件或子目录。对于文件这样操作会返回null。




2,路径分隔符。
路径就是文件或文件夹所在的位置。
上下级文件夹之间使用分隔符分开:
在Windows中分隔符为'\',在Unix/Linux中分隔符为'/'。
跨平台的目录分隔符
更专业的做法是使用File.separatorChar或File.separator,这个值就会根据系统得到的相应的分割符。
例:new File("c:" + File.separator + "a.txt");
注意,如果是使用"\",则需要进行转义,写为"\\"才可以,如果是两个"\",则写为"\\\\"。
3,绝对路径与相对路径
对于UNIX平台,绝对路径名的前缀是"/"。相对路径名没有前缀。
对于Windows平台,绝对路径名的前缀由驱动器号和一个":"组成,例"c:\\..."。相对路径没有盘符前缀。
相对路径:
相对路径是指相对于某位置的路径,是指相对于当前目录。
在执行Java程序时,相对路径为执行java命令时当前所在的目录。


4,io流简介
File对象可以表示存在的文件或文件夹,也可以表示不存在的。
我们想要得到文件的内容怎么办,File只是操作文件,文件的内容如何处理就需要使用io流技术了。
例如在C盘下有一个名称为a.txt的文本文件.想要通过Java程序读出来文件中的内容,需要使用IO流技术.同样想要将程序中的数据,保存到硬盘的文件中,也需要IO流技术.
读和写文件文件示例:

public class IoTest {
	public static void main(String[] args) throws FileNotFoundException,
			IOException {
		writFileTest();

		readFileTest();
	}

	private static void writFileTest() throws FileNotFoundException,
			IOException {
		// 创建文件对象
		File file = new File("c:\\a.txt");
		// 创建文件输出流
		FileOutputStream fos = new FileOutputStream(file);
		fos.write('g');
		fos.write('z');
		fos.write('i');
		fos.write('t');
		fos.write('c');
		fos.write('a');
		fos.write('s');
		fos.write('t');
		fos.close();
	}

	private static void readFileTest() throws FileNotFoundException,
			IOException {
		// 创建文件对象
		File file = new File("c:\\a.txt");
		// 创建文件输入流
		FileInputStream fis = new FileInputStream(file);
		// 有对多长,就读多少字节。
		for (int i = 0; i < file.length(); i++) {
			System.out.print((char) fis.read());
		}
		fis.close();
	}
}

当完成流的读写时,应该通过调用close方法来关闭它,这个方法会释放掉十分有限的操作系统资源.如果一个应用程序打开了过多的流而没有关闭它们,那么系统资源将被耗尽.


I/O类库中使用“流”这个抽象概念。Java对设备中数据的操作是通过流的方式。
表示任何有能力产出数据的数据源对象,或者是有能力接受数据的接收端对象。“流”屏蔽了实际的I/O设备中处理数据的细节。IO流用来处理设备之间的数据传输。设备是指硬盘、内存、键盘录入、网络等。
Java用于操作流的对象都在IO包中。IO流技术主要用来处理设备之间的数据传输。
由于Java用于操作流的对象都在IO包中。所以使用IO流需要导包如:import java.io.*;
IO流的分类
流按操作数据类型的不同分为两种:字节流与字符流。

流按流向分为:输入流,输出流(以程序(内存)为参照物,输入到程序,或是从程序输出)

4.1 字节流
什么是字节流
    计算机中都是二进制数据,一个字节是8个2进制位.字节可以表示所有的数据,比如文本,音频,视频.图片,都是作为字节存在的.也就是说字节流处理的数据非常多。
在文本文件中存储的数据是以我们能读懂的方式表示的。而在二进制文件中存储的数据是用二进制形式表示的。我们是读不懂二进制文件的,因为二进制文件是为了让程序来读取而设计的。例如,Java的源程序(.java源文件)存储在文本文件中,可以使用文本编辑器阅读,但是Java的类(字节码文件)存储在二进制文件中,可以被Java虚拟机阅读。二进制文件的优势在于它的处理效率比文本文件高。
我们已经知道File对象封装的是文件或者路径属性,但是不包含向(从)文件读(写)数据的方法。为了实现对文件的读和写操作需要学会正确的使用Java的IO创建对象。
字节流的抽象基类:
输入流:java.io.InputStream
输出流:java.io.OutputStream
特点:
字节流的抽象基类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:FileInputStream, ByteArrayInputStream等。
说明:
字节流处理的单元是一个字节,用于操作二进制文件(计算机中所有文件都是二进制文件)
InputStream 字节输入流
InputStream 有read方法,一次读取一个字节,OutputStream的write方法一次写一个int。发现这两个类都是抽象类。意味着不能创建对象,那么需要找到具体的子类来使用。
通过查看api文档,找到了FileInputStream类,该类正是我们体验Io流的一个输入流。
实现;显示指定文件内容。
明确使用流,使用哪一类流?使用输入流,FileInputStream
第一步:
1:打开流(即创建流)
第二步:
2:通过流读取内容
第三步:
3:用完后,关闭流资源
显然流是Java中的一类对象,要打开流其实就是创建具体流的对象,由于是读取硬盘上的文件,应该使用输入流。所以找到了InputStream类,但是InputStream是抽象类,需要使用它的具体实现类来创建对象就是FileInputStream。通过new 调用FileInputStream 的构造方法来创建对象。发现FileInputStream的构造方法需要指定文件的来源。查看构造方法,可以接受字符串也可以接受File对象。我们通过构建File对象指定文件路径。
使用流就像使用水管一样,要打开就要关闭。所以打开流和关闭流的动作是比不可少的。如何关闭流?使用close方法即可,当完成流的读写时,应该通过调用close方法来关闭它,这个方法会释放掉十分有限的操作系统资源.如果一个应用程序打开了过多的流而没有关闭它们,那么系统资源将被耗尽.
如何通过流读取内容?
查找api文档通过read方法,查看该方法,发现有返回值,并且是int类型的,该方法一次读取一个字节(byte)
例子:

/**
	 * 使用字节数组当缓冲
	 * */
	private static void showContent7(String path) throws IOException {
		FileInputStream fis = new FileInputStream(path);
		byte[] byt = new byte[1024];
		int len = 0;
		while ((len = fis.read(byt)) != -1) {
			System.out.println(new String(byt, 0, len));
		}
	}

OutputStream 字节输出流
OutputStream。该类是抽象类,需要使用具体的实现类来创建对象查看API文档,找到了OutputStream的实现类FileOutputStream 创建FileOutputStream 流对象,必须指定数据要存放的目的地。通过构造函数的形式。创建流对象时,调用了系统底层的资源。在指定位置建立了数据存放的目的文件。
流程:
1:打开文件输出流,流的目的地是指定的文件
2:通过流向文件写数据
3: 用完流后关闭流
例子:

private static void writeTxtFile(String path) throws IOException {
		// 1:打开文件输出流,流的目的地是指定的文件 ,不加后面的true则会将内容写在开始出也即把之前的内容覆盖掉。加上之后,写在文件末尾处。
		FileOutputStream fos = new FileOutputStream(path,true);

		// 2:通过流向文件写数据
		byte[] byt = "java".getBytes();
		fos.write(byt);
		// 3:用完流后关闭流
		fos.close();
	}

使用字节流拷贝文件实现

public static void copyFile2(String srcPath, String destPath)
			throws IOException {
		// 打开输入流,输出流
		FileInputStream fis = new FileInputStream(srcPath);
		FileOutputStream fos = new FileOutputStream(destPath);

		// 读取和写入信息
		int len = 0;

		// 使用字节数组,当做缓冲区
		byte[] byt = new byte[1024];
		while ((len = fis.read(byt)) != -1) {
			fos.write(byt, 0, len);
		}

		// 关闭流
		fis.close();
		fos.close();
	}

异常处理
上述案例中所有的异常都只是进行了抛出处理,这样是不合理的。所以上述代码并不完善,因为异常没有处理。
当我们打开流,读和写,关闭流的时候都会出现异常,异常出现后,后面的代码都不会执行了。假设打开和关闭流出现了异常,那么显然close方法就不会再执行。那么会对程序有什么影响?
文件拷贝的异常处理:

public static void copyFile(String srcPath, String destPath) {

		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcPath);
			fos = new FileOutputStream(destPath);

			byte[] byt = new byte[1024 * 1024];
			int len = 0;
			while ((len = fis.read(byt)) != -1) {

				fos.write(byt, 0, len);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {

			try {
				if (fis != null) {
					fis.close();
				}
			} catch (IOException e) {
				throw new RuntimeException(e);
			} finally {
				if (fos != null) {
					try {
						fos.close();
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
				}

			}
		}

	}

4.2 字节缓冲流
上述程序中我们为了提高流的使用效率,自定义了字节数组,作为缓冲区.Java其实提供了专门的字节流缓冲来提高效率.
BufferedInputStream和BufferedOutputStream
BufferedOutputStream和BufferedOutputStream类可以通过减少读写次数来提高输入和输出的速度。它们内部有一个缓冲区,用来提高处理效率。

查看API文档,发现可以指定缓冲区的大小。

其实内部也是封装了字节数组。没有指定缓冲区大小,默认的字节是8192。
显然缓冲区输入流和缓冲区输出流要配合使用。首先缓冲区输入流会将读取到的数据读入缓冲区,当缓冲区满时,或者调用flush方法,缓冲输出流会将数据写出。
注意:当然使用缓冲流来进行提高效率时,对于小文件可能看不到性能的提升。但是文件稍微大一些的话,就可以看到实质的性能提升了。

public class IoTest5 {
public static void main(String[] args) throws IOException {
String srcPath = "c:\\a.mp3";
String destPath = "d:\\copy.mp3";
copyFile(srcPath, destPath);
}


public static void copyFile(String srcPath, String destPath)
throws IOException {
// 打开输入流,输出流
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);


// 使用缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);


// 读取和写入信息
int len = 0;


while ((len = bis.read()) != -1) {
bos.write(len);
}


// 关闭流
bis.close();
bos.close(); 
}


}

4.3 字符流

计算机并不区分二进制文件与文本文件。所有的文件都是以二进制形式来存储的,因此,从本质上说,所有的文件都是二进制文件。所以字符流是建立在字节流之上的,

它能够提供字符层次的编码和解码。例如,在写入一个字符时,Java虚拟机会将字符转为文件指定的编码(默认是系统默认编码),在读取字符时,再将文件指定的编码转化

为字符。常见的码表如下:
ASCII:a 97 美国标准信息交换码。用一个字节的7位可以表示。
ISO8859-1: 拉丁码表。欧洲码表,用一个字节的8位表示。又称Latin-1(拉丁编码)或“西欧语言”。ASCII码是包含的仅仅是英文字母,并且没有完全占满256个编码位置,

所以它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入192个字母及符号,
藉以供使用变音符号的拉丁字母语言使用。从而支持德文,法文等。因而它依然是一个单字节编码,只是比ASCII更全面。
GB2312: 英文占一个字节,中文占两个字节.中国的中文编码表。
GBK: 中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:  国际标准码规范,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode。
UTF-8: 最多用三个字节来表示一个字符。
(我们以后接触最多的是iso8859-1、gbk、utf-8)
查看上述码表后,很显然中文的‘中’在iso8859-1中是没有对映的编码的。或者一个字符在2中码表中对应的编码不同,例如有一些字在不同的编码中是有交集的,

例如bjg5 和gbk 中的汉字简体和繁体可能是一样的,就是有交集,但是在各自码表中的数字不一样。
例如 
使用gbk 将中文保存在计算机中,
中  国
对映  100  200   如果使用big5 打开
可能   ?  ...   
不同的编码对映的是不一样的。
很显然,我们使用什么样的编码写数据,就需要使用什么样的编码来对数据。
ISO8859-1:一个字节
GBK: 两个字节包含了英文字符和扩展的中文 ISO8859-1+中文字符
UTF-8 万国码表,推行的。是1~3个字节不等长。英文存的是1个字节,中文存的是3个字节,是为了节省空间。


那么我们之前学习的流称之为字节流,以字节为单位进行操作之情的操作全是英文,如果想要操作中文呢?
很显然,我们的中文就不能够再一个字节一个字节的读了。所以字节流处理字符信息时并不方便那么就出现了字符流。
字节流是 字符流是以字符为单位。
体验字符流:

public static void main(String[] args) throws IOException {
		
		String path = "c:\\a.txt";
		readFileByReader(path);
	}
	private static void readFileByReader(String path) throws IOException {
		FileReader fr = new FileReader(path);
		int len = 0;
		while ((len = fr.read()) != -1) {
			System.out.print((char) len);
		}
	}

总结:字符流就是:字节流 + 编码表,为了更便于操作文字数据。字符流的抽象基类:
Reader , Writer。
由这些类派生出来的子类名称都是以其父类名作为子类名的后缀,如FileReader、FileWriter。
4.3.1 Reader
方法:
1,int read():
读取一个字符。返回的是读到的那个字符。如果读到流的末尾,返回-1.
2,int read(char[]):
将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1.
3,close()
读取字符其实用的是window系统的功能,就希望使用完毕后,进行资源的释放
由于Reader也是抽象类,所以想要使用字符输入流需要使用Reader的实现类。查看API文档。找到了FileReader。
1,用于读取文本文件的流对象。
2,用于关联文本文件。
构造函数:在读取流对象初始化的时候,必须要指定一个被读取的文件。

如果该文件不存在会发生FileNotFoundException.

public class IoTest1_Reader {

	public static void main(String[] args) throws Exception {
		String path = "c:/a.txt";
		// readFileByInputStream(path);
		readFileByReader(path);
	}

	/**
	 * 使用字节流读取文件内容
	 * 
	 * @param path
	 */
	public static void readFileByInputStream(String path) throws Exception {
		InputStream in = new FileInputStream(path);

		int len = 0;
		while ((len = in.read()) != -1) {
			System.out.print((char) len);
		}

		in.close();
	}

	/**
	 * 使用字符流读取文件内容
	 */
	public static void readFileByReader(String path) throws Exception {
		Reader reader = new FileReader(path);
		int len = 0;
		while ((len = reader.read()) != -1) {
			System.out.print((char) len);
		}

		reader.close();
	}

}

4.3.2 Writer

Writer中的常见的方法:
1,write(ch): 将一个字符写入到流中。
2,write(char[]): 将一个字符数组写入到流中。
3,write(String): 将一个字符串写入到流中。
4,flush():刷新流,将流中的数据刷新到目的地中,流还存在。
5,close():关闭资源:在关闭前会先调用flush(),刷新流中的数据去目的地。然流关闭。
发现基本方法和OutputStream 类似,有write方法,功能更多一些。可以接收字符串。
同样道理Writer是抽象类无法创建对象。查阅API文档,找到了Writer的子类FileWriter
1:将文本数据存储到一个文件中。

public class IoTest2_Writer {

	public static void main(String[] args) throws Exception {
		String path = "c:/ab.txt";

		writeToFile(path);
	}

	/**
	 * 写指定数据到指定文件中
	 * 
	 */
	public static void writeToFile(String path) throws Exception {
		Writer writer = new FileWriter(path);
		writer.write('中');
		writer.write("世界".toCharArray());
		writer.write("中国");

		writer.close();
	}
}

2:追加文件:
默认的FileWriter方法新值会覆盖旧值,想要实现追加功能需要
使用如下构造函数创建输出流 append值为true即可。
FileWriter(String fileName, boolean append)
FileWriter(File file, boolean append)
3:flush方法
如果使用字符输出流,没有调用close方法,会发生什么?

private static void writeFileByWriter(File file) throws IOException {
		FileWriter fw = new FileWriter(file);
		fw.write('新');
fw.flush();
		fw.write("中国".toCharArray());
		fw.write("世界你好!!!".toCharArray());
		fw.write("明天");	
		// 关闭流资源
		//fw.close();
	}

程序执行完毕打开文件,发现没有内容写入.原来需要使用flush方法. 刷新该流的缓冲。
为什么只要指定claose方法就不用再flush方法,因为close也调用了flush方法.
字符流拷贝文件

public static void main(String[] args) throws Exception {
		String path1 = "c:/a.txt";
		String path2 = "c:/b.txt";

		copyFile(path1, path2);
	}

	public static void copyFile3(String path1, String path2) throws Exception {
			Reader reader = new FileReader(path1);
			Writer writer = new FileWriter(path2);

			int ch = -1;
			char [] arr=new char[1024];
			while ((ch = reader.read(arr)) != -1) {
				writer.write(arr,0,ch);
			}

			reader.close();
			writer.close();
		}

字节流可以拷贝视频和音频等文件,那么字符流可以拷贝这些吗?
经过验证拷贝图片是不行的。发现丢失了信息,为什么呢?
计算机中的所有信息都是以二进制形式进行的存储(1010)图片中的也都是二进制
在读取文件的时候字符流自动对这些二进制按照码表进行了编码处理,但是图片本来就是二进制文件,不需要进行编码。有一些巧合在码表中有对应,就可以处理,

并不是所有的二进制都可以找到对应的。信息就会丢失。所以字符流只能拷贝以字符为单位的文本文件
(以ASCII码为例是127个,并不是所有的二进制都可以找到对应的ASCII,有些对不上的,就会丢失信息。)

4.3.3 字符流的缓冲区

查看Reader 发现Reader,操作的是字符,我们就不需要进行编码解码操作,由字符流读到二进制,自动进行解码得到字符,写入字符自动编码成二进制.
Reader有一个子类BufferedReader。子类继承父类显然子类可以重写父类的方法,也可以增加自己的新方法。例如一次读一行就是常用的操作.那么BufferedReader 类就提供了

这个方法,可以查看readLine()方法具备 一次读取一个文本行的功能。很显然,该子类可以对功能进行增强。
注意:
在使用缓冲区对象时,要明确,缓冲的存在是为了增强流的功能而存在,所以在建立缓冲区对象时,要先有流对象存在.
缓冲区的出现提高了对流的操作效率。原理:其实就是将数组进行封装。
使用字符流缓冲区拷贝文本文件.

public class Demo7 {
	public static void main(String[] args) throws IOException {
		// 关联源文件
		File srcFile = new File("c:\\linux大纲.txt");
		// 关联目标文件
		File destFile = new File("d:\\linux大纲.txt");
		// 实现拷贝
		copyFile(srcFile, destFile);

	}

	private static void copyFile(File srcFile, File destFile)
			throws IOException {
		// 创建字符输入流
		FileReader fr = new FileReader(srcFile);
		// 创建字符输出流
		FileWriter fw = new FileWriter(destFile);

		// 字符输入流的缓冲流
		BufferedReader br = new BufferedReader(fr);
		// 字符输出流的缓冲流
		BufferedWriter bw = new BufferedWriter(fw);

		String line = null;
		// 一次读取一行
		while ((line = br.readLine()) != null) {
			// 一次写出一行.
			bw.write(line);
			// 刷新缓冲
			bw.flush();
			// 进行换行,由于readLine方法默认没有换行.需要手动换行
			bw.newLine();
		}
		// 关闭流
		br.close();
		bw.close();
	}
}

最近发现自己很怕去做一些事情,没有以前的冲劲了,其实是自己变懒了,你想拥有你从未拥有的东西,就要去做你从未做过的事。人生下来不就是修行,通过生活中的一切事来提高自己么?

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:68374次
    • 积分:1305
    • 等级:
    • 排名:千里之外
    • 原创:63篇
    • 转载:27篇
    • 译文:1篇
    • 评论:2条