Bootstrap

24.Java I/O流概述

Java I/O流概述


一、IO流概述

  • 流的分类
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)  

按数据流的流向不同分为:输入流,输出流

按流的角色的不同分为:节点流,处理流

在这里插入图片描述

  • 流的继承关系
4个抽象基类:
InputStream
OutputStream
Reader
Writer

在这里插入图片描述

说明:上图中蓝色背景的流是本章的重点。

二、节点流的使用

2.1 FileReader 和 FileWriter

//FileReader的基本使用
    @Test
    public void testFileReader() throws IOException {

        //1.创建文件对象
        File file1 = new File("hello.txt");

        //2. 创建流对象
        FileReader fr = new FileReader(file1);

        //3. 读入数据的过程
        int data = fr.read();
        while (data != -1) {
            System.out.print((char) data);
            data = fr.read();
        }

        //4.对于流的关闭操作,必须手动实现
        fr.close();

    }
  • 优化以后:
 //对上述程序优化1:在io流中,凡是需要手动关闭流资源的程序中,都需要使用try-catch-finally处理异常
    @Test
    public void testFileReader1() {
        FileReader fr = null;

        try {
            //1.创建文件对象
            File file1 = new File("hello.txt");

            //2. 创建流对象
            fr = new FileReader(file1);

            //3. 读入数据的过程
            int data;//保存每次读取的字符数据
            while ((data = fr.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.对于流的关闭操作,必须手动实现
            try {
                if (fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • 进一步优化:
//对上述程序优化2:每次读取一个字符数组
    @Test
    public void testFileReader2() {
        FileReader fr = null;

        try {
            //1.创建文件对象
            File file1 = new File("hello.txt");

            //2. 创建流对象
            fr = new FileReader(file1);

            //3. 读入数据的过程
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中的字符的个数
            while((len = fr.read(cbuf)) != -1){
                //错误的:
//                for (int i = 0; i < cbuf.length; i++) {
//                    System.out.print(cbuf[i]);
//
//                }
                //错误的:
//                String str = new String(cbuf);
//                System.out.print(str);
                //正确的:
//                for (int i = 0; i < len; i++) {
//                    System.out.print(cbuf[i]);
//                }
                //正确的:
                String str = new String(cbuf,0,len);
                System.out.print(str);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.对于流的关闭操作,必须手动实现
            try {
                if (fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • FileWriter的使用
@Test
    public void testFileWriter() {
        FileWriter fw = null;
        try {
            //1. 创建输出到的File对象
            File file1 = new File("info.txt");
            //2. 创建输出流
//            fw = new FileWriter(file1);
            fw = new FileWriter(file1,true);
            //3. 输出数据的过程
//        fw.write('a');
//        fw.write("I love You!");
            fw.write("I love You!\n".toCharArray());
            fw.write("You love him".toCharArray());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 手动的关闭资源
            try {
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • 使用总结
结论:
    1. 对于输入流来说,读取的文件一定要存在,否则会报FileNotFoundException
    2. 对于输出流来说,要写出到的文件可以不存在。
            如果不存在,则在执行过程中,会自动创建对应的文件
            如果存在,1)使用FileWriter(File file)FileWriter(File file,false):会对已有的文件进行覆盖
                     2FileWriter(File file,true):在现有文件内容的末尾,追加内容
    3. 流,因为都需要进行资源的关闭,所有本章所有的异常,只要涉及流资源的关闭,都必须使用try-catch-finally
  • 综合使用FileReader和FileWriter实现文本文件的复制
/*
    综合使用FileReader和FileWriter实现文本文件的复制
     */
    @Test
    public void testFileReaderWriter(){
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.指明读取的File对象和写出到的File对象对应的文件
            File srcFile = new File("hello.txt");
            File destFile = new File("hello1.txt");
            //不能处理非文本文件
//            File srcFile = new File("baby.jpg");
//            File destFile = new File("baby1.jpg");


            //2. 创建对应的FileReader和FileWriter
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);

            //3. 读取并写出数据的过程
            char[] cbuf = new char[5];
            int len;//记录每次读入到char[]中字符的个数
            while((len = fr.read(cbuf)) != -1){
    //            fw.write(cbuf);//错误的
                fw.write(cbuf,0,len);
            }
            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            //方式一:
//            try {
//                if(fw != null)
//                    fw.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }finally{
//                try {
//                    if(fr != null)
//                        fr.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//
//            }
            //方式二;
            try {
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
  • 思考
 //FileReader和FileWriter:属于字符流,不能用来处理非文本文件的数据!
//如果想处理非文本文件的数据,就需要使用字节流:FileInputStream和FileOutputStream

2.2 FileInputStream和FileOutputStream

//复制一个文件
    @Test
    public void testFileInputOutputStream() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1. 造文件
//            File srcFile = new File("baby.jpg");
//            File destFile = new File("baby2.jpg");
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            //2. 造流
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            //3. 读取并写出的操作
            byte[] buffer = new byte[5];
            int len;//记录每次读取到buffer中的字节的个数
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);

//                String data = new String(buffer, 0, len);
//                System.out.print(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

结论:

 *  通常,我们使用字节流来处理非文本文件(.jpg, .mp3,.mp4,.avi,.doc,.ppt,.xls)
 *            使用字符流来处理文本文件(.txt,.java,.py)

三、处理流之一:缓冲流的使用

3.1 基本结构

 * 抽象基类             文件流                       缓冲流(处理流的一种):提高数据读写的效率
 * InputStream          FileInputStream             BufferedInputStream
 * OutputStream         FileOutputStream            BufferedOutputStream
 * Reader               FileReader                  BufferedReader
 * Writer               FileWriter                  BufferedWriter

3.2 BufferedInputStream和BufferedOutputStream的使用

@Test
    public void testBufferedInputOutput() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1. 造文件
            File srcFile = new File("baby.jpg");
            File destFile = new File("baby3.jpg");
            //2. 造流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);

            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3. 读写的细节操作
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 关闭资源
            //先关闭外面的流,再关闭内部的流
            try {
                if (bos != null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            //可以省略
//        fos.close();
//        fis.close();
        }
    }

3. 3 对比文件流与缓冲流的执行效率

/*
    对比FileInputStream + FileOutputSteam 与 BufferedInputStream + BufferedOutputStream的读写效率
     */
    public void copyFileWithFiled(String src,String dest){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1. 造文件
            File srcFile = new File(src);
            File destFile = new File(dest);

            //2. 造流
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            //3. 读取并写出的操作
            byte[] buffer = new byte[1024];
            int len;//记录每次读取到buffer中的字节的个数
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void copyFileWithBuffered(String src,String dest){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1. 造文件
            File srcFile = new File(src);
            File destFile = new File(dest);
            //2. 造流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);

            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3. 读写的细节操作
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 关闭资源
            //先关闭外面的流,再关闭内部的流
            try {
                if (bos != null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testCopyTime(){

        long start = System.currentTimeMillis();
        String src = "G:\\教学视频\\01-Java基础\\尚硅谷_200213大数据Java基础_宋红康\\6-每日视频\\01-作业题.avi";
        String dest = "G:\\教学视频\\01-Java基础\\尚硅谷_200213大数据Java基础_宋红康\\6-每日视频\\test02.avi";
//        copyFileWithFiled(src,dest);
        copyFileWithBuffered(src,dest);

        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:" + (end - start));//2028 -  507

    }

3.4 BufferedReader和BufferedWriter的使用

@Test
    public void testBufferedReaderWriter() {
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            //1. 造文件、造流
            br = new BufferedReader(new FileReader(new File("dbcp.txt")));
            bw = new BufferedWriter(new FileWriter(new File("dbcp-1.txt")));
            //2. 读写文件的细节
            //写法一:
//            char[] cbuf = new char[1024];
//            int len;
//            while ((len = br.read(cbuf)) != -1) {
//                bw.write(cbuf, 0, len);
//            }
            //写法二:
            String data;
            while((data = br.readLine()) != null){
//                bw.write(data + "\n");
                bw.write(data);
                bw.newLine();//换行
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭资源
            try {
                if (bw != null)
                    bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (br != null)
                    br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
  • 体会在BufferedReader中使用readLine()

四、处理流之二:转换流

4.1 介绍

 * 一、
 * 解码:字节、字节数组  --->  字符、字符数组、字符串
 * 编码:字符、字符数组、字符串 ---> 字节、字节数组
 *
 * 解码过程中,使用的字符集必须是当初编码时使用的字符集!否则,解码就会出现乱码!
 *
 * 二、转换流:
 * InputStreamReader:将输入型的字节流转换为输入型的字符流
 * OutputStreamWriter:将输出型的字符流转换为输出型的字节流

4.2 代码实现

//如下的代码应该还是需要使用try-catch-finally处理的!
    @Test
    public void testInputStreamReader() throws IOException {
        //1.
        FileInputStream fis = new FileInputStream("dbcp.txt");
//      InputStreamReader isr = new InputStreamReader(fis);//默认与IDEA设置的字符集相同:utf-8
        InputStreamReader isr = new InputStreamReader(fis, "utf-8");//显示指明字符集:utf-8

        //2.
        char[] cbuf = new char[1024];
        int len;
        while ((len = isr.read(cbuf)) != -1) {
            String str = new String(cbuf, 0, len);
            System.out.print(str);
        }
        //3.
        isr.close();

    }

    //如下的代码应该还是需要使用try-catch-finally处理的!
    @Test
    public void testInputStreamReaderOutputStreamWriter() throws IOException {
        //1.
        InputStreamReader isr = new InputStreamReader(new FileInputStream("dbcp.txt"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("dbcp_gbk200213.txt"),"gbk");//指明编码时的字符集
        //2.
        char[] cbuf = new char[1024];
        int len;
        while((len = isr.read(cbuf)) != -1){
            osw.write(cbuf,0,len);
        }
        //3.
        osw.close();
        isr.close();
    }

五、处理流之三:对象流

5.1 举例说明序列化过程

//序列化过程:ObjectOutputStream:实现内存中的数据,写入到具体的文件中
    @Test
    public void testObjectOutputStream() throws Exception {
        //1.创建文件和流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
        //操作基本数据类型
//        oos.writeByte();
//        oos.writeBoolean();

        //2.操作对象
        oos.writeObject(new String("Tom"));
        oos.flush();//刷新
        oos.writeObject(new String("王辰硕"));
        oos.flush();//刷新

        //3.关闭资源
        oos.close();
    }

5.2 举例说明反序列化过程

//反序列化过程:ObjectInputStream:将磁盘文件中保存的对象,还原为内存中的对象
    @Test
    public void testObjectInputStream() throws IOException, ClassNotFoundException {
        //1.
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"));
        //2.
        String s1 = (String) ois.readObject();
        System.out.println(s1);

        String s2 = (String) ois.readObject();
        System.out.println(s2);

        //3.
        ois.close();
    }

5.3 小结:

对象流的使用:
 * 1. ObjectInputStream 和 ObjectOutputStream
 * 2. 作用:用于存储和读取基本数据类型数据或对象的处理流。
 *    它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
 *
 *
 *  面试题:你是如何理解对象的序列化机制的?
 *  对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,
 *  或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

5.4 要想自定义的类可序列化,则需要满足

/**
 * @author shkstart
 * @create 2020 下午 4:56
 * <p>
 * 要想自定义的类可序列化,则需要满足:
 * ① 实现接口:java.io.Serializable
 * ② 显式声明全局常量:serialVersionUID,用于唯一标识当前类
 * ③ 要想当前类的对象可序列化,必须其所有的属性也是可以序列化的
 *
 * 说明:1. 默认情况下,基本数据类型的变量都可以序列化
 *       2. ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
 *
 */
public class Person implements Serializable {
    static final long serialVersionUID = 43453452L;
    private int age;
    private String name;
    private Account acct;

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", acct=" + acct +
                '}';
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public Person() {
    }

    public Person(int age, String name, Account acct) {
        this.age = age;
        this.name = name;
        this.acct = acct;
    }
}

class Account implements Serializable {
    private double balance;
    static final long serialVersionUID = 43456453452L;

    @Override
    public String toString() {
        return "Account{" +
                "balance=" + balance +
                '}';
    }

    public Account(double balance) {
        this.balance = balance;
    }
}

六、(了解)处理流之四:标准的输入输出流

七、(了解)处理流之五:打印流

setAge(int age) {
this.age = age;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Person(int age, String name) {
    this.age = age;
    this.name = name;
}

public Person() {
}

public Person(int age, String name, Account acct) {
    this.age = age;
    this.name = name;
    this.acct = acct;
}

}

class Account implements Serializable {
private double balance;
static final long serialVersionUID = 43456453452L;

@Override
public String toString() {
    return "Account{" +
            "balance=" + balance +
            '}';
}

public Account(double balance) {
    this.balance = balance;
}

}

## 八、(了解)处理流之四:标准的输入输出流

## 九、(了解)处理流之五:打印流

## 十、(了解)处理流之六:数据流


其他的处理流(了解):

标准的输入输出流

 /*
    标准的输入流:System.in 默认从键盘输入
    标准的输出流:System.out 默认从显示屏输出

    重定向:通过System类的setIn,setOut方法对默认设备进行改变。
        public static void setIn(InputStream in)
        public static void setOut(PrintStream out)

     */
    @Test
    public void test1() {
        Scanner scann = new Scanner(System.in);
        System.out.println("请输入一个字符串");
        String inf = scann.nextLine();
        System.out.println(inf);
        //关闭操作
        scann.close();
    }

    /*
    从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,
    直至当输入“e”或者“exit”时,退出程序。

     */
    @Test
    public void test2() {
        System.out.println("请输入信息(退出输入e或exit):");
        // 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = null;
        try {
            while ((s = br.readLine()) != null) { // 读取用户输入的一行数据 --> 阻塞程序
                if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
                    System.out.println("安全退出!!");
                    break;
                }
                // 将读取到的整行字符串转成大写输出
                System.out.println("-->:" + s.toUpperCase());
                System.out.println("继续输入信息");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

打印流

/*
    打印流: PrintStream 和 PrintWriter
    1. 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
    2. System.out返回的是PrintStream的实例
     */
    @Test
    public void test3() {
        PrintStream ps = null;
        try {
            FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
            // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
            ps = new PrintStream(fos, true);
            if (ps != null) {// 把标准输出流(控制台输出)改成文件
                System.setOut(ps);
            }
            for (int i = 0; i <= 255; i++) { // 输出ASCII字符
                System.out.print((char) i);
                if (i % 50 == 0) { // 每50个数据一行
                    System.out.println(); // 换行
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }


    }

数据流

/*
    数据流:
        DataInputStream 和 DataOutputStream
    作用:为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。
    说明:可以使用对象流替换数据流
     */
    @Test
    public void test4() {
        DataOutputStream dos = null;
        try { // 创建连接到指定文件的数据输出流对象
            dos = new DataOutputStream(new FileOutputStream("destData.dat"));
            dos.writeUTF("我爱北京天安门"); // 写UTF字符串
            dos.writeBoolean(false); // 写入布尔值
            dos.writeLong(1234567890L); // 写入长整数
            System.out.println("写文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally { // 关闭流对象
            try {
                if (dos != null) {
                    // 关闭过滤流时,会自动关闭它包装的底层节点流
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @Test
    public void test5() {
        DataInputStream dis = null;
        try {
            dis = new DataInputStream(new FileInputStream("destData.dat"));
            String info = dis.readUTF();
            boolean flag = dis.readBoolean();
            long time = dis.readLong();
            System.out.println(info);
            System.out.println(flag);
            System.out.println(time);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

随机存取文件流

/**
 * RandomAccessFile类的使用:
 * 1. 此类直接继承于java.lang.Object类
 * 2. 此类实现类DataInput和DataOutput接口,此类既可以作为输入流,有可以作为输出流
 * 3. 如果写出到的文件存在,则不会对文件进行覆盖,而是从头开始对文件内容进行覆盖
 * 4. 可以实现从文件内容的指定位置开始写入数据:seek(int position)
 *
 * @author shkstart
 * @create 2020 上午 10:13
 */
public class RandomAccessFileTest {

    @Test
    public void test1() {
        RandomAccessFile raf1 = null;
        RandomAccessFile raf2 = null;
        try {
            raf1 = new RandomAccessFile("baby.jpg", "r");
            raf2 = new RandomAccessFile("baby1.jpg", "rw");

            byte[] buffer = new byte[1024];
            int len;
            while ((len = raf1.read(buffer)) != -1) {
                raf2.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (raf2 != null)
                    raf2.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (raf1 != null)
                    raf1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @Test
    public void test2() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("hello.txt","rw");
        raf.seek(5);
        raf.write("xyz".getBytes());

        raf.close();
    }
}

八、介绍NIO

1. Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

2. NIO与IO的对比:
IO		NIO
byte[] / char[]		Buffer
流Stream		FileChannel

NIO : Non-Blocking  非阻塞式

3.NIO的相关API:
|-----java.nio.channels.Channel
	|-----FileChannel:处理本地文件
	|-----SocketChannel:TCP网络编程的客户端的Channel
	|-----ServerSocketChannel:TCP网络编程的服务器端的Channel
	|-----DatagramChannel:UDP网络编程中发送端和接收端的Channel

4. NIO2
随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。

5. 
Path:替换原有的File类,表示一个文件或文件目录
     提供了丰富的方法
Paths:用来实例化Path
Files:用来操作文件或文件目录的工具类

九、练习题

  • 简答题
*	说明流的三种分类方式
	流的流向:输入流、输出流
	单位的不同:字节流、字符流
	流的角色:节点流、处理流
	
*	谈谈你对对象序列化机制的理解?我们以对象流为载体,讲解的。
序列化过程:对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
反序列化过程:当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

*	谈谈你对编码与解码过程的理解?
载体:String < ---- > byte[]
     InputStreamReader\OutputStreamWriter

编码:String ---> byte[] ; OutputStreamWriter
解码: byte[] ----> String ; InputStreamReader

 * 对象要想实现序列化,需要满足哪几个条件
 * ① 实现接口:java.io.Serializable
 * ② 显式声明全局常量:serialVersionUID,用于唯一标识当前类
 * ③ 要想当前类的对象可序列化,必须其所有的属性也是可以序列化的

  • 编程题

练习:

使用缓冲流实现a.jpg文件复制为b.jpg文件的操作
public class Exer01 {
    public static void main(String[] args) {
        String src = "/exer01/a.jpg" ;
        String dest = "/exer01/b.jpg" ;
      
        bufferedCopyFile(src,dest);
        
    }
    /**
     * 使用缓冲流从原件中复制文件到目标文件中
     * @param src 源文件的路径
     * @param dest 目标文件的路径
     */
    public static void bufferedCopyFile(String src , String dest){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try{
           	//1.创建IO流
        	bis = new BufferedInputStream(new FileInputStream(src));
        	bos = new BufferedOutputStream(new FileOutputStream(dest));
        	//2.数据的读写
        	byte[] data = new byte[1024];
        	int length ;
        	while ((length = bis.read(data)) != -1){
            	bos.write(data,0,length);
        	}
        	System.out.println("文件复制成功");
        	    
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //3.关闭文件
            try {
                if (bos != null)
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            
        }
    }
}

练习:

将gbk格式的文件转换为utf-8格式存储
public class GBKToUTF_8 {
    @Test
    public void test() {
        InputStreamReader isr = null;
        OutputStreamWriter osw = null;
        try {
            isr = new InputStreamReader(new FileInputStream(new File("test.txt")),"GBK");
            osw = new OutputStreamWriter(new FileOutputStream(new File("test_copy.txt")), "UTF-8");

            char[] cbuf = new char[8192];
            int len;
            while ((len = isr.read(cbuf)) != -1) {
                osw.write(cbuf, 0, len);
            }
            System.out.println("格式转换成功<GBK → UTF-8>");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (osw != null)
                    osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (isr != null)
                    isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

十、小结

  • 流的分类

  • 流的体系结构:4个抽象基类、具体的流

  • 4个文件流:FileXxxx,是最基本的处理文件的流

  • 4个缓冲流:BufferedXxxx,是处理流的一种,包裹上面提到的4个文件流,可以提高读写效率

  • 2个转换流:InputStreamReader、OutputStreamWriter

  • 2个对象流:ObjectInputStream、ObjectOutputStream,对象的序列化机制

  • 处理流的过程非常的规范!

    1)创建文件及必要的流
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(String path)));
    
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(String path));
    
    2) 读写数据的过程:  read(byte[] / char[])  ; write(byte[] / char[] , 0, len)
        
    3) 关闭资源:必须在finally中显式的声明流的close()
    
;