一、Java io流 的概念

流存在的意义:

1. 数据的传输量很大

2. 内存有限

3. 带宽有限

而Stream可以1点1点地逐步传输所有数据, 这就是Stream存在的根本意义。想想我们是怎样下载1个大文件的, 下载软件(例如x雷)并不会占用你内存很大的空间, 而只是在内存划分1个缓冲区, 一点一点地下载到自己的内存(缓冲区满了再写到硬盘), 这也是流的1个例子。

1、java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。

2、Stream是java的1个类, 这个类专门用于程序和外部设备的输入输出(IO). 基本上所有流都在 java.io这个包中.

实际上Stream就是数据在程序和外部设备的单向管道, 流的各种方法相当于管道上的各种按钮.  

所谓的外部设备可以包括硬盘文件, 网络设备, 另个程序等. 也就是当前程序之外的数据设备

Java将所有传统的流类型都放在Java.io包下,用于实现输入和输出功能。

二、io 流的分类

按照流的不同的分类方式,可以把流分为不同的类型,流常见的分类有3种:

1、按照流的流向分:输入流和输出流

  • 输入流: 只能从中读取数据,而不能向其写入数据。
  • 输出流:只能向其写入数据,而不能向其读取数据。

此处的输入,输出涉及一个方向的问题,对于如图15.1所示的数据流向,数据从内存到硬盘,通常称为输出流——也就是说,这里的输入,输出都是从程序运行所在的内存的角度来划分的。

Java中的IO机制 java中的io流知识总结_数据

注:java的输入流主要是InputStream和Reader作为基类,而输出流则是主要由outputStream和Writer作为基类。它们都是一些抽象基类,无法直接创建实例。

2、按照操作单元划分,可以划分为字节流和字符流。

    字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。

字节流主要是由InputStream和outPutStream作为基类,而字符流则主要有Reader和Writer作为基类。

3、按照流的角色划分为节点流和处理流。

Java中的IO机制 java中的io流知识总结_java_02

    可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,称为节点流。节点流也被称为低级流。图15.3显示了节点流的示意图。 
    从图15.3中可以看出,当使用节点流进行输入和输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。 
处理流则用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能。处理流也被称为高级流。

节点流:

节点流的类型:

Java中的IO机制 java中的io流知识总结_数据_03

 1)File 文件流。对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream。、
(2)Memory 
1)从/向内存数组读写数据: CharArrayReader与 CharArrayWriter、ByteArrayInputStream与ByteArrayOutputStream。 
2)从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream。
(3)Pipe管道流。 实现管道的输入和输出(进程间通信): PipedReader与PipedWriter、PipedInputStream与PipedOutputStream

节点流执行图示:

Java中的IO机制 java中的io流知识总结_输出流_04

 

 处理流:

处理流的类型:

Java中的IO机制 java中的io流知识总结_Java中的IO机制_05

 (1)Buffering缓冲流:在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader与BufferedWriter、BufferedInputStream与BufferedOutputStream。 
- (2)Filtering 滤流:在数据进行读或写时进行过滤:FilterReader与FilterWriter、FilterInputStream与FilterOutputStream。 
- (3)Converting between Bytes and Characters 转换流:按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader):InputStreamReader、OutputStreamWriter。 
- (4)Object Serialization 对象流 :ObjectInputStream、ObjectOutputStream。 
- (5)DataConversion数据流: 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream 。 
- (6)Counting计数流: 在读入数据时对行记数 :LineNumberReader、LineNumberInputStream。 
- (7)Peeking Ahead预读流: 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream。 
- (8)Printing打印流: 包含方便的打印方法 :PrintWriter、PrintStream。
 

处理流的执行图示:

Java中的IO机制 java中的io流知识总结_Java中的IO机制_06

 

缓冲流(buffering)是处理流的一种,对I/O进行缓冲是一种常见的性能优化,缓冲流为I/O流增加了内存缓冲区,增加缓冲区的两个目的: 
(1)允许Java的I/O一次不只操作一个字符,这样提高䇖整个系统的性能; 
(2)由于有缓冲区,使得在流上执行skip、mark和reset方法都成为可能。

【2】缓冲流:它是要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能, 
提高了读写的效率,同时增加了一些新的方法。例如:BufferedReader中的readLine方法, 
BufferedWriter中的newLine方法。

j2sdk提供了4种缓冲流,重用的构造方法如下:

//字符输入流
BufferedReader(Reader in)//创建一个32字节的缓冲区
BufferedReader(Reader in, int size)//size为自定义缓存区的大小

//字符输出流
BufferedWriter(Writer out)
BufferedWriter(Writer out, int size)

//字节输入流
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)

//字节输出流
BufferedOutputStream(OutputStream in)
BufferedOutputStream(OutputStream in, int size)

备注:

  (1)缓冲输入流BufferedInputSTream除了支持read和skip方法意外,还支持其父类的mark和reset方法; 
  (2)BufferedReader提供了一种新的ReadLine方法用于读取一行字符串(以\r或\n分隔); 
  (3)BufferedWriter提供了一种新的newLine方法用于写入一个行分隔符; 
 (4)对于输出的缓冲流,BufferedWriter和BufferedOutputStream,写出的数据会先在内存中缓存, 
使用flush方法将会使内存的数据立刻写出。

 

分类2:

1、java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java Io流的40多个类都是从如下4个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

2、java输入/输出流体系中常用的流的分类表

分类

字节输入流

字节输出流

字符输入流

字符输出流

抽象基类

InputStream

OutputStream

Reader

Writer

访问文件

FileInputStream

FileOutputStream

FileReader

FileWriter

访问数组

ByteArrayInputStream

ByteArrayOutputStream

CharArrayReader

CharArrayWriter

访问管道

PipedInputStream

PipedOutputStream

PipedReader

PipedWriter

访问字符串

 

 

StringReader

StringWriter

缓冲流

BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter

转换流

 

 

InputStreamReader

OutputStreamWriter

对象流

ObjectInputStream

ObjectOutputStream

 

 

抽象基类

FilterInputStream

FilterOutputStream

FilterReader

FilterWriter

打印流

 

PrintStream

 

PrintWriter

推回输入流

PushbackInputStream

 

PushbackReader

 

特殊流

DataInputStream

DataOutputStream

 

 

注:表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:斜体字标出的类代表抽象基类,无法直接创建实例。

 

 

CODE:

/**2019.6.14
 * cpu将数据传入设备叫输出流-写入数据write,设备将数据传到cpu输入流-读数据
 *java.io 包
 * 定规范就是为了让别人写出想要的方法
 * InputStream int read()
 * OutputStream void write(,int);
 * File:对文件进行的操作:FileInputStream 是从文件里面读东西的 ,调用read
 * FileOutStream :从文件里面输出信息 ,调用write()方法
 */
package flow;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.*;
import java.util.Date;
import test.Student;
public class TestIo {
    /*测试文件输出流*/
    @Test
    public void test1() throws Exception {
        //与众多的设备打交道的流叫基础流(低级流)文件流是低级流
        FileOutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\a.txt");
        int a = 20;//00000000 00000000 00000000 00010100 一个字节代表8位
        byte[] b = {0,0,0,20};
        fout.write(b[0]);
        fout.write(b[1]);
        fout.write(b[2]);
        fout.write(b[3]);
        fout.close();
        //0号字符,20号字符显示不出来
        FileInputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\a.txt");
        byte[] c = new byte[4];
        c[0] = (byte) fin.read();
        c[1] = (byte) fin.read();
        c[2] = (byte) fin.read();
        c[3] = (byte) fin.read();
        System.out.println(b[3]);
        fin.close();
    }

    @Test
    public void test2() throws Exception {
        FileOutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt");
        String mesg = "Hello!Laurence。";
        // 00000000 1010111 Java采用Unicod码
        byte[] ss = mesg.getBytes();
        for (byte s : ss) {//for循环遍历的另一种形式
            fout.write(s);//对象流
        }
        fout.close();
    }

//读
    @Test
    public void test3() throws Exception {
        FileInputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt");
        byte[] b = new byte[fin.available()];//测量文件占用的字节长度
        int t;
        byte i = 0;
        while ((t = fin.read()) != -1) {
            b[i] = (byte) t;
            i++;
        }
        String c = new String(b);
        System.out.println(c);
        fin.close();//释放流的资源
    }


    /*
     高级流一般不读写文件
     */
    @Test
    public void test4() throws Exception {
        OutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt");
        //OutputStream :写
        ObjectOutputStream objout = new ObjectOutputStream(fout);//实际上是fout在写字节
        // ObjectOutputStream :类型转换:将其他类型转换为字节类型
        //write()是写字节的方法
        objout.writeInt(222);//对象流完成其他类型到字节类型的转换
        objout.writeDouble(22.0);
        objout.writeBoolean(true );
        objout.writeChar('l');
        objout.writeChars("liujiaheng is a good man!");
        objout.flush();//刷新
        objout.close();
        fout.close();//释放流的资源
     }




     //装饰模式:两个流类合作完成一个功能
    @Test
    public void test5() throws Exception {
        InputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt");
        ObjectInputStream objout = new ObjectInputStream(fin);//实际上是fout在写字节
        int r=objout.readInt();//对象流完成其他类型到字节类型的转换
        System.out.println(r);
        double t1=objout.readDouble();
        System.out.println(t1);
        boolean t2=objout.readBoolean();
        System.out.println(t2);
        char e=objout.readChar();
        System.out.println(e);
//        String t3=objout.readUTF();//此方法读不出字符串
        System.out.println(t3);
        objout.close();
        fin.close();//释放流的资源
     }

@Test
    public void test6() throws Exception {
          OutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
         //OutputStream :写
          ObjectOutputStream objout =new ObjectOutputStream(fout);
          Date d=new Date();
          objout.writeObject(d);
          Student s=new Student(2000,"Laurence");
          objout.writeObject(s);
          objout.flush();
          objout.close();
    }


@Test
    public void test7() throws Exception {
        InputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
        //OutputStream :实现写的操作

        ObjectInputStream obin = new ObjectInputStream(fin);//实际上是fout在写字节
        // ObjectOutputStream :类型转换
        //write()是写字节的方法;
          Date d=(Date)obin.readObject();
          Student s=(Student)obin.readObject();
          System.out.println(d);
          System.out.println(s.getName());
          System.out.println(s.getId());
          obin.close();
    }
         //高级流实现读写操作
    public void test8() throws Exception {
        OutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
        //OutputStream :实现写的操作
        //下面也是高级流
        BufferedOutputStream bout = new BufferedOutputStream(fout);
        ObjectOutputStream obin = new ObjectOutputStream(bout);//实际上是fout在写字节
        fout.write(22);
        bout.write(222);
        bout.flush();//此时将数据写入文件里了
        ObjectOutputStream objout = new ObjectOutputStream(
                new BufferedOutputStream(
                        new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\d.txt")
                )
        );
        Date d=new Date();
        objout.writeObject(d);
        Student s=new Student(2000,"Laurence");
        objout.writeObject(s);
        objout.flush();
        objout.close();

    }



        public void test9() throws Exception {
            InputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
            //OutputStream :实现写的操作

            ObjectInputStream obin = new ObjectInputStream(

                    new BufferedInputStream(
                         new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt")
                    )
            );//实际上是fout在写字节
            // ObjectOutputStream :类型转换
            //write()是写字节的方法;
            Date d=(Date)obin.readObject();
            Student s=(Student)obin.readObject();
            System.out.println(d);
            System.out.println(s.getName());
            System.out.println(s.getId());
            obin.close();
        }
    }

 

package flow;
import org.junit.Test;
import java.io.*;
import  java.util.*;

public class testIo2 {

    @Test
    public void test1() throws Exception{
        FileInputStream fin=new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
          int a;
          while ((a=fin.read())!=-1){
              System.out.println((char)a);
          }
        fin.close();//因为字节流是一个字节一个字节读的,但是一个字符是占用两个字节。所以读出来的数据是乱码的
        //Reader、 Writer字符流
    }


    @Test
    public  void test2() throws Exception{
        FileWriter  w=new FileWriter("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
        w.write('A');
        w.write("刘家亨真棒!");
        w.close();

    }


    //字符流
    @Test
    public  void test3() throws Exception{
        BufferedWriter  w=new BufferedWriter(
                new FileWriter("D:\\新建文件夹\\练习\\javacode\\src\\test\\e.txt")
            );
        w.write("sucessful\n");
        w.flush();//
        w.close();
    }


    @Test
    public  void test4() throws Exception{
        FileReader  w=new FileReader("D:\\新建文件夹\\练习\\javacode\\src\\test\\e.txt");
        int a;
        while((a=w.read())!=-1){
            System.out.print((char)a);
        }
        w.close();
    }


    @Test
    public  void test5() throws Exception{
        BufferedReader  w=new BufferedReader(new FileReader("D:\\新建文件夹\\练习\\javacode\\src\\test\\e.txt")
         );
         String mesg = w.readLine();
         System.out.println(mesg);
         w.close();
    }


    //输入流 //输入流不需要flush//输入流包装字符流
    @Test
    public void tes6() throws Exception {
        InputStreamReader ir=new InputStreamReader(System.in) ;
        BufferedReader br=new BufferedReader(ir);
        String mesg=br.readLine();
        System.out.println(mesg);
    }

    @Test
    public void  test7() throws Exception{
        OutputStreamWriter w=new OutputStreamWriter(System.out);
         BufferedWriter bw=new BufferedWriter(w);
         bw.write("中国你好!");
         bw.flush();
         bw.close();

    }




    @Test//PrintStream主要用于包装其他字符流,还可以用来写字符串
    public void  test8() throws Exception{
      PrintStream c=new PrintStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\rr.txt");
      c.print("hello"+"my name is Laurence");//
      c.flush();//刷新后才能写进去
    }

    //既可以包装字符流,又可以包装字节流,还可以给文件直接写字符,它可以和其他的流搭配着用
    @Test
    public void test9()throws Exception {
    PrintWriter pw = new PrintWriter("D:\\新建文件夹\\练习\\javacode\\src\\test\\rr.txt");
    pw.println("hello"+"bjdhsvcjhdbvcjhdsc");//给文件中写入这样一段字符
    pw.flush();
    pw.close();

    }


    @Test//文件对象   用来标识文件的,判断文件或目录是否存在
    public void test10() throws Exception{
        File f=new  File("D:\\liujiaheng\\pp");
        if(f.exists()){//判断文件目录是否存在
            System.out.println("存在");
        }else{
            System.out.println("不存在");
            System.out.println( f.mkdirs());//如果不存在就创建一个目录树
        }
    }

    @Test
    public void tes11() throws Exception {
        File f = new File( "D:\\新建文件夹\\练习\\aa.txt" );
        if(f.exists()){//判断文件是否存在
            System.out.println("存在");
        }else{
            System.out.println("不存在");
            f.createNewFile();//必须在已将创建的目录下才可以创建一个空文件
        }
    }


    @Test
    public void tes12() throws Exception {
        File f = new File("D:\\新建文件夹\\练习\\javacode\\src\\test");//这是一个目录
        File e=new File("D:\\新建文件夹\\练习\\javacode\\src\\test\\rr.txt");
        System.out.println(f.isDirectory());//判断目录是否存在
        System.out.println(f.isFile());//判断是否为文件返回值为true或false
        System.out.println(e.isDirectory());
        System.out.println(e.isFile());
    }


    @Test
    public void tes13() throws Exception {
        File f = new File("D:\\新建文件夹");
       //目录名两端加一个[]代表它是一个目录。
        if(f.isDirectory()){
            System.out.println("[" + f.getName() + "]");
        }else{
            System.out.println(f.getName());
        }
        File[] fs = f.listFiles();
        if(fs != null && fs.length > 0){
            listFile(fs,1);
        }
    }

    private void writeBlank(int level){
        for(int i = 0 ; i < level ; i++)
            System.out.print("  ");//一级打两个空格
    }

    private void listFile(File[] fs,int level){//file数组,level表示是第几层
        for(File f : fs){//循环
            writeBlank(level);//打空格
            if(f.isDirectory()){//判断这个文件是不是目录
                System.out.println("[" + f.getName() + "]");
                File[] ff = f.listFiles();
                if(ff != null && ff.length > 0){//判断目录是否为空
                    listFile(ff,++level);//不为空就迭代
                }
            }else{
                System.out.println(f.getName());//不是目录打印出名字
            }
        }
    }

}

流的重要特性:

1、流是Java 的1个类,但类不是流。

2、数据并不会在流里自动流动,因此需要我们调用流的方法,一次传输一定量的数据。

3、一个流对象只有一个传输方向,也就是说流是单向的, 数据要么从程序到设备(OutputStream), 要么从设备到程序(InputStream)。