IO流
 首先说明一下本章主要学什么,IO流,其实就是升级版的File文件操作,在本章通过学习一些流,调用相应的方法,就可以实现从程序到文件,再从文件到程序的相应操作,而这一章节的代码也比较模板化,因此只需要记住基本的流,基本的使用方法,具体应用可以在项目中进行实践。
一、流的三种分类方式
1.流向
输入流、输出流
2.数据单位
字节流、字符流
3.流的角色
节点流、处理流
二、流的结构体系
在这里只提供一些常用常见的流
这个表其实也不用记,看一眼,知道有啥东西就行了,重点在后边。
| 分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | 
|---|---|---|---|---|
| 抽象基类 | InputStream | OutputStream | Reader | Writer | 
| 访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter | 
| 访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter | 
| 访问字符串 | StringReader | StringWriter | ||
| 缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter | 
| 转换流 | InputStreamReader | OutputStreamWriter | ||
| 对象流 | ObjectInputStream | ObjectOutputStream | ||
| 特殊流 | DataInputStream | DataOutputStream | 
三、输入、输出的标准化过程
模板的输入输出过程我用文件的字节输入输出流演示
1.输入过程
①创建File类的对象,指明读取的数据的来源(要求此文件一定存在)
File srcFile = new File("d:\\zyb.jpg");
②创建相应的输入流,将File类的对象作为参数,传入流的构造器中
FileInputStream fis = new FileInputStream(srcFile);
③读入过程:创建相应的byte[]或char[],并调用fis的read方法,放入循环中
byte[]buffer=new byte[10];
int len=0;
while((len=fis.read(buffer))!=-1){
    System.out.println(new String(buf,0,len));
}
④关闭流资源
fis.close();
说明:程序中出现的异常需要使用try-catch-finally处理
2.输出过程
①创建File对象,指明写出数据的位置(不要求此文件一定要存在)
File destFile = new File("d:\\zyb2.jpg");
②创建相应的输出流,将File类的对象作为参数,传入流的构造器中
FileOutputStream fos = new FileOutputStream(destFile);
③写出(创建buffer数组和len指针变量的过程和输入过程相同)
fos.write(buffer,0,len);
④关闭流资源
fos.close();
四、节点流
1.FileReader(字符输入流)
说明点:1、read()理解:返回读入的一个字符。如果到达文件末尾,则返回-1.
 2、异常处理:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally
 3、读入的文件一定要存在,否则会报FileNotFoundException异常
代码演示:
@Test
public void testFileReader1() throws IOException {
    //1.File类的实例化
    File file = new File("e:\\news2.txt");
    //2.FileReader流的实例化
    FileReader fileReader = new FileReader(file);
    //3.读入文件的操作
    //read(char[] cubf):每次返回读入cubf数组中的字符个数,如果到达文件末尾,则返回-1
    char[]cbuf = new char[5];
    int len=0;
    while((len = fileReader.read(cbuf))!=-1){
        String str = new String(cbuf,0,len);
        System.out.println(str);
    }
    //4.资源的关闭
    fileReader.close();
}
2.FileWriter(字符输出流)
说明:1.输出到文件的操作,对应的File可以不存在,不会报异常
 2.File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
 File对应的硬盘中的文件如果存在:
 有两种构造器,如果流使用的构造器是FileWriter(file)/FileWriter(file,false)则写入的信息会对原文件进行覆盖,比如原文件中已经含有字符“hello”,而我使用writer写入“world”,则最后文件的内容只有“world”。
 如果流使用的构造器是**FileWriter(file,true)**那么进行的是对原文件的续写操作,不会进行覆盖。比如原文件中已经含有字符“hello”,我写入“world”,则最后文件的内容为“hello world”。
代码演示:
@Test
public void testFileWriter() throws IOException {
    
    //1.提供File类的对象,指明写出到的文件
    File file = new File("hello1.txt");
    
    //2.提供FileWriter的对象,用于数据的写出
    FileWriter fw = new FileWriter(file, false);
    
    //3.写出到文件操作
    fw.write("wu zi bu xing\n");
    fw.write("edg nb!");
    
    //4.流资源的关闭
    fw.close();
    
}
3.文本文件的复制
文本文件的复制操作是通过在一个程序中同时调用输入流和输出流,进而实现创建一个新的文本,并对其进行赋值。
代码如下:
其中,srcFile是被复制的文件,destFile是将要创建,进行复制的文件。
@Test
public void testFileReaderWriter() throws IOException {
    
    //1.创建File类的对象,指明读入和写出的文件
    File srcFile = new File("hello1.txt");
    File destFile = new File("hello2.txt");
    
    //2.创建输入流和输出流的对象
    FileReader fr = new FileReader(srcFile);
    FileWriter fw = new FileWriter(destFile);
    
    //3.数据的读入和写出操作
    char[] cubf = new char[5];
    int len;
    while((len=fr.read(cubf))!=-1){
        fw.write(cubf,0,len);
    }
    
    //4.关闭流
    fw.close();
    fr.close();
}
4.FileInputStream / FileOutputStream(字节流)
 呃,这个和上面的字符流一样,没什么好说的,代码模板都一样,只需要在写入写出的时候把char[]数组换成byte[]数组就行了。。。
 比较一下:对于文本文件(.txt,.java,.c,.cpp)之类的都使用字符流处理
 对于非文本文件(图片,音频,视频),使用字节流处理
五、缓冲流
1.缓冲流涉及到的类:
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
2.作用
提高流的读取、写入速度
原理:内部提供了一个缓冲区。默认情况下是8kb
3.代码实现
·1BufferedInputStream、BufferedOutStream
该代码是用节点流+缓冲流实现图片的复制
其实缓冲流的代码跟上边节点流的代码模板一样,只不过多了一步将节点流的对象放入缓冲流的构造器中的步骤
@Test
public void BufferedStreamTest() throws IOException {
    
        //1.造文件
        File srcFile = new File("d:\\zyb.jpg");
        File destFile = new File("d:\\zyb2.jpg");
        //2.造节点流
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
        //3.造缓冲流
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //4.复制:用bis读取,用bos写入
        byte[]buffer=new byte[10];
        int len=0;
        while((len=bis.read(buffer))!=-1) {
            bos.write(buffer, 0, len);
        }
        //5.资源关闭
        //要求:先关闭外层的流,再关闭内层的流
        bos.close();
        bis.close();
    }
·2BufferedReader、BufferedWriter
之前一直都是分开写,代码比较多,现在简写一下,试试将造文件的过程和造节点流的过程一起合并到造缓冲流的过程。
@Test
public void testBufferedReaderBufferedWriter() throws IOException {
    
        //1.将造文件,造节点流合成一步
        BufferedReader  bufferedReader = new BufferedReader(new FileReader(new File("e:\\news1.txt")));
        BufferedWriter   bufferedWriter = new BufferedWriter(new FileWriter(new File("e:\\news2.txt")));
       
        //2.数据的读入写出
        char[]cbuf=new char[1024];
        int len=0;
        while((len=bufferedReader.read(cbuf))!=-1){
            bufferedWriter.write(cbuf,0,len);
        }
        
        //3.关流
        bufferedReader.close();
        bufferedWriter.close();
    }
六、转换流
1.涉及到的类
InputStreamReader:将一个字节的输入流转换为字符的输入流
解码:字节、字节数组—>字符数组、字符串
OutputStreamWriter:将一个字符的输出流转换为字节的输出流
编码:字符数组、字符串—>字节、字节数组
编码决定解码!!!
2.作用
提供字节流的字符流之间的转换
3.代码实现
·InputStreamReader
将字节输入流转换为字符输出流
呃,其实就多加了一步将字节输入流的对象放到转换流的构造器中而已
//转换输入流
@Test
public void test1() throws IOException {
    
    FileInputStream fileInputStream = new FileInputStream("e:\\news1.txt");						           
    //参数“UTF-8”指明了字符集(根据文件初始存的时候决定)
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");
    char[]cbuf=new char[1];
    int len;
    while((len=inputStreamReader.read(cbuf))!=-1){
        String str=new String(cbuf,0,len);
        System.out.print(str);
    }
    
    inputStreamReader.close();
}
·OutputStreamWriter
将字符输出流转换为字节输出流
写了这么多了,学了节点流,缓冲流,转换流,那现在咱们把这三个流都用到一起,综合升华一下,嘿嘿
这段代码的作用是实现文本的复制,就是将news1.txt中的文本信息复制到nesw3.txt中
快结束了,再来综合复习一下吧兄弟们
①造文件
②造节点流
③造缓冲流,提高运行效率
④造转换流,分别将输入流和输出流放入两个转换流中
⑤数组+循环 实现读入和写出
⑥关闭流
//转换输出流
@Test
public void test2() throws IOException{
	
    
    File file1 = new File("e:\\news1.txt");
    File file2 = new File("e:\\news3.txt");
    FileInputStream fileInputStream = new FileInputStream(file1);
    FileOutputStream fileOutputStream = new FileOutputStream(file2);
    
    BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"gbk");
    char[]cubf=new char[1];
    int len;
    while((len=inputStreamReader.read(cubf))!=-1){
        outputStreamWriter.write(cubf,0,len);
    }
    inputStreamReader.close();
    outputStreamWriter.close();
}
七、其它特殊流
1.标准的输入输出流
System.in:标准的输入流,默认从键盘输入 例如:new Scanner(System.in)
System.out:标准的输出流,默认从控制台输出 例如:System.out.println()
2.打印流
PrintStream和PrintWriter
说明:提供了一系列重载的print()和println()方法,用于多种数据类型的输出
 System.out返回的是PrintStream的实例
3.数据流
DataInputStream和DataOutputStream
作用:用于读取或写出基本数据类型的变量或字符串
上面的三个流作为了解即可,不用深入
八、对象流
1.介绍
ObjectInputStream和ObjectOutputStream
作用:
ObjectInputStream:**内存中的对象—>存储中的文件、通过网络传输出去 ** 序列化过程
ObjectOutputStream:存储中的文件、通过网络接受过来—>内存中的对象 反序列化过程
2.对象的序列化机制
概念描述:
序列化过程:把内存中Java对象转换成平台无关的二进制流,从而把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
反序列化过程:当其它程序获取了这种二进制流之后,就可以恢复成原来的Java对象
3.代码实现
序列化过程
 在这里说一点就是,java可以对自定义类进行序列化转换,但这个自定义类需要满足两个条件,第一个就是需要实现Serializable接口,这个接口是一个标识接口,不需要重写任何方法。第二个就是在类中需要提供一个全局常量serialVersionUID,其中这个全局常量的值必须进行自定义修改,任何值都可以。
-  * @author 一只鱼zzz * @version 1.0 * Person类需要满足一下的要求,方可序列化 * 1.需要实现接口:Serializable(标识接口,无需重写方法) * 2.当前类需要提供一个全局常量:serialVersionUID */ public class Person implements Serializable { public static final long serialVersionUID = 1231213123132L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
/*
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
*/
@Test
public  void test1(){
   
       //1.造流造文件
       ObjectOutputStream oos = null;
   	try {
       oos = new ObjectOutputStream(new FileOutputStream("e:\\news1.dat"));
       //2.写
       oos.writeObject(new String("我爱轻大"));
       oos.flush();
       //3.刷新
       oos.writeObject(new Person("一只鱼zzzz",19));
       oos.flush();
   } catch (IOException e) {
       e.printStackTrace();
   } finally {
       //4.关
       try {
           oos.close();
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}
反序列化过程
@Test
public void  test2(){
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream("e:\\news1.dat"));
        Object obj=ois.readObject();
        String str=(String)obj;
        Person p = (Person) ois.readObject();
        System.out.println(p);
        System.out.println(str);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        try {
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
九、RandomAccessFile流
1.介绍
(1)该类直接继承于Object类,实现了DataInput和DataOutput接口
(2)既可以作为一个输入流,又可以作为一个输出流
(3)如果作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。如果写出到的文件存在,则会对原文件内容进行覆盖。(默认情况下,从头覆盖)
(4)通过相关的操作,可以实现RandomAccessFile“插入数据”的功能
2.典型代码1
@Test
public void test1() throws IOException {
    RandomAccessFile raf1 = new RandomAccessFile(new File("hello1.txt"), "r");
    RandomAccessFile raf2 = new RandomAccessFile(new File("hello3.txt"), "rw");
    
    byte[]buffer = new byte[1024];
    int len;
    while((len=raf1.read(buffer))!=-1){
        raf2.write(buffer,0,len);
    }
    raf2.close();
    raf1.close();
}
3.典型代码2(插入效果)
@Test
public void test2() throws IOException {
    RandomAccessFile raf1 = new RandomAccessFile("hello3.txt", "rw");
    raf1.seek(3);//将指针调节到角标为3的位置
    //保存指针3后面的所有数据到StringBuilder中
    StringBuilder builder = new StringBuilder((int) new File("hello3.txt").length());
    byte[] buffer = new byte[20];
    int len;
    while((len=raf1.read(buffer))!=-1){
        builder.append(new String(buffer,0,len));
    }
    //调回指针,写入“xyz”
    raf1.seek(3);
    raf1.write("xyz".getBytes());
    //将StringBuilder中的数据写入到文件中
    raf1.write(builder.toString().getBytes());
    raf1.close();
}










