Java基础之IO流

I/O流

  • 字符流: Reader、Writer(文字、字符串等)
  • 字节流:InputStream、OutputStream(歌曲、图片等)

file.write("xxx")只是将内容存在了缓冲区,flush()后可立即写入。close()关闭文件,隐式执行了flush。执行了close()后不可再write和flush。

FileWriter fw = new FileWriter("abc.txt",true);后面的参数为true表示可以再在原文件的基础上续写而不覆盖原文件。

打开/新建文件时必须处理IOException,可以在方法头上家throws IOException,也可以使用try catch处理,就是比较麻烦。

FileWriter

public static void main(String[] args) {
    FileWriter fw = null; // 先初始化为空,不然会提醒没有处理异常
    try {
        fw = new FileWriter("abc.txt");
        fw.write("123");
        fw.write("456");
    } catch (IOException e) {
        e.printStackTrace();
      // finally文件关闭里面也要try、catch
      // 最好进行一次文件是否为空的判断
    } finally {
        try {
            if (fw != null)
                fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileReader

  • read()每次读一个字符,若到结尾返回-1。
int ch;
    while ((ch=fr.read()) != -1) {
        System.out.print((char)ch); // ASCII码到字符的强转
    }
  • 一次读一个数组,顺便演示了续写,和一次写入一个数组
public static void main(String[] args) throws IOException {
    char[] buff = new char[1024];
    int len;
    FileReader fr = new FileReader("abc.txt");
    FileWriter fw = new FileWriter("abc.txt", true); // 参数true,可以续写而不覆盖
    while ((len=fr.read(buff)) != -1) {
        System.out.println(new String(buff, 0 ,len));
        fw.write(buff, 0 ,len);
    }
    fw.close(); // 关流顺序:先开的后关,后开的先关,可以只关处理流(如BufferedReader、BufferedWriter)就不用关节点流了(如FileReader、FileWriter)
    fr.close();
}

BufferedReader&BufferedWriter

像FileWriter和FileReader这类被称为节点流。而BufferedWriter和BufferedReader被称为处理流。它们对节点流进行了装饰,使得其功能更加灵活。

缓冲读写,提高效率。Filewriter每次write都是直接操作硬盘,FileReader同理。而BufferedWriter的write是先写入自己的缓冲区,待缓冲区满了之后再刷新到硬盘。

BufferedReader有一个readLine(),BufferedWriter有一个newLine根据系统自动选择换行,用法如下。

public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader("abc.txt"));
    BufferedWriter bw = new BufferedWriter(new FileWriter("def.txt"));
    String line; // readLine返回的是String
    while ((line=br.readLine()) !=null) {
        System.out.println(line);
        bw.write(line);
        bw.newLine();
    }
    bw.close();
    br.close();
}

以上体现了装饰模式,与继承相比更加灵活,装饰者与被装饰者的关系不是父子关系,而更像是同事,或者继承自同一个父类。

下面是一个装饰的例子。

public class NewPerson {
    private Person p;
  // 传入的是Person
    public NewPerson(Person p) {
        this.p = p;
    }
    public void eat() {
        System.out.println("I am New Person");
        p.eat(); // 注意继承的话这里是super.eat()
    }
    public static void main(String[] args) {
        NewPerson b = new NewPerson(new Person("Bob")); // 装饰
        b.eat();
    }
}

class Person {
    private String name;

    Person(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println("I am eating...");
    }
  
    public String getName() {
        return name;
    }

}

下面再用继承的思想,和上面比较就能看出不同。

public class NewPerson extends Person {
    private int age;
  // 这里传入的参数和Person类似,而不是直接传Person
    public NewPerson(String name, int age) {
        super(name); // 在构造函数中super必须放在第一行
        this.age = age;
    }
// 继承过来的方法需要覆盖,而装饰不存在继承关系
    @Override
    public void eat() {
        System.out.println("I am NewPerson");
        super.eat();
        System.out.println("I am "+age+"years old.");
    }

    @Override
    public String getName() {
        return super.getName();
    }
    public int getAge() {return age;}

    public static void main(String[] args) {
        NewPerson a = new NewPerson("Bob", 34);
        a.eat();
    }
}

class Person {
    private String name;

    Person(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println("I am eating...");
    }

    public String getName() {
        return name;
    }

}

以上方法针对的是字符输入输出,若要针对字节则用FileOutpurStream(写)、FileInputStream(读)、BufferedInputStream(带缓冲的读)、BufferedOutputStream(带缓冲的写)。使用方法大同小异。

InputStream

InputStream input = System.in从键盘读取,System.in只有一个,不用关闭,一旦关闭不可再打开。

char和int之间可以直接比较,自动提升类型。

public static void main(String[] args) throws IOException {
   int ch = 'a';
   char abc = 97;
   System.out.println(ch); // 打印97
   System.out.println(abc); // 打印a
}

InputStreamReader&OutputStreamWriter

以上两个是字节 <--> 字符的桥梁,举个例子。

InputStreamReader isr = new InputStreamReader(System.in);将传入的System.in进行修饰,本来从硬盘读取到的是字节,现在装饰后变成字符,而且再经BufferedReader装饰后,可以读取一整行。如下可实现一次从键盘输入中读取一行。

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

同理OutputStreamWriter是将字符转为字节,方便打印到控制台(控制台接收的是字节)。

// 从键盘读并打印到控制台
public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    String line = br.readLine();
    bw.write(line);
    bw.flush(); // 因为只是写入了缓冲区,必须刷新才能在控制台看到,系统的标准输出,不用关闭
}

FileReader的父类就是InputStreamReader,所以从硬盘读到的字节已经被转为字符。FileWriter的父类就是OutputStreamWriter,所以写入的是字符,被转成字节存到硬盘。

OutputStreamWriter osw = new OutputStream(new FileOutputStream("aa.txt"));
FileWriter fw = new FileWriter("aa.txt");
// 以上两句等价的

什么时候用OutputStreamWriter

以指定的码表读写,不能用FileReader和FileWriter,这两个默认用的UTF-8。要是指定了其他码表,就要用OutPutStreamWriter和InputStreamReader了。什么时候用?

  • 需要从字符转为字节(或反之)时
  • 需要指定码表时

小结

流那么多,该怎么快速选择我们需要用到的,有几个方法可以参考。

  1. 明确源和目的
    • 源:InputStream和Reader
    • 目的:OutputStream和Writer
  2. 是否是纯文本
    • 源(是):Reader (否):InputStream
    • 目的(是):Writer (否):OutputStream
  3. 明确设备
    • (源)硬盘 --> File
    • (源)控制台 --> System.in
    • (源)内存 --> 数组
    • (源)网络 --> Socket流
    • (目的)硬盘 --> File
    • (目的)控制台 --> System.out
    • (目的)内存 --> 数组
    • (目的)网络 --> Socket流
  4. 是否需要高效
    • 是:加上装饰,Buffered缓冲

File

该类时文件和目录的抽象表示。

其构造函数可以传两个字符串,一个是父路径,一个是子路径。也可第一个传File,第二个传字符串。

File f = new File("C:\\");
File file1 = new File("c:\\", "Windows"); // 该对象是个目录
File file2 = new File(f, "aa.txt"); // 该对象是个文件

一些常用的方法:

  • delete() 删除该File
  • exists()该File是否已经存在
  • createNewFile()该File不存在时,创建之
  • getAbsoluteFile()返回File,绝对路径
  • getAbsolutePath()返回String,绝对路径
  • getName()返回 路径末尾的目录或者文件
  • mkdir()只能创建单一的目录如F:\\ABC
  • mkdirs()可创建多层目录如F:\\ABC\\DEF
  • list()返回String[],该目录下所有子目录和文件的列表
  • listFiles()返回File[],该目录下所有子目录和文件的列表
  • isFile()、isDir()、isHidden()是否是文件、目录、是否被隐藏

FilenameFilter文件过滤,测试指定文件是否应该包含在某一文件列表中。需要覆盖accept方法。

通常写法如下,该例子是测试文件是否以某个特定的关键词结尾。

public class FilterByKey implements FilenameFilter {
    private String key;

    FilterByKey(String key) {
        this.key = key;
    }

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(key);
    }
}

Properties和I/O相关联,不支持泛型,通常存储以key-value对应的配置文件。

  • getProperty(String key) 根据键获取值。
  • setProperty**(String key, String value) 相当于put(key, value)
  • list(System.out)可以将属性列表输出。
  • store()可以存储到文件

PrintWriter&PrintStream

字符/字节打印流,print()方法和write()方法有区别,就是print方法可以输出原始数据。

PrintWriter pw = new PrintWriter(System.out);
pw.println(98); // 打印98,保持了原始数据
pw.write(98);  // 打印b,ASCII码表
pw.flush();

操作对象流ObjectOutputStream和ObjectInputStream可以将对象存入硬盘,或者从硬盘读取对象,可以时间对象的永久化。

new ObjecOutputStream(new FileOutputStream),所写入的类必须实现Serialiable接口。


by @sunhiayu

2017.1.10

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

推荐阅读更多精彩内容

  • 一、IO流概述 概述: IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间的数据传输,Ja...
    侯蛋蛋_阅读 418评论 0 1
  • 概述: 1、IO流:即Input Output的缩写。 2、特点:1)IO流用来处理设备间的数据传输。2)Java...
    玉圣阅读 1,239评论 0 3
  • 一、IO流整体结构图 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称...
    慕凌峰阅读 1,151评论 0 12
  • 一、流的概念和作用。 流是一种有顺序的,有起点和终点的字节集合,是对数据传输的总成或抽象。即数据在两设备之间的传输...
    布鲁斯不吐丝阅读 10,018评论 2 95
  • 闲读《百年百篇经典短篇小说》(上)——这是雷达先生在2003年“非典”蔓延的日子主编的一本自认很认真、很费力...
    书叉脸困觉猪2阅读 973评论 0 7