关于java的IO流详细介绍

从今以后,别再过你应该过的人生,去过你想过的人生吧!

                                                       ——梭罗

1.什么是IO流

现实中我们要对电脑上的很多文件进行读写等操作(比如查看复制图片),其底层就是要用到IO,它是(input 和output)缩写,用于实现对数据的输入与输出。Java IO是对数据输入输出进行操作的一些接口和类。

Java IO 的基本操作都是围绕着流(Stream)对象展开的,流是一个抽象的概念(想象成水流),它是一系列连续的字节或字符,数据从一个端点流入,从另一个端点流出。Java IO 从两个维度来区分流,即字节流和字符流。

流的分类:

方向上: 输入流(只可以读数据)和输出流(只可以写数据)

数据类型上:字节流(以字节来传输数据)和字符流(以字符来传输数据)

功能上:节点流(可以直接向io设备读取数据)和处理流(节点流的加强版)


2.Java IO的分类

 

总共可以分为四大类:​字节输入流,字节输出流,字符输入流,字符输出流。​​​​​​

cdce8bbac04b40408f7d5eb8eaaa85fe.jpg

 输入是读数据的,输出是写数据的。

咱们一起来看看具体的吧!

97dfa1443b8841f6baa6b49755455f9f.png

 

1.抽象基流(节点流,毫无疑问都是抽象类,开发中都是用它们的子类的)


  
  输入流
  InputStream
                 int  read()         读取下一个字节
                 int  read(byte [] b)           读取下一批字节,将其存入数组,返回读取的字节数
                 int  read(byte [] b, int off, int len)    从off开始最多读取len个字节,将其存入数组,返回读取的字节数
  
  Reader 
                 int read()            读取下一个字符 
                 int read(char [] c)   读取下一批字符,将其存入数组,返回读取的字符串数
                 int read(char [] c, int off, int len )         从off开始最多读取len个字符,将其存入数组,返回读取的字符数
  
  
  


  输出流
  OutputStream
                void write(int b)  输出指定的字节b
                void write(byte [] b)      输出指定的字节数组b
                 void write( byte [] b, int off, int len )      输出指定的字节数组,从off开始最多输出len个字节
  


  Writer 
                 void write(int c)   输出指定的字符
                 void write(char [] c)     输出指定的字符数组
                 void write(char [] c, int off, int len ) 输出指定的字符数组, 从off开始最多输出len个字符
                 void write (String str)              输出指定的字符串
                 void write(String str, int off, int len)     输出指定的字符串,从off开始最多输出len个字符串
  

 

 

 


  注意:
    1.上面的四个类都是抽象类,是常见io流的的父类
    2.上面的四个类都定义了close()方法,你需要在使用完后调用此方法
    3.无论是否发生异常,都要关闭它,通常在finally中关闭
    4.上面的四个类都实现了Closeable接口,因此可以在使用try中创建流,方便自动关闭

 

 

2.文件流(节点流,即用来直接访问文件的类)

FileInputStream,   FileoutputStream

FileReader,     FileWriter

用文件流来读写文件

//定义一个方法,用字节流来拷贝文件,需要老的文件和新的文件

 

public static void copyFile(String newsrc, String oldsrc){

    try (

//就是在try执行完毕后它会自动关闭,但是前提它必须实现AutoCloseable接口,自动回收资源

        FileInputStream f = new FileInputStream(oldsrc); //输入流,读数据的

 

        FileOutputStream f1 = new FileOutputStream(newsrc); //输出流,写数据,写入新的文件里面

    )

    {

 

        byte[] bytes = new byte[128];

 //创建一个能存128位字节数组

        int len = 0;      //数据读到的字节数

 

        // 一个公式,读取文件中的数据,如果文件长度非常大要反复读,当里面已经没有数据了就读到0个字节,假,循环结束

        while ((len = f.read(bytes, 0, 128)) >0) {

 

//从第0开始,最多读128个字节存入bytes数组,这里的len是实际读到的实际字节长度

            f1.write(bytes, 0, len);

//写入指定的字节,从数组0开始,最多写len

            

        }

        System.out.println(Arrays.toString(bytes));

        System.out.println("复制成功");

 

    } catch (Exception e) {

        new Exception("文件复制失败");

    }

}

 

//定义一个方法,用字符流来拷贝文件,需要老的文件和新的文件

public static void copyFile1(String oldsrc, String newsrc){

    try (

        FileReader f = new FileReader(oldsrc); //字符输入流。读数据的

        FileWriter f1 = new FileWriter(newsrc); //字符输出流,写入数据的

    )

    {

        char[] c = new char[128];

        int len = 0;

        while ((len = f.read(c, 0, 128))>0) {

            f1.write(c, 0, len);

        }

        System.out.println(Arrays.toString(c));

        System.out.println("复制成功");

 

 

    } catch (Exception e) {

        new Exception("字符流复制失败");

    }

 

    

}

结论:

读文件要输入流,即 输入流  f =new 输入流(“文件绝对地址”),然后再调用read方法读数据

写文件要用输出流,即输出流  f =new 输出流(“文件绝对地址”),然后再调用writer方法写数据

 

访问数组和访问管道和访问字符串的差不多,就不一一介绍啦!

 

3.缓冲流(处理流,实例化要传入节点流的实例,不是文件地址啦)

BufferedInputStream, BufferedOutputStream

BufferedReader,     BufferedWriter

可能有人会问为什么用这个,解释一下,当我们使用字节流的时候每读一个字节就需要与磁盘交互一次,效率太低啦。缓冲流就是在内存中设置一个缓冲区用来存足够多的数据,再拿就在内存中拿啦,不用再跑磁盘里面去啦。(就相当于搬砖,有个小车)

// 创建复制方法,使用缓冲流
public static void copyFile(String oldsrc, String newsrc) {


    try (   //自动关闭的
            BufferedInputStream b = new BufferedInputStream(new FileInputStream(oldsrc));   //字节输入缓冲流


            BufferedOutputStream b1 = new BufferedOutputStream(new FileOutputStream(newsrc));   //字节输出输出流,实例时用对象啦,不是地址
          


        ) {

            byte[] bytes = new byte[128];  

//创建一个128位的存储数组
            int len = 0;
            while ((len = b.read(bytes, 0, 128))>0) {
                b1.write(bytes, 0, len);
            }


    } catch (Exception e) {
        new Exception("复制失败");
    }
}

注意:

使用缓冲输入流时,它会把缓冲区填满,每一次掉用方法时从缓冲区读数据;

使用缓冲输出流时,每一次写入在缓冲区里,满了自动写到设备中,当然也可以调用flush()方法手动刷入(关闭流时会自动调)。

 

4.转换流(处理流,实例化时要有相应的实例对象)

InputStreamReader 是字节流转换成字符流OutPutStreamWriter是字符流转为字节流

使用

//从键盘录入打印到控制台
try (


    //把键盘上打的文字的字节流--变成字符流,读
    //InputStreamReader实例的时候需要传入一个低级流,而System.in是InputStream类型
    BufferedReader d = new BufferedReader(new InputStreamReader(System.in));

 

  //然后把他们放到缓冲区里面,用字符输入缓冲流

    //把字节---字符输出,System.out是OutputStream类型的,写
    BufferedWriter bufferedWriter = new BufferedWriter(new  OutputStreamWriter(System.out));


){
    String line = null;
    while ((line = d.readLine()) != null) {    

 

       //readLine读取一行字符

       if(line.equalsIgnoreCase("out")) {break;}               //如果匹配到out,就跳出


       //否则
        bufferedWriter.write(line);    //把这个字节变成字符输出到终端
       bufferedWriter.newLine();      //换行
       bufferedWriter.flush();       //输入到硬盘

    }

 

} catch (Exception e) {
    // TODO: handle exception

结论:

InputStreamReader类包含了一个底层输入流,可以从中读取原始字节。它根据指定的编码方式,将这些字节转换为Unicode字符。

OutputStreamWriter从运行的程序中接收Unicode字符,然后使用指定的编码方式将这些字符转换为字节,再将这些字节写入底层输出流中。

1. 其是字符流和字节流之间的桥梁

2. 可对读取到的字节数据经过指定编码转换成字符

3. 可对读取到的字符数据经过指定编码转换成字节

转换流的主要作用的是方便使用人员操作,便捷使用,方便用户在字节和字符之间进行操作。

 

 

5. 打印流(处理流,实例化时需要节点流才可以实例化)

PrintStream,   PrintWriter  两个只负责输出(写)。

创建一个方法,使用打印流

 

public static void testprint(){

  try(

    FileOutputStream f = new FileOutputStream("D:\\JAVA文件\\API文件\\1.txt");

            //创建一个字节输出流,写入数据

    PrintStream pr = new PrintStream(f);                 //创建打印流,处理流需要节点流实例

  ) {

    pr.println("晓看天色暮看云,行也思君,坐也思君"); //会打印到指定的地方

    pr.println("真棒");

 

  } catch (Exception e) {

    e.printStackTrace();

  }

}

//它不会打印在控制台上面了,直接到文件里面去了

8034b0aa67e54be681a48b675913393e.png

 结论:

1.都是处理流,实例化时需要对应的节点流,两个类用法差不多

2.System.out就是PrintStream类型的

都可以高效的把数据打印到文件中

 

6.RandomAccessFile(一个支持随机访问的类,只能访问文件)

之前我们那些io流读都是从文件开始到结束,并且要么只读只写,这个类就不一样了,既可以读也可以写,当我们新建了一个RandomAccessFile对象,这个指针指向文件的开始处,即0字节的位置,当读/写了n个字节,这个指针后移到n,除此之外,指针可以根据需要自由移动到指定位置。

两个构造方法:

1、RandomAccessFile(File file, String mode)

2、RandomAccessFile(String name, String mode)

mode为下面的一种:

b4d994a6b89b4751958f3aab1bf42832.jpg

 

使用

// 如果我现在想在12.txt文件再追假加几句话

        try (

            RandomAccessFile r = new RandomAccessFile("D:\\JAVA文件\\API文件\\IO流\\12.txt","rw");

             //rw表示可以读,也可以写

        ){

            //把指针定位到末尾,加数据,seek方法调整指针的位置

            r.seek(r.length());     

            r.write("我以为忘了想念\n".getBytes());

//写数据,以指定字符集utf-8把字符串编码成字节

            r.write("i like you ".getBytes());

 

            //再把指针点位到开头

            r.seek(0);

            String line = null;

            while ((line = r.readLine()) != null) { //解码,读一行,如果那一行为空则结束循环

             

               String l = new String(line.getBytes("ISO8859-1"),"utf-8"); //用ISO8859-1来编码,再用utf-8解码,得到字符串

                System.out.println(l);

            }

 

        } catch (Exception e) {

            // TODO: handle exception

        }

结论:

它的用法跟io流差不多

1. long getFilePointer() 返回文件指针当前所指向的位置

*2. void seek(long pos) 将文件指针定位到指定位置(pos)

 

 

7.NEW IO

它是什么?它是jdk1.4后推出的更高效处理文件读写的API,它是基于通道Channel和缓存Buffer来实现滴(说好听一点就是通道负责传输,缓存负责储存)!两个要一起用。

 

Buffer接口介绍

1.它下面有很多子类,最常用的是ByteBuffer,CharBuffer。同时它只能只能通过静态方法来实例化对象,public static CharBuffer allocate(int capacity)

2.它的四个核心成员变量:

*1.容量(capacity): Buffer可以存储的最大数据量,该值设置后不可以改变   

 * 2.界限(limit):Buffer中可以读/写数据的边界,limit之后的数据不能访问 (如果1000个存储量,存了600,那么limit就指向600,表示最多读到这)

 * 3.位置(position):下一个可以被读写的数据的位置(就是当前数据的索引)

 * 4.标记(mark):Buffer允许将位置直接定位到该标记处,这是一个可选的属性

* 上述变量满足如下的关系:0 <= mark <= position <= limit <= capacity

3.它的一些重要方法:

allocate():创建一个Buffer类的实例对象。

put():将数据写入缓冲区。

flip():将缓冲区切换成读模式。

 get():从缓冲区读取数据。

clear():清空缓冲区,将缓冲区从读模式切换成写模式。

compact():将缓冲区从读模式切换成写模式。

 

4.用法

public static void main(String[] args) {

        CharBuffer buffer = CharBuffer.allocate(8); //创建一个容量位8字节的缓存

        printBuffer(buffer, "创建Buffer对象"); //printBuffer方法为自定义打印方法

 

        // 存放数据,put用于存放数据

        buffer.put("A").put("B").put("C");

        printBuffer(buffer, "数据存放");

 

        //准备取出,切换为读模式 ,flip方法

        buffer.flip();                

        printBuffer(buffer, "准备取出");

 

        //取出数据 ,get方法用于读取一个byte

        System.out.println("###:\t"+buffer.get()+", "+buffer.get()); 

        printBuffer(buffer, "取出");

 

        //重置指针,并没有清空数据

        buffer.clear();

        printBuffer(buffer, "重置指针");

 

        //绝对位置(不影响指针,相当于从数

组里面取值)

        System.out.println("###: \n"+buffer.get(0)+","+buffer.get(1));

        printBuffer(buffer, "绝对取值");

    }

 

Channel接口介绍

1. 它的子类也有很多,常用的为FileChannel(文件通道,读写)。它实例化有两种方法,一是各个Channel提供的open()方法, 二是FileInputStream,FileOutputsream,RandomAccessFile类提供了getChannel()方法,可以直接返回FileChannel。

2.它的一些常用方法

*map()方法用于将Channel对应的数据映射成ByteBuffer

* read(buffer)方法有一系列重载的形式,用于在此通道里面读字节序列到缓冲里面

*write(buffer)方法有一系列重载的形式,用于从缓冲里面写字节序列到通道中

3.它的使用

//追加内容到文件的方法

public static void appendF (String filesrc,String content ){

    try (

        FileChannel channel = new RandomAccessFile(filesrc, "rw").getChannel();

//使用RandomAccessFile来创建FileChannel对象

    ){

        //追加内容首先把指针定位到文件末尾

        channel.position(channel.size()); //得到文件的大小

 

        //创建Buffer,把buffer加入到channel ,容量位1024字节

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        

        buffer.put(content.getBytes()); //把字符串内容转换位字节,然后存入buffer

        

        buffer.flip(); //准备取值,要从缓冲里面拿数据

        channel.write(buffer); //从缓冲区写数据到通道

 

        System.out.println("添加成功");

 

    } catch (Exception e) {

        // TODO: handle exception

    }

}

结论:

nio要基于Channel和Buffer一起使用才可以

 

8. 序列化机制

它是什么?简单的说就是可以把对象变成二进制字节序列,这些序列保存在磁盘上或网络中传输,并且允许它们恢复成之前的对象(对象<——>字节序列)

1.对象序列化,对象→字节序列

2.反序列化,字节序列→对象

3.要实现Seriazable接口才支持序列化,它只是一个标识里面没有任何方法,同时现在很多类都已经实现了这个类了,包装类,String,Date等

4.怎么用,当然啦,要使用对象流ObjectInputStream和ObjectOutputStream两个处理流

//自己定义一个类,可以被序列化这个类

class Car implements Serializable{

    private String brand;

    private String color;

   // private int age;

 

 

    public Car(String b ,String color ){

        System.out.println("初始化成功");

        this.brand = b;

        this.color = color;

    }

 

    public String getB(){

        return brand;

    }

 

    public String getC(){

        return color;

    }

 

    public void setB(String b ){

        this.brand = b;

    }

 

    public void setC(String c ){

        this.color = c;

    }

 

    public String toString(){

        return "Car{" + "brand:" + brand + "--color:"+color+"}";

    }

 

 

}

序列化(对象变字节序列)

1.创建ObjectOutputStream,然后调用2.writeObject()方法

 

//定义一个方法,用来序列化

public static void testS(){

    try (

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\JAVA文件\\API文件\\IO流\\122.txt"))

//它是处理流,把对象转换成字节,然后写到文件里面

    ){

        oos.writeObject(new Car("兰博基尼", "红色")); //把对象变成字节写入文件,当打开文件时会看到乱码

        oos.writeObject(new Car("宝马", "蓝色"));

        oos.writeObject(new Car("红旗", "黑色"));

 

    } catch (Exception e) {

        new Exception("序列化错误");

    }

}

 

反序列化(字节序列变对象)

1.创建ObjectInputStream对象,然后调用2.readObject()方法

 

//再定义一个方法,用来反序列化

public static void testD(){

    try (

        ObjectInputStream bj = new ObjectInputStream(new FileInputStream("D:\\JAVA文件\\API文件\\IO流\\122.txt"))

//把之前的里面的字节转化成对象

    ){

        System.out.println(bj.readObject()); //它不用调用构造器

        System.out.println(bj.readObject()); //把字节转成对象,并调用ToString方法

        System.out.println(bj.readObject()); 

    } catch (Exception e) {

        // TODO: handle exception

    }

}

结论:

一.序列化只是把对象中的成员变量转换为字节序列了,与其他的成员方法无关

* 1.该对象中引用类型的成员变量也必须是可序列化的(string可以)

* 2.该类的直接或间接的父类,要么具有无参构造器,要么也是可以序列化的

* 3. 一个对象只能被序列化一次,再次序列化时仅仅会输出它的序列号而已(每个被序列化的对象都有一个序列号,在序列化对象之前,程序会检测它是否被

* 序列化过,若没有程序会把它转化字节序列,若对象已经被序列化过,程序会直接输出它的序列号)

transient关键字

* transient关键字用于修饰成员变量,表示序列化时将会忽略它,反序列化时它会把这个成员变量输出为null

* transient关键字只能修饰成员变量,不能修饰类中的其他的内容

* 为什么要这个关键字:因为在某些场景里,不希望序列化某个成员变量 1.该成员变量是敏感信息,如银行密码,账号,怕传到网上去 2.该成员变量是引用类型,但它没有实现序列化接口

 


c75425ad375545fe8e6d4db7867a7428.jpg

 

(◦˙▽˙◦)(◦˙▽˙◦)(◦˙▽˙◦)

这是我的总结与想法,就简单分享到这里啦,大家有什么问题评论区见,一起向前!

 

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值