IO流(一)
IO概述:IO(Input/Output),即输入/输出;泛指对某个设备或环境进行数据的数据或输出。
例如:对硬盘进行输入输出,对视频设备进行输入输出,对网络主机进行输入输出等。
我们可以想象,因设备或环境的不同,会有各式各样的输入输出问题与解决方案。对于输入输出问题,Java将之抽象为流对象来解决。对不同的输入输出问题,会有相应的流对象提供解决方案。
IO流:IO流用来处理不同设备间的数据传输。Java中用于操作流的对象都封装在IO包中。
IO流分类:
A:按操作数据不同:字节流和字符流
B:按数据的流向不同:输入流(读取数据)和输出流(写入数据)
这点儿注意啦:不知道有没有和我一样的,老是记反,总是觉得输入流就是往里面写入数据的,输出流才是读取数据,看过视频后终于明白了,一定要明白自己站的角度,咱们是Java程序的编写者,始终都是从某个地方读取数据,然后再写入到指定的地方。
字节流比字符流出现事的要早,而且以后对字符流搞不懂的话,永远用字节流它也肯定不会报错,因为没有字符流的时候,字节流也什么都能做。说到这儿,肯定会有人问那字符流有什么用呢?
字符流一次能读一个字符,相当于两个字节,说白了就是一个汉字,而字节流一次只能读8个字节,也就是一个字符,所以字符流的出现就是为了解决文本数据的问题。有时候为了操作的方便,我们使用字符流。
那到底什么时候使用字符流呢? 通过windows自带的记事本,能够把这个文件打开,并且里面的内容你还能够读懂,这样的文件就可以使用字符流 。否则使用字节流。
在正式了解Java中如何处理文件的输入输出之前,我们需要了解Java是如何表示一个文件的:
我们硬盘上数据最常见的方式就是以文件存在的,而文件本身有很多的属性,比如说:文件大小,是否可读啊,是否隐藏啊。那么,java为了方便操作这些文件,Java就在IO包中提供了File类对文件本身进行封装。
File类中定义了一些与平台无关的方法供我们来操作文件。例如创建、删除、重命名文件等。在Java中,目录也是用File类来进行封装的。
File对象可以作为参数传递给流对象的构造方法。
下面我们一起通过学习API,来掌握File类常见方法的使用吧~~
File既可以表示文件,也可以表示文件夹。(目录)
1)构造方法:
File(String pathname):根据指定的路径创建File对象。
File(String parent, String child):根据指定的父文件夹和子文件或者文件夹创建File对象
File(File parent, String child):根据指定的父文件夹对象和子文件或者文件夹创建File对象
在这儿写一个小的Demo来帮助记忆:
public class FileDemo {
public static void main(String[] args) {
// 方式1
File file = new File("d:\\a.txt");
File file2 = new File("d:\\aaa");
// 方式2
File file3 = new File("d:\\", "a.txt");
File file4 = new File("d:\\", "aaa");
// 方式3
File file5 = new File("d:\\");
File file6 = new File(file5, "a.txt");
File file7 = new File(file5, "aaa");
}
}
2)成员方法:
1:创建功能
创建文件:public boolean createNewFile() -- -- -- 如果指定的文件不存在,就创建。如果存在,就不创建。
创建文件夹:public boolean mkdir() -- ---- -- 创建指定的目录,如果存在,就不创建。
当且仅当已创建目录时,返回true
;否则返回false
public boolean mkdirs() --- -- --创建指定的目录,如果存储,就不创建。
这个时候注意,如果父目录不存在,它也会自动创建。
在这儿引用老师的一句话:你要创建什么,自己最清楚。也就是说你要调用哪个方法,你自己必须明白,如果你不明白,就会出问题的!
什么问题呢? 就是如果你明明想创建文件,却调用了创建创建文件夹的方法,最终也会创建成功,但是却是文件夹。所以大家有时候在网上看到例如.txt 结尾的,未必是文件,也有可能是文件夹。就像骑白马的不一定是王子,可能是叫花子。哈哈哈哈~~~~ 好了,下面言归正传:
绝对路径:以盘符开始的路径。
相对路径:不以盘符开始的路径。
附个小练习,帮助理解:
public class FileDemo {
public static void main(String[] args) throws IOException {
//创建文件
File file=new File("d:\\a.txt");
System.out.println("createNewFile():"+file.createNewFile()); //file.createNewFile():true
// 创建文件 忘了写路径名称了,以当前项目路径所在路径为父目录
File file1=new File("b.txt");
System.out.println("createNewFile():"+file1.createNewFile());//file1.createNewFile():true
// 创建目录
File file2=new File("ccc");
System.out.println("mkdir():"+file2.mkdir()); //mkdir():true
// 创建多级目录 如果想创建一个指定的目录或者文件,要求父目录必须存在。所以下面打印的是false
File file3=new File("bbb\\ccc");
System.out.println("mkdir():"+file3.mkdir()); //mkdir():false
// 解决方案:
File file4=new File("bbb");
File file5=new File(file4,"ccc");
System.out.println("mkdir:" + file4.mkdir());//mkdir:true
System.out.println("mkdir:" + file5.mkdir());//mkdir:true
// 但是如果目录过多,这样做就太麻烦。肿么办呢?
File file6=new File("ddd\\eee");
System.out.println("file6.mkdirs()"+file6.mkdirs());
//创建文件
File file7=new File("f.txt");
System.out.println("file7.mkdir()"+file7.mkdir());
}
}
下面是运行后的结果:
2:删除功能
public boolean delete():既可以删除文件,也可以删除文件夹。取决于你使用的对象。
删除功能比较好理解,但是有两点需要特别注意:
A:如果你删除的目录下还有内容,那么,必须先把所有内容删除完毕后,再删除目录。
B:java语言的删除不走回收站。
下面附上小练习:(接着上面的创建过的文件夹和文件,做的删除)
import java.io.File;
public class FileDelete {
public static void main(String[] args) {
// 需求:我要删除a.txt
File file = new File("b.txt");
System.out.println("delete:" + file.delete());
// 需求:我要删除aaa
File file2 = new File("ccc");
System.out.println("delete:" + file2.delete());
// 需求:我要删除bbb
File file3 = new File("bbb");
System.out.println("delete:" + file3.delete());
}
}
运行后结果为:
大家可以对比上面两次截图,可以发现,前两个删除成功,最后一个没有删除掉,这是因为bbb目录下面还有东西,必须删除所有内容后,目录bbb才能删除成功。
3:判断功能
boolean exists(): 判断file对象是否存在
boolean isFile(): 判断file对象是否是文件
boolean isDirectory(): 判断file对象是否是文件夹
boolean isAbsolute(): 判断file对象是否是绝对路径
boolean canRead(): 判断file对象是否可读
boolean canWrite(): 判断file对象是否可写
boolean isHidden(): 判断file对象是否隐藏
4:获取功能
String getAbsolutePath(): 绝对路径
String getPath(): 相对路径
String getName(): 文件名称
long length(): 文件大小,单位是字节
long lastModified(): 上次修改时间的毫秒值。
5:获取功能(特殊且重要)
public static File[] listRoots(): 列出可用的系统文件根目录
public String[] list(): 返回的是指定目录下所有文件或者文件夹的名称数组
public File[] listFiles(): 返回的是指定目录下所有文件或者文件夹对象数组
最后综上,写几个案例:1、获取指定目录下所有.mp4文件的名称。
import java.io.File;
/**
* 需求:获取指定目录下所有的.mp4文件的名称。
* 思路: 1、封装指定目录
* 2、获取指定目录下所有的文件或者文件夹File数组
* 3、遍历该对象是否是文件
* 4、判断该对象的后缀名是否是以.mp3结尾。
*/
public class FileTest {
public static void main(String[] args) {
//1、封装指定目录
File f=new File("f:\\");
// 2、获取指定目录下所有的文件或者文件夹File数组
File[] fileArray=f.listFiles();
//3、遍历File数组,获取得到每一个File对象
for(File file:fileArray){
//判断该对象是否是文件
if(file.isFile()){
String fileName=file.getName();
//判断该对象的后缀名是否是.mp3结尾的
if(fileName.endsWith(".mp3")){
System.out.println(fileName);
}
}
}
}
}
输出结果:
方法二:用文件名称过滤器改进版:
import java.io.File;
import java.io.FilenameFilter;
/**
* 获取指定目录下指定后缀的文件名称:
* 1、先获取指定目录下的所有文件或者文件夹的File数组,然后再遍历的时候判断,满足条件的就输出。
* 2、直接获取指定目录下所有满足条件的File数组,然后遍历数组即可。
* 文件名称过滤器:FilenameFilter
* public String[] list(FilenameFilter filter)
*/
public class FileTest1 {
public static void main(String[] args) {
//封装文件目录
File file=new File("d:\\");
//获取满足条件的数组
String[] strArray=file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File f=new File(dir,name);// d:\我的文档
//判断文件是否以.mp3结尾
boolean flag=f.isFile();
boolean flag1=name.endsWith(".mp3");
return flag&&flag1;
}
});
//遍历字符串数组,输出所有满足条件的目录
for(String str: strArray){
System.out.println(str);
}
}
}
2、删除指定的目录(目录里面带有目录或文件)。
import java.io.File;
/**
* 需求:删除指定目录(目录里面带有目录或文件)
* 思路:
* 1、封装目录
* 2、获取该目录下的所有文件或文件夹的File数组
* 3、遍历File数组,获取每一个File对象
* 4、判断该File对象是否为目录
* 是:返回第二步
* 否:删除文件
*/
public class FileDelete1 {
public static void main(String[] args) {
File file=new File("ddd");
deleteFiles(file);
}
private static void deleteFiles(File file) {
File[] fileArray=file.listFiles();
if(fileArray!=null){
for(File f:fileArray){
if(f.isDirectory()){
deleteFiles(f);
}else{
System.out.println(f.getName()+"***"+f.delete());
}
}
// 如果for循环结束,就表示目录下的文件被删除完毕。
System.out.println(file.getName()+"***"+file.delete());
}
}
}
运行结果(没刷新之前)为:
刷新之后:
介绍完File类,接下来才真正进入IO流的详细讲解啦~~
1)IO流的分类:
字节流
字节输入流 读取数据 InputStream
字节输出流 写入数据 OutputStream
字符流
字符输入流 读取数据 Reader
字节输出流 写入数据 Writer
注:本来想画个图呢,这样会更清晰,看视频上老师讲课时都会画图,帮助理解,但试了几次都不好看,算了,有机会再练练~~~
FileWriter:
下面来写一个练习来用一下:
需求:我要把一句话:"为自己加油!!! 再默默坚持一下,黑马,我马上就来了! "写入一个test.txt中。
<span style="font-size:14px;">import java.io.FileWriter;
import java.io.IOException;
/**
* 需求:我要把一句话:"为自己加油!!! 再默默坚持一下,黑马,我马上就来了! "写入一个test.txt中。
* 分析:
* A:由于我们是写数据,所以使用OutputStream或者Writer。
* B:由于我们要写入到一个文本文件中,所以最终选择了Writer。
* 通过查看API,我们发现,Writer是一个抽象类。那么,怎么办呢?
* 只能找子类使用。又由于我们是对文件操作,我们猜测了这个子类的名称是:FileWriter
*/
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
//创建字符输出流对象
FileWriter fw=new FileWriter("c.txt");
/*
* <span style="color:#cc0000;">创建字符输出流对象做了几件事情? A:调用系统功能创建了文件 B:创建字符输出流对象 C:把字符输出流对象指向创建的文件</span>
*/
//调用写数据的功能
// public void write(String str)
fw.write("为自己加油!!! 再默默坚持一下,黑马,我马上就来了!");
/*
* <span style="color:#cc0000;">字符流 1字符 = 2字节 文件数据底层单位是字节,而我们现在是字符,所以它不能直接把数据写入文件。 把字符输出存储到缓冲区里面。</span>
*/
//刷新缓冲区
// public void flush()
fw.flush();
//释放资源
// public void close()
fw.close();
/*
* <span style="color:#cc0000;">为什么要close()呢?</span>
* <span style="color:#cc0000;">1:让流对象变成垃圾。</span>
* <span style="color:#cc0000;">2:通知系统去释放和文件相关的资源。</span>
*/
}
}</span>
运行后结果为:
分析一下程序:上面用红色标记的字,都是在写的过程中,遇到的问题以及有疑问的地方,老师都讲到了,觉得写在出问题的代码旁,更容易理解。
下面是通过上面的程序总结的一些笔记:
字符输出流操作步骤:
A:创建字符输出流对象
B:调用对象的写入数据方法,并刷新缓冲区
C:释放资源
问题1:为什么FileWriter没有无参构造方法?
因为写数据的时候,一定要明确写到哪里去。
问题2:flush()和close() 都有刷新功能,它们有什么区别呢?
flush():只刷新缓冲区,流对象还可以继续使用。
close() :先刷新缓冲区,再关闭流对象。流对象不可以继续被使用。
问题3:难道每次调用方法的时候,都需要刷新吗?或者说,不用刷,直接等到close() 来解决,行不行?
这两种方式都不可取。
FileWriter的追加与换行:
写入数据的方式,有5种:
write(int ch)
write(char[] chs,int index,int len)
write(char[] chs)
write(String str,int index,int len)
write(String str)
在练习的时候遇到的问题:
为什么连续两次没有换行呢? 如果想换行,怎么办呢?
因为你没让它换行,所以没有换行。那就给出换行符\n不就可以了吗 ?不行,不同的操作系统,对换行符的要求不一样:
windows:\r\n linux:\n mac:\r
而有些软件在制作的时候,为了通用,对任何换行符都是可以识别的。
在不断的往下写的时候,本来想着往里面追加,结果新写入的每次都把以前的给覆盖了,如果我想追加写入怎么办?
查API,构造的时候用带两个参数的就可以实现追加了:
public FileWriter(String fileName,boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
FileWriter写入数据加入异常处理(把上面的代码改进了一下):
import java.io.FileWriter;
import java.io.IOException;
/*
* 加入异常处理的代码
*/
public class FileWriterDemo4 {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("c.txt");
fw.write("为自己加油!!! 再默默坚持一下,黑马,我马上就来了!");
fw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileReader:
字符输入流读取数据步骤:A:创建字符输入流对象
B:调用读取数据功能,并显示
C:释放资源
下面写一下刚看API学的时候的过程:
字符输入流读取数据方式一:一次读取一个字符
import java.io.FileReader;
import java.io.IOException;
/*
* 字符输入流读取数据步骤:
* A:创建字符输入流对象
* B:调用读取数据功能,并显示
* C:释放资源
*
* 一次读取一个字符。
*/
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
// 创建字符输入流对象
// FileReader fr = new FileReader("a.txt");
FileReader fr = new FileReader("FileWriterDemo.java");
// 调用读取数据功能,并显示
// public int read()
// int ch = fr.read();
// // System.out.println(ch);
// System.out.print((char) ch);
//
// ch = fr.read();
// // System.out.println(ch);
// System.out.print((char) ch);
//
// // read()方法读取数据返回int类型,并自动移动到下一个数据位置等待读取。
// // 我们发现数据的读取和显示操作是重复的,所以我们决定用循环改进。
// // 关键是:循环的结束条件是什么
// ch = fr.read();
// System.out.println(ch);
//
// ch = fr.read();
// System.out.println(ch);
// 通过测试,我们发现,如果数据没有了,读取的时候,将返回-1
// int ch = fr.read();
// while (ch != -1) {
// System.out.print((char) ch);
// // 重新读取一次
// ch = fr.read();
// }
//开发写法
int ch = 0;
while((ch=fr.read())!=-1){
System.out.print((char) ch);
}
// 释放资源
fr.close();
}
}
运行后结果:
字符输入流读取数据方式一:一次读取一个字符串
开发中的代码运行结果:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 需求:我要把项目路径下的FileWriterDemo.java复制到f:\\Copy.java中
* 分析:
* 数据源:
* 读取数据:
* FileWriterDemo.java -- Reader -- FileReader
* 目的地:
* 写入数据:
* f:\\Copy.java -- Writer -- FileWriter
* 下面我用两种方式来做:
*/
public class CopyFileDmeo {
public static void main(String[] args) throws IOException {
//封装数据源
FileReader fr=new FileReader("E:/JavaSE-2015/StringDemo/src/cn/itcast02/ExceptionDemo.java");
//封装目的地
FileWriter fw=new FileWriter("f:\\Copy.java");
//读取数据
//方式一:一次读取一个字符
// int ch=0;
// while((ch=fr.read())!=0){
// fw.write(ch);
// fw.flush();
// }
//方式二:一次读取多个字符
char[] chs=new char[1024];
int len=0;
while((len=fr.read(chs))!=-1){
fw.write(chs,0,len);
}
fw.close();
fr.close();
}
}
运行结果:
先到这儿吧,东西太多了,有些理解的不对的地方,真诚地希望大家帮我指正出来,谢谢!!!