java基础之IO流

Java的IO流包括输入流和输出流,字节流和字符流。字节流处理所有类型数据,字符流专用于处理字符。字符流由Reader和Writer类构成,解决字节流处理字符编码问题。字节流的InputStream和OutputStream为基础类,处理流如BufferedInputStream和BufferedOutputStream提供缓冲功能。在实际开发中,通常使用字符流的BufferedReader和BufferedWriter提高效率,并结合InputStreamReader和OutputStreamWriter处理编码。

IO流相关类的关系结构图

Java的输入输出功能来自java.io 包中的InputStream类、OutputStream类、Reader类和Writer类以及继承它们的各种子类。

在这里插入图片描述

2.流的分类:
在这里插入图片描述
根据数据的流向可以分为:输入流和输出流

  • 输入流:把数据从存储设备上读取到内存中的流。
  • 输出流:把数据从内存 中写出到存储设备上的流。

根据数据的类型分为:字节流 和 字符流

  • 字节流 :以字节为单位,读写数据的流。
  • 字符流 :以字符为单位,读写数据的流。

字符流和字节流:

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。

字节流和字符流的区别:

  • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
  • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
  • 字节流:一次读入或读出是8位二进制。
  • 字符流:一次读入或读出是16位二进制。

设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

字节的输入输出流

字节流:InputStream和OutputStream是java中可以按照最小字节单位读取的流,即每次读写一个字节,字节流是直接连接到输入源的流。

1.输入流:

InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

输入流InputStream抽象类的方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class InputStream {
    public static void main(String[] args) throws IOException {
        //创建FileInputStream对象
        java.io.InputStream inputStream=new FileInputStream("F:\\test.txt");
        byte[] byteData = new byte[200];
        //将文件中的数据读入字节数组中
        inputStream.read(byteData);
        System.out.println(new String(byteData));
        inputStream.close();
    }
}

2.输出流:

OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
PipedOutputStream 是向与其它线程共用的管道中写入数据。
ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。

输出流OutputStream 抽象类的方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

FileOutputStream:

OutputStream有很多子类,我们从最简单的一个子类FileOutputStream开始。看名字就知道是文件输出流,用于将数据写出到文件

FileOutputStream构造方法:

1、 public FileOutputStream(File file):根据File对象为参数创建对象。
2、 public FileOutputStream(String name): 根据名称字符串为参数创建对象。

推荐第二种构造方法【开发常用】:

FileOutputStream outputStream = new FileOutputStream("abc.txt");

就以上面这句代码来讲,类似这样创建字节输出流对象都做了三件事情:
1、调用系统功能去创建文件【输出流对象才会自动创建】
2、创建outputStream对象
3、把foutputStream对象指向这个文件

public class OutputStream {
    public static void main(String[] args) throws IOException {
        // 第1步:使用File类找到一个文件
        File f = new File("f:" + File.separator + "test2.txt");// 声明File 对象
        FileOutputStream outputStream=new FileOutputStream(f);
        String s = "你好啊 码农";
        byte[] bytes = s.getBytes();
        outputStream.write(bytes);

    }
}

字符流
1.字符输入流:Reader类

字符输入流类都是Reader的子类,Reader类是一个抽象类,方法与InputStream类类似;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class ReadTest {
    public static void main(String[] args) {
        File file = new File("F:\\test.txt");
        reader(file);

    }
    /**
     * 读取文件内容
     * @param file
     */
    public static void reader(File file) {
        Reader read = null;  //声明Reader对象
        char[] c = new char[(int)file.length()];   //根据文件大小定义数组
        try {
            read = new FileReader(file); //使用FileReader实例化Reader
             read.read(c);//读取内容到数组
            read.close();
        }catch(IOException e) {
            System.out.println("读取失败");
            e.printStackTrace();
        }
        System.out.println(c);
    }

}

2.字符输出流:Writer类

Writer类和OutputStream类,在功能上是一致的,但两者前者写入的是字符,后者写入的是字节,Writer本身也是一个抽象类:

在这里插入图片描述
在这里插入图片描述

    /**
	 * 向文件写入内容
	 * @param file
	 */
	public static void writer(File file) {
		String str = "java";
		char[] c = str.toCharArray();  //将字符串转换为char数组
		try {
			Writer writer = new FileWriter(file);   //实例化对象
			writer.write(c);    //写入数据
			writer.close();
			System.out.println("写入成功");
		}catch(IOException e) {
			System.out.println("写入失败");
			e.printStackTrace();
		}
	}

public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("F:\\test5.txt");
        // 写出数据
        fw.write(97); // 写出第1个字符
        fw.write('b'); // 写出第2个字符
        fw.write('C'); // 写出第3个字符

        //关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
         fw.close();
    }
}

流的定义

1.流的概念:什么是流?

流是用来读写数据的,是内存与存储设备之间传输数据的通道,Java中的流实际上是一个对象,真正的文件是在硬盘上的一块空间,在这个文件里面存放着各种各样的数据。而文件的数据通过以流为载体来进行数据的读写。这里的水桶相当于硬盘的文件等,水管相当于流,接水桶相当于程序猿操作的对象或JVM,水桶和接水桶之间通过流来进行数据传输。

如图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关于流的理解的具体图:
在这里插入图片描述
举个栗子:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
io类的关联的具体图解:
在这里插入图片描述
1.输入流与输出流:

个人见解:所谓输入输出针对程序而言。输入流就是外部(存储设备)读入程序(内存)中,输出就是程序(内存)输出到外部(存储设备)

在这里插入图片描述
2.字节流与字符流:

字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符

为什么要有字符流?

Java中字符是采用Unicode标准,Unicode 编码中,一个英文为一个字节,一个中文为两个字节。

在这里插入图片描述

而在UTF-8编码中,一个中文字符是3个字节。例如下面图中,“云深不知处”5个中文对应的是15个字节:-28-70-111-26-73-79-28-72-115-25-97-91-27-92-124
在这里插入图片描述
那么问题来了,如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。

字节流和字符流的其他区别:
字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。

3.节点流和处理流:

节点流:直接操作数据读写的流类,比如FileInputStream
处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)

处理流和节点流应用了Java的装饰者设计模式。
下图就很形象地描绘了节点流和处理流,处理流是对节点流的封装,最终的数据处理还是由节点流完成的。
在这里插入图片描述
在诸多处理流中,有一个非常重要,那就是缓冲流。

我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。
在这里插入图片描述
联想一下生活中的例子,我们搬砖的时候,一块一块地往车上装肯定是很低效的。我们可以使用一个小推车,先把砖装到小推车上,再把这小推车推到车前,把砖装到车上。这个例子中,小推车可以视为缓冲区,小推车的存在,减少了我们装车次数,从而提高了效率。

处理流:

处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

在这里插入图片描述
常用的处理流

  • 缓冲流:BufferedInputStrean 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。
  • 转换流:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。
  • 数据流: DataInputStream 、DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。

构造函数:

InputStreamReader(InputStream);        //通过构造函数初始化,使用的是本系统默认的编码表GBK。
InputStreamReader(InputStream,String charSet);   //通过该构造函数初始化,可以指定编码表。
OutputStreamWriter(OutputStream);      //通过该构造函数初始化,使用的是本系统默认的编码表GBK。
OutputStreamwriter(OutputStream,String charSet);   //通过该构造函数初始化,可以指定编码表。

注意:

1.在实际的项目中,所有的IO操作都应该放到子线程中操作,避免堵住主线程。
2.FileInputStream在读取文件内容的时候,我们传入文件的路径(“D:/abc.txt”), 如果这个路径下的文件不存在,那么在执行readFile()方法时会报FileNotFoundException异常。
3.FileOutputStream在写入文件的时候,我们传入文件的路径(“D:/abc.txt”), 如果这个路径下的文件不存在,那么在执行writeFile()方法时, 会默认给我们创建一个新的文件。还有重要的一点,不会报异常。

节点流处理流分类图
在这里插入图片描述
转换流:
在这里插入图片描述
字符编码与解码:

众所周知,计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。

简单一点说:

编码:字符(能看懂的)–字节(看不懂的)
解码:字节(看不懂的)–>字符(能看懂的)

代码解释:

String(byte[] bytes, String charsetName):通过指定的字符集解码字节数组
byte[] getBytes(String charsetName):使用指定的字符集合把字符串编码为字节数组

编码:把看得懂的变成看不懂的
String -- byte[]

解码:把看不懂的变成看得懂的
byte[] -- String

从另一角度来讲:字符流=字节流+编码表

在这里插入图片描述
为了达到最高效率,可以考虑在 BufferedReader 内包装 InputStreamReader

BufferedReader in = new BufferedReader(new InputStreamReader(System.in))

IO的实例操作

1、FileInputStream、FileOutputStream(字节流)

字节流的方式效率较低,不建议使用

public class InputOuputTest {
    public static void main(String[] args) throws IOException {
        File file=new File("F:\\test3.txt");
        writeFile(file);
        read(file);
    }
    //写文件
    public static void writeFile(File file) throws IOException {
        OutputStream outputStream=new FileOutputStream(file);
        String niuBPlus="各位看官 细听分说";
        byte[] bytes = niuBPlus.getBytes();
        outputStream.write(bytes);

//        outputStream.close();   注释了也能完成效果不知为何
    }
    //读文件
    public static void read(File file) throws IOException {
        InputStream inputStream=new FileInputStream(file);
        byte[] bytes = new byte[200];
        inputStream.read(bytes);
        System.out.println("不多bb,各位大佬看结果:"+new String(bytes));
    }
}

在开发中一般强烈推荐使用数组读取文件,代码如下:

package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class input2 {
    public static void main(String args[]){
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream("a.txt");
            int len = 0 ;
            byte[] bys = new byte[1024];
            while ((len = inputStream.read(bys)) != -1) {
                System.out.println(new String(bys,0,len));
            }
        
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}


输出结果:
ab
cd
e

2.BufferedInputStream、BufferedOutputStream(缓冲字节流)

缓冲字节流是为高效率而设计的,真正的读写操作还是靠FileOutputStream和FileInputStream,所以其构造方法入参是这两个类的对象也就不奇怪了。

public class IOTest {
    public static void main(String[] args) throws IOException {
        File file=new File("F:\\test4.txt");
        write(file);
        String read = read(file);
        System.out.println(read);
    }

    public static void write(File file) throws IOException {
        // 缓冲字节流,提高了效率
        BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true));

        // 要写入的字符串
        String string = "各位看官 细听分说";
        // 写入文件
        bis.write(string.getBytes());
        // 关闭流
        bis.close();
    }

    public static String read(File file) throws IOException {
        BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));

        // 一次性取多少个字节
        byte[] bytes = new byte[1024];
        //方法1
        // 用来接收读取的字节数组
//        StringBuilder sb = new StringBuilder();
        // 读取到的字节数组长度,为-1时表示没有数据
//        int length = 0;
        // 循环取数据
//        while ((length = fis.read(bytes)) != -1) {
//            // 将读取的内容转换成字符串
//            sb.append(new String(bytes, 0, length));
//        }
        
        //方法2
        fis.read(bytes);
        // 关闭流
        fis.close();

//        return sb.toString();
        return new String(bytes);
    }
}


3.InputStreamReader、OutputStreamWriter(字符流)

字符流适用于文本文件的读写,OutputStreamWriter类其实也是借助FileOutputStream类实现的,故其构造方法是FileOutputStream的对象

public class IOTest {
	
	public static void write(File file) throws IOException {
		// OutputStreamWriter可以显示指定字符集,否则使用默认字符集
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8");

		// 要写入的字符串
		String string = "各位看官 细听分说";
		osw.write(string);
		osw.close();
	}

	public static String read(File file) throws IOException {
		InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
		// 字符数组:一次读取多少个字符
		char[] chars = new char[1024];
		// 每次读取的字符数组先append到StringBuilder中
		StringBuilder sb = new StringBuilder();
		// 读取到的字符数组长度,为-1时表示没有数据
		int length;
		// 循环取数据
		while ((length = isr.read(chars)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(chars, 0, length);
		}
		// 关闭流
		isr.close();

		return sb.toString()
	}
}

如何写好IO程序

(1)明确要操作的数据是数据源还是数据目的(也就是要读还是要写)
(2)明确要操作的设备上的数据是字节还是文本
(3)明确数据所在的具体设备
(4)明确是否需要额外功能(比如是否需要转换流、高效流等)

本文摘抄自:云深i不知处
jack-chan
赵彦军

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值