IO流总结(基础知识)

时间:2021-06-04 21:35:39

IO流总结

 IO这章的知识在上面一篇博客也说过一点,主要要体会一下装饰者设计模式和适配器设计模式,这样更利于我们理解复杂的IO体系结构。今天就让我们看一看。不过在讲IO
之前,我们先把文件(File)的知识简单过一下。

一、文件File

文件大家都不陌生,查看JDK帮助文档,我们知道File的定义——文件和目录(文件夹)路径名的抽象表示形式。

1、构造方法

File(String pathname):根据一个路径得到File对象
File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象

2、创建功能

public boolean createNewFile():创建文件 如果存在这样的文件,就不创建了
public boolean mkdir():创建文件夹 如果存在这样的文件夹,就不创建了
public boolean mkdirs():创建文件夹,如果父文件夹不存在,会帮你创建出来

举例:
         //需求1:我要在e盘目录下创建一个文件demo
File file = new File("e:\\a.txt");
大家可能会认为这里用了关键字new,那么文件File就会出来了,其实不是,因为File的定义是:文件和目录(文件夹)路径名的抽象表示形式。它仅仅是文件的表示
形式,不是真实的文件。 所以需要调用这样一句代码file.createNewFile()

      

        //需求2:我要在e盘目录test下创建一个文件b.txt

File file = new File("e:\\test\\b.txt");
System.out.println("createNewFile:" + file3.createNewFile());
运行之后可以看到控制台打印出:
Exception in thread "main" java.io.IOException: 系统找不到指定的路径。

所以注意:要想在某个目录下创建内容,该目录首先必须存在。我们可以这么做:File file1=new File("e:\\test);file1.mkdir();//先把文件夹(目录)创建出来然后在:

        File file2=new   File(e:\\test\\b.txt);file2.createNewFile();//在创建文件

        //看下面这行代码
        File file = new File("e:\\liuyi\\a.txt");
System.out.println("mkdirs:" + file8.mkdirs());
你会看到a.txt是个目录,不要认为指定了.txt后缀就是文件了,要看它调用的是creatNewFile()还是mkdir();

3、删除功能

public boolean delete()
这个方法比较简单,注意:
A:如果你创建文件或者文件夹忘了写盘符路径,那么,默认在项目路径下。
B:Java中的删除不走回收站。
C:要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹,所以delete一次只能删除一个文件,如果想删除一个文件夹,那必须要循环遍历了。

4、重命名功能

    public boolean renameTo(File dest)
如果路径名相同,就是改名。
        如果路径名不同,就是改名并剪切。

需求:将G盘所有视频文件该名成“0?-网络编程(概述).avi”,原来视频文件名称为:黑马程序员_毕向东_Java基础视频教程第23天-0?-网络编程(概述).avi

<span style="font-size:18px;">public class FileDemo {
private static File dest;
public static void main(String[] args) {
//封装目录
File file=new File("G:\\黑马入学\\网络编程1");
File[] sorcFolder = file.listFiles();
for (File f : sorcFolder) {
String name = f.getName();
//黑马程序员_毕向东_Java基础视频教程第23天-01-网络编程(概述).avi
//System.out.println(name);
int index = name.indexOf('-');
String string = name.substring(index+1);
//再次封装新的文件目录
dest = new File(file,string);
f.renameTo(dest);
}
}
}</span>
执行结果:
IO流总结(基础知识)

一看全部改名成功,哈哈。。。

5、判断功能

  public boolean isDirectory():判断是否是目录
  public boolean isFile():判断是否是文件
  public boolean exists():判断是否存在
  public boolean canRead():判断是否可读
  public boolean canWrite():判断是否可写
  public boolean isHidden():判断是否隐藏

6、简单获取功能

 public String getAbsolutePath():获取绝对路径
 public String getPath():获取相对路径
 public String getName():获取名称
 public long length():获取长度。字节数
 public long lastModified():获取最后一次的修改时间,毫秒值

 示例代码:

<span style="font-size:18px;"> public class FileDemo {
public static void main(String[] args) {
// 创建文件对象
File file = new File("demo\\test.txt");

System.out.println("getAbsolutePath:" + file.getAbsolutePath());
System.out.println("getPath:" + file.getPath());
System.out.println("getName:" + file.getName());
System.out.println("length:" + file.length());
System.out.println("lastModified:" + file.lastModified());

// 1432518063391
Date d = new Date(1432518063391L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
System.out.println(s);
}
}</span>
执行结果是:

IO流总结(基础知识)

7、高级获取功能

public String[] list():获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组
后者功能更为强大一点,因为返回的是File对象,所以可以得到File的各种属性。


8.文件名称过滤器——FilenameFilter

需求:判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)

示例代码:

<span style="font-size:18px;">public class FileDemo {
public static void main(String[] args) {

//封装e盘
File file=new File("e:\\");
// 获取该目录下所有文件或者文件夹的String数组
String[] list = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
//如果是文件并且是以.jpg结尾的,返回true,一旦返回true,name就会被放进String数组中,
return new File(dir, name).isFile() && name.endsWith(".jpg");
}
});
for (String string : list) {
System.out.println(string);
}
}
}</span>
虽然普通的方式也能实现,这样看起来,代码更加优雅,现在我们来看看list(FilenameFilter filter)的源码
<span style="font-size:18px;">public String[] list(FilenameFilter filter) {        String names[] = list();        if ((names == null) || (filter == null)) {            return names;        }        List<String> v = new ArrayList<>();        for (int i = 0 ; i < names.length ; i++) {            if (filter.accept(this, names[i])) {                v.add(names[i]);            }        }        return v.toArray(new String[v.size()]); }</span>

二、IO相关操作

    IO因为体系过于庞大,这里并不面面俱到,捡其重点来说。上面简单说了一下File的使用,File的读取,写入,复制。相信都是我们经常使用的功能。谈起IO流,首先我们要对它有个大致的认识,也就是IO的分类,请看下面:
A:按流向分
输入流 读取数据
输出流 写出数据
B:按数据类型
字节流
字节输入流
字节输出流
字符流
字符输入流
字符输出流

      如果你不知道什么是字节流,什么是字符流,我告诉你,如果电脑记事本可以打开的并且可以看得懂的,那就是可以用字符流操作的文件,当然也可以用字节流来操作,如果电脑记事本能打开,但是我们看不懂的,那就是用字节流来操作的文件,比如MP3文件、JPG文件等,现在我们来看看IO是怎么来操作File的。

1、字节流操作文件

1.1、FileOutputStream写出数据

A:操作步骤
a:创建字节输出流对象
b:调用write()方法
c:释放资源

B:示例代码:
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("hello".getBytes());
fos.close();

1.2、FileInputStream读取数据

FileInputStream读取数据
A:操作步骤
a:创建字节输入流对象
b:调用read()方法
c:释放资源

B:代码体现:
FileInputStream fis = new FileInputStream("fos.txt");
//方式1,我们一次读取一个字节
int by = 0;
while((by=fis.read())!=-1) {
System.out.print((char)by);
}
//方式2  我们一次读取一个字节数组
byte[] bys = new byte[1024];
int len = 0;
while((len=fis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));
}
fis.close();

1.3、四种方式复制文件效率比较

FileInputStream、FileOutputStream这两个类我们称之为基本字节流,相对于基本字节流,IO中还有高效字节流BufferedInputStream、BufferedOutputStream 
看完了这个,那么问题来,哪种方式好呢?
下面我们来看一个例子:
需求:把G:\IO流(IO流小结图解).avi复制到当前项目目录下的copy.avi中
 字节流四种方式复制文件:
 * 基本字节流一次读写一个字节
 * 基本字节流一次读写一个字节数组
 * 高效字节流一次读写一个字节
 * 高效字节流一次读写一个字节数组

G:\IO流(IO流小结图解).av的信息如下:

IO流总结(基础知识)
示例代码:

<span style="font-size:18px;">public class CopyAVI {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
method1("G:\\IO流(IO流小结图解).avi", "copy1.avi");
//method2("G:\\IO流(IO流小结图解).avi", "copy2.avi");
//method3("G:\\IO流(IO流小结图解).avi", "copy3.avi");
//method4("G:\\IO流(IO流小结图解).avi", "copy4.avi");
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "毫秒");
}

// 高效字节流一次读写一个字节数组:
public static void method4(String srcString, String destString)throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcString));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}

bos.close();
bis.close();
}

// 高效字节流一次读写一个字节:
public static void method3(String srcString, String destString)throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcString));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString));

int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);

}
bos.close();
bis.close();
}

// 基本字节流一次读写一个字节数组
public static void method2(String srcString, String destString)throws IOException {
FileInputStream fis = new FileInputStream(srcString);
FileOutputStream fos = new FileOutputStream(destString);

byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
fos.close();
fis.close();
}

// 基本字节流一次读写一个字节
public static void method1(String srcString, String destString)throws IOException {
FileInputStream fis = new FileInputStream(srcString);
FileOutputStream fos = new FileOutputStream(destString);
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
}</span>
执行结果:

方法一执行:IO流总结(基础知识)

方法二执行:IO流总结(基础知识)

方法三执行:IO流总结(基础知识)

方法四执行:IO流总结(基础知识)

四种方式的性能差异,通过这个执行结果,想必大家也知道了 ,所以在实际开发中选择哪一个来操作文件,相信大家已经有了选择了,反正我是喜欢用方法四!!

2、字符流操作文件

      大家可能会问,操作文件已经有了字节流,为什么还要说字符流呢,哎,谁叫咱们是中国人呢,汉字比较复杂,一个汉字站了两个字节,所以直接用字符流操作不够方便所以就有了字符流。我们来看看字符流的体系结构。

 2.1字符流体系结构  

   |--字符流
|--字符输入流
Reader
int read():一次读取一个字符
int read(char[] chs):一次读取一个字符数组

|--InputStreamReader
|--FileReader
|--BufferedReader
String readLine():一次读取一个字符串
|--字符输出流
Writer
void write(int ch):一次写一个字符
void write(char[] chs,int index,int len):一次写一个字符数组的一部分

|--OutputStreamWriter
|--FileWriter
|--BufferedWriter
void newLine():写一个换行符
void write(String line):一次写一个字符串


       字符流的读写是由基类Reader和Writer来完成的,它们各有两个子类来帮助我们来操作文件。Reader类的子类BufferedReader与Writer的子类BufferedWriter,我们一定可以猜到这是高效字符流操作文件使用的!那么,Reader类的子类InputStreamReader与Writer的子类OutputStreamWriter是干什么的呢?从类的名称来来看,前面是字节的,后面是字符的,我告诉你,这两个类就是转换流,把字节流转换成字符流,那么这有什么用呢?-----键盘录入!!!一会你们就知道了。。。。。注意OutputStreamWriter还有子类FileWriter,InputStreamReader还有子类FileReader。


2.1、字符流操作文件

我们先看一个字符流操作文件的例子。
需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中

 数据源:
a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader -- FileReader -- BufferedReader
 目的地:
b.txt -- 写出数据 -- 字符转换流 -- OutputStreamWriter -- FileWriter -- BufferedWriter

示例代码:

<span style="font-size:18px;">public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 封装数据源
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
// 封装目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

// 两种方式其中的一种一次读写一个字符数组,当然此处你也可以一次读取一个字节的方法
// char[] chs = new char[1024];
// int len = 0;
// while ((len = br.read(chs)) != -1) {
// bw.write(chs, 0, len);
// bw.flush();
// }

// 还有一种方式读写数据,这就是BufferedWriter、BufferedReader的强大之处。。。。
String line = null;
//下面三句话,我习惯连着写,写入、换行、刷新,动作相当连贯有木有啊!!!!!!
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}

// 释放资源
bw.close();
br.close();
}
}</span>
代码解读: bw.newLine();这句话的意思是换行,调用的效果和我们写“\r\n”是一样的,但是newLine可以夸平台的,这就是区别!
           readLine()这个方法有点厉害,因为它一次读一行!!!
博客写到这里,我们发现,复制文件文件起码有5种方式,前面所讲的1.基本字节流一次读写一个字节、2.基本字节流一次读写一个字节数组、3.高效字节流一次读写一个字节、4.高效字节流一次读写一个字节数组共4种,再加上上面的一种,共5种,而且第五种是最好的,个人感觉。。。。

3、IO编程实战

3.1、复制单级文件夹

/*
 * 需求:复制指定目录下的指定文件,并修改后缀名。
 * 指定的文件是:.java文件。
 * 指定的后缀名是:.txt
 * 指定的目录是:txt
 * 数据源:G:\designModel
 * 目的地:G:\txt
 * 
 * 分析:
 * A:封装目录
 * B:获取该目录下的java文件的File数组
 * C:遍历该File数组,得到每一个File对象
 * D:把该File进行复制
 * E:在目的地目录下改名
 */

 * 数据源:G:\designModel如下图所示:

IO流总结(基础知识)

 示例代码:

<span style="font-size:18px;">public class CopyFolder {

public static void main(String[] args) throws IOException {
//A:封装目录
File srcFolder=new File("G:\\designModel");
//封装目的地
File destFolder=new File("G:\\myNote");
//如果目的地文件不存在,就创建一个
if(!destFolder.exists()){
destFolder.mkdir();
}
//B:获取该目录下的java文件的File数组
File[] srcFiles = srcFolder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
// 文件过滤器,得到.java文件,去除其他类型的文件
return new File(dir,name).isFile()&&name.endsWith(".java");
}
});

// C:遍历该File数组,得到每一个File对象

for (File file : srcFiles) {
String name = file.getName();
File newFile=new File(destFolder,name);
copyFile(file,newFile);
}

//E:在目的地目录下改名
for (File file :destFolder.listFiles()) {

String name = file.getName();
String newName = name.replace(".java", ".txt");
File newFile=new File(destFolder,newName);
file.renameTo(newFile);
}

}

// D:把该File进行复制
private static void copyFile(File file, File newFile) throws IOException {

BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(newFile));
byte [] bys=new byte[1024];
int len=0;
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
//关闭资源
bis.close();
bos.close();
}
}</span>
执行结果:

IO流总结(基础知识)

3.2、复制多级文件夹

思路分析
  数据源:G:\\
  目的地:G:\\


    A:封装数据源File
    B:封装目的地File
    C:判断该File是文件夹还是文件
  a:是文件夹
                 就在目的地目录下创建该文件夹
         获取该File对象下的所有文件或者文件夹File对象
         遍历得到每一个File对象
           回到C
           b:是文件
          复制(字节流)

示例代码:

<span style="font-size:18px;">public class CopyFolders{
public static void main(String[] args) throws IOException {
// 封装数据源File
File srcFile = new File("G:\\");
// 封装目的地File
File destFile = new File("E:\\");
// 复制文件夹的功能
copyFolder(srcFile, destFile);
}

private static void copyFolder(File srcFile, File destFile)throws IOException {
// 判断该File是文件夹还是文件
if (srcFile.isDirectory()) {
// 文件夹
File newFolder = new File(destFile, srcFile.getName());
newFolder.mkdir();
// 获取该File对象下的所有文件或者文件夹File对象
File[] fileArray = srcFile.listFiles();
for (File file : fileArray) {
copyFolder(file, newFolder);
}
} else {
// 文件
File newFile = new File(destFile, srcFile.getName());
copyFile(srcFile, newFile);
}
}

//复制文件
private static void copyFile(File srcFile, File newFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
bis.close();
}
}</span>


3.3 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件

思路分析:
A:创建学生类
B:创建集合对象
    因为要排序,所以用TreeSet<Student>
C:键盘录入学生信息存储到集合
D:遍历集合,把数据写到文本文件


<span style="font-size:18px;">public class Student {
// 姓名
private String name;
// 语文成绩
private int chinese;
// 数学成绩
private int math;
// 英语成绩
private int english;

//构造方法和setter/getter方法省去了。。。。。。。。
}</span>
<span style="font-size:18px;">public class StudentIO {	public static void main(String[] args) throws IOException {		// 创建集合对象(采用匿名内部类的方式,创建带有比较器的集合对象)		TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {			@Override			public int compare(Student s1, Student s2) {				int num = s2.getSum() - s1.getSum();				int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;				int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;				int num4 = num3 == 0 ? s1.getName().compareTo(s2.getName()): num3;				return num4;			}		});		// 键盘录入学生信息存储到集合		for (int x = 1; x <= 5; x++) {			Scanner sc = new Scanner(System.in);			System.out.println("请录入第" + x + "个的学习信息");			System.out.println("姓名:");			String name = sc.nextLine();			System.out.println("语文成绩:");			int chinese = sc.nextInt();			System.out.println("数学成绩:");			int math = sc.nextInt();			System.out.println("英语成绩:");			int english = sc.nextInt();			// 创建学生对象			Student s = new Student();			s.setName(name);			s.setChinese(chinese);			s.setMath(math);			s.setEnglish(english);			// 把学生信息添加到集合			ts.add(s);		}		// 遍历集合,把数据写到文本文件		BufferedWriter bw = new BufferedWriter(new FileWriter("students.txt"));		//以下三行代码连写		bw.write("学生信息如下:");		bw.newLine();		bw.flush();	    bw.write("姓名\t语文成绩\t数学成绩\t英语成绩");		bw.newLine();		bw.flush();		for (Student s : ts) {			StringBuilder sb = new StringBuilder();			sb.append(s.getName()).append("\t").append(s.getChinese())					.append("\t").append(s.getMath()).append("\t")					.append(s.getEnglish());			bw.write(sb.toString());			bw.newLine();			bw.flush();		}		// 释放资源		bw.close();		System.out.println("学习信息存储完毕");	}}</span>
输入:

IO流总结(基础知识)
执行结果:
IO流总结(基础知识)

好啦,如果上面的例子可以弄懂,IO就基本没有什么问题啦,哈哈。。。