IO流

字符集

常见字符集介绍

字符集基础知识:

  • 计算机底层不可以直接存储字符。计算机中底层只能存储二进制(0、1)。
  • 二进制是可以转换为十进制的
  • 结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。

ASCII字符集:

  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。
  • ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说是够用的。

GBK

  • Windows系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体字以及部分日韩文字。
  • 注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。

Unicode码表:

  • Unicode(又称统一码、万国码、单一码) 是计算机科学领域里的一项业界字符编码标准。
  • 容纳世界上大多数国家的所有常见文字和符号。
  • 由于Unicode会先通过UTF-8,UTF-16,以及UTF-32的编码成二进制后再存储到计算机,其中最常见的就是UTF-8。

注意:

  • Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储
  • UTF-8也要兼容ASCII编码表。
  • 技术人员都应该使用UTF-8的字符集编码。
  • 编码前和编码后的字符集需要一致,否则会出现中文乱码。

总结:

  1. 字符串常见的字符底层组成是什么样的?
  • 英文和数字等在任何国家的字符集中都占1个字节
  • GBK字符中一个中文字符占2个字节
  • UTF-8编码中一个中文字符一般占3个字节
  1. 编码前的字符集和编码好的字符集有什么要求?
  • 必须一致,否则会出现中文字符乱码
  • 英文和数字在任何国家的编码中都不会乱码

字符集编码和解码操作

String编码

方法名称 说明
byte[] getBytes() 使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName) 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

String解码

构造器 说明
String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[ ] bytes, String charsetName) 通过指定的字符集解码指定的字符数组来构造新的String
public class Test {
    public static void main(String[] args) throws Exception {
        //1.把文字转换成字节(使用指定的编码)
        String name = "abc我爱你中国";
        byte[] bytes = name.getBytes();//以默认的字符集(UTF-8)
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));

        byte[] bytes1 = name.getBytes("GBK");
        System.out.println(bytes1.length);
        System.out.println(Arrays.toString(bytes1));

        //2.解码,把字节码转换为对应的中文形式(编码前后的字符集要一致,否则乱码)
        String rs = new String(bytes);
        System.out.println(rs);

        String rs1 = new String(bytes1,"GBK");
        System.out.println(rs1);

        String rs2 = new String(bytes1);//前后编码不一致,乱码
        System.out.println(rs2);
    }
}
output:
18
[97, 98, 99, -26, -120, -111, -25, -120, -79, -28, -67, -96, -28, -72, -83, -27, -101, -67]
13
[97, 98, 99, -50, -46, -80, -82, -60, -29, -42, -48, -71, -6]
abc我爱你中国
abc我爱你中国
abc�Ұ����й�

IO流概述

IO流也称为输入、输出流,就是用来读写数据的

IO流概述

  • I表示input,是数据从硬盘文件读入到内存的过程,称之输入,负责读。
  • O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写。

IO流的分类

总结流的四大类:

  • 字节输入流:以内存为基准,来自磁盘的文件/网络中的数据以字节的形式读入到内存中去的流称之为字节输入流。
  • 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称之为字节输出流。
  • 字符输入流:以内存为基准,来自磁盘的文件/网络中的数据以字符的形式读入到内存中去的流称之为字符输入流。
  • 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络中去的流称之为字符输出流。

字节流的使用

文件字节输入流:每次读取一个字节

文件字节输入流:FileInputStream

  • 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。
构造器 说明
public FileInputStream(File file) 创建字节输入流管道与源文件对象接通
public FileInputStream(String pathname) 创建字节输入流管道与源文件路径接通
方法名称 说明
public int red() 每次读取一个字节返回,如果字节已经没有可读的返回-1
public int red(byte[] buffer) 每次读取一个字节数组返回,如果字节已经没有可读的返回-1
public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
//        1.创建一个文件字节输入流管道与源文件对象接通
//        InputStream is = new FileInputStream(new File("file-io-app1\\src\\data2.txt"));
//        简化写法
        InputStream is = new FileInputStream("src\\data2.txt");

//        //2.读取一个字节返回
//        int b1 = is.read();
//        System.out.print((char)b1);
//
//        int b2 = is.read();
//        System.out.print((char)b2);
//
//        int b3 = is.read();
//        System.out.print((char)b3);
//
//        int b4 = is.read();//读取完毕返回-1
//        System.out.print(b4);
        //循环改进
        int b;
        while ((b = is.read()) != -1) {
            System.out.print((char)b);
        }
    }
}
output:
ab2

每次读取一个字节存在什么问题?

  • 性能较慢
  • 读取中文字符输出无法避免乱码问题。

文件字节输入流:每次读取一个字节数组

public class FileInputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        //1.创建一个字节输入流管道与源文件接通
        InputStream is = new FileInputStream("src/data2.txt");

        //2.定义一个字节数组,用于读取字节数组
        byte[] buffer = new byte[3];//3B
        int len = is.read(buffer);
        System.out.println("读取了几个字节:"+len);
        String rs = new String(buffer);
        System.out.println(rs);

        int len1 = is.read(buffer);
        System.out.println("读取了几个字节:"+len1);
        String rs1 = new String(buffer);
        System.out.println(rs1);

        int len2 = is.read(buffer);
        System.out.println("读取了几个字节:"+len2);
        //读多少取多少
        String rs2 = new String(buffer,0,len2);
        System.out.println(rs2);

        int len3 = is.read(buffer);
        System.out.println("读取了几个字节:"+len3);

        //3.改进使用循环,每次读取一个字节数组
        InputStream is1 = new FileInputStream("src/data2.txt");
        byte[] buffer1 = new byte[3];
        int len11;//记录每次读取的字节数。
        while ((len11 = is1.read(buffer1)) != -1) {
            System.out.print(new String(buffer1, 0, len11));
        }
    }
}
output:
读取了几个字节:3
ab2
读取了几个字节:3
abc
读取了几个字节:2
cd
读取了几个字节:-1
ab2abccd

每次读取一个字节数组存在什么问题?

  • 读取的性能得到了提升
  • 读取中文字符输出无法避免乱码问题

文件字节输入流:一次读完全部字节

  1. 如何使用字节输入流读取中文内容输出不乱码呢?
  • 定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。
  1. 直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
  • 如果文件过大,字节数组可能引起内存溢出。
public class FileInputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        //1.创建一个文件字节输入流与源文件接通
        File f = new File("src/data3.txt");
        InputStream is = new FileInputStream(f);

        //2.定义一个字节数组与文件大小一样大
//        byte[] buffer = new byte[(int)f.length()];
//        int len = is.read(buffer);
//        System.out.println("文件大小:"+f.length());
//        System.out.println("读取了多少个字节:"+len);
//        System.out.println(new String(buffer));

        //读取全部字节数组
        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));
    }
}
output:
ab2abccd中国ab2abccd中国ab2abccd中国
ab2abccd中国
ab2abccd中国ab2abccd中国ab2abccd中国

文件字节输出流:写字节数据到文件

文件字节输出流(FileOutputStream)写数据出去的API

方法名称 说明
public void write(int a) 写一个字节出去
public void write(byte[] buffer) 写一个字节数组出去
public void write(byte[] buffer ,int pos ,int len) 写一个字节数组的一部分出去

流的关闭与刷新

方法 说明
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
public class FileOutputStreamDemo04 {
    public static void main(String[] args) throws Exception {
        //1.创建一个文件字节输出流管道与目标文件接通
//        OutputStream os = new FileOutputStream("src\\outdata.txt");//先清空之前的数据,写新数据进入
        //创建一个追加数据的字节输出流管道流向目标文件路径
        OutputStream os = new FileOutputStream("src\\outdata.txt",true);
        //2.写数据出去
        //a.public void write(int a):写一个字节出去
        os.write('a');
        os.write(98);
        os.write(99);
        os.write("\r\n".getBytes());

        //b.public void write(byte[] buffer):写一个字节数组出去
        byte[] buffer = {'a',97,98,99};
        os.write(buffer);//abcaabc
        os.write("\r\n".getBytes());

        //c.public void write(byte[] buffer,int pos, int len):写一个字节数组的一部分出去
        os.write(buffer,0,3);//abcaabcaab
        os.write("\r\n".getBytes());

        byte[] buffer2 = "我是中国人".getBytes();
        os.write(buffer2);//abcaabcaab我是中国人
        os.write("\r\n".getBytes());


        //os.flush();//写数据必须刷新数据 可以继续使用
        os.close();//释放资源,包含了刷新的,关闭后不可使用!
    }
}
output:
abc
aabc
aab
我是中国人
abc
aabc
aab
我是中国人

小结

  1. 字节输出流如何实现数据追加?
public FileOutputStream(String filepath, boolean append)
  1. 字节输出流如何实现写出去的数据能换行
  • os.write("\r\n".getBytes())
  1. 如何让写出去的数据能成功生效?
  • flush()刷新数据
  • close()方法是关闭流,关闭包含刷新,关闭流后不可以继续使用流了

文件拷贝

需求:

  • 把某个视频拷贝到其他目录下

思路:

  1. 根据数据源创建字节输入流对象
  2. 根据目的地创建字节输出流对象
  3. 读写数据,复制视频
  4. 释放资源
package com.csl.d4_byte_stream;

import java.io.*;

public class CopyDemo05 {
    public static void main(String[] args)  {
        //1.根据数据源创建字节输入流对象
        try {
            InputStream is = new FileInputStream("D:\\BaiduNetdiskDownload\\介绍.mp4");
            OutputStream  os = new FileOutputStream("D:\\code\\new.mp4");

            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer,0,len);
            }
            System.out.println("复制完成了!");
            os.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
output:
复制完成了!

资源释放的方式

try-catch-finally

  • finally :在异常处理时提供finally块来执行所有清除操作,比如IO流中的释放资源
  • 特点:被finally控制的语句最终一定会执行,除非JVM退出
  • 异常处理标准格式:try...catch..finally
package com.csl.resource;

import java.io.*;

public class TryCatchFinallyDemo01 {
    public static void main(String[] args) {
        //1.根据数据源创建字节输入流对象
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream("D:\\BaiduNetdiskDownload\\第1阶段—Java SE基础\\1、Java基础--20天学会Java\\20天学会java—视频\\day02-数据类型、运算符、API介绍、键盘录入\\01、课程总体介绍.mp4");
            os = new FileOutputStream("D:\\code\\new.mp4");

            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成了!");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null)
                    os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

try-with-resource

注意:

  • JDK 7 以及 JDK9的()中只能放置资源对象,否则报错
  • 什么是资源呢?
  • 资源都是实现了Closeable/AutoCloseable接口的类对象
public abstract class InputStream implements Closeable{}

public abstract class OutputStream implements Closeable,Flushable{}
public class TryCatchFinallyDemo02 {
    public static void main(String[] args) {
        //1.根据数据源创建字节输入流对象

        try (
                //这里只能放置资源对象,用完会自动关闭,自动调用资源的 close方法关闭资源(即使出现异常也会做关闭操作)
                //1.根据数据源创建字节输入流对象
                InputStream is = new FileInputStream("D:\\BaiduNetdiskDownload\\第1阶段—Java SE基础\\1、Java基础--20天学会Java\\20天学会java—视频\\day02-数据类型、运算符、API介绍、键盘录入\\01、课程总体介绍.mp4");
                //2.根据目的地创建字节输出流对象
                OutputStream os = new FileOutputStream("D:\\code\\new.mp4");) {
            //3.定义一个字节数组,转移数据
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成了!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

作用:自动释放资源、代码简洁

字符流的使用

文件字符输入流- 一次读取一个字符

  1. 字节流读取中文可能会存在什么问题?
  • 会乱码,或者内存溢出
  1. 读取中文输出,哪个流更合适,为什么?
  • 字符流更合适,最小单位是按照单个字符读取的
方法名称 说明
public int read() 每次读取一个字符返回,如果字节已经没有可读的返回-1
public class FileReaderDemo01 {
    public static void main(String[] args) throws Exception {
        //1.创建一个字符输入流管道与源文件接通
        Reader fr = new FileReader("src/data3.txt");

//        int code = fr.read();
//        System.out.println((char)code);
//
//        int code1 = fr.read();
//        System.out.println((char)code1);

        int code;
        while ((code = fr.read()) !=-1){
            System.out.print((char)code);
        }
        fr.close();
    }
}
output:
ab2abccd中国ab2abccd中国ab2abccd中国
ab2abccd中国
ab2abccd中国ab2abccd中国ab2abccd中国
  • 读取中文字符不会出现乱码(如果代码和文件编码一致)
  • 性能较慢

文件字符输入流- 一次读取一个字符数组

文件字符输入流:FileReader

  • 作用:以内存为基础,将磁盘文件中的数据以字符的形式读取到内存中去。
方法名称 说明
public int read(char[ ] buffer) 每次读取一个字符数组,返回读取的字符数,如果字符已经没有可读的返回-1
public class FileReaderDemo02 {
    public static void main(String[] args) throws Exception {
        //1.创建一个字符输入流对象与源文件接通
        Reader rf = new FileReader("src/data3.txt");

        //2.用循环,每次读取一个字符数组的数据
        char[] buffer = new char[1024];//1K的字符
        int len;
        while ((len = rf.read(buffer)) != -1){
            System.out.println(new String(buffer,0,len));
        }
    }
}
output:
ab2abccd中国ab2abccd中国ab2abccd中国
ab2abccd中国
ab2abccd中国ab2abccd中国ab2abccd中国

文件字符输出流

void
write(char[] cbuf)

写入一个字符数组。

abstract void
write(char[] cbuf, int off, int len)

写入字符数组的一部分。

void
write(int c)

写入单个字符。

void
write(String str)

写入一个字符串。

void
write(String str, int off, int len)

写入字符串的一部分。

缓冲流

缓冲流概述

字节缓冲流

字节缓冲流优化原理

  • 字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能比较好。
  • 字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能提高了。

字节缓冲流

  • 字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能,读写功能上并无变化。
  • 字节缓冲输出流:BufferedOutputStream,提高了字节输出流读取数据的性能,读写功能上并无变化。
构造器 说明
public BufferedInputStraem(InputStream is) 可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能
public BufferedOutputStream(OutputStream os) 可以把低级的字节输出流包装成一个高级的缓冲字节输出流管道,从而提高字节输出流写数据的性能
public class ByteBufferDemo02 {
    public static void main(String[] args)  {
        try (
                //1.创建一个低级的字节输入流管道与源文件接通
                InputStream is = new FileInputStream("file-io-app/src/data.txt");
                //a.包装字节输出流为缓冲字节输出流
                BufferedInputStream bis = new BufferedInputStream(is);
                //2.创建一个字节输出流管道与目的文件接通
                OutputStream os = new FileOutputStream("file-io-app/src/data3.txt");
                //b.包装字节输出流为缓冲字节输出流
                BufferedOutputStream bos = new BufferedOutputStream(os);
                ){
            //3.创建一个字节数组来转移数据
            byte[] buffer = new byte[1024];
            int len;//记录每次读取的字节数
            while ((len = bis.read(buffer))!=-1){
                bos.write(buffer,0,len);
            }
                System.out.println("写入成功!");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  1. 字节缓冲流为什么提高了操作数据的性能?
  • 字符缓冲流自带了8KB的缓冲区
  • 可以提高原始字节流、字符流读写数据的性能

字节缓冲流的性能分析

需求:

  • 分别使用低级字节流和高级字节流缓冲流拷贝文件,记录耗时
package com.csl.d1_byte_buffer;

import java.io.*;

public class ByteBufferTimeDemo {
    public static final String SRC_FILE = "E:\\picture\\srcFile.mp4";
    public static final String DEST_FILE = "E:\\picture\\";

    public static void main(String[] args) {
        copy01();//很长时间
//        copy02();//字节流一个一个数组复制耗时:1.216s
//        copy03();//字节缓冲流一个一个复制完成,用时:4.18s
//        copy04();//字节缓冲流一个一个数组复制耗时:0.237s
    }

    //4.缓冲流一个一个数组复制
    private static void copy04() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream("E:\\picture\\srcFile.mp4");
                BufferedInputStream bis = new BufferedInputStream(is);
                OutputStream os = new FileOutputStream("E:\\picture\\new4.mp4");
                BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {
            //定义一个字节数组来转移数据
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("字节缓冲流一个一个数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    //3.缓冲流一个一个字节复制
    private static void copy03() {
        long stratTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream("E:\\picture\\srcFile.mp4");
                BufferedInputStream bis = new BufferedInputStream(is);
                OutputStream os = new FileOutputStream("E:/picture/new3.mp4");
                BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {

            int len;
            while ((len = bis.read()) != -1) {
                bos.write(len);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("字节缓冲流一个一个复制完成,用时:" + (endTime - stratTime) / 1000.0 + "s");
    }

    //2.使用低级的字节流按照一个一个字节数组的形式来复制文件
    private static void copy02() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream("E:\\picture\\srcFile.mp4");
                OutputStream os = new FileOutputStream("E:\\picture\\new2.mp4");
        ) {
            //定义一个字节数组来转移数据
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("字节流一个一个数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");//字节流一个一个数组复制耗时:1.216s

    }

    //1.使用低级的字节流一个一个字节的形式复制文件
    private static void copy01() {
        long stratTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream("E:\\picture\\srcFile.mp4");
                OutputStream os = new FileOutputStream("E:/picture/new1.mp4");
        ) {

            int len;
            while ((len = is.read()) != -1) {
                os.write(len);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("字节流一个一个复制完成,用时:" + (endTime - stratTime) / 1000.0 + "s");
    }
}
  • 建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前是性能最优组合!

字符缓冲流

字符缓冲输入流

  • 字符缓冲输入流:BufferedReader
  • 作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能
构造器 说明
public BufferedReader(Reader r) 可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能

字符缓冲输入流新增功能

方法 说明
public String readLine() 读取一行数据返回,如果读取没有完毕,无行可读返回null
package com.csl.d2_char_buffer;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class BufferedReaderDemo01 {
    public static void main(String[] args) {
        try(
                //1.创建一个字符输入流管道与目标文件接通
                Reader rf = new FileReader("file-io-app/src/data.txt");
                //包装成字符缓冲流
                BufferedReader brf = new BufferedReader(rf);
                ) {
//            char[] buffer = new char[1024];//1K字符
//            int len;
//            while ((len = brf.read(buffer))!=-1){
//                String rs = new String(buffer,0,len);
//                System.out.print(rs);
//            }
//            System.out.println(brf.readLine());
//            System.out.println(brf.readLine());
//            System.out.println(brf.readLine());
//            /*龙agkahg124我是中国人gjh2
//            龙agkahg124我是中国人gjh2龙agkahg124我是中国人gjh2
//            null*/
            String line;
            while ((line=brf.readLine())!=null){
                System.out.println(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
output:
龙agkahg124我是中国人gjh2
龙agkahg124我是中国人gjh2龙agkahg124我是中国人gjh2

字符缓冲输出流

  • 字符缓冲输出流:BufferedWriter
  • 作用:提高字符输出流写数据的性能,除此之外多了换行功能
构造器 说明
public BufferedWriter(Writer w) 可以把低级的字符输出流包装成一个高级的字符缓冲输出流,从而提高字符输出流写数据的性能
方法 说明
public void newLine() 换行操作
public class BufferedWriterDemo {
    public static void main(String[] args) {
       try(
               //1.创建一个字符输出流管道与源文件接通
//               Writer fw = new FileWriter("file-io-app/src/write.txt");//每次新数据会覆盖原数据
               Writer fw = new FileWriter("file-io-app/src/write.txt",true);
               BufferedWriter bw = new BufferedWriter(fw);

               ) {
           //a.public void write(int c):写一个字符出去
           bw.write(98);
           bw.write('a');
           bw.write('陈');
           bw.write("\r\n");//换行

           //b,public void write(String c):写一个字符串出去
           bw.write("abc我是中国人!");
           bw.newLine();

           //c.public void write(char[] buffer):写一个字符数组出去
           char[] chars = "我是中国人csl".toCharArray();
           bw.write(chars);
           bw.newLine();

           //d.public void write(String c, int pos, int len):写字符串的一部分出去
           bw.write(chars,0,6);
           bw.write("\r\n");

       }catch (Exception e){
           e.printStackTrace();
       }
    }
}
output:
ba陈
abc我是中国人!
我是中国人csl
我是中国人c
ba陈
abc我是中国人!
我是中国人csl
我是中国人c

案例:拷贝出师表到另一个文件,恢复数据

分析:

  1. 定义一个缓存字符输入流管道与源文件接通
  2. 定义一个List集合存储读取的每行数据
  3. 定义一个循环按照行读取数据,存入到List集合中去
  4. 对List集合中的每行数据按照首字母符号升序排序
  5. 定义一个缓存字符输出流管道与目标文件接通
  6. 遍历List集合中的每个元素,用缓冲输出流管道写出并执行
package com.csl.d2_char_buffer;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class BufferedTestDemo {
    public static void main(String[] args) {
        try(
                //1.定义一个缓存字符输入流管道与源文件接通
//                Reader rf = new FileReader("E:\\picture\\出师表.txt");
                BufferedReader br = new BufferedReader(new FileReader("E:\\picture\\出师表.txt"));
                //5.定义一个缓存字符输出流管道与目标文件接通
                BufferedWriter bos = new BufferedWriter(new FileWriter("E:\\picture\\新出师表.txt"))

                ) {
            //2.定义一个List集合存储读取的每行数据
            List<String> data = new ArrayList<>();
            //3.定义一个循环按照行读取数据,存入到List集合中去
            String len;
            while ((len=br.readLine())!=null){
                data.add(len);
            }
            System.out.println(data);
//            //4.对List集合中的每行数据按照首字母符号升序排序
//            Collections.sort(data);
//            System.out.println(data);
            //4.自定义排序
            List<String> sizes = new ArrayList<>();
            Collections.addAll(sizes,"一","二","三","四","五","六","七","八","九","十","十一","十二");
            Collections.sort(data, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return sizes.indexOf(o1.substring(0,o1.indexOf(".")))-sizes.indexOf(o2.substring(0,o2.indexOf(".")));
                }
            });
            System.out.println(data);
            //6.遍历List集合中的每个元素,用缓冲输出流管道写出并执行
            for (String datum : data) {
                bos.write(datum);
                bos.newLine();
            }
            System.out.println("写入成功!");

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
output:
[九.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。, 三.诚宜开张圣听,以光先帝遗德,恢弘志士之气;不宜妄自菲薄,引喻失义,以塞忠谏之路也。, 四.宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科,及为忠善者,宜付有司,论其刑赏,以昭陛下平明之治;, 六.愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。, 二.然侍卫之臣,不懈於内;忠志之士,忘身於外者:盖追先帝之殊遇,欲报之于陛下也。, 七.将军向宠,性行淑均,晓畅军事,试用之於昔日,先帝称之曰“能”,是以众议举宠为督;, 八.愚以为营中之事,事无大小,悉以咨之,必能使行阵和穆,优劣得所也。, 五.不宜偏私,使内外异法也。侍中、侍郎郭攸之、费依、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:, 十.先帝在时,每与臣论此事,未尝不叹息痛恨於桓、灵也!侍中、尚书、长史、参军,此悉贞亮死节之臣也,愿陛下亲之、信之,则汉室之隆,可计日而待也。, 十一.臣本布衣,躬耕南阳,苟全性命於乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣於草庐之中,谘臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任於败军之际,奉命於危难之间:尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。, 十二.受命以来,夙夜忧虑,恐付托不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,甲兵已足,当奖帅三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还於旧都:此臣所以报先帝而忠陛下之职分也。至於斟酌损益,进尽忠言,则攸之、依、允等之任也。愿陛下托臣以讨贼兴复之效,不效则治臣之罪,以告先帝之灵;, 一.臣亮言:先帝创业未半,而中道崩殂;今天下三分,益州疲敝,此诚危急存亡之秋也。]
[一.臣亮言:先帝创业未半,而中道崩殂;今天下三分,益州疲敝,此诚危急存亡之秋也。, 二.然侍卫之臣,不懈於内;忠志之士,忘身於外者:盖追先帝之殊遇,欲报之于陛下也。, 三.诚宜开张圣听,以光先帝遗德,恢弘志士之气;不宜妄自菲薄,引喻失义,以塞忠谏之路也。, 四.宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科,及为忠善者,宜付有司,论其刑赏,以昭陛下平明之治;, 五.不宜偏私,使内外异法也。侍中、侍郎郭攸之、费依、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:, 六.愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。, 七.将军向宠,性行淑均,晓畅军事,试用之於昔日,先帝称之曰“能”,是以众议举宠为督;, 八.愚以为营中之事,事无大小,悉以咨之,必能使行阵和穆,优劣得所也。, 九.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。, 十.先帝在时,每与臣论此事,未尝不叹息痛恨於桓、灵也!侍中、尚书、长史、参军,此悉贞亮死节之臣也,愿陛下亲之、信之,则汉室之隆,可计日而待也。, 十一.臣本布衣,躬耕南阳,苟全性命於乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣於草庐之中,谘臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任於败军之际,奉命於危难之间:尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。, 十二.受命以来,夙夜忧虑,恐付托不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,甲兵已足,当奖帅三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还於旧都:此臣所以报先帝而忠陛下之职分也。至於斟酌损益,进尽忠言,则攸之、依、允等之任也。愿陛下托臣以讨贼兴复之效,不效则治臣之罪,以告先帝之灵;]
写入成功!

转换流

字符输入转换流

  • 字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转成字符输入流
构造器 说明
public InputStreamReader(InputStream is) 可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样。
public InputStreamReader(InputStream is , String charset) 可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不会乱码了
package com.csl.d3_transfer_stream;

import java.io.*;

public class InputStreamReaderDemo {
    public static void main(String[] args) throws Exception {
        //代码是UTF-8 文件是GBK "E:\\picture\\GBK.txt"
        //1.提取GBK文件的原始字节流
        InputStream is = new FileInputStream("E:\\picture\\GBK.txt");
        //把原始字节流转换为字符流
//        Reader rf = new InputStreamReader(is);//默认是以UTF-8的方式转换成字符流,还是会乱码
        Reader rf = new InputStreamReader(is,"GBK");//以指定GBK的编码转换为字符流

        BufferedReader br = new BufferedReader(rf);
        String line;
        while ((line=br.readLine())!=null){
            System.out.print(line);
        }
        br.close();
    }
}
output:
ahgoiahgaH我是中国人我骄傲我自豪
  1. 字符输入转换流(InputStreamReader)作用:
  • 可以解决字符流读取不同编码乱码的问题
  • public InputStreamReader(InputStream is,String charset)

字符输出转换流

  1. 如果需要控制写出去的字符使用的编码,怎么办?
    • 可以把字符以指定的编码获取字节后再使用字节输出流写出去:
      • "我爱你中国".getBytes(编码)
    • 也可以使用字符输出转换流实现。

字符输出转换流

  • 字符输出转换流:OutputStreamWriter,可以把字节输出流按照指定的编码转换成字符输出流。
构造器 说明
public OutputStream(OutputStream os) 可以把原始的字节输出流按照代码默认编码转换成字符输出流。几乎不用
public OutputStream(OutputStream os ,String charset) 可以把原始的字节流按照指定编码转换成字符输出流
package com.csl.d3_transfer_stream;

import java.io.*;

public class OutputStreamWriterDemo {
    public static void main(String[] args) throws Exception {
        //1.定义一个字节输出流
        OutputStream os = new FileOutputStream("file-io-app/src/write1.txt");

        //2.把原始字节流转换为字符输出流
        Writer w = new OutputStreamWriter(os,"GBK");//指定GBK的方式写出去

        //3.把字符输出流包装成高级的缓冲字符输出流
        BufferedWriter bw = new BufferedWriter(w);
        bw.write("我爱中国1");
        bw.write("我爱中国2");
        bw.write("我爱中国3");

        bw.close();
    }
}
output;
�Ұ��й�1�Ұ��й�2�Ұ��й�3

序列化对象

创建序列化对象

public class Student implements Serializable {
    private static final long serialVersionUID = 1;
    private String name;
    private String loginName;
    private int age;
    private transient double salary;

    public Student() {
    }

    public Student(String name, String loginName, int age, double salary) {
        this.name = name;
        this.loginName = loginName;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

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

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public int getAge() {
        return age;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", loginName='" + loginName + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

对象序列化

对象序列化

  • 作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化。
  • 使用到的流是对象字节输出流:ObjectOutputStream
构造器 说明
public ObjectOutputStream(OutputStream out) 把低级字节输出流包装成高级的对象字节输出流
public class ObjectOutputStreamDemo {
    public static void main(String[] args) throws Exception {

        //1.创建学生对象
        Student s = new Student("陈太帅","csl",23,20000);
        //2.对象序列化,使用对象字节输出流包装字节输出流管道
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file-io-app/src/obj.txt"));
        //3、调用序列化方法
        oos.writeObject(s);
        System.out.println("对象序列化成功!");
    }
}

1.对象序列化的含义:

  • 把对象数据存入到文件中去。

2.对象序列化用到了哪个流?

  • 对象字节输出流ObjectOutputStream
  1. 序列化对象的要求是怎么样的?
  • 对象必须实现序列化接口

对象反序列化

  • 使用的流是对象字节输入流:ObjectInputStream
  • 作用:以内存为基础准,把存储到磁盘中的对象数据恢复成内存中的对象,称为对象反序列化
构造器 说明
public ObjectInputStream(InputStream out) 把低级字节输入流包装成高级的对象字节输入流

ObjectInputStream序列化方法

方法名称 说明
public Object readObject() 把存储到磁盘文件中的对象恢复成内存中的对象返回
public class ObjectInputStreamDemo {
    public static void main(String[] args) throws Exception {
        //1.包装低级的字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file-io-app/src/obj.txt"));

        //2.直接调用序列化方法来
        Student s = (Student) ois.readObject();
        System.out.println(s);
    }
}
output:
Student{name='陈太帅', loginName='csl', age=23, salary=0.0}
  • transient修饰的成员变量不参与序列化
  • 对象如果要实现序列化,必须实现Serializable序列化接口
  • 序列化版本号必须要与反序列化版本号一致

打印流

printStream、printWriter

打印流

  • 作用:打印流可以实现方便、高效的打印数据到文件中去。打印一般是指:printStream,printWriter两个类
  • 可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true

printStream

构造器 说明
public PrintStream(OutputStream os) 打印流直接通向字节输出流管道
public PrintStream(File f) 打印流直接通向文件对象
public PrintStream(String filePath) 打印流直接通向文件路径
方法 说明
public void print(XXX XX) 打印任意类型的数据出去
public class PrintDemo01 {
    public static void main(String[] args) throws Exception {
        //1.创建一个打印流对象
        PrintStream ps = new PrintStream(new FileOutputStream("file-io-app/src/ps.txt",true));
//        PrintStream ps = new PrintStream("file-io-app/src/ps.txt");

        ps.println(97);
        ps.println('a');
        ps.println(22.3);
        ps.println(true);
        ps.println("我是中国人");

        ps.close();
    }
}

printWriter

构造器 说明
public PrintWriter(OutputStream os) 打印流直接通向字节输出流管道
public PrintWriter(Writer w) 打印流直接通向字符输出流管道
public PrintWriter(File f) 打印流直接通向文件对象
public PrintWriter(String filePath) 打印流直接通向文件路径
方法 说明
public void print(XXX XX) 打印任意类型的数据出去
public class PrintDemo01 {
    public static void main(String[] args) throws Exception {
        //1.创建一个打印流对象
//        PrintStream ps = new PrintStream(new FileOutputStream("file-io-app/src/ps.txt",true));
//        PrintStream ps = new PrintStream("file-io-app/src/ps.txt");
        PrintWriter ps = new PrintWriter("file-io-app/src/ps.txt");//打印功能上没有区别
        ps.println(97);
        ps.println('a');
        ps.println(22.3);
        ps.println(true);
        ps.println("我是中国人");
        ps.write("哈哈哈哈");
        ps.close();
    }
}
output:
97
a
22.3
true
我是中国人
哈哈哈哈

printStream和printWriter的区别

  • 打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势,因为源码里包装类缓冲字符流)
  • printStream继承自字节输出流OutputStream,支持字节写数据的方法
  • printWriter继承自字符输出流Writer,支持字符写数据出去

输出语句的重定向

  • 属于打印流的一种应用,可以把输出语句的打印位置改到文件
public class PrintDemo02 {
    public static void main(String[] args) throws Exception {
        System.out.println("赵今麦");
        System.out.println("白敬亭");

        //改变输出语句的位置(重定向)
        PrintStream ps = new PrintStream("file-io-app/src/log.txt");
        System.setOut(ps);

        System.out.println("开端");
        System.out.println("猫之使徒,哮喘征服者");
        System.out.println("被光选中的男人");
    }
}

Properties

Properties属性集对象

  • 其实就是一个Map集合,但是我们一般不会当集合使用,因为HashMap更好用

Properties核心作用:

  • Properties代表的是一个属性文件 ,可以把自己对象中的键值对信息存入到一个属性文件中。
  • 属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value,后续做系统配置信息的。

Properties的API

  • Properties和IO流结合的方法:
构造器 说明
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store(OutputStream out,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader reader)方法的格式写入输出字符流
public Object setProperty(String key, String value) 保存键值对(put)
public String getProperty(String key) 使用此属性列表中指定的键搜索属性值(get)
public Set stringPropertyNames() 所有键的名称的集合(keySet())
public class PropertiesDemo01 {
    public static void main(String[] args) throws Exception {
        //需求:使用properties把键值对信息存入到属性文件中去
        Properties properties = new Properties();
//        properties.put("admin","12345");
        properties.setProperty("admin","12345");
        properties.setProperty("csl","666");
        properties.setProperty("try","again");
        System.out.println(properties);//{admin=12345, try=again, csl=666}

        /**
            参数一:保存管道    字符输出流管道
            参数二:保存心得,可不写
         */
        properties.store(new FileWriter("file-io-app/src/users.properties")
                ,"this is my first use properties");
    }
}
output:
#this is my first use properties
#Fri Jan 21 20:14:41 CST 2022
admin=12345
try=again
csl=666
public class PropertiesDemo02 {
    public static void main(String[] args)throws Exception {
        //需求:Properties读取属性文件中的键值对信息。(读取)
        Properties properties = new Properties();
        System.out.println(properties);

        //加载属性文件中的键值对数据到属性对象properties中去
        properties.load(new FileReader("file-io-app/src/users.properties"));
        System.out.println(properties);
        String rs = properties.getProperty("csl");
        System.out.println(rs);
        String rs1 = properties.getProperty("admin");
        System.out.println(rs1);

        Set<String> set = properties.stringPropertyNames();
        System.out.println(set);

    }
}
output:
{}
{admin=12345, try=again, csl=666}
666
12345
[admin, try, csl]

1.Properties的作用?

  • 可以存储Properties属性集的键值对数据到属性文件中去:
    • void store(Writer writer,String comments)
  • 可以加载属性文件中的数据到Properties对象中来:
    • void load(Reader reader)

IO框架

commons-io概述

  • commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
  • commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils,IOUtils
方法名 说明
String readFileToString(File file, String encoding) 读取文件中的数据,返回字符串
void copyFile(File srcFile , File destFile) 复制文件
void copyDirectoryToDirectory(File srcDir,File destDir) 复制文件夹
  1. 在项目中创建一个文件夹:lib
  2. 将commons-io-2.6.jar文件复制到lib文件夹
  3. 在jar文件上右键,选择 Add as Libray -> 点击OK
  4. 在类中导包使用
package com.csl.d7_commons_io;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class CommonsIODemo {
    public static void main(String[] args)throws Exception {
        //1.完成文件的复制
//        IOUtils.copy(new FileInputStream("file-io-app\\src\\data.txt"),
//                new FileOutputStream("D:/code/new.txt"));

        //2.完成文件复制到某个文件夹下
//        FileUtils.copyFileToDirectory(new File("file-io-app\\\\src\\\\data.txt")
//                ,new File("D:/"));
        //3.完成文件夹复制到文件夹下。
//        FileUtils.copyDirectoryToDirectory(new File("E:\\picture")
//                ,new File("D:/"));
        //4.删除文件夹,全部删除
//        FileUtils.deleteDirectory(new File("D:/picture"));

        // JDK 1.7自己也做了一些一行代码完成复制的操作:New IO技术
        Files.copy(Path.of("file-io-app\\src\\data1.txt"),Path.of("D:\\datenew.txt"));
    }
}
posted @ 2022-01-21 21:00  tryAgainCs  阅读(57)  评论(0编辑  收藏  举报