JAVA基础 IO流技术学习笔记 2

简介: JAVA基础 IO流技术学习笔记

八、常用流详解

8.1 文件字节流

8.1.1   FileInputStream文件字节输入流

FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)。文件字节流是一个字节一个字节读取数据,如果数据源的编码方式和目的地的解码方式不同就会造成乱码问题。

package cn.it.bz.IO;
import java.io.FileInputStream;
import java.io.IOException;
public class TestFileInputStream {
    public static void main(String[] args) {
        //将磁盘D中a.txt以字节输入到程序中
        try(FileInputStream fileInputStream = new FileInputStream("D:/a.txt");)
        {
            StringBuilder stringBuilder = new StringBuilder();
            int temp = 0;
            while ((temp = fileInputStream.read()) != -1){//获取文件中数据的字节
                //将字节转成字符
                stringBuilder.append((char)temp);
            }
            System.out.println(stringBuilder);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

需要注意的是read()方法无参数,返回值是int类型,代表的是文件中字符对应的ASCII编码。返回值如果是-1的话表示文件已经读取完毕。对于得到的ASCII一般需要在程序中再将其强制转换为char字符类型

8.1.2  FileOutputStream文件字节输出流

FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件(图像、视频、文本文件等。这里的字节指的是写出数据的参数是字节,不是将字符的字节输到文件中。

package cn.it.bz.IO;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileOutputStream {
    public static void main(String[] args) {
        //将Java以字节输出到D盘下的a.txt文件中
        //true:表示会追加到文件末尾。false(默认):表示重写整个文件的内容。
        try (FileOutputStream fileOutputStream = new FileOutputStream("d:/a.txt",true);)
        {
            //准备输出的数据
            String data = " java";
            //将字符串转为字节数组:j=>106  a=>97  v=>118 a=>97
            byte[] bytes = data.getBytes();
            fileOutputStream.write(data.getBytes());
            //刷新,将数据从内存中写入到磁盘中
            fileOutputStream.flush();
        } catch (IOException E) {
            E.printStackTrace();
        }
    }
}

需要注意的是write()方法的参数是字节或者是字节数组,输出到文件的时候会自动将字节或者是字节数组转换为对应ASCII码表示的字符。

8.1.3 通过字节缓冲区提高读写速度

package cn.it.bz.IO;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class TestFileBuffer {
    public static void main(String[] args) {
        //获取当前时间的毫秒数
        long startTime = System.currentTimeMillis();
        copyFile("D:/a.txt","d:/b.txt");
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
    //文件复制。source:源文件。destination:目的地文件
    public static void copyFile(String source,String destination){
        //流的关闭顺序是后开先关
        try(FileInputStream fileInputStream = new FileInputStream(source);
            FileOutputStream fileOutputStream = new FileOutputStream(destination))
        {
            //字节缓冲区。因为输入输出流是文件字节流,因此使用字节数组作为缓冲区
            byte[] buffer = new byte[1024];
            //先将文件读到程序
            int temp = 0;
            while ((temp = fileInputStream.read(buffer))!= -1){
                System.out.println("temp:"+temp);
                //将文件写出去。buffer:表示一次写出的字节的大小。off:表示从第0个位置开始写出。temp:表示写出的文件取决于temp,temp决定了缓冲区有多少字节数。
                fileOutputStream.write(buffer,0,temp);
            }
            //将数据写出到磁盘中
            fileOutputStream.flush();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

fileInputStream.read(buffer)

将文件中的数据,读入到缓冲区buffer中,返回的是该组数据的大小。假设,被读取文件的最后一组只有200个字节,那么输入字节流也只会读取这200个字节存放到缓冲区,缓冲区剩下的空间是没有数据的。


fileOutputStream.write(buffer,0,temp);

将本次循环中的数据写出到目标文件中,如果不给0,temp这两个参数,则将缓冲区中1024个字节全部输出到目标文件中,也就是即使缓冲区中1024个字节只有200个字节是数据,那输出流也会将这1024个字节输出到你目标文件中,剩下的824个字节用空格补齐。


注意 在使用字节缓冲区时,我们需要注意:


为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法为:read(byte[] b);写入时的方法为:write(byte[ ] b, int off, int length)

程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法关闭的情况。

8.1.4  缓冲字节流

Java缓冲流本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包装流)

BufferedInputStream和BufferedOutputStream这两个流是缓冲字节流,通过内部缓存数组(其实和前面的缓冲区数组一样,只不过缓冲区大小默认是8192个字节,可以通过构造方法来修改该缓冲区的大小)来提高操作流的效率。

package cn.it.bz.IO;
import java.io.*;
public class TestFileBufferedStream {
    public static void main(String[] args) {
        //获取当前时间的毫秒数
        long startTime = System.currentTimeMillis();
        copyFile("D:/abc.jpg","d:/2.jpg");
        long endTime = System.currentTimeMillis();
        long time = endTime-startTime;
        System.out.println("时间:"+time);
    }
    public static void copyFile(String source,String destination){
        //两个节点流,两个处理流
        try(FileInputStream fileInputStream = new FileInputStream(source);
            FileOutputStream fileOutputStream = new FileOutputStream(destination);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            )
        {
            int temp = 0;
            while ((temp = bufferedInputStream.read()) != -1){
                bufferedOutputStream.write(temp);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

8.2 文件字符流

前面介绍的文件字节流可以处理所有的文件,如果我们处理的是文本文件,也可以使用文件字符流,它以字符为单位进行操作。

文件字符流一般用于读写文本文件,不用于读取二进制文件,会产生乱码。字符流在遇到英文时一次读取一个字节,在遇到中文时一次读取多个字节(gbk编码一次读三个。utf-8一次读两个),这和字符集有关。

8.2.1  FileReader文件字符输入流

使用文件字符输入流读取数据时,遇到汉字会读取多个字节,GBK编码的汉字一次读取两个字节,UTF-8 编码的一次读取三个字节,在程序得到汉字字节时会将其按照指定或者是默认的解码规则解码得到十进制数。

package cn.it.bz.IO;
import java.io.FileReader;
import java.io.IOException;
public class TestFileReader {
    public static void main(String[] args) {
        //创建文件字符输入流对象
        try(FileReader fileReader = new FileReader("D:/a.txt");)
        {
            StringBuilder stringBuilder = new StringBuilder();
            int temp = 0;
            while ((temp = fileReader.read()) != -1){
                System.out.println(temp);
                stringBuilder.append((char)temp); //将字符解码得到的十进制数据还原回字符
            }
            System.out.println("输出:"+stringBuilder);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

8.2.2  FileWriter文件字符输出流

package cn.it.bz.IO;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestFileWriter {
    public static void main(String[] args) {
        //创建文件字符输出流对象,默认将源文件的内容覆盖,true表示开启追加。
        try(FileWriter fileWriter = new FileWriter("D:/a.txt",true);)
        {
            fileWriter.write("张三\r\n");//"\r\n"表示回车换行
            fileWriter.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

8.2.3 缓冲字符流

BufferedReader/BufferedWriter增加了缓存机制,大大提高了读写文本文件的效率。

字符输入缓冲流

BufferedReader是针对字符输入流的缓冲流对象,提供了更方便的按行读取的方法:readLine(); 在使用字符流读取文本文件时,我们可以使用该方法以行为单位进行读取。

package cn.it.bz.IO;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TestBufferedReader {
    public static void main(String[] args) {
        //创建字符输入缓冲流和字符输入节点流
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("D:/a.txt")))
        {
            String temp = "";
            while ((temp = bufferedReader.readLine()) != null){//循环读取一行字符串
                System.out.println(temp);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

字符输出缓冲流

BufferedWriter是针对字符输出流的缓冲流对象,在字符输出缓冲流中可以使用newLine();方法实现换行处理。

package cn.it.bz.IO;
import java.io.*;
public class TestBufferedWriter {
    public static void main(String[] args) {
        //创建字符输入缓冲流和字符输入节点流
        try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:/a.txt")))
        {
            //向文件写出字符串
           bufferedWriter.write("摄像头");
           bufferedWriter.newLine(); //换行 
            bufferedWriter.write("青青子衿悠悠我心");
            bufferedWriter.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

注意

  • readLine()方法是BufferedReader的方法,可以对文本文件进行更加方便的读取操作。
  • newLine()方法BufferedWriter的方法,可以使用newLine()方法换行。

8.2.4 小练习:为文件中的内容添加行号

package cn.it.bz.IO;
import java.io.*;
public class TestLineNumber {
    public static void main(String[] args) {
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("D:/a.txt"));
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("D:/a1.txt"))
        ) {
            String temp = " ";
            int  i = 1;
            while ((temp = bufferedReader.readLine()) != null){
               //向a1.txt文件写入数据
                bufferedWriter.write(i+"、"+temp);
                //换行
                bufferedWriter.newLine();
                //i+1
                i++;
            }
            //刷新
            bufferedWriter.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

8.3 转换流

InputStreamReader字节流转换为字符流/OutputStreamWriter将字符流转换为字节流。转换流也是处理流。

8.3.1 转换流处理乱码问题

计算机中数据的存储规则:

计算机中的数据都是二进制的数据,八个二进制位组成一个字节,英文字母占一个字节,汉字占两个字节。

ASCII字符集:

ASCII字符集最多表示128个字符(7位)且只能存储英文字符,一个字节最多表示256个字符(8位),因此英文字符占一个字节足够。计算机中存储的二进制数据都是8位,只有7位的

ASCII码是不能直接存储到计算机中的。因此还需要对7位的ASCII码进行编码,使其转换为8为的二进制数据,编码的结果就是在ASCII码的最高位补0。

GBK字符集:

中国win电脑上显示的ANSI实际上就是指的GBK字符编码集。GBK完全兼容ASCII

需要注意的是:解码是将二进制转为十进制。

Unicode字符编码:

存储全世界的字符编码,Unicode字符编码针对的是全世界的字符,因此针对不同国家地区的文字采取不同的编码方式,最常见的就是UTF-8(注意UTF-8不是字符集),规定英文字符占一个字节且最高位为0(0xxxxxxx),汉字占三个字节从高位到低位依次是 1110xxxx

10xxxxxxxx   10xxxxxxxx。

乱码产生的原因

1、读取字符时没有读完。

2、编码和解码方式不统一。


如何避免产生乱码?

1、不使用字节流读取文件

2、使用相同的编码和解码方式

package cn.it.bz.IO;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestInputStreamReader {
    public static void main(String[] args) {
            try(
                //创建文件字节输入流对象
                FileInputStream fileInputStream = new FileInputStream("D:/a2.txt");
               //创建字节到字符的转换流
                InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"gbk");
                )
            {
                StringBuilder stringBuilder = new StringBuilder();
                int temp = 0;
                while ((temp = inputStreamReader.read()) != -1){
                    stringBuilder.append((char) temp);
                }
                System.out.println(stringBuilder);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

8.3.2  小练习:通过字节流读取文本文件并添加行号

package cn.it.bz.IO;
import java.io.*;
public class TestLineNumber2 {
    public static void main(String[] args) {
        try (
                //创建字节文件输入流
                FileInputStream fileInputStream = new FileInputStream("D:/a.txt");
                //将字节流转换为字符流(解决中文乱码)
                InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8") ;
                //再使用字符缓冲(读一行)
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
               //创建字符文件输出流
                FileWriter fileWriter = new FileWriter("D:/a1.txt");
                //创建文件字符缓冲区
                BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
        )
        {
            int i = 1;
            StringBuilder stringBuilder  = new StringBuilder();
            String s = "";
            while ((s = bufferedReader.readLine()) != null){
                System.out.println(s);
                //添加行号
                bufferedWriter.write(i+"、"+s);
                //换行,相当于是bufferedWriter.write("\r\n")
                bufferedWriter.newLine();
                i++;
            }
            //释放资源
          bufferedWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

原理图:


相关文章
|
9天前
|
算法 Java 程序员
在Java的编程世界里,多态不仅仅是一种代码层面的技术,它是思想的碰撞,是程序员对现实世界复杂性的抽象映射,是对软件设计哲学的深刻领悟。
在Java的编程世界里,多态不仅仅是一种代码层面的技术,它是思想的碰撞,是程序员对现实世界复杂性的抽象映射,是对软件设计哲学的深刻领悟。
40 9
|
6天前
|
XML JavaScript Java
【JAVA XML 探秘】DOM、SAX、StAX:揭秘 Java 中 XML 解析技术的终极指南!
【8月更文挑战第25天】本文详细探讨了Java中三种主流的XML解析技术:DOM、SAX与StAX。DOM将XML文档转换为树状结构,便于全方位访问和修改;SAX采取事件驱动模式,适用于大型文件的顺序处理;StAX则兼具DOM和SAX的优点,支持流式处理和随机访问。文中提供了每种技术的示例代码,帮助读者理解如何在实际项目中应用这些解析方法。
35 1
|
2天前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
7 3
|
5天前
|
Java 程序员 数据库连接
Java中的异常处理:从基础到高级云计算与网络安全:技术融合的双刃剑
【8月更文挑战第26天】在Java编程的世界中,异常处理是一块基石,它确保了程序的健壮性和稳定性。本文将带你从异常处理的基本概念出发,逐步深入到高级应用,包括自定义异常和最佳实践。你将学会如何优雅地处理程序中可能遇到的各种问题,以及如何设计异常处理策略来提升代码质量和维护性。
|
10天前
|
Java 数据处理
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
18 2
|
2天前
|
缓存 Java API
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
6 0
|
2天前
|
jenkins Java Shell
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
|
4天前
|
Java 网络安全 云计算
深入理解Java异常处理机制云计算与网络安全:技术挑战与应对策略
【8月更文挑战第27天】在Java编程的世界里,异常处理是维护程序健壮性的重要一环。本文将带你深入了解Java的异常处理机制,从基本的try-catch-finally结构到自定义异常类的设计,再到高级特性如try-with-resources和异常链的应用。通过具体代码示例,我们将探索如何优雅地管理错误和异常,确保你的程序即使在面对不可预见的情况时也能保持运行的稳定性。
|
4天前
|
Java API 网络安全
探索Java中的Stream API:从基础到高级应用云计算与网络安全:技术融合与挑战
【8月更文挑战第27天】在Java的海洋中,Stream API犹如一艘强大的船,让开发者能以声明式的方式处理集合数据。本文将启航,先带你了解Stream的基本概念和用法,再深入探讨其高级特性,如并行流、管道操作以及性能考量。我们将通过具体代码示例,展示如何高效利用Stream API简化数据处理流程,提升代码的可读性和性能。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往更优雅编程风格的大门。
|
7天前
|
NoSQL Java Redis
【Azure Spring Cloud】Java Spring Cloud 应用部署到Azure上后,发现大量的 java.lang.NullPointerException: null at io.lettuce.core.protocol.CommandHandler.writeSingleCommand(CommandHandler.java:426) at ... 异常
【Azure Spring Cloud】Java Spring Cloud 应用部署到Azure上后,发现大量的 java.lang.NullPointerException: null at io.lettuce.core.protocol.CommandHandler.writeSingleCommand(CommandHandler.java:426) at ... 异常
下一篇
云函数