从0开始复习java(9)--IO

Java的io通过java.io包下的类和接口支持。主要有输入、输出流,又分为字节流和字符流。Java的io流使用了一种装饰器设计模式,他将io流分为底层节点流和上层处理流,节点流用于和底层的物理存储结点直接关联。

Java7在java.nio及其子包下提供了一系列全新的api,是对原有新io的升级。

一、File类

File类是java.io包下代表与平台无关的文件和目录。File能新建、删除、重命名文件和目录,不能访问文件本身内容。

访问文件和目录

访问文件名的方法

  1. String getName()
  2. String getPath()
  3. File getAbsoluteFile()
  4. String getAbsolutePath()
  5. String getParent()
  6. boolean renameTo(FIle newName)

文件检测相关的方法

  1. boolean exists()
  2. boolean canWrite()
  3. boolean canRead()
  4. boolean isFile()
  5. boolean isDirectory()
  6. boolean isAbsolute()

获取常规文件信息

  1. long lastModified()
  2. long length()

文件操作相关的方法

  1. boolean createNewFile()
  2. boolean delete()
  3. static File createTempFile(String prefix, String suffix)
  4. static File CreateTempFIle(String prefix, String suffix, File directory)
  5. void deleteOnExit()

目录操作相关的方法

  1. boolean mkdir()
  2. String[] list()
  3. File[] listFiles()
  4. static File[] listRoots()

文件过滤器

File类的list()方法可以接收一个FilenameFilter参数,通过该参数可以只列出符合条件的文件。

FilenameFilter接口和javax.swing.filechooser包下的FileFilter抽象类的功能非常相似。

二、Java的IO流

流的分类

输入流和输出流

抽象基类:

InputStream和Reader

OutputStream和Writer

字节流和字符流

字节流:8位,InputStream和OutputStream

字符流:16位,Reader和Writer

节点流和处理流

向一个特定的io设备读写数据的流为节点流,Low Level Stream

处理流对于已有的节点流进行连接和封装,通过封装后的流实现数据读/写功能。High Level Stream

处理流是一种典型的装饰器设计模式,也称为包装流

流的概念模型

io流涉及40多个类。

三、字节流和字符流

InputStream和Reader

InputStream接口的方法:

  • int read()
  • int read(byte[] b)
  • int read(byte[] b, int off, int len)

Reader接口的方法:

  • int read()
  • int read(char[] cbuf)
  • int read(char[] cbuf, int off, int len)

读取文件:FileInputStream,FileReader

还有一些移动指针的方法:

  • void mark(int readAheadLimit)
  • boolean markSupported()
  • void reset()
  • long skip(long n)

OutputStream和Writer

提供的方法

  • void write(int c)
  • void write(byte[]/char[]/String buf)
  • void write(byte[]/char[]/String buf, int off, int len)

Writer可以使用String代替char[]

四、输入/输出流体系

处理流的用法

使用处理流包装节点流,程序通过处理流执行输入/输出功能,让节点流与底层的IO设备、文件交互。

所有节点流都是以物理io节点作为构造器参数的。其他的流为处理流。

处理流的优点

  1. 操作简单
  2. 执行效率更高

PrintStream的输出功能很强大,System.out的类型就是PrintStream

在使用处理流包装了底层节点流之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可。

输入/输出流体系

输入输出流分类
输入输出流分类

规则:

输入输出是文本,则考虑使用字符流;是二进制,则使用字节流

转换流

InputStreamReader,OutputStreamWriter

将字节流转换为字符流。使处理更方便。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Test{
    public static void main(String[] args) {
        try(
            InputStreamReader reader = new InputStreamReader(System.in);
            BufferedReader br = new BufferedReader(reader);
        ){
            String line = null;
            while((line = br.readLine() != null){
                if (line.equals("exit")){
                    System.exit(1);
                }
                System.out.println(line);
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

BufferedReader有一个readLine()方法,很方便,所以经常把读取文本内容的流包装成BufferedReader

推回输入流

PushbackInputStream和PushbackReader。

提供的方法:

  1. void unread(byte[]/char[] buf)
  2. void unread(byte[]/char[] b, int off, int len)
  3. void unread(int b)

五、重定向标准输入/输出

Java的标准输入/输出分别通过SysteminSystem.out代表。在默认情况下代表键盘和显示器。

System类里包含三个重定向标准输入/输出的方法:

  • static void setErr(PrintStream err)
  • static void setIn(InputStream in)
  • static void setOut(OutputStream out)
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

public class Test{
    public static void main(String[] args) {
        try(
            PrintStream ps = new PrintStream(new FileOutputStream("out.txt"));
        ){
            System.setOut(ps);
            System.out.println();
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;

public class Test{
    public static void main(String[] args) {
        try(
            FileInputStream fis = new FileInputStream("test.txt");
        ){
            System.setIn(fis);
            Scanner s = new Scanner(System.in);
            s.useDelimiter('\n');
            while(s.hasNext()){
                System.out.println(s.next());
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

六、RandomAccessFile

可以读写文件。支持"随机访问"。

只访问文件的部分内容,可以选择使用该流。

追加内容也应该选择。

只能读写文件。

  • long getFilePointer()

返回文件记录指针的当前位置

  • void seek(long pos)

将文件记录指针定位到pos位置

包含了InputStream的read方法和OutputStream的write方法。

创建RandomAccessFile除了需要一个StringFile还需要一个mode参数。

  • "r":只读。
  • "rw":读写
  • "rws":读写,每个更新同步写入到底层存储设备
  • "rwd":读写,同步写入到底层设备。
import java.io.IOException;
import java.io.RandomAccessFile;

public class Test{
    public static void main(String[] args) {
        try(
            RandomAccessFile ra = new RandomAccessFile("Test.java", "r");
        ){
            ra.seek(300);
            byte[] buf = new byte[1024];
            int hasRead = 0;
            while((hasRead = ra.read(buf))>0){
                System.out.println(new String(buf, 0, hasRead));
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

追加内容:

import java.io.IOException;
import java.io.RandomAccessFile;

public class Test{
    public static void main(String[] args) {
        try(
            RandomAccessFile ra = new RandomAccessFile("Test.java", "r");
        ){
            ra.seek(ra.length());
            ra.write("\r\nHello world".getBytes());
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

向指定文件、指定位置插入内容:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Test{
    public static void insert(String filename, long pos, String content) throws IOException{
        File temp = File.createTempFile("temp", null);
        temp.deleteOnExit();
        try(
            RandomAccessFile ra = new RandomAccessFile(filename, "rw");
            FileOutputStream fos = new FileOutputStream(temp);
            FileInputStream fis = new FileInputStream(temp);
        ){
            raf.seek(pos);
            byte[] buf = new byte[64];
            int hasRead = 0;
            while((hasRead=ra.read(buf))>0){
                fos.write(buf, 0, hasRead);
            }
            raf.seek(pos);
            raf.write(content.getBytes());
            while((hasRead=fis.read(buf))>0){
                raf.write(buf, 0, hasRead);
            }
        }
    }
    public static void main(String[] args) {
        try{
            Test.insert("test.java", 45, "Hello World\r\n");
        }
        catch(IOException e){
            e.printStackTrace();
        }
        
    }
}

七、NIO

Java7对原来的io进行了重大改进:

  • 提供了全面的文件IO和文件系统访问支持
  • 基于异步Channel的IO

第一个体现为java.nio.file包及其子包

第二个体现为java.nio.channels包下面增加了多个以Asynchronous开头的Channel接口和类。

Path、Paths和Files核心api

早期只有File访问文件系统,但File类的功能比较有限,不能利用特定文件系统的特性;FIle提供的方法的性能不高,其方法在出错时返回失败,但不提供异常信息。

NIO为了弥补这种不足,引入了一个Path接口,代表一个平台无关的平台路径。FilesPaths两个工具类。

Files包含大量静态工具方法来操作文件,Paths包含两个返回Path的静态工厂方法。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Test{
    public static void main(String[] args) {
        Path path = Paths.get(".");
        System.out.println(path.getNameCount());
        Path abPath = path.toAbsolutePath();
        System.out.println(abPath.getRoot());
        //c:/test
        Path path2 = Paths.get("c", "test");
    }
}
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test{
    public static void main(String[] args) {
        Files.copy(Paths.get("Test.java"), new FileOutputStream("a.txt"));
        System.out.println(Files.isHidden(Pahts.get("Test.java")));
        List<String> lines = Files.readAllLines(Paths.get("Test.java"), Charset.forName("gbk"));
        System.out.println(Files.size(Paths.get("test.java")));
        Files.write(Paths.get("test.txt"), lines, Charset.forName("gbk"));
        Files.list(Paths.get("."));
        Files.lines(Paths.get("Test.java"), Charset.forName("gbk"));
        FileStore cStore = Files.getFileStore(Paths.get("c:"));
        cStore.getTotalSpace();
        cStore.getUsableSpace();
    }
}

使用FileVisitor遍历文件和目录

  • walkFileTree(Path start, FileVisitor<? super Path> visitor)
  • walkFileTree(Path start, Set<FileVisitOpeion> options, int maxDepth, FileVisitor<? super Path> visitor)

FileVisitor定义了如下4个方法:

  • FileVisitResult postVisitDirectory(T dir, IOException exc)
  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
  • FileVisitResult visitFIle(T file, BasicFileAttributes attrs)
  • FileVisitResult visitFileFailed(T file, BasicFileAttributes attrs)

FileVisitResult 是一个枚举类,代表了访问之后的后续行为。

  • CONTINUE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE
  • TERMINATE

实际编程可以通过继承SimpleFileVisitor(FileVisitor的实现类)实现自己的文件访问器可以根据需要选择性重写指定方法。

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Test{
    public static void main(String[] args) throws IOException{
        //c:/test
        Files.walkFileTree(Paths.get("c:", "test"), new SimpleFileVisitor<Path>(){
            //访问文件时触发的方法
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException{
                return FileVisitResult.CONTINUE;
            }
            //开始访问目录时触发该方法
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException{
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

使用WatchService监控文件变化

早期需要后台启用一个线程每隔一段时间去遍历一次指定目录的文件。繁琐,性能不好

Path类提供了如下一个方法监控文件系统的变化:

  • register(WatchService watcher, WatchEvent.Kind<?>... events)

WatchService代表一个文件系统监听服务,它负责监听path代表目录下的文件变化。一旦使用register方法完成注册,接下来可以调用WatchService的如下三个方法获取监听目录的文件变化:

  • WatchKey poll()

获取一个WatchKey,如果没有WatchKey发生,则返回null

  • WatchKey poll(long timeout, TimeUnit unit)

尝试等待timeout时间去获取下一个WatchKey

  • WatchKey take()

获取下一个WatchKey,如果没有则一直等待

如果程序需要一直监控,应该选用take方法,否则可以选用poll方法

import java.nio.file.FileSystems;
import java.nio.file.WatchService;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;

public class Test{
    public static void main(String[] args) {
        WatchService watchService = FileSystems.getDefault().newWatchService();

        Paths.get("c:/").register(watcherService, 
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_DELETE,
            StandardWatchEventKinds.ENTRY_MODIFY);
        while(true){
            WatchKey key = watchService.take();
            for(WatchEvent<?> event:key.pollEvents()){
                System.out.println(event.context()+" 文件发生了 "+event.kind()+" 事件!");
            }
            //重设watchkey
            boolean valid = key.reset();
            if(!valid){
                break;
            }
        }
        
    }
}

访问文件属性

早期的File类可以访问一些简单的文件属性。获取或者修改更多会很难。

Java7下面的java.nio.file.attribute包下面提供了大量的工具类,通过这些工具类,可以简单方便的读取、修改文件属性。这些工具类主要分为一下两类:

  • XxxAttributeView

代表某种文件属性的"视图"

  • XxxAttributes

代表某种文件属性的"集合",程序一般通过XxxAttributeView获取XxxAttributes

FileAttributeView是其他view的父接口。

AclFileAttributeView

可以为特定文件设置ACL(access control list)及文件所有属性。getAcl(),setAcl(List)

BasicFileAttributeView

获取和修改文件的基本属性,包括文件的最后修改时间、最后访问时间、创建时间、大小、是否为目录、是否为符号链接等。readAttributes()放回BasicFileAttributes对象

DosFileAttributeView

主要用于获取或修改文件dos相关属性。是否只读、是否隐藏、是否为系统文件、是否是存档文件等。

FileOwnerAttributeView

获取或修改文件的所有者

PosixFileAttributeView

主要用于修改POSIX(portable operating system interface of unix)属性。

UserDefinedFileAttributeView

让开发者为文件设置一些自定义属性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,571评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Java 语言支持的类型分为两类:基本类型和引用类型。整型(byte 1, short 2, int 4, lon...
    xiaogmail阅读 1,342评论 0 10
  • 一、 1、请用Java写一个冒泡排序方法 【参考答案】 public static void Bubble(int...
    独云阅读 1,346评论 0 6
  • 电影最迷人的地方就是,可以让人沉浸其中,经历一段现实中不可能经历的人生。 看完《爱乐之城》,唏嘘之余,只觉得“爱情...
    烟行阅读 579评论 0 5