24Java基础之IO

字符集

标准的ASCII字符集

  • ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文,符号等。
  • 标准ASCII使用1个字节存储一个字符,首位是0,总共可表示128个字符,对美国佬来说完全够用。
    image

GBK(汉字内码拓展规范,国际)

  • 汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储。
  • 注意:GBK兼容了ASCII字符集。
  • GBK规定汉字的第一个字节的第一位必须是1.

Unicode字符集(统一码,也叫万国码)

  • UTF-32,每四个字节表示一个字符。

UTF-8

  • 是Unicode字符集的一种编码方案,采取可变长编码方案,共分4个长度区:1个字节,2个字节,3个字节,4个字节。
  • 英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节。
    image
    注意:
  • 技术人员在开发时都应该使用UTF-8编码。
  • 字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。
  • 英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码。

字符集的编码、解码操作

  • 编码:把字符按照指定字符集编码成字节。
  • 解码:把字节按照指定字符集解码成字符。

Java代码完成对字符的编码
image
Java代码完成对字符的解码
image

//目标:掌握字符集的编码和解码
public class CharSetDemo1 {
    public static void main(String[] args) throws Exception {
        String info = "我在公司当牛马";

        //1. 编码成字节
        byte[] datas = info.getBytes();//默认用的平台编码,utf-8
        System.out.println(Arrays.toString(datas));

        byte[] datas1 = info.getBytes("GBK");//默认用的平台编码,utf-8
        System.out.println(Arrays.toString(datas1));

        //2. 解码成字符串
        String rs = new String(datas);  //默认用的平台解码,utf-8
        System.out.println(rs);

        String rs1 = new String(datas1, "GBK"); //指定解码方式,GBK
        System.out.println(rs1);
    }
}

IO流概述

I指的是input,称为输入流:负责把数据读到内存中去。
O指的是output,称为输出流:负责写数据出去。

IO流的应用场景

image

怎么学IO流?

  1. 先搞清楚IO流的分类、体系。
  2. 再挨个学习每个IO流的作用、用法。

IO流的分类

image
image

IO流总体来看就是有四大流

  • 字节输入流、字节输出流、字符输入流、字符输出流

总结流的四大类:

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

IO流的体系

image

FileInputStream(文件字节输入流)

  • 作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
    image
//目标:掌握字节流输入每次读取一个字节的形式
public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        //1. 创建文件字节输入流管道与目标文件接通
        InputStream in = new FileInputStream("day09-io-code\\src\\test.txt");
        //2. 读取一个字节,如果读取不到字节,返回-1.
        int b = in.read();
        System.out.println((char)b);
        int b1 = in.read();
        System.out.println((char)b1);
        int b2 = in.read();
        System.out.println(b2);

        //3. 使用循环改进
        int c;
        while((c = in.read()) != -1){
            System.out.print((char)c);
        }

        //拓展:1. 性能差,每次一个一个字节的读取,性能差。
        //     2. 无法避免读取汉字输出乱码的问题。  
    }
}

注意事项:使用FileInputStream每次读取一个字节,读取性能差,而且无法避免读取汉字输出乱码的问题。

一次读取多个字节

//目标:掌握文件输入流每次读取多个字节。
public class FileInputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        //1. 创建一个文件直接输入流管道与源文件对接
        InputStream in = new FileInputStream("day09-io-code\\src\\test.txt");
      /*  //2. 定义一个字节数组,用于存储读取到的字节。
        byte[] buf = new byte[3];
        //3. 调用输入流对象的read方法,读取字节。
        int len = in.read(buf);
        System.out.println("字节长度为:" +len + "读取内容为:" +  (new String(buf)));
        //读取多少就倒出多少
        int len2 = in.read(buf);
        System.out.println(new String(buf));*/

        //4. 使用循环改进
        byte[] buf = new byte[3];
        int len;
        while((len = in.read(buf)) != -1){
            String str = new String(buf, 0, len);
            System.out.print(str);
        }

        //拓展:1. 性能是比较好的!缺点:无法避免读取汉字输出乱码的问题。
    }
}

注意事项:使用FileInputstream每次读取多个字节,读取性能得到提升,但依然无法避免读取汉字输出乱码问题。

使用字节流读取中文,如何保证输出不乱码问题呢?

  • 定义一个与文件一样大的字节数组,一次性读完文件的全部内容。
    image
//目标:学会如何使用文件字节流一次性读取完文本的全部字节。
public class FileInputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        //1. 创建一个文件字节输入流管道与源文件连通
        InputStream in = new FileInputStream("day09-io-code\\src\\test.txt");
       /* //2. 定义一个字节数组
        File file = new File("day09-io-code\\src\\test.txt");
        long size = file.length();
        System.out.println("文件的大小是:" + size);
        byte[] buf = new byte[(int) size];
        //3. 调用流对象的读取方法,把数据一次性读取到字节数组中
        int len = in.read(buf);
        System.out.println(new String(buf));
        System.out.println(len);*/

        byte[] buf = in.readAllBytes();
        System.out.println(new String(buf));
    }
}
  • 如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
  • 读写文本内容更适合用:字符流。
  • 字节流:更适合做数据的转移,如文件复制等。

FileOutputStream(文件字节输出流)

  • 作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。
    image
    案例
//目标:文件字节输出流的使用
public class FileOutputStreamDemo04 {
    public static void main(String[] args) throws Exception {
        //1. 创建一个文件字节输出流管道与目标文件连通
//        OutputStream out = new FileOutputStream("day09-io-code\\src\\test1.txt"); // 这是一个覆盖管道
        OutputStream out = new FileOutputStream("day09-io-code\\src\\test1.txt", true); // 这是一个追加管道

        //2. 开始写字节数据出去
        // public void write(int a):每次写出去一个字节
        out.write('a');
        out.write(98);
        out.write('郭'); //会乱码,因为一个中文是三个字节,它只写一个
        out.write("\r\n".getBytes());

        // public void wirte(bute[] buffer):每次写一个字节数组的数据出去
        // 参数1:字节数组
        // 参数2:数组的起始索引
        // 参数3:写出去的字节个数
        byte[] buf = "我是中国人,我爱我的祖国!".getBytes();
        out.write(buf, 0, 15);
        out.write("\r\n".getBytes());

        //3. 关闭流
//        out.flush();//刷新缓存中的数据到磁盘文件中去。
        out.close(); //关闭管道,包含刷新
        //io流管道属于系统资源,会占用内存和相应的IO资源。
        // 用完之后要关闭管道,释放占用的系统资源
    }
}

案例:复制文件

//目标:复制文件
public class CopyTest05 {
    public static void main(String[] args) throws Exception {
        try{
            //1. 创建字节流管道与源文件接通
            InputStream in = new FileInputStream("D:\\java_project\\resource\\test.jpg");

            //2.创建一个字节输出流管道与目标文件接通
            OutputStream out = new FileOutputStream("D:\\java_project\\resource\\test_bak.jpg");

            //3. 创建一个字节数组
            byte[] buf = new byte[1024];


            //4. 转移数据
            int len;
            while((len = in.read(buf))!= -1){
                out.write(buf, 0, len);
            }

            //5.释放资源
            out.close();
            in.close();

            System.out.println("复制完成!");
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 任何文件的底层都是字节,字节流做复制,是一个字不漏的转移完全部字节,只要复制后的文件格式一致就没问题。

释放资源的方式

  1. try-catch-finally
    image
  • finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
  • 作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)

案例

//目标:认识finally的作用。try后或者catch后,最终一定要跑一次,除非JVM崩溃。
public class FinallyDemo01 {
    public static void main(String[] args) {
        try{
            System.out.println(10/0);
        }
        catch (Exception e){
            e.printStackTrace();
        }
        finally{
            System.out.println("finally");
        }
    }

    public static int divide(){
        try{
            return 10/2;
        }
        catch (Exception e){
            e.printStackTrace();
            return -1;
        }
        finally{
            System.out.println("finally");
            return 100; //如果finally中也有return,会覆盖try或catch中的return。
        }
    }
}

改良复制文件案例

//目标:复制文件
public class FinallyDemo02 {
    public static void main(String[] args){
        InputStream in = null;
        OutputStream out = null;

        try{
            //1. 创建字节流管道与源文件接通
            in = new FileInputStream("D:\\java_project\\resource\\test.jpg");

            //2.创建一个字节输出流管道与目标文件接通
            out = new FileOutputStream("D:\\java_project\\resource\\test_bak.jpg");

            //3. 创建一个字节数组
            byte[] buf = new byte[1024];


            //4. 转移数据
            int len;
            while((len = in.read(buf))!= -1){
                out.write(buf, 0, len);
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        finally{
            //5.释放资源
            try{
                if(out != null) out.close();
            }catch (Exception e){
                e.printStackTrace();
            }

            try{
                if(in!= null) in.close();
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("复制完成!");
        }
    }
}

JDK7开始提供了更简单的资源释放方案:try-with-resource
image

  • 该资源使用完毕后,会自动调用其close()方法,完成对资源的释放。
    • ()中只能放置资源,否则报错。
    • 什么是资源呢?
    • 资源一般指的是最终实现了AutoCloseable接口。
      image

案例

//目标:JDK7开始的资源释放的新方式:try-with-resource
public class FinallyDemo03 {
    public static void main(String[] args){
        try(//这里只能防止资源对象,用完后会自动调用资源的close()方法,释放资源
                //1. 创建字节流管道与源文件接通
            InputStream in = new FileInputStream("D:\\java_project\\resource\\test.jpg");
            //2.创建一个字节输出流管道与目标文件接通
                OutputStream out = new FileOutputStream("D:\\java_project\\resource\\test_bak.jpg");
            MyConn conn = new MyConn();
        ){
            //3. 创建一个字节数组
            byte[] buf = new byte[1024];


            //4. 转移数据
            int len;
            while((len = in.read(buf))!= -1){
                out.write(buf, 0, len);
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    static class MyConn implements AutoCloseable{
        @Override
        public void close() throws Exception {
            System.out.println("我的资源被自动关闭了");
        }
    }
}
posted @ 2025-09-12 09:20  狂风将军  阅读(11)  评论(0)    收藏  举报