IO流一直以来都是半懂不懂的,今天来好好复习一下吧。
JAVA的IO通过java.io包下的类和接口来支持,在java.io包下主要包括输入,输出两种IO流,每种输入输出又分为字节流和字符流两个大类。其中字节流以字节为单位处理输入输出操作,字符流用字符来处理输入输出操作。
在介绍 IO流之前,有一个经常和IO流结合起来使用的File类也需要提及一下:
File 类:
File类也是java.io包下的类,File类可以实现新建,删除,重命名文件和目录,但是File类不能访问文件内容本身,如果需要访问文件内容本身,还需要使用输入,输出流。
File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。、
File(File parent, String child);
通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。
File(String pathname)
创建File对象成功后,可以使用以下列表中的方法操作文件。
序号 | 方法描述 |
---|---|
1 | public String getName() 返回由此抽象路径名表示的文件或目录的名称。 |
2 | public String getParent()、 返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null 。 |
3 | public File getParentFile() 返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null 。 |
4 | public String getPath() 将此抽象路径名转换为一个路径名字符串。 |
5 | public boolean isAbsolute() 测试此抽象路径名是否为绝对路径名。 |
6 | public String getAbsolutePath() 返回抽象路径名的绝对路径名字符串。 |
7 | public boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。 |
8 | public boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。 |
9 | public boolean exists() 测试此抽象路径名表示的文件或目录是否存在。 |
10 | public boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。 |
11 | public boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。 |
12 | public long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。 |
13 | public long length() 返回由此抽象路径名表示的文件的长度。 |
14 | public boolean createNewFile() throws IOException 当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。 |
15 | public boolean delete() 删除此抽象路径名表示的文件或目录。 |
16 | public void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。 |
17 | public String[] list() 返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。 |
18 | public String[] list(FilenameFilter filter) 返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的。 |
19 | public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。 |
20 | public File[] listFiles(FileFilter filter) 返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。 |
21 | public boolean mkdir() 创建此抽象路径名指定的目录。 |
22 | public boolean mkdirs() 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。 |
23 | public boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。 |
24 | public boolean setLastModified(long time) 设置由此抽象路径名所指定的文件或目录的最后一次修改时间。 |
25 | public boolean setReadOnly() 标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。 |
26 | public static File createTempFile(String prefix, String suffix, File directory) throws IOException 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。 |
27 | public static File createTempFile(String prefix, String suffix) throws IOException 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。 |
28 | public int compareTo(File pathname) 按字母顺序比较两个抽象路径名。 |
29 | public int compareTo(Object o) 按字母顺序比较抽象路径名与给定对象。 |
30 | public boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等。 |
31 | public String toString() 返回此抽象路径名的路径名字符串。 |
实例代码:
import java.io.File;
import java.io.IOException;
public class Filetest {
private void fileTest()
{
File file=new File("xqy");
//获取文件名
System.out.println(file.getName());
//获取相对路径的父路径,会返回null
System.out.println(file.getParent());
//获取绝对路径
System.out.println(file.getAbsoluteFile());
//获取上一级路径
System.out.println(file.getAbsoluteFile().getParent());
//在当前路径下创建一个临时文件
try {
File tmpFile=File.createTempFile("aaa",".txt",file);
//指定当jvm退出时删除该文件
tmpFile.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
//以系统当前时间作为新文件名来创建新文件
File newFile=new File(System.currentTimeMillis()+"");
System.out.println("newFile对象是否存在:"+newFile.exists());
try {
//指定newFile对象来创建文件
newFile.createNewFile();
//以newFile对象来创建一个目录,因为newFile已经存在,所以下面的额方法返回false,无法创建该目录
//newFile.mkdir();
//用list方法列出当前路径下所有文件和路径
String[] fileList=file.list();
for(String filename:fileList)
{
System.out.println(filename);
}
//listRoots()静态方法列出所有磁盘根路径
File[] roots=File.listRoots();
System.out.println("======系统所有根路径如下=======");
for(File root:roots)
{
System.out.println(root);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args)
{
Filetest test1=new Filetest();
test1.fileTest();
}
}
使用相对路径的File对象获取父路经会返回null。
FileFilter:文件过滤器
FileFileter是一个接口,可以规定过滤条件,在获取某个目录时可以通过给定的删选条件来获取满足要求的子项。
回调模式:
我们定义一段逻辑。在调用其他方法时将该逻辑通过参数传入。这个方法在执行过程中会调用我们传入的逻辑来达成目的。这种现象就是回调模式。
最常见的应用环境:
按钮监听器
过滤器的应用
package day01;
import java.io.File;
import java.io.FileFilter;
/**
* 文件过滤器
* 用于在获取某个目录下的子项时筛选出符号条件的子项
* @author Administrator
*
*/
public class DemoFileFilter {
public static void main(String[] args) {
/**
* 定义一个文件过滤器,用于过滤.java文件
*/
FileFilter filter = new FileFilter(){
/**
* accept方法是用来定义过滤条件的
* 参数pathname是将被过滤的目录中的每个子项依此传入进行匹配
* 若我们认为该子项满足条件则返回true
*/
public boolean accept(File pathname){
//保留文件名以.java结尾的
return pathname.getName().startsWith(".");
}
};
File dir = new File(".");
//获取所有子项
// File[] sub = dir.listFiles();
//获取过滤器中满足条件的子项
File[] sub = dir.listFiles(filter);//回调模式
for(File file:sub){
System.out.println(file);
}
}
}
Java的IO流
流的分类:
1.输入流和输出流
输入流:只能从中读取数据,而不能向其中写入数据。
输出流:只能向其中写入数据,不能从中读取数据。
Java的输入流主要由InputStream和Reader作为基类,而输出流则主要由OutputStream和Writer作为基类。
2.字节流和字符流
字节流:操作的是8位的字节,主要由InputStream和OutputStream作为基类。
字符流:操作的是16位的字符,主要由Reader和Writer作为基类。
基于字节的IO操作:
基于字符的IO操作:
3.节点流和处理流
可以从/向一个特定的IO设备(如网络,磁盘)读/写数据的流,称为节点流。节点流也被称为低级流。使用节点流进行输入输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。
处理流则用于对一个已经存在的流进行连接和封装
流的原理浅析:
Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java Io流的40多个类都是从如下4个抽象类基类中派生出来的。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
对于InputStream和Reader而言,它们把输入设备抽象成为一个”水管“,这个水管的每个“水滴”依次排列,如图15.5所示:
从图15.5可以看出,字节流和字符流的处理方式其实很相似,只是它们处理的输入/输出单位不同而已。输入流使用隐式的记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。
对于OutputStream和Writer而言,它们同样把输出设备抽象成一个”水管“,只是这个水管里面没有任何水滴,如图15.6所示:
正如图15.6所示,当执行输出时,程序相当于依次把“水滴”放入到输出流的水管中,输出流同样采用隐示指针来标识当前水滴即将放入的位置,每当程序向OutputStream或者Writer里面输出一个或者多个水滴后,记录指针自动向后移动。
图15.5和图15.6显示了java Io的基本概念模型,除此之外,Java的处理流模型则体现了Java输入和输出流设计的灵活性。处理流的功能主要体现在以下两个方面。
- 性能的提高:主要以增加缓冲的方式来提供输入和输出的效率。
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入和输出大批量的内容,而不是输入/输出一个或者多个“水滴”。
处理流可以“嫁接”在任何已存在的流的基础之上,这就允许Java应用程序采用相同的代码,透明的方式来访问不同的输入和输出设备的数据流。图15.7显示了处理流的模型。
java输入/输出流体系中常用的流的分类表
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
抽象基类 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
打印流 | PrintStream | PrintWriter | ||
推回输入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
常见IO流的用法:
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中包含如下3个方法。
int read(); 从输入流中读取单个字符(相当于从图15.5所示的水管中取出一滴水),返回所读取的字符数据(字节数据可直接转换为int类型)。
int read(char[] b)从输入流中最多读取b.length个字符的数据,并将其存储在字节数组b中,返回实际读取的字符数。
int read(char[] b,int off,int len); 从输入流中最多读取len个字符的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。
对比InputStream和Reader所提供的方法,就不难发现这两个基类的功能基本是一样的。InputStream和Reader都是将输入数据抽象成如图15.5所示的水管,所以程序即可以通过read()方法每次读取一个”水滴“,也可以通过read(char[] chuf)或者read(byte[] b)方法来读取多个“水滴”。当使用数组作为read()方法中的参数, 我们可以理解为使用一个“竹筒”到如图15.5所示的水管中取水,read(char[] cbuf)方法的参数可以理解成一个”竹筒“,程序每次调用输入流read(char[] cbuf)或read(byte[] b)方法,就相当于用“竹筒”从输入流中取出一筒“水滴”,程序得到“竹筒”里面的”水滴“后,转换成相应的数据即可;程序多次重复这个“取水”过程,直到最后。程序如何判断取水取到了最后呢?直到read(char[] chuf)或者read(byte[] b)方法返回-1,即表明到了输入流的结束点。
InputStream代码示例:
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamTest {
public static void main(String[] args) throws IOException {
//创建字节输入流
FileInputStream fis=new FileInputStream("C:\\Users\\hai\\IdeaProjects\\FileProject\\src\\FileInputStreamTest.java");
//创建一个长度为1024的竹筒
byte[] bbuf=new byte[1024];
//用于保存实际读取的字节数
int hasRead=0;
//使用循环来重复取水的过程
while((hasRead=fis.read(bbuf))>0)
{
//将字节转换成字符串输入
System.out.println(new String (bbuf,0,hasRead));
}
fis.close();
}
}
运行结果就是输出上面的源代码。
使用FileReader读取文件:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args)
{
try {
FileReader fr = new FileReader("C:\\Users\\hai\\IdeaProjects\\FileProject\\src\\FileReaderTest.java");
char[] cbuf = new char[32];
int hasRead = 0;
while ((hasRead = fr.read(cbuf)) != -1)
{
System.out.println(new String (cbuf,0,hasRead));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以看出使用FileInputStream和FileReader进行文件的读写并没有什么区别,只是操作单元不同
FileOutputStream&&FileWriter
FileOutputStream/FileWriter是Io中的文件输出流,下面介绍这两个类的用法。
FileOutputStream的用法:
void write(int c):将指定的字符/字节输出到输出流中,其中c既可以代表字节,也可以代表字符。
void write(byte[]/char[] buf):将字节数组/字符数组中的数据输出到指定输出流中。
void write(byte[]/char[] buf ,int off,int len):将字节数组/字符数组从off位置开始,长度为len的字节/字符输出到输出流中。
因为字符流直接以字符为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里还包含如下两个方法/
void write(String str):将str字符串里包含的字符输出到指定输出流中。
void write(String str,int off,int len):将str字符串里从off位置开始,长度为len的字符串输出到指定输出流中。
FileOutputStream的用法:
import java.io.*;
public class FileOutputStreamTest {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("C:\\Users\\hai\\IdeaProjects\\FileProject\\src\\FileOutputStreamTest.java");
File file=new File("newFile.txt");
FileOutputStream fos = new FileOutputStream(file);
byte[] bbuf=new byte[32];
int hasread=0;
while((hasread=fis.read(bbuf))!=-1)
{
//每读取一次,即写入文件输出流,读了多少,就写多少
fos.write(bbuf,0,hasread);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果希望直接输出字符串内容,使用Writer会有更好的效果。
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args)
{
try {
FileWriter fw=new FileWriter("poem.txt");
fw.write("鹅鹅鹅\r\n");
fw.write("哪只鹅?\r\n");
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意 fw.flush();这行代码需要加上去才能写入。
处理流的用法:
上面介绍了节点流的用法,如果希望简化编程,需要借助处理流,使用处理流的典型思路是:使用处理流来包装节点流,程序通过处理流来执行输入/输出功能,让节点流与底层的I/O设备,文件交互。
实际识别处理流非常简单,只要流的构造器不是一个物理节点,而是已经存在的流,那么这种流就一定是处理流,而所有的节点流都是直接以物理IO节点作为构造器参数的。
下面使用PrintStream处理流来包装OutputStream,使处理流后的输入输出时将更加方便。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args)
{
FileOutputStream fos= null;
try {
fos = new FileOutputStream("test.txt");
PrintStream ps=new PrintStream(fos);
ps.println("普通字符串");
ps.println(new PrintStreamTest());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
程序运行结果:
首先先定义了一个节点输出流FileOutputStream,然后程序使用PrintStream包装了该节点输出流,然后使用PrintStream输出字符串,输出对象。值得一提的是,标准输出System.out的类型是PrintStream。
查阅资料:《疯狂java讲义》
https://blog.csdn.net/qq_38826019/article/details/80462822