栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java IO流

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java IO流

输出/输入

IO(输入/输出)是比较乏味的事情,因为看不到明显的运行效果,但输入/输出是所有程序都必需的部分----使用输入机制,允许程序读取外部数据(包括来自磁盘、光盘等存储设备的数据)、用户输入数据,使用输出机制,允许程序记录允许状态,将程序数据输出到磁盘、光盘等存储设备。

使用File类访问本地文件系统 File类

File类是Java.io包下代表与平台无关的文件和目录,也就是说,如果希望在程序中操作文件和目录,都可以通过File类来完成。值得指出的是,不管是文件还是目录都是使用File来操作的,File能新建、删除、重命名文件和目录,File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。

访问文件和目录

File类可以使用文件路径字符串来创建File实例,该文件路径字符串既可以是绝对路径,也可以是相对路径。一旦创建了File对象后,就可以调用File对象的方法来访问,File提供了很多方法来操作文件和目录。

方法名说明
String getName()返回此FIle对象所表示的文件名或路径名(如果是路径,则返回最后以及子路径名)
String getPath()返回此File对象所对应的路径名
File getAbsoluteFile()返回此File对象的绝对路径
String getAbsolutePath()返回此File对象所对应的绝对路径名
String getParent()返回此File对象所对应目录(最后一级子目录)的父目录名
Boolean renameTo(File newName)重命名此File对象所对应的文件或目录,如果重命名成功,则返回true;否则返回false
文件检测相关方法
方法名说明
boolean exists()判断File对象所对应的文件和目录是否存在
boolean canWrite()判断File对象所对应的文件和目录是否可写
boolean canRead()判断File对象所对应的文件和目录是否可读
boolean isFile()判断File对象所对应的是否是文件,而不是目录
boolean isDirectory判断File对象所对应的是否是目录,而不是文件
boolean isAbsolute()判断File对象所对应的文件和目录是否是绝对路径。该方法消除了不同平台的差异,可以直接判断File对象是否为绝对路径。在UNIX/Linux/BSD等系统上,如果路径名开头是一条斜线(/),则表明该File对象对应一个绝对路径;在Window系统上,如果路径开头是盘符,说明它是一个绝对路径。
获取常规文件信息
方法名说明
long lastModified()返回文件的最后修改时间
long length()返回文件内容的长度
文件操作相关的方法
方法名说明
boolean createNewFile()当此File对象所对应的文件不存在时,该方法将新建一个该File对象所指定的新文件,如果创建成功则返回true;否则返回false
boolean delete()删除File对象所对应的文件或路径
static File createTempFile(String prefix,String suffix)在默认的临时文件目录中创建一个临时的空文件,使用给定前缀、系统生成的随机数和给定后缀作为文件名。这是一个静态方法,可以直接通过File类来调用。prefix参数必须至少是3个字节长。建议前缀使用一个短的、有意义的字符串,比如"hjb"或"mail"。suffix参数可以为null,在这种情况下,将使用默认的后缀".tmp"
static File createTempFile(String prefix,String suffix,FIle directory)在directory所指定的目录中创建一个临时的空文件,使用给定前缀、系统生成的随机数和给定后缀作为文件名。这是一个静态方法,可以直接通过File类来调用。
void deleteonExit()注册一个删除钩子,指定当Java虚拟机退出时,删除File对象所对应的文件和目录
目录操作相关的方法
方法名说明
boolean mkdir()试图创建一个File对象所对应的目录,如果创建成功,则返回true;否则返回false。调用该方法时File对象必须对应一个路径,而不是一个文件。
String[ ] list()列出File对象的所有子文件名和路径名,返回String数组
File[ ] listFiles()列出File对象的所有子文件和路径,返回File数组
static File[ ] listRoots()列出系统所有的根路径。这是一个静态方法,可以直接通过File类来调用

Windows的路径分隔符使用反斜线(),而Java程序中的反斜线表示转义字符,所以如果需要在Windows的路径下包括反斜线,则应该使用两条反斜线,如F: anc test.txt,或者直接使用斜线(/)也可以,Java程序支持将斜线当成平台无关的路径分隔符

使用文件过滤器

FilenameFilter接口里包含了一个accept(File dir,String name)方法,该方法将依次对指定File的所有子目录或者文件进行迭代,如果该方法返回true,则list()方法会列出该子目录或者文件。

public static void main(String[] args) {

		File file = new File("D:/TestDownload");

		// listFiles(FileFilter filter)
		// FileFilter是过滤规则
		File[] childList = file.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				// return true; 代表要筛选出来的
				// return false; 代表不需要的
				return pathname.getName().endsWith(".pdf");
			}
		});

		for (File file2 : childList) {
			System.out.println(file2);
		}

	}

通过实现accept()方法,实现accept()方法就是指定自己的规则,指定哪些文件应该由list()方法列出。

理解Java的IO流

Java的IO流就是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出源(键盘、文件、网络连接等)抽象表述为"流"(stream),通过流的方式允许Java程序使用相同的方式来访问不同的输入/输出源。stream是从起源(source)到接受(sink)的有序数据。

流的分类 1. 输入流和输出流

输入流:只能从中读取数据,而不能向其写入数据

输出流:只能向其写入数据,而不能从中读取数据

Java的输入流主要由InputStream和Reader作为基类,而输出流则主要由OuputStream和Writer作为基类。

2. 字节流和字符流

字节流和字符流的用法几乎完全一样,区别在于字节流和字符流所操作的数据单元不同—字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符

字节流主要由InputStream和OutputStream作为基类,而字符流主要由Reader和Writer作为基类。

3. 节点流和处理流

可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被称为低级流。

----当使用节点流进行输入/输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。

处理流则用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。处理流也被称为高级流。

----当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入/输出节点连接。

使用处理流的一个明显好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际访问的数据源也相应地发生变化。

流的概念模型

对于InputStream和Reader而言,它们把输入设备抽象成一个"水管",这个水管里的每个"水滴"依次排列

对于OutputStream和Writer而言,它们同样把输出设备抽象成一个"水管",只是这个水管里没有任何水滴

字节流和字符流 InputStream和Reader

在InputStream里包含如下三个方法

方法名说明
int read()从输入流中读取单个字节(相当于从水管中取出一滴水),返回所读取的字节数据(字节数据可直接转换为int类型)
int read(byte[ ] b)从输入流最多读取b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数
int read(byte[ ] b, int off,int Len)从输入流中最多读取len个字节的数据,并将其存储在数组b中,加入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数

在Reader里包含如下三个方法

方法名说明
int read()从输入流中读取单个字符(相当于从水管中取出一滴水),返回所读取的字符数据(字符数据可直接转换为int类型)
int read(byte[ ] cbuf)从输入流最多读取cbuf.length个字符的数据,并将其存储在字符数组b中,返回实际读取的字符数
int read(byte[ ] cbuf, int off,int Len)从输入流中最多读取len个字符的数据,并将其存储在数组cbuf中,加入数组cbuf中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数

当使用数组作为read()方法的参数时,可以理解为使用一个"竹筒"到水管中去取水。read(char[ ] cbuf)方法中的数组可理解为一个"竹筒",程序每次调用输入流的read(char[ ] cbuf)或read(char[ ] b)方法,就相当于用"竹筒"从输入流中取出一筒"水滴",程序得到"竹筒"里的"水滴"后,转换为相应的数据即可。程序如何判断取水取到了最后呢?直到read(char[ ] cbuf)或read(char[ ] b)方法返回-1,即表明到了输入流的结束点。

public static void main(String[] args) {

		

		try {
			File file = new File("D:/xxxx.m4a");
			Reader reader = new FileReader(file);

			// length() 获取文件的字节数大小
			// long size = file.length();
			// char[] charArray = new char[(int) size];
			// reader.read(charArray);

			String str = "";
			while (true) {
				int i = reader.read();
				if (i == -1) {
					break;
				}
				str += (char) i;
			}

			System.out.println(str);

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

注意!!最后一定要关闭输入流或者输出流的资源!!!

使用Java的IO流不要忘记关闭输出流,关闭输入流除了可以保证保证流的物理资源被回收外,可能还可以将输出缓冲区中的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)。Java的很多输出流默认都提供了缓冲功能,其实没有必要刻意去记忆哪些流有缓冲功能、哪些流没有,只要正常关闭所有的输出流即可保证程序正常。


InputStream和Reader还支持以下几个方法来移动记录指针

方法名说明
void mark(int readAheadLimit)在记录指针当前位置记录一个标记(mark)
boolean markSupported()判断此输入流是否支持mark()操作,即是否支持记录标记
void reset()将此流的记录指针重新定位到上一次记录标记(mark)的位置
long skip(long n)记录指针向前移动n个字符/字节
OutputStream和Writer

OutputStream提供了三个方法

方法名说明
void write(int c)将指定的字节/字符输出到输出流中,其中c既可以代表字节,也可以代表字符
void write(byte[ ]/char[ ] buf)将字节数组/字符数组中的数据输出到指定输出流中
void write(byte[ ]/char[ ] buf,int off,int len)将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中

因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。

方法名说明
void write(String str)将str字符串里包括的字符输出到指定输出流中
void write(String str,int off,int len)将str字符串里从off位置开始,长度为len的字符输出到指定输出流中
输出/输入流体系
分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流PrintStreamPrintWriters
推回输入流PushbackInputStreamPushbackReader
特殊流DataInputStreamDataOutputStream

之前有了输出/输入流的4个抽象基类,并介绍了4个访问文件的节点流的用法。会发现4个基类使用起来有些烦琐。如果希望简化编程,这就需要借助于处理流了。

处理流的用法

处理流可以隐藏底层设备上节点流的差异,并对外提供更加方便的输入/输出方法,让程序员只需关心高级流的操作。

使用处理流时的典型思路是,使用处理流来包装节点流,程序通过处理流来执行输入/输出功能,让节点流与底层的I/O设备、文件交互

实际识别处理流也很简单,只要流的构造器参数不是一个物理节点,而是已经存在的流,那么这种流一定是处理流;而所有节点流都是直接以物理IO节点作为构造器参数的

关于使用处理流的优势:1.对开发人员来说,使用处理流进行输入/输出操作更简单;2.使用处理流的执行效率更高

public class 处理流的使用 {
	public static void main(String[] args) {
		PrintStream p = null;
		try {
			p = new PrintStream(new FileOutputStream(new File("/Users/wushiwei/文件/C语言.md")));
			String str = "hello world";
			int i = 5;
			double d = 7.9;
			p.println(str);
			p.println(i);
			p.println(d);

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			p.close();
		}

	}
}

在使用处理流包装了底层节点流之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可。关闭最上层的处理流时,系统会自动关闭被该处理流包装的节点流

输入/输出流体系

通常来说,字节流的功能比字符流的功能更强大,因为计算机里所有的数据都是二进制的,而字节流可以处理所有的二进制文件–但是问题是,如果使用字节流来处理文本文件,则需要使用合适的方式把这些字节转换成字符,这就增加了编程的复杂度。所以通常有个规则:

如果进行输入/输出的内容是文本内容,则应该考虑使用字符流;如果进行输入/输出的内容是二进制内容,则应该考虑使用字节流。

转换流

输入/输出流体系中还提供了两个转换流,这两个转换流用于实现将字节流转换成字符流,其中InputStreamReader将字节输入流转换成字符输入流;OutputStreamWriter将字节输出流转换成字符输出流。

怎么没有字符流转字节流的转换流呢???

想一想字节流和字符流的差别:字节流比字符流的使用范围更广,但字符流比字节流操作方便。如果有一个流已经是字符流了,也就是说,是一个用起来更方便的流了,为什么要转换成字节流呢??反之,如果现在有一个字节流,但可以确定这个字节流的内容都是文本内容,那么把它转换成字符流来处理就会更方便一点,所以Java只提供了将字节流转换成字符流的转换流。


这边以获取键盘输入为例来介绍转换流的用法

public class KeyinTest {
	public static void main(String[] args) {
		try (
      // 将System.in对象转换成Reader对象
      InputStreamReader reader = new InputStreamReader(System.in);
      // 将普通的Reader包装成BufferedReader
				BufferedReader br = new BufferedReader(reader))
    {
			String line = null;
      // 采用循环方式来逐行读取
			while ((line = br.readLine()) != null) {
				if (line.equals("exit")) {
					System.exit(1);
				}
				System.out.println("输入内容为:" + line);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

上面程序将System.in包装成BufferedReader,BufferedReader流具有缓冲功能,它可以一次读取一行文本—以换行符为标志,如果它没有读到换行符,则程序阻塞,等到读到换行符为止。运行这个程序会发现,在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容

由于BufferedReader具有一个readLine()方法,可以非常方便地一次读入一行内容,所以经常把读取文本内容的输入流包装成BufferedReader,用来方便地读取输入流的文本内容。

推回输入流

在输入/输出流体系中,有两个特殊的流与众不同,就是PushbackInputStream和PushbackReader。

方法名说明
void unread(byte[ ]/char[ ] buf )将一个字节/字符数组内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
void unread(byte[ ]/char[ ] b,int off,int len)将一个字节/字符数组里从off开始,长度为len字节/字符的内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
void unread(int b )将一个字节/字符推回到推回缓冲区里,从而允许重复读取刚刚读取的内容

PushbackInputStream和PushbackReader的奥秘所在就是这三个方法与InputStream和Reader中的三个read()方法一一对应

这两个推回输入流都带有一个推回缓冲区,当程序调用这两个推回输入流的unread()方法时,系统将会把指定数组的内容推回到该缓冲区里,而推回输入流每次调用read()方法时总是先从推回缓冲区读取,只有完全读取了推回缓冲区的内容后,但还没有装满read()所需的数组时才会从原输入流中读取。

已知,当程序创建一个PushbackInputStream和PushbackReader时需要指定推回缓冲区的大小,默认的推回缓冲区的长度为1。如果程序中推回到推回缓冲区的内容超出了推回缓冲区的大小,将会引发Pushback buffer overflow 的IOEXception异常。

虽然图中的推回缓冲区的长度看似比read()方法的数组参数的长度小,实际上,推回缓冲区的长度与read()方法的数组参数的长度没有任何关系,完全可以更大。

还未更新完

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/677992.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号