当前位置: 首页 > news >正文

JAVA入门——IO流

一、了解File类

  • 这个类里面提供了一些文件相关的方法,了解即可,方法有很多,不好背
  • 下面这个是最常用的
  • 只能对文件本身操作,不能读取数据

public File[] listFiles();//获取当前路径下的所有内容

注意:如果是需要权限才能访问的文件夹,那么会返回null

二、初识IO流以及四个基本流

  • 为了解决数据的存储与读取问题

IO流的分类

按流的方向分:


  • 输入流:文件→程序
  • 输出流:程序→文件

按操作文件的类型分:


  • 字节流:操作所有类型文件
    • InputStream:字节输入流
    • OutputStream:字节输出流
  • 字符流:操作纯文本文件
    • Reader:字符输入流
    • Writer:字节输出流
  • 上面四个都是顶层的抽象类

初识字节输出流:


  • 1.创建字节输出流对象
    • 参数是字符串表示的路径或者FIle对象都是可以的
    • 如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
    • 如果文件已经存在,会清空文件
  • 2.写数据
    • 传的是整数,实际上写到文件里是ASCII码对应的值
  • 3.释放资源
    • 结束资源的占用,不然其他人无法操作这个文件
public class Demo1 {

    public static void main(String[] args) throws IOException {

        FileOutputStream fos = new FileOutputStream("src\\a.txt");
        fos.write(97);
        fos.close();
    }

}

1.FileOutputStream写数据的三种方式:

  •  例:
public class Demo1 {

    public static void main(String[] args) throws IOException {

        FileOutputStream fos = new FileOutputStream("a.txt");
        byte[] arr = {97,98,99,100,101};
        //1.
//        fos.write(arr);
//        fos.close();
        //2.
        //参数:数组,起始索引,要写入的长度(个数)
        fos.write(arr,1,2);
        fos.close();
    }
}

2.写数据的两个问题:

  • 换行:
    • 注意不同操作系统换行符不同
public class Demo1 {

    public static void main(String[] args) throws IOException {

        FileOutputStream fos = new FileOutputStream("a.txt");
        String str = "1145141919810";
        byte[] arr = str.getBytes();
        fos.write(arr);

        //换行符
            //windows:\r\n 回车换行
            //Linux:\n
            //Mac:\r
        //Java中进行了优化,我们写不全也能自动补全
        String wrap = "\r\n";
        byte[] arr2 = wrap.getBytes();
        fos.write(arr2);

        String str2 = "wsnd";
        byte[] arr3 = str2.getBytes();
        fos.write(arr3);
        

        fos.close();

    }
}
  • 不删除原来数据:
    • 在创建对象时,第二个参数传ture,表示打开续写开关,他默认是false
FileOutputStream fos = new FileOutputStream("a.txt",true);

初识字节输入流:


1.步骤:和字节输出流基本一致,一定注意要释放资源

public class Demo2 {

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");

        int b1 = fis.read();//会一个一个读取,如果读取不到,返回-1
        System.out.println((char)b1);
    }
}

2.细节:

  • 创建对象:
    • 如果文件不存在,就直接报错
  • 读取数据:
    • 一次读取一个字节,读出来的是数据对应的ASCII码值
    • 读到文件末尾时,read方法返回-1
  • 释放资源:
    • 每次读取完毕,必须释放资源
    • 先开的后关闭

3.FileInputStream循环读取:(拷贝练习)(这里不要写太大的文件,这个方法拷贝很慢)

public class Demo2 {

    public static void main(String[] args) throws IOException {

        //拷贝练习
        FileInputStream fis = new FileInputStream("D:\\My_study_resoure\\概率论\\1-3 随机事件的概率(1).pdf");
        FileOutputStream fos = new FileOutputStream("copy.pdf");

        int b;
        while((b = fis.read()) != -1){
            fos.write(b);
        }
        //规则:先开的最后关闭
        fos.close();
        fis.close();
    }
}

4.读取优化,一次读取多个数据:

  • 每次读取会尽可能把数组装满
    • 建议数组大小为1024的整数倍
  • 返回值表示这次读取了多少个字节的数据
  • 注意:每次读取到数组中,不会清除上一次的数据,如果读入的比上次读入的少,可能会多打印出来其他的值

public int read(byte[] buffer)

 拷贝代码优化:

public class Demo2 {

    public static void main(String[] args) throws IOException {

        //拷贝练习
        FileInputStream fis = new FileInputStream("D:\\My_study_resoure\\概率论\\1-3 随机事件的概率(1).pdf");
        FileOutputStream fos = new FileOutputStream("copy.pdf");

        int len;
        byte[] bytes = new byte[1024 * 1024 * 5];
        while((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);//一定不能读取数组中的所有的数据
        }
        fos.close();
        fis.close();
    }
}

try..catch异常处理:


1.认识finally

finally{

        //里面的代码一定被执行,除非虚拟机停止

        //适合把资源释放写在这里面

}

public class Demo2 {

    public static void main(String[] args) {


        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //拷贝练习
            fis = new FileInputStream("D:\\My_study_resoure\\概率论\\1-3 随机事件的概率(1).pdf");
            fos = new FileOutputStream("copy.pdf");

            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);//一定不能读取所有的数据
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //这里还需要对fis fos进行非空判断
            if(fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            }
        }
    }
}

 2.优化


  • 可以看见,非常的繁琐,所以JDK7以后有了一些优化方案
  • 可以自动释放资源,但是这个类必须实现了AutoColseable这个接口

字符集ASCII、GBK、Unicode:


  • GBK:
    • 一个汉字用两个字节去存储
    • 高位二进制一定是以1开头,转成十进制后是负数
      • 这是为了区分汉字与英文,英文是以0开头
    • 完全兼容ASCII
  • Unicode(字符集):
    • UTF-8(编码方式):1~4个字节来存储
      • 中文汉字3个字节
      • 英文1个字节
    • UTF-16:用2~4个字节存储
    • UTF-32:固定用4个字节存储

为什么会有乱码?

  • 1.读取数据时未读取完整的汉字
    • 不要用字节流读取文本文件(一次只读一个字节,那么汉字三个字节就炸了)
    • 但是拷贝可以,因为我们并不会在拷贝中途使用数据,每个字节都完整拷贝过来了。 (不存在数据丢失)
  • 2.编码和解码的方式不统一

JAVA中编码、解码的方法:

  • 注意:
    • 指定方式里面小写,大写均可
    • 不同编译器默认的编码方式可能不同,一定要注意

字符流:


1.特点:

  • 底层其实就是字节流,在字节流的基础上添加了字符集的概念
  • 输入流:一次读一个字节,遇到中文时,一次读多个字节
  • 输出流:底层会把数据按照指定的编码方式进行编码,变成字节后再写到文件中

2.输入流学习——FileReader为例

  • 1.创建对象
    • 文件不存在,会报错

  • 2.读取数据
    • 一次读一个字节,遇到中文时,一次读多个字节,解码后返回一个整数
    • 读到末尾返回 -1

  • 3.释放资源/关流

例:

  •  注意:read方法读取到的都是解码后转换成的十进制数据,我们要强制转换成char类型才能看见字符
public class Demo2 {

    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("a.txt");
        int ch;
        while((ch = fr.read()) != -1) {
            System.out.print((char)ch);
        }
        fr.close();

    }
}

 有参的read方法底层将读取数据、解码、强制类型转换三步合并了,把得到的字符放到了数组中,这个和无参的不一样!

3.输出流学习——FileWriter为例(书写细节和字节输出流那里一样)

1.构造方法:

2.成员方法:

4.字符输入流底层原理

1.创建字符流输入对象

  • 底层:关联文件,并创建缓冲区(长度为8192的字节数组 1024 * 8)(bytebuffer)

2.读取数据

  • 底层:
    • 判断缓冲区中是否有数据可以读取
    • 缓冲区中没有数据:从文件中获取数据,撞到缓冲区中,每次尽可能装满缓冲区
      • 如果文件中也没有数据了,返回-1
    • 缓冲区有数据:从缓冲区中读取

5.字符输出流底层原理

  • 底层:
    • 写数据时,会先写到缓冲区中(缓冲区大小也是8192个字节)
    • 数据不会直接写到本地中,写到本地的方法:
      • 缓冲区满后,会进行一次写出
      • 调用flush()方法,可以进行一次写出
      • 关流后,可以写出(注意关流后与文件的关联就断开了,不能再进行文件相关的操作了)

三、其他高级流

1.缓冲流


原理:

  • 底层自带了8192的缓冲区来提高性能
  • 只是进行了包装,底层实际上还是基本流

字节缓冲流的两个包装方法:

  • BufferedInputStream(InputStream is);
  • BufferedOutputStream(OutputStream os);

拷贝文件的例子:

  • 注意:我们不用关闭基本流,因为缓冲流底层有关闭基本流的代码
public class Demo2 {

    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.txt"));

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

提高效率的原理:

  • 读取时,先尽量读满缓冲区,写时会往另一块缓冲区里写,这些都是在内存中的,减少了和磁盘之间的读写,所以更加的快

字符缓冲流:

  • 大部分都和字节缓冲流一样,这里只介绍最常用的两个方法(很好用)
  • 注意JAVA中char类型占两个字节,字符缓冲流底层是个char类型数组,所以是16kb
  • public String readLine();//一次读取一行代码,如果没数据可读会返回null
    • 遇到回车、换行会停止,但是不会读出来回车和换行,想要换行要手动加
  • public void newLine();//跨平台的换行
    • 不同的平台换行不同,用这个可以跨平台

2.转换流(字符流的一种)


作用:

  • 转换字符流和字节流
    • 指定字符集进行读写数据:(JDK11后淘汰)(因为可以在FileReader中指定编码格式了,所以之间创建FileReader的对象就行了,输出流也一样,在FileWriter中用即可)
    • 字节流想用字符流中的方法(重要)
  • 对象名:
    • InputStreamReader
    • OutputStreamReader
  • 例:想一次读一行数据,并且不出现乱码
public class Demo2 {

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        //我们想一次读取一行,并且不出现乱码
        //所以要用转换流,变成字符流
        InputStreamReader isr = new InputStreamReader(fis);

        //参数中,只要是Reader就可以,所以我们传isr
        BufferedReader br = new BufferedReader(isr);

        String line;
        while((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

3.序列化流(对象操作输出流)


作用:

  • 把Java中的对象写到本地文件
  • 写到本地的文件长得很抽象,用反序列化流才能看懂

方法:

  • ObjectOutputStream(OutputStream out);//把基本流包装成高级流
  • public final void writeObject(Object obj);//把对象序列化写出到文件中去

细节:

  • 直接输出对象,会报错NotSerializableException异常
    • 解决方案:要让对应的类实现Serializable接口(这是个空接口,只用来作为标记作用)

 例:

public class Demo2 {

    public static void main(String[] args) throws IOException {
        //1.创建对象
        Student stu = new Student("wangjunyang",114);

        //2.创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.txt"));

        //3.写出数据
        oos.writeObject(stu);

        oos.close();
    }
}

4.反序列化流(对象操作输入流)


方法:

  • ObjectInputStream(InputStream is);
  • Object readObject();//注意读到的是Object类型

细节:

  • 当我们序列化一个类后,修改了JavaBean类,我们再去反序列化这个类会报错
    • 原因:序列号不匹配
    • 处理方案:固定序列号的值
  • private static final long serialVersionUID = xxxx;//注意名字只能是这个
  • 可以通过设置中搜索Serializable,勾选上JVM中的那个不带xxxUID的可序列化类和transient字段在反序列化时未初始化,这样就能在IDEA中提示你了,然后自动生成
  •  如果我们不想某个属性被序列化呢?可以加上transient修饰
  • 当我们要序列化多个对象时,我们一般放在集合中,然后去序列化这个集合,这样可以避免忘了对象有几个

例:

public class Demo2 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.txt"));

        Student o = (Student)ois.readObject();

        System.out.println(o);

        ois.close();
    }
}

5.打印流(只有输出流)


分类:

PrintStream

PrintWriter

特点:

  • 只操作目的地文件,不操作数据源
  • 特有的写出方法,可以实现数据的原样写出
  • 特有的写出方法,可以实现自动刷新,自动换行

字节打印流:

注意:

  • 字节流底层没有缓冲区,开不开自动刷新都一样

成员方法:

  • 常规方法:
    • write(int b)
  • 特有方法:
    • println()        打印任意数据,自动刷新,自动换行
    • print()            打印任意数据,没有换行
    • printf(String format,Object..args)  带有占位符,不换行

字符打印流:

  • 底层多了缓冲区,自动刷新那个参数就有用了
  • 其他的和上面的字节流基本一致

打印流应用:

  • 其实System.out就是自动创建了一个打印流的对象,是一个特殊的打印流,称作系统的标准输出流
  • 注意这个流是不能关闭的,在系统中是唯一的
public class Demo1 {

    public static void main(String[] args) throws IOException {

        PrintStream out = System.out;
        out.println("123");
    }
}

6.解压缩流(输入流)

  • 压缩包里每一个文件都是ZIpEntry对象
  • 解压本质就是把ZipEntry按照层级拷贝到本地

相关文章:

  • idea中或pycharm中编写Markdown文件
  • 小结:计算机网路中的性能指标小结
  • Windows提权之第三方提权(九)
  • I/O多路转接之select:定义、原理及应用场景
  • solidwork智能尺寸怎么对称尺寸
  • 【大模型系列篇】Vanna-ai基于检索增强(RAG)的sql生成框架
  • odoo初始化数据库
  • Ubuntu20.04确认cuda和cudnn已经安装成功
  • #渗透测试#批量漏洞挖掘#(0day)某智能终端操作平台前台存在通用SQL注入漏洞(CVE-2022-21047)
  • 3.对象生活的地方—Java环境搭建
  • Spring-全面详解(学习总结)
  • 【AD】3-5 元件在原理图中的基本操作2
  • 从零开始开发纯血鸿蒙应用之语音朗读
  • Gopeed 各种类型的文件资源下载器 v1.6.7 中文版
  • CentOS与Ubuntu操作系统的基本对比介绍
  • 如何在WMare上安装ubuntu(乌班图)
  • Asp.Net Core WebAPI开发教程(入门)
  • 【合集】Java进阶——Java深入学习的笔记汇总 再论面向对象、数据结构和算法、JVM底层、多线程、类加载、
  • 计算机等级考试
  • 力扣hot100——回溯
  • 深圳龙华做网站的公司/seo页面优化技术
  • 可以做烟的网站吗/全国网站排名
  • 佛山网站建设开发/优化视频
  • 网站建设需要每年交钱吗/线上宣传渠道
  • 哪个不是网站开发工具/网站开发的流程
  • 网站做图分辨率是多少合适/seo软件定制