本文的首要内容有以下几点

  • File类介绍
  • 传统IO流介绍
  • 传统IO流常见的文件操作
  • 规范流
  • NIO
  • PathsFiles东西类的介绍和文件操作

Java中的File类

在Java中,File类是对文件和文件夹的笼统,因而能够用一个File实例来表示一个文件或许文件夹。如在E:\java-io-file\cat.txt文件夹中存在cat.txt文件。,

谈谈Java中的IO流

如下代码就能看见Java对文件和文件夹的笼统,便能够运用封装好的API得到一些文件信息。

public static void main(String[] args) {
  File diction = new File("E:\java-io-file");
  System.out.println(diction.isDirectory()); // true
  File file = new File("E:\java-io-file\cat.txt");
  System.out.println(file.isDirectory()); // false
  System.out.println(file.isFile()); // true
}

File类常见的api

//获取类功用

  • public String getAbsolutePath(); //获取绝对途径
  • public String getPath(); //获取相对途径
  • public String getName(); //获取文件名
  • public String getParent(); //获取上级目录
  • public int length(); //长度 字节
  • public long lastModeified(); //最后一次修正的时刻 //以下适用于文件目录
  • public String [] list(); //获取指定途径下的文件或许文件目录的目录称号数组
  • public File[] listFiles();//获取指定目录下的一切文件或文件目录的File数组
    //file1.renameTo(file2) file1有必要存在,file2不能存在 才能回来true
  • public boolean renameTo(); //File 类 判别
  • public boolean isDirectory();
  • public boolean isFile();
  • public boolean exists();
  • public boolean canRead();
  • public boolean canWrite();
  • public boolean isHidden(); //File 创立
  • public boolean createNewFile(); //创立文件
  • public boolean mkdir(); //创立文件目录 假如上级目录不存在 则不创立
  • public boolean mkdirs(); //创立文件目录 假如上级目录不存在,则一同创立。
  • public boolean delete();//删去 不走回收站

递归拜访文件和文件夹的完成

在有了上述根本的认识之后,就能够运用这些知识做到递归拜访文件

// recursiveVisitedFiles("E:\springboot-websocket");
public void recursiveVisitedFiles(String path){
  File root = new File(path);
  if (root.isDirectory()){
    System.out.println("directoryName = "+root.getName());
    File[] subFiles = root.listFiles();
    for (File file : subFiles){
      recursiveVisitedFiles(file.getAbsolutePath());
     }
   }else {
    System.out.println("fileName = " + root.getName());
   }
}

得到如下输出

谈谈Java中的IO流

File类中值得注意的是

delete(): 由于Java是把文件和文件夹都笼统为一个File实例,因而在调用该办法删去文件夹时,需求确保文件夹为为空时,才能正确删去,否则回来false.

传统的流式IO

Java1.4曾经,Java中规划的IO比较复杂。Java的IO流规划屏蔽了实际的i/o设备中处理的细节,其原则如下

  • 字节省对应原生的二进制数据。
  • 字符流对应字符数据,主动处理与本地字符集之间的转化。
  • 缓冲流能够提高读写性能,经过减少底层API的次数来优化IO。

规划原则

在JDK1.0时,一切与输入有关系的类都承继于InputStream , 一切与输出相关的类都承继自OutputStream。Java1.1 对根本的IO流类库做了许多的修正,添加了许多以Reader/writer为基类的衍生类,Reader和Writer的承继体系首要是为了国际化。旧的IO承继体系仅支撑8bit的字节省,不能够很好的处理16bit的Unicode字符。

一切InputStream/reader派生而来的类都含有read()办法,用来读取单个字节或许字节数组。

一切OutputStream/Writer派生而来的类都含有write()办法,用来写单个字节或许字节数组。

字节省能够操作一切类型的文件。

  • read()办法能够读取单个字节(字符)也能够读取多个字节(字符),读取单个字节(字符)时,回来的是当时字节,
  • read(array)时,回来的是读取的字节(字符)数
  • -1 都表示读到文件结尾, 读完了整个文件。

输入流和输出流

InputStream/OutputStream基于字节省的。

InputStream作为输入流,是从外部获取数据到内存中,因而输入流类型有

功用
ByteArrayInputStream 允许将内存的缓冲区作为InputStream
StringBufferInputStream String转化成InputStream
FileInputStream 从文件中读取信息
PipedInputStream 产生用于写入相关 PipedOutputStream 的数据。
SequenceInputStream 将两个或多个 InputStream 目标转化成一个 InputStream

OutputStream做为输出流,该类别的类首要决定输出到哪里:文件,字节数组等

功用
ByteArrayOutputStream 在内存中创立缓冲区,一切送往的数据都要放置在此缓冲区。
FileOutputStream 用于将信息写入文件
PipedOutputStream 任何写入其中的信息都会主动作为相关 PipedInputStream 的输出

以字节省演示:仿制文件

// String source = "E:\java-io-file\cat.png";
// String dest1 = "E:\java-io-file\cat1.png";;
// copyFileWithStream(source,dest1);
public void copyFileWithStream(String source,String dest){
  File sourceFile = new File(source);
  File destFile = new File(dest);
​
  FileInputStream fis = null;
  FileOutputStream fos = null;
  try {
    fis = new FileInputStream(sourceFile);
    fos = new FileOutputStream(destFile);
    int len = 0;
    byte[] bytes = new byte[1024];
    while ((len = fis.read(bytes)) != -1){
      fos.write(bytes,0,len);
     }
    fos.flush();
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
  fos.close();
  fis.close();
}

仿制成果如下:

谈谈Java中的IO流

字符流Writer和Reader

字符流首要针对的是文本文件,假如运用字符流进行图片的仿制,则会导致仿制生成的文件打不开!

字符流的引进首要是首要是为了国际化Unicode, 旧的IO流不能够很好的处理16bit的字符。新的类库的IO操作比旧类库要快。

除此之外,有时候也能够将面向字节省的操作转化为面向字符流的操作如以下

字节省 字符流适配
InputStream InputStreamReader
OutputStream OutputStreamWriter
FileInputStream FileReader
FileOutputStream FileWriter
ByteArrayInputStream CharArrayReader
ByteArrayOutputStream CharArrayWriter

还有许多其他的列别,未罗列全,请参阅JDK相关文档

以字符流为例:演示仿制文本文件

//  copyFileWithChar("E:\java-io-file\cat.txt","E:\java-io-file\cat_.txt");
public void copyFileWithChar(String source,String dest){
  try {
    FileReader reader = new FileReader(source);
    FileWriter fileWriter = new FileWriter(dest);
    int len = 0;
    char[] chars = new char[1024];
    while ((len = reader.read(chars)) != -1){
      fileWriter.write(chars,0,len);
     }
    fileWriter.flush();
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
}

演示成果如下:

谈谈Java中的IO流

缓冲流

为了IO流的操作速度,Java供给了缓冲流。缓冲流作用于已有流的基础上如FileInputStream/FileReader

缓冲流 被缓冲的流
BufferedReader FileReader
BufferefWriter BufferedWriter
BufferedInputStream FileInputStream
BufferedOutputStream FileOutputStream
相同以仿制图片为例、
// String dest3 = "E:\java-io-file\cat3.png";
// String source = "E:\java-io-file\cat.png";
// copyFileWithBufferStream(source,dest3);
public void copyFileWithBufferStream(String source, String dest) {
  try (FileInputStream fis = new FileInputStream(source);
     FileOutputStream fos = new FileOutputStream(dest);
     BufferedInputStream bis = new BufferedInputStream(fis);
     BufferedOutputStream bos = new BufferedOutputStream(fos)) {
    int len = 0;
    byte[] chars = new byte[1024];
​
    while ((len = bis.read(chars)) != -1) {
      bos.write(chars, 0, len);
     }
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
}

谈谈Java中的IO流

其他流

RandomAccessFile类:

RandomAccessFile 适用于由巨细已知的记载组成的文件,所以咱们能够运用 seek() 将文件指针从一条记载移动到另一条记载,然后对记载进行读取和修正。用得不多。

  • 直接承继java.lang.Object类 完成了DataInput, DataOutput接口
  • 即可输入也可输出

ObjectOutputStream / ObjectInputStream

前者将Java目标序列化到磁盘中或许经过网络传出去,后者将前者转化为Java目标

需求阐明的是:

  • 有必要完成Serializable接口
  • 类的一切属性属性有必要可序列化,默许情况下根本数据类型是可序列化的。
  • 供给静态常量 serialVersionUID;

规范流

程序的一切输入都能够来自于规范输入,程序的一切输出都能够流向规范输出,程序的一切过错都能够发送到规范过错。

Java供给以下规范流

  • System.in: 规范输入流
  • System.out: 规范输出流
  • System.err: 规范过错

规范输出流和规范过错流现已被预先包装为PrintStream目标,因而能够直接运用,而规范输入流是原生的InputStream,所以在读取规范输入流的内容时,需求将其转化包装为其他流。

// System源码
public static final InputStream in = null;
public static final PrintStream out = null;
public static final PrintStream err = null;

规范输入流结合Scanner读取用户输入:

public static void main(String[] args) {
  Scanner scanner = new Scanner(System.in);
  System.out.println("准备输入");
  do {
    String next = scanner.next();
    System.out.println("读取用户输入:"+next);
   } while (scanner.hasNext());
  scanner.close();
}

谈谈Java中的IO流

注:Scanner.next()默许以空格分隔.

NIO

NIO是Java1.4引进的,以同步非堵塞的办法重写了老的IO。

在此之前,咱们处理IO的办法根本上都是以字节或许字符。如InputStream/OutputStream或许FileReader/FileWriter体系,在NIO中不需求这么底层的操作。通常是和各种Buffer目标,如

  • ByteBuffer: 字节缓冲区
  • DoubleBuffer
  • ShortBuffer
  • LongBuffer
  • IntBuffer
  • FloatBuffer
  • CharBuffer

xxxBuffer目标都是都相应类型的一个封装,在上面所罗列的目标中,首要运用的是ByteBuffer目标。

nio中运用了更挨近操作体系IO履行办法的结构:Channel和Buffer只有上述的这一类型可直接与Channel通道交互。

而旧IO中的FileInputStream、FileOutputStream、RandomAccessFile被更新成FileChannel。而字符形式的Writer/Reader不能生成Channel,但Channel相关的类具有生成Reader和Writer的办法

public void bufferTest() {
  try {
    FileChannel out = new FileOutputStream("data.txt").getChannel();
    FileChannel in = new FileInputStream("data.txt").getChannel();
    out.write(ByteBuffer.wrap("Yierisacat".getBytes())); // 生成data.txt 文件内容是Yierisacat
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    in.read(buffer);
    buffer.flip();
    while (buffer.hasRemaining())
      System.out.write(buffer.get()); // output: Yierisacat
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
}

注:Java NIO 的ByteBuffer API内容较多且规划得比较复杂,这儿仅仅简略提及一下。

Files和Paths类的运用

经过前文的简略描绘,也能够发现在进行文件IO操作时,需求运用到许多的类,创立许多的目标。Java的规划者为了便利相关操作,引进了Files东西类,对常见的文件操作供给了封装办法,如创立、删去、判别文件是否存在,可读可写、遍历等

  • Files.createFile()
  • Files.createDirectory()
  • FIles.createDirectories()
  • Files.delete()
  • Files.deleteIfExist()
  • Files.copy()
  • Files.exists()
  • Files.walkFileTree()

Files的大多数办法都需求传递一个Path目标作为参数,因而在运用之前,需求先把Path目标弄理解。

一个Path目标表示一个文件或许目录的途径,是一个跨操作体系和文件体系的笼统。Java的规划者供给了Paths东西类对相关操作进行了封装。其中供给了一个静态办法用于获取Path实例

public static Path get(URI uri) {
  return Path.of(uri);
}
public static Path get(String first, String... more) {
  return Path.of(first, more);
}

get办法能够承受一系列String字符串或一个统一资源标识符作为参数。回来一个Path目标。

Path目标封装了对途径的相关操作,如增添,删去部分途径,判别途径以什么最初或许结尾

  • getNameCount(),回来途径的个数
  • getName(int index): 回来index处的称号
  • getRoot()
  • getParent()
  • startsWith()
  • endsWith()
public void testPaths(){
  String p = "E:\java-io-file\cat.png";
  Path path = Paths.get(p);
  System.out.println("getNameCount = "+path.getNameCount());
  System.out.println("getName(1) = "+path.getName(1));
  System.out.println("getRoot = "+ path.getRoot() );
  System.out.println("getParent = "+path.getParent());
  System.out.println("start with [E] ="+ path.startsWith("E"));
  System.out.println("start with [E:] ="+ path.startsWith("E:"));
  System.out.println("start with [E:\] ="+ path.startsWith("E:\"));
  System.out.println("endsWith cat ="+path.endsWith("cat"));
  System.out.println("endsWith .png ="+path.endsWith(".png"));
  System.out.println("endsWith cat.png ="+path.endsWith("cat.png"));
  System.out.println("parent end with [java-o-file] = "+ path.getParent().endsWith("java-o-file"));
  System.out.println("parent end with [java-o-file\] = "+ path.getParent().endsWith("java-io-file\"));
}

谈谈Java中的IO流

需求阐明的是startWith、endsWith这两个办法比较的是当时部分途径的全部途径,请注意赤色框出的部分。

Path接口还供给了resolve()办法增添尾途径和relativize()将绝对途径转化为相对途径

Path path1 = Paths.get("E:\java-io-file\");
System.out.println(path1.resolve("gus")); 
System.out.println("上一级:"+path1.resolve("gus").relativize(Paths.get("E:\java-io-file\")));
System.out.println("同等级:"+path1.resolve("gus").relativize(Paths.get("E:\java-io-file\gus\")));
System.out.println("下一等级:"+path1.resolve("gus").relativize(Paths.get("E:\java-io-file\gus\next")));

注:gus文件夹并不存在,Path办法许多,其他办法参请看源码。

谈谈Java中的IO流

演示了Path的根本API,接下来看一下啊Files循环遍历。

try {
  Files.walkFileTree(Paths.get("E:\BrowserDownLoad\GoogleChromeDownLoad"), new SimpleFileVisitor<>() {
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
      System.out.println("进入文件夹 dirName:" + dir.getFileName() + " 之前");
      return super.preVisitDirectory(dir, attrs);
     }
​
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      System.out.println("拜访当时文件 fileName = " + file.getFileName());
      return super.visitFile(file, attrs);
     }
​
    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
      System.out.println("拜访失利");
      return super.visitFileFailed(file, exc);
     }
​
    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      System.out.println("拜访文件夹:" + dir.getFileName() + " 结束");
      return super.postVisitDirectory(dir, exc);
     }
   });
} catch (IOException e) {
  e.printStackTrace();
}

部分成果截图如下:

谈谈Java中的IO流

java.nio.file.SimpleFileVisitor 供给了一切办法的默许完成。这样,在咱们的匿名内部类中,咱们只需求重写非规范行为的办法:visitFile()postVisitDirectory() 完成删去文件和删去目录

总结

前文中的传统流式IO也好或许规范IO也罢。在实际工作中都用得不多,毕竟没有那个公司的事务需求你从控制台输入指令或许操作。Java中的文件操作如读取或许删去等操作优先考虑Files、Paths相关类的运用。

别的无论是输入流仍是输出流,只需理解了输入是从其他地方读入内存,输出是从内存到其他地方。挑选什么样的流,或许什么样的API去完成这个过程就简略明了了。

参阅资料

  • on Java 8 中文版