Java IO流(一)

IO(Input Output)流的概述

下面给出IO流的基本概述,这样可以对IO流有一个宏观上的基本了解。

    1. IO流用来处理设备之间的数据传输。
    1. Java对数据的操作是通过流(系统资源)的方式。
    1. Java用于操作流的对象都在java.io包中。
    1. 流按操作数据分为两种:字节流与字符流。
    1. 流按流向分为:输入流,输出流。

虽如此,但我对IO流有一个自己的认识。IO流是一门用于处理设备上的数据的技术,此设备包括内存、硬盘、光盘。流即系统资源,Windows系统本身就可以操作设备,各种语言只是使用了系统平台上的这个资源,并对外提供了各种语言自己的操作功能,这些功能最终调用的是系统资源。当我们使用完资源后一定要记住释放。最后,Java中所涉及的功能对象都存储到了java.io包中。
设备上数据最常见的存储表现形式是文件(file),因此接下来先学习一下文件的基本操作。

File

File类的简要概述

  1. 用来将文件或者文件夹封装成对象。
  2. 方便对文件与文件夹的属性信息进行操作。
  3. File对象可以作为参数传递给流的构造函数。

File类中的常用方法#

#File类的构造方法

创建File对象有三种方法,下面我会依次详述。

第①种方法,将a.txt封装成File对象,可以将已有和未出现的文件或文件夹封装成对象,如下:

File f1 = new File("a.txt");

第②种方法:

File f2 = new File("c:\abc", "b.txt");
1
第③种方法:

File d = new File("c:\\abc");
File f3 = new File(d, "c.txt");

打印时,new File()封装的是什么,就打印什么,即以下语句

System.out.println("f1:"+f1);
System.out.println("f2:"+f2);
System.out.println("f3:"+f3);

会依次输出:

f1:a.txt 
f2:c:\abc\b.txt 
f3:c:\abc\c.txt

separator字段

与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。该字段是跨平台的,可这样创建File对象:

File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");

获取文件(夹)相关信息

获取文件相关信息的方法如下:

  1. getName():获取文件或目录的名称。

  2. long length():获取文件的大小。

  3. getParent():该方法返回的是绝对路径中的父目录,如果获取的是相对路径,返回null。如果相对路径中有上一层目录,那么该目录就是返回结果。

  4. getPath():返回的就是File类构造函数里面的字符串,不管什么绝对或相对路径,你给我什么,我就返回什么。

  5. getAbsolutePath():该方法返回的是文件对象的绝对路径,即使封装的是相对的,获取到的也是绝对的。

    File f = new File("abc\file.txt");

    System.out.println("path:"+f.getPath()); // abc\file.txt
    System.out.println("abspath:"+f.getAbsolutePath()); // E:\MyJava\Java_Basic\day17\abc\file.txt
    System.out.println("parent:"+f.getParent()); // abc

long lastModified():文件最后一次被修改的时间。

创建文件(夹)

boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false;不存在,就会创建,创建成功并返回true。和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,会覆盖。示例代码如下:

File f = new File("file.txt");
System.out.println("create:"+f.createNewFile());

boolean mkdir():创建文件夹,但只能创建一级目录。示例代码如下:

File dir = new File("abc");
System.out.println("mkdir:"+dir.mkdir());

boolean mkdirs():创建多级文件夹。示例代码如下:

File dir = new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");
System.out.println("mkdirs:"+dir.mkdirs());

删除文件(夹)

boolean delete():删除失败返回false,删除文件夹时,必须保证该文件夹没有内容,如果有内容,必须先把内容删除后,才可以删除当前文件夹。示例代码如下:

File f = new File("file.txt");
System.out.println("delete:"+f.delete());

void deleteOnExit():在程序退出时删除指定文件,告诉JVM,一会退出时删除文件(一般是临时文件),即使发生异常。示例代码如下:

File f = new File("file.txt");
f.deleteOnExit();
code(); // 省略一些代码,这些代码可能会抛出异常
System.out.println("delete:"+f.delete());

File类中的判断方法

boolean exists():判断文件(夹)是否存在。示例代码如下:

File f = new File("file.txt");
System.out.println("exists:"+f.exists());

boolean canExecute():测试应用程序是否可以执行此抽象路径名表示的文件。示例代码如下:

File f = new File("FileDemo.java");
System.out.println("execute:"+f.canExecute());

isDirectory():判断是否是一个目录。

isFile():判断是否是一个标准文件。记住在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在,通过exists()判断。
isAbsolute():判断是否为绝对路径,文件不存在也可以判断。示例代码如下:

File f = new File("c:\\file.txt");
System.out.println(f.isAbsolute());

isHidden():判断是否是一个隐藏文件。

列出可用的文件系统根

File类中有static File[] listRoots()这样一个方法可列出可用的文件系统根,示例代码如下:

File[] files = File.listRoots();
for (File f : files) {
System.out.println(f);
}

运行以上代码,会输出计算机可用盘符,诸如下面这样:

C:\ 
D:\ 
E:\ 
F:\ 
G:\

list()与listFiles()方法

String[] list():列出当前目录下的所有文件和文件夹名称,包含隐藏文件。注意:调用list()方法的File对象必须是封装了一个目录,而且该目录还必须存在。示例代码如下:

File f = new File("c:\\");
String[] names = dir.list();
if (names != null) 
for (String name : names) {
System.out.println(name);
}

File[] listFiles():返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。如果目录为空,那么数组也将为空。如果此抽象路径名不表示一个目录,或者发生I/O错误,则返回null。示例代码如下:

File dir = new File("c:\\");

File[] files = dir.listFiles();
for (File file : files) {
System.out.println(file.getName() + "..." + file.length());
}

String[] list(FilenameFilter filter):返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。用途:专门找指定目录下指定后缀名(例如:.java/.jpg/.mp3/.log)的文件。示例代码如下:

public static void listDemo_2() {
File dir = new File("E:\\MyJava\\Java_Basic\\day16");
String[] arr = dir.list(new FilenameFilter() {

        /**
         * @param dir   被过滤的目录。
         * @param name  被遍历目录中的文件夹或者文件的名称。
         */
@Override
public boolean accept(File dir, String name) {
        // System.out.println("dir:"+dir+".....name:"+name);
        // 谁会这样写代码呢?显然很麻烦
        /*
        if(name.endsWith(".java"))
            return true;
        else
            return false;
        */
        // 简写
        return name.endsWith(".java");
}
});
        System.out.println("len:"+arr.length);
        for (String name : arr) {
         System.out.println(name);
        }
}

递归

递归就是函数自身调用自身,即函数内部又使用到了该函数功能。虽然知道是这么一个意思,但一写代码遇到递归就懵逼了。所以这里我就举几个例子来详述一下它。
例1,列出指定目录下的文件或者文件夹,包含子目录中的内容。也就是列出指定目录下的所有内容。
分析:因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身。这种表现形式或者编程手法,称为递归。
首先定义一个列出目录功能的递归函数,如下:

public static void getAllFiles(File dir) {
System.out.println("dir:" + dir);

// 1、获取该目录的文件对象数组。
File[] files = dir.listFiles();

// 2、对数组进行遍历。
for (File file : files) {

if (file.isDirectory()) {
getAllFiles(file);
} else {
System.out.println("file:" + file);
}

}
}

然后调用以上递归函数,调用代码如下:

public static void main(String[] args) {
File dir = new File("F:\\Java\\java_bxd");
getAllFiles(dir);
}

最后,我们似乎可以知道了递归什么时候使用了?——功能被重复使用,但是每次该功能使用参与运算的数据不同时,可以考虑递归方式解决。
例2,十进制数转二进制数,代码如下:

public static void toBin(int num) {
if(num > 0) {
toBin(num / 2);
System.out.print(num % 2);
}
}

不理解以上代码没关系,下面我会图解,如下:
这里写图片描述
例3、求和,代码如下:

public static int getSum(int n) {
if(n == 1)
return 1;
return n + getSum(n - 1);
}

我图解如下:
这里写图片描述
从以上两个例子我们可以得出如下结论——递归要注意:

限定条件。
要注意递归的次数,尽量避免内存溢出(java.lang.StackOverflowError)。
现在我们来思考一个问题——删除一个目录的过程是如何进行的呢?
分析:在Windows中,删除目录从里面往外删除的。既然是从里往外删除,就需要用到递归了。这里,就直接给出程序代码了,如下:

import java.io.*;

class RemoveDir {
public static void main(String[] args) {
File dir = new File("d:\\java");
removeDir(dir);
}

public static void removeDir(File dir) {
File[] files = dir.listFiles();
for (int x = 0; x < files.length; x++) {
if(!files[x].isHidden() && files[x].isDirectory()) // 避开隐藏文件
removeDir(files[x]);
else
System.out.println(files[x].toString()+":-file-:"+files[x].delete());
}
System.out.println(dir+"::dir::"+dir.delete());
}
}

此处须注意Java删除时是不走回车站的,除此之外,还须注意两点,如下:

隐藏目录——无法访问就不能删除,返回的files为空,会导致空指针异常。
系统中的有些文件虽然看上去是一个文件,其实是一个目录,或反之。
递归综合练习

获取一个想要的指定文件的集合,例如获取F:\Java\java_bxd目录下(包含子目录)的所有的.java的文件对象,并存储到集合中。
以下是我的分析:

对指定的目录进行递归。
在递归的过程中需要过滤器,获取指定过滤器条件的.java文件对象。
将满足指定过滤器条件的.java文件对象都添加到集合中。
首先编写一个对指定的目录进行递归的函数,可写成如下,以供参考。

/**
 * @param dir 需要遍历的目录。
 * @param list 用于存储符合条件的File对象。
 * @param filter 接收指定的过滤器。
 */
public static void getFileList(File dir, List list, FileFilter filter) {

// 1、通过listFiles方法,获取dir当前下的所有的文件或文件夹对象。
File[] files = dir.listFiles();

// 2,遍历该数组
for (File file : files) {

// 3,判断是否是文件夹。如果是,递归!如果不是,那就是文件,就需要对文件进行过滤。
if (file.isDirectory()) {
getFileList(file, list, filter);
} else {
// 4,通过过滤器对文件进行过滤。
if (filter.accept(file)) {
list.add(file);
}
}
}

}

在编写该函数时一定要注意:多级目录下都要用到相同的集合和过滤器,那么不要在递归方法中定义,而是不断地进行传递。
然后我们再定义一个获取指定过滤器条件的文件的集合的函数,可写成如下,以供参考。

public static List fileList(File dir, String suffix) {

// 1,定义集合。
List list = new ArrayList();

// 2,定义过滤器。
FileFilter filter = new FileFilterBySuffix(suffix);

getFileList(dir, list, filter);

return list;

}

从以上函数可看出,需要用到文件过滤器,所以我们还需要定义一个文件过滤器,这里我设计了一个——FileFilterBySuffix.java,其内容如下:

public class FileFilterBySuffix implements FileFilter {

private String suffix;

public FileFilterBySuffix(String suffix) {
super();
this.suffix = suffix;
}

@Override
public boolean accept(File pathname) {

return pathname.getName().endsWith(suffix);
}

}

最后,我们编写测试代码进行测试。

public static void main(String[] args) {

File dir = new File("F:\\Java\\java_bxd");

List list = fileList(dir, ".java");

for (File file : list) {
System.out.println(file);
}

}

此综合练习用到了比较多的知识点,做起来还蛮不错的,知识点都串联起来了。
转载地址:https://blog.csdn.net/yerenyuan_pku/article/details/78231697

你可能感兴趣的:(Java IO流(一))