一文带你看懂JAVA IO流(一),史上最全面的IO教学啦(附送JAVA IO脑图

一、IO流是什么

惯例引用百科的回答

流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出 [1] .

我对IO流的理解就是"你的程序和系统之间读写文件的操作就是IO操作,和系统之间读写用的东西就是IO流"。

JAVA IO流就是JAVA程序和操作系统之间通信用的方法。

二、JAVA IO系统脑图

给大家看下JAVA IO的脑图

javaIO.png

自己画的JAVA的脑图,如果有需要原文件的去我公众号,发送:JAVA IO,就可以得到脑图的原文件了,带黄色文件夹标注的是我自己写的注释嗷。

什么你还不知道我公众号,微信搜索,千珏(jue),就可以关注到我了。

三、JAVA IO流详解

在这里由于篇幅的原因我只讲解JAVA IO流中的字节流和字符流,别的就等以后再写,比如:NIO AIO BIO这些,以后有时间抽出时间出来写一篇,要是想看的记得点个关注哦。

下面进入正题:

3.1 字节流和字符流的区别

字节流和字符流操作的本质区别只有一个:字节流是原生的操作,字符流是经过处理后的操作。

画个图,字节流在操作时不会用到缓冲区,也就是不会用到内存,文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,看下图:

zifuAndzijie.png

为什么要有字符流而不直接用字节流呢?

我相信有些读者心里肯定要问这个问题,我刚开始学习的时候也想过这个问题,为什么不直接用字节流解决呢,还非要搞个字符流出来呢。

我的理解就是字节流处理多个字节表示的东西的时候有可能会出现乱码的问题,比如汉字,用字节流读取的时候有可能因为一位字节没有读到就变成了乱码,字符流呢就完美解决了这个问题,字符流你们可以这样理解,字节流和编码表的组合就是字符流。因为有了编码表所以可以确定这个汉字有多少个字节,这样字节流就可以根据位数准确的读写汉字了。

以上纯为个人理解,如有不对的地方请在评论区给我留言哦。

3.2 字节流

字节流顾名思义就是通过字节直接操作字符,更底层一些。

字节流最基础的两个类就是 InputStreamOutputStream ,根据这两个派生而来类都含有 read()write() 的基本方法,用于读写单个字节或者字节数组。

3.2.1 InputStream 和 OutputStream类

InputStream类是一个抽象类 ,是所有字节输入流类的父类。

OutputStream类是一个抽象类,是所有字节输出流的父类

InputStream的常见子类有:

  • FileInputStream:看这个名字就知道用于从文件中读取信息。
  • ByteArrayInputStream: 字节数组输入流,
  • ObjectInputStream:序列化时使用 一般和ObjectOutputStream一起使用
  • FilterInputStream: 过滤输入流,为基础的输入流提供一些额外的操作。

OutputStream的常见子类有:

  • FileOutPutStream: 文件输出流对文件进行操作
  • ByteArrayOutputStream: 字节数组输出流
  • ObjectOutputStream: 序列化时使用 一般和OjbectInputStream一起使用
  • FilterOutputStream:过滤输出流,为基础的输出流提供一些额外的操作。

我们一个一个过要不然怎么能叫一文带你看懂JAVA IO流了呢,那样不就是标题党了吗[滑稽]。

3.2.1.1 FileInputStream 和 FileOutPutStream类
1) FileInputStream 和 FileOutPutStream概念

FileInputStream是文件字节输入流,就是对文件数据以字节的方式来处理,如音乐、视频、图片等。

FileOutPutStream是文件字节输出流,

2)FileInputStream里面的方法
//通过文件的名字来创建一个对象
public FileInputStream(String name) throws FileNotFoundException{}
//通过File对象来创建一个对象
public FileInputStream(File file) throws FileNotFoundException{}
 /**
   * 通过FileDescriptor来创建一个对象
   * FileDescriptor是一个文件描述符号
   * 有in,out,err三种类型
   * in:标准输入描述符,out:标准输出的描述符,err:标准错误输出的描述号
   */
public FileInputStream(FileDescriptor fdObj){}
//打开指定的文件进行读取 ,是java和c之间进行操作的api 我们并不会用到
private native void open0(String name){}
//打开指定的文件进行读取,我们并不会用到 因为在构造方法里面帮我们打开了这个文件
private void open(String name){}
//从输入流中读取一个字节的数据,如果到达文件的末尾则返回-1
public int read() throws IOException{}
//读取一个字节数组
private native int readBytes(byte b[], int off, int len) throws IOException;
private native int read0() throws IOException;
//从输入流中读取b.length的数据到b中
public int read(byte b[]) throws IOException{}
//从输入流中读取off到len之间的数据到b中
public int read(byte b[], int off, int len) throws IOException{}
//跳过并丢弃输入流中的n个数据
public long skip(long n) throws IOException{}
private native long skip0(long n) throws IOException;
//可以从此输入流中读取的剩余字节数
public int available() throws IOException {}
private native int available0() throws IOException;
//关闭此文件输入流并释放与该流关联的所有系统资源
public void close() throws IOException {}
//返回FileDescriptor对象 
public final FileDescriptor getFD() throws IOException{}
//该方法返回与此文件输入流关联的通道 NIO中会用到 本文不会提及
public FileChannel getChannel(){}
private static native void initIDs();
private native void close0() throws IOException;
//没有更多引用时,调用此方法来关闭输入流 一般不使用
protected void finalize() throws IOException {}

由于篇幅起见FileOutputStream代码里面的方法我就不仔细的带你们看了(我不会说我是因为懒才不带你们看的,溜。

一般常用的方法就几个,举个例子,往D盘下面hello.txt里面输入“hello world”

public class Test {
    public static void main(String []args) throws IOException {
        //根据文件夹的名字来创建对象
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\hello.txt");
        //往文件里面一个字节一个字节的写入数据
        fileOutputStream.write((int)'h');
        fileOutputStream.write((int)'e');
        fileOutputStream.write((int)'l');
        fileOutputStream.write((int)'l');
        fileOutputStream.write((int)'o');
        String s = " world";
        //入文件里面一个字节数组的写入文件
        fileOutputStream.write(s.getBytes());
        fileOutputStream.close();
        //传文件夹的名字来创建对象
        FileInputStream fileInputStream = new FileInputStream("D:\\hello.txt");
        int by = 0;
        //一个字节一个字节的读出数据
        while((by = fileInputStream.read()) != -1){
            System.out.println((char)by);
        }
        //关闭流
        fileInputStream.close();
        //通过File对象来创建对象
        fileInputStream = new FileInputStream("new File("D:\\hello.txt")");
        byte []bytes = new byte[10];
        //一个字节数组的读出数据
        while ((by = fileInputStream.read(bytes)) != -1){
            for(int i = 0; i< by ; i++){
                System.out.print((char) bytes[i]);
            }
        }
        //关闭流
        fileInputStream.close();
    }
}

常用的就上述代码里面的三种方法。

3.2.1.2 ByteArrayInputStream和ByteArrayOutputStream
1)ByteArrayInputStream和ByteArrayOutputStream概念

ByteArrayInputStream是字节数组输入流,它里面包含一个内部的缓冲区(就是一个字节数组 ),该缓冲区含有从流中读取的字节。

ByteArrayOutputStream是字节数组输出流

2)ByteArrayInputStream里面的方法
//通过byte数组来创建对象
public ByteArrayInputStream(byte buf[]) {}
//通过byte数组,并给定开始下标和结束下标来创建对象
public ByteArrayInputStream(byte buf[], int offset, int length){}
//从这个输入流读取下一个字节 末尾会返回
public synchronized int read(){}
//从输入流中读取off到len之间的数据到b中
public synchronized int read(byte b[], int off, int len){}
//跳过并丢弃输入流中的n个数据
public synchronized long skip(long n){}
//可以从此输入流中读取的剩余字节数
public synchronized int available(){}
//判断这个输入流是否支持标记,他一直返回true
public boolean markSupported(){}
//将mark的值设置为当前读取的下标,readAheadLimit这个参数没有意义,因为没用到
public void mark(int readAheadLimit){}
//将当前的下标设置为mark一般和mark()方法一起使用
public synchronized void reset(){}
//关闭这个输入流,因为ByteArrayInputStream操作的是数组所以没有必要关闭流
public void close() throws IOException{}

由于篇幅起见ByteArrayOutputStream代码里面的方法我就不仔细的带你们看了(我不会说我是因为懒才不带你们看的,溜

举个例子,从一个字符串读取数组

public class Test {
    public static void main(String[] args) throws IOException {
        //创建一个字节输出流对象
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        //一个字节一个字节的写入数据
        byteArrayOutputStream.write('h');
        byteArrayOutputStream.write('e');
        byteArrayOutputStream.write('l');
        byteArrayOutputStream.write('l');
        byteArrayOutputStream.write('o');
        //一个字节数组的写入数据
        byteArrayOutputStream.write(" world".getBytes());
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray();
        //从这个流中读取数据
        int b = 0;
        //从这个流中一个字节一个字节的读数据
        while ((b = byteArrayInputStream.read()) != -1) {
            System.out.println((char) b);
        }
        byteArrayInputStream = new ByteArrayInputStream(bytes);
        byte[] bs = new byte[10];
        //从这个流中一次性读取bs.length的数据
        while ((b = byteArrayInputStream.read(bs)) != -1) {
            for (int i = 0; i < b; i++) {
                System.out.print((char) bs[i]);
            }
            System.out.println();
        }
    }
}

如上代码所示,我平时常用的也就这几个方法。

3.2.1.3 ObjectInputStream 和ObjectOutpuStream
1)概念

ObjectInputStream是反序列化流,一般和ObjectOutputStream配合使用。

用ObjectOutputStream将java对象序列化然后存入文件中,然后用ObjectInputStream读取出来

这个类的作用,我的理解是有些类在这个程序生命周期结束后,还会被用到所以要序列化保存起来

2)ObjectInputStream 和 ObjectOutpuStream 基本方法

常用的其实就两个方法

public final Object readObject(){}
public final void writeObject(Object obj) throws IOException{}
 class Data implements Serializable {
    private int n;
    public Data(int n){
        this.n=n;
    }
    @Override
    public String toString(){
        return Integer.toString(n);
    }
}
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Data w=new Data(2);
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("worm.out"));
        //序列化对象,把对象写到worm.out里面
        out.writeObject("Worm storage\n");
         //序列化对象,把对象写到worm.out里面
        out.writeObject(w);
        out.close();
        //从worm.out里面读取对象 
        ObjectInputStream in=new ObjectInputStream(new FileInputStream("worm.out"));
        //读取String对象
        String s=(String)in.readObject();
        //读取Data对象
        Data d=(Data)in.readObject();
        System.out.println(s+"Data = "+d);
    }
}
3.2.1.4 FilterInputStream 和 FilterOutputStream
1) 概念

FilterInputStream和FilteOutputStream分别是过滤输入流和过滤输出流,他们的作用是为基础流提供一些额外的功能

2)FilterInputStream 和 FilterOutputStream的常用子类

FilterInputStream常用子类

  • DataInputStream:可以从流中读取基本数据类型,与DataOutpuStream配合一起使用
  • BufferedInputStream:可以从缓冲区中读取数据,不用每次和文件的操作都进行实际操作了。

FilterOutputStream常用子类

  • DataOutputStream:可以向文件中写入基本类型的数据
  • PrintStream:用于产生格式化的输出
  • BufferedOutputStream:通过缓冲区像文件中写入数据。

DataInputStream基本类型写入方法。

// 将一个 byte 值写入流中。
void writeByte(int v) 
// 将一个 short 值写入流中 
void writeShort(int v) 
//将一个 int 值写入流中
void writeInt(int v) 
// 将一个 long 值写入流中 
void writeLong(long v) 
//使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后该int值写入流中  
void writeFloat(float v) 
//使用Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然后将该long写入到流中。
void writeDouble(double v) 
//写入一个char 
void writeChar(int v) 
//将一个 boolean 值写入流。   
void writeBoolean(boolean v)
//将字节数组写入流中
void write(byte[] b, int off, int len) 
 //将字符串写出到基础输出流中。
oid writeBytes(String s) 
//采用UTF-16be方式写入,也就是java字符串的编码
void writeChars(String s) 
// 以utf-8形式写入一个字符串    
void writeUTF(String str)
//清空此数据输出流,写入文件
void flush()

测试一下方法试试

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("D:\\hello.txt"));
        // 写入byte类型数据
        dataOutputStream.writeByte(20);
        // 写入short类型数据
        dataOutputStream.writeShort(30);
        // 写入int类型
        dataOutputStream.writeInt(900);
        // 写入float类型
        dataOutputStream.writeFloat(12.3f);
        // 写入long类型
        dataOutputStream.writeLong(800L);
        // 写入double类型
        dataOutputStream.writeDouble(14.23);
        //写入boolean类型
        dataOutputStream.writeBoolean(true);
        // 写入char类型
        dataOutputStream.writeChar('中');
        dataOutputStream.close();
        DataInputStream dataInputStream = new DataInputStream(new FileInputStream("D:\\hello.txt"));
        System.out.println(dataInputStream.readByte());
        System.out.println(dataInputStream.readShort());
        System.out.println(dataInputStream.readInt());
        System.out.println(dataInputStream.readFloat());
        System.out.println(dataInputStream.readLong());
        System.out.println(dataInputStream.readDouble());
        System.out.println(dataInputStream.readBoolean());
        System.out.println(dataInputStream.readChar());

        dataInputStream.close();
        //创建一个对象
        PrintStream printStream = new PrintStream("D:\\hello.txt");
        //写入一个字节数组
        printStream.write("helloworld".getBytes());
        //写入一个换行符号
        printStream.println();
        //格式化写入数据
        printStream.format("文件名称:%s","hello.txt");
        printStream.println();
        printStream.append("abcde" );
        printStream.close();
    }
}

BufferedInputStream和BufferedOutputStream我另开一篇文章写,里面要介绍的东西很多,一篇文章介绍不完。

emmm,还有字符流下篇文章写,今天是完不成了,觉得这篇文章还可以想看下一篇文章的关注我呀,或者可以关注我公众号:千珏呀,后台留言给我呀,是千珏(jue),不是千钰。

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

推荐阅读更多精彩内容