九、黑马程序员—IO 操作(1)
第九篇 IO 文件操作(1)
1、IO 的概述和 File 方法
IO 流用来处理设备之间的数据传输
Java 对数据的操作是通过流的方式
Java 用于操作流的对象都在 IO 包中
File 类在整个 IO 包中与文件本身有关的操作类,所有的与文件本身有关指的是创建、删除
文件等操作。在 java.io 包中的 File 类本身是一个跨平台的文件操作类,所以在操作中要更
多的考虑到各个操作系统的区别。
File 即指文件也指文件夹。
File 类构造方法和字段摘要
static String pathSeparator 路径分隔符,window 下是";"。
static char pathSeparatorChar 路径分隔符,window 下是";"。
static String separator 路径分隔符,window 下是"\"。
static char separatorChar 路径分隔符,window 下是"\"。
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新
File 实例。
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实
例。
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个
新 File 实例。
File(URI uri) 通过将给定的 file: /URI 转换为一个抽象路径名来创建一个新的 File 实例。
File 的相关方法
String getName():返回文件名或路径名(若是路径,返回最后一级子路径名)
String getPath():返回对象对应的路径名
File getAbsoluteFile():返回绝对路径
String getAbsolutePath():返回对象对应的绝对路径
String getParent():返回文件目录的上一级目录名
boolean renameTo(File newName):重命名此 File 对象对应的文件或目录,若重命名成功返回
true;
boolean exists():判断对象对应的文件或目录是否存在;
boolean canWrite():判断对象对应文件或目录是否可写;
boolean canRead():判断对象对应文件或目录是否可读;
boolean isFile():判断对象是文件,不是目录;
boolean isDirectory() 判断对象的文件是否是一个目录;
boolean isAbsolute() 判断对象对应文件或目录是否为绝对路径名;
boolean createNewFile() 当且仅当不存在,该方法创建一个该 File 对象所指定的新文件,创
建成功返回 true。
boolean delete():删除 File 对象所对应的文件或路径;
boolean mkdir() 创建 File 对象所对应的目录,调用该方法的 File 对象必须对应路径,而不
是文件。
String[] list():列出 File 对象的所有子文件名和路径名。
File[] listFiles():列出 File 对象的所有子文件和路径。
static File[] listRoots():列出系统所有的根路径;
我的总结:IO 这一章节最应该记住的关键字:读进来,写进去!
Eg:
package july7file;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File f = new File("E:/你好.txt");
System.out.println(f.createNewFile());
System.out.println(f.getName());
System.out.println(f.getParent());
System.out.println(f.length());
}
}
输出:
false
你好.txt
E:\
6905
2、递归(Recursion)
现在要求输出一个给定目录中的全部文件的路径。
本程序肯定只能依靠递归的操作完成,因为在一个给定的路径下有可能还是文件夹,那么如
果是文件夹的话则肯定要继续列出,重复判断。
递归:程序调用自身的编程技巧
递归就是在方法里调用自身;
在使用递归时,必须有一个明确的递归结束条件,称为递归出口。
练习:列出文件夹下所有文件(包含子文件夹内)
package july7file;
//利用递归遍历输出
import java.io.File;
public class Demo2 {
public static void main(String[] args) {
File f = new File("D:/V5");
mylist(f);
}
public static void mylist(File f) {
System.out.println(f);// 先输出一下,因为不能确定接受来的文件是否是
文件夹!
if (f.isDirectory()) {
File[] file = f.listFiles();
for (File file2 : file) {
mylist(file2);
}
}
}
}
练习:删除一个目录(注意:要删除目录必须删除目录下的文件和子目录)
package july7file;
import java.io.File;
public class Demo11 {
public static void main(String[] args) {
File f = new File("D:/V5");
deleter(f);
System.out.println("删除成功 !");
}
public static void deleter(File f){//程序简陋,就没有判断空引用!
if(f.isFile()){
f.delete();
}else if(f.isDirectory()){
File []file = f.listFiles();
for (File file2 : file) {
deleter(file2);//调用自身,递归!
file2.delete();//删除子文件夹(内部没有文件的时候可以删除),
如果这里写上f.delete();那么V5这个文件夹也没有了
}
}
}
}
3、文件过滤器 java.io.FilenameFilter
File 类里有方法: String[] list(FilenameFilter filter) 返回一个字符串数组,这些字符串指
定此抽象路径名表示的目录中满足指定过滤器的文件和目录。
FilenameFilter(文件过滤器)该接口里包含 accept(File dir,String name)方法,该方法依次对指定
File 的所有子目录,子文件夹进行迭代。
dir - 被找到的文件所在的目录。
name - 文件的名称。
当且仅当该名称应该包含在文件列表中时返回 true;否则返回 false
Eg:
package july7file;
//构造过滤器,只输出需要的文件!
import java.io.File;
import java.io.FilenameFilter;
class MyFilter implements FilenameFilter {
private String ext;
public MyFilter(String ext) {
super();
this.ext = ext;
}
@Override
public boolean accept(File dir, String name) {
return name.endsWith(ext);// 真正起作用的还是这里的ext
}
}
public class Demo3 {
public static void main(String[] args) {
File f = new File("D:/V5/牛/水牛");
File[] file = f.listFiles(new MyFilter(".txt"));
for (File file2 : file) {
System.out.println(file2);
}
}
}
4、流
数据流是一串连续不断的数据的集合,就像水管里的水流,在水管的一端一点一点地供水,而
在水管的另一端看到的是一股连续不断的水流.
数据写入程序可以使一段一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一
个长的数据流.
在程序中所有的数据都是以流的方法进行传输和保存的。
Java 的 IO 是实现输入和输出的基础。
Java 把所有传统的流类型(类或抽象类)都放在 java.io 包中,用以实现输入输出功能。
输入和输出是一个相对的概念,我们一般站在程序的角度来分析和处理问题的。
程序需要数据 --> 读进来 --> 输入
程序保存数据 --> 写出去 --> 输出
水流
我的总结:最重要的:从程序的角度出发,读进来,写出去!(在储存数据的时候是把数据
写出去,这时候数据就储存在了文件里面,在需要调用数据的时候就把数据读进来,这样
数据就又到了程序中!)
流的分类(面试常考)
从不同角度分类:
按流动方向的不同可以分为输入流和输出流;
按处理数据的单位不同分为字节流和字符流;
按功能的不同可分为节点流和处理流;
节点流:直接操作目标设备,例如:磁盘或一块内存区域。
处理流:通过操作节点流,从而间接完成输入或输出功能的流。处理流是的存在是建立
在一个已经存在的输入流或输出流的基础之上的。
所有流都继承于以下四种抽象流类型的某一种:(抽象流)
5、操作流的步骤(重点)
File 类本身是与文件操作有关,但是如果要想操作内容则必须使用字节流或字符流完成,但
是不管是使用何种的输入输出流,其基本的操作原理是一样的(以文件流为准):
一、使用 File 类找到一个文件对象,得到 IO 操作的源或目标
二、通过字节流或字符流的子类创建对象,(得到 IO 操作的通道)
三、进行读或写的操作,(IO 操作)
四、关闭输入/输出,(打完收工,注意节约资源,关掉)
由于流的操作属于资源操作,所以在操作的最后一定要关闭以释放资源。
其实上面的流操作步骤可以联系生活中的例子:比如想把水井里的水弄到家里的大水缸去,怎
么搞呢?
1.找到水井在哪里;2.找根管子一头接在水井里,一头接在家里的大水缸里;3.打开管子上的龙
头,放水;4.水放满了,关掉水龙头.
计算机访问外部设备,要比直接访问内存慢得多,若我们每一次 write 方法调用都是直接写到
外部设备(比如磁盘上的一个文件),CPU 就要花费更多的时间去等待外部设备;我们可以开辟
一个内存缓冲区,程序每一次的 write 方法都是写到这个内存缓冲区中,只有这个缓冲区装满
了之后,系统才将这个缓冲区的内容一次集中写到外部设备.
我的总结:
好处:1.有效提高了 CPU 的使用率;2.write 方法并没有马上真正写入到外部设备,我们还
有机会回滚部分写入的数据;
Eg:
package july7file;
//构建输入流,读进来,输出到控制台!
import java.io.FileInputStream;
import java.io.InputStream;
public class Demo4 {
public static void main(String[] args) throws Exception {
//第一步:创建源!
String filename = "6.4";//这个文件是在工作空间里面,没有后缀名!
//第二步:创建管道!
InputStream ips = new FileInputStream(filename);
//第三步:操作!
byte []buff = new byte[1024];
int len;//定义缓冲区
while((len = ips.read(buff)) != -1){
System.out.println(new String(buff,0,buff.length));//输出
到控制台!此时的输出流就是打印流!
System.out.println("==========================================
");//打印下,看哪里在1024。1024的地方被隔开了
}
//第四步:关闭资源(字符流必须关闭资源,因为它中间有缓冲区!对于字节流可
以不用关闭,但是还是建议写上,习惯!)
ips.close();
}
}
输出:就将文件 6.4 中的数据打印到了控制台!
6、字节流和字符流
二者仅仅是操作单位不一样。
InputStream 和 Reader 是所有输入流的基类,他们都是抽象类,本身不能创建实例,但是他
们是所有输入流的模板。
一般来说处理字符或字符串时使用字符流,处理字节或二进制对象时应使用字节流;
备注:字符流必须关闭资源,因为它中间有缓冲区!而字节流不需要!但是一般都会(最后)
关闭资源!
字节流
字节流主要是操作 byte(字节)的类型数据:
字节输出流:OutputStream
字节输入流:InputStream
字符流
Java 中的字符是 Unicode 编码,是双字节的,1 个字符 等于 2 个字节;
使用字节来处理字符文本就不太方便了,此时可以考虑使用字符流;
字符流主要是操作 char 的类型数据:
字符输出流:Writer
字符输入流:Reader
字节流和字符流的区别
字节流和字符流在使用上的代码结构都是非常类似的,但是其内部本身也是有区别的,因为
在进行字符流操作的时候会使用到缓冲区(内存中),而字节流操作的时候是不会使用
到缓冲区的。
在输出的时候,OutputStream 类即使最后没有关闭内容也可以输出。但是如果是 Writer
的话,则如果不关闭,最后一条内容是无法输出的,因为所有的内容都是保存在了缓冲区
之中,每当调用了 close()方法就意味着清空缓冲区了。那么可以证明字符流确实使用了
缓冲区:
字节流:程序 → 文件
字符流:程序 → 缓冲区(内存中) → 文件
如果现在字符流即使不关闭也可以完成输出的话,则必须强制性清空缓冲区:
方法:public void flush() throws IOException
我的总结:
两者相比,肯定使用字节流更加的方便,而且在程序中像图片、MP3 等都是采用字节的方式
的保存,那么肯定字节流会比字符流使用的更广泛。
但是需要说明的是,但是如果要是想操作中文的话,字符流肯定是最好使的。(字节流的话
可能会出现乱码(一个汉字分成了两份)!)
Eg:
package july7file;
//字符流读出来,这时候就不会出现乱码的情况,在进行文字操作的时候最好使用字符流!
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo6 {
public static void main(String[] args) {
File src = new File("6.4");
read(src);
}
public static void read(File src){
Reader r = null;
try {
r = new FileReader(src);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
char []c = new char[1024];
int len;
try {
while((len = r.read(c)) != -1){
System.out.println(new String(c,0,c.length));//打印到
控制台
}
} catch (IOException e) {
e.printStackTrace();
}
try {
r.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7、文件拷贝
需求:源和目标!
那么我们需要源文件和目标文件!
构建管道的时候就需要两个:输出流和输入流管道!
Eg:
package july7file;
//java7开始的自动关闭资源
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Demo8 {
public static void main(String[] args) throws IOException {
File src = new File("E:/自荐信.doc");
File tar = new File("E:/自荐信1.doc");
copy(src, tar);
System.out.println("Well done !");
}
public static void copy(File src, File tar) throws IOException {
try (InputStream is = new FileInputStream(src);
OutputStream os = new FileOutputStream(tar);) {
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
题目:复制图片!
package july7file;
/**
* 文件的复制!对于本题而言,因为是图片,所以要想读出来,必须使用字节流!
* 字符流必须关闭资源,而字节流可以不关闭资源!但是还是建议全部的关闭,因为也不
会出错,这是关闭资源的习惯!
* 另外:最常用的是字节流,因为字节流在内存中不需要缓冲区,图片,mp3 等都是字节
流!但是对于文字的话还是字符流比较好;
* 因为字符流可以避免在字节流操作文字时出现的乱码现象(正好读取到了自定义缓冲区
的分割处);
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Demo7 {
public static void main(String[] args) throws Exception {
File src = new File("D:/java.jpg");
File tar = new File("D:/meinv.jpg");
copy(src,tar);
System.out.println("复制完成!");
}
public static void copy(File src,File tar) throws Exception{
/* Reader r = new FileReader(src);
Writer w = new FileWriter(tar);*/
/*if(!src.exists()){
throw new Exception("对不起,源文件不存在!");
}*/
InputStream in = new FileInputStream(src);
OutputStream os = new FileOutputStream(tar);
byte []c = new byte[1024];
int len;
while((len = in.read(c)) != -1){
os.write(c);
}
/* w.close();
r.close();*/
}
}
我的总结:对于图片的复制,可以使用字符流,但是这样的话文件可以复制成功但是
无法读取!
8、字节→字符转换流
OutputStreamWriter:把字节输出流对象转成字符输出流对象
InputStreamReader:把字节输入流对象转成字符输入流对象
FileWriter 和 FileReader 分别是 OutputStreamWriter 和 InputStreamReader 的直接子类,而不
是 Writer 和 Reader 的直接子类,区别于 FileInputStream 和 InputStream。
我的总结:无论使用字节流还是字符流实际上在内存中最终都是通过字节的形式来操
作流的。
所以并没有字符流转换字节流。
Eg:
//构建一个字节输出流对象
OutputStream out = new FileOutputStream("");
//把字节输出流转成字符输出流
Writer w = new OutputStreamWriter(out);
//然后的操作和使用字符输出流的操作一样
---------------------------------------------
//构建一个字节输入流对象
InputStream is = new FileInputStream("");
//把字节输入流转成字符输入流
Reader r = new InputStreamReader(is);
//然后的操作和使用字符输入流的操作一样
9、自动关闭资源的 try 语句
Java 7 简化资源清理(try-with-resources)自动关闭资源的 try 语句
自动关闭资源格式:
try( )//此处多了圆括号,()圆括号内写打开资源的代码,在这里创建的对象必须实现
Autocloseable 接口
{
IO 操作
}
catch(){
处理异常的代码
}
Eg:package july7file;
//java7开始的自动关闭资源
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Demo8 {
public static void main(String[] args) throws IOException {
File src = new File("E:/自荐信.doc");
File tar = new File("E:/自荐信1.doc");
copy(src, tar);
System.out.println("Well done !");
}
public static void copy(File src, File tar) throws IOException {
try (InputStream is = new FileInputStream(src);
OutputStream os = new FileOutputStream(tar);) //圆括号
内写打开资源的操作
{
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}