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天前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
19 1
|
17天前
|
SQL 监控 Java
技术前沿:Java连接池技术的最新发展与应用
本文探讨了Java连接池技术的最新发展与应用,包括高性能与低延迟、智能化管理和监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,为开发者提供了一份详尽的技术指南。
23 7
|
19天前
|
移动开发 前端开发 Java
过时的Java技术盘点:避免在这些领域浪费时间
【10月更文挑战第14天】 在快速发展的Java生态系统中,新技术层出不穷,而一些旧技术则逐渐被淘汰。对于Java开发者来说,了解哪些技术已经过时是至关重要的,这可以帮助他们避免在这些领域浪费时间,并将精力集中在更有前景的技术上。本文将盘点一些已经或即将被淘汰的Java技术,为开发者提供指导。
48 7
|
15天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
30 3
|
15天前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
9 2
|
17天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
24 4
|
15天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
16 1
|
15天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
30 1
|
17天前
|
SQL Java 数据库连接
打破瓶颈:利用Java连接池技术提升数据库访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,避免了频繁的连接建立和断开,显著提升了数据库访问效率。常见的连接池库包括HikariCP、C3P0和DBCP,它们提供了丰富的配置选项和强大的功能,帮助优化应用性能。
36 2
|
19天前
|
前端开发 Java API
过时Java技术的退役:这些技能你不再需要掌握!
【10月更文挑战第22天】 在快速变化的技术领域,一些曾经流行的Java技术已经逐渐被淘汰,不再适用于现代软件开发。了解这些过时的技术对于新手开发者来说尤为重要,以避免浪费时间和精力学习不再被行业所需的技能。本文将探讨一些已经或即将被淘汰的Java技术,帮助你调整学习路径,专注于那些更有价值的技术。
30 1