Java IO流的那些事

2021/5/23 1:25:14

本文主要是介绍Java IO流的那些事,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Java IO流介绍

  • IO流的概念
  • 1.Java IO的初步了解
    • 1.1 Java IO的分类
  • 2 Java IO 的对象
    • 2.1 File类
        • 2.1.3 File类的实例实现
    • 2.2 字节流
        • InputStream 和 OupStream 类来拷贝文件
        • 2.2.2 FileInputStream 类的实例
        • 2.2.3 FileOutputStream 类的构造方法
    • 2.3 字符流
        • 2.3.1 字符流基类
        • 2.3.2 FileWriter 类
        • 2.3.3 FileReader 类
    • 2.4 缓冲流
        • 2.4.1 字节缓冲流
        • 2.4.2 字符缓冲流
    • 2.5 转换流
        • 2.5.1 OutputStreamWriter 类
        • 2.5.2 InputStreamReader 类
    • 2.6 序列化流
    • 2.7 打印流
  • 3 Java IO流的面试题

IO流的概念

Java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在Java中把不同的输入/输出(Input/output)源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许Java程序使用相同的方式来访问不同的输入/输出源。stream是从起源(source)到接收的(sink)的有序数据。

1.Java IO的初步了解

IO流主要是由 java.io 包下提供了各种"流"类和接口,用以获取不同种类的数据,并通过标准的
方法输入或输出数据。

输入流Input: 读取外部数据(磁盘,光盘等存储设备的数据)到程序(内存)中。

输出流output:将程序(内存)数据输出到磁盘,光盘等存储设备中。

流的特性

  1. Java对数据的操作是通过流(系统资源)的方式;
  2. IO流用来处理设备之间的数据传输;
  3. 顺序存取:和队列相似,先进先出;不能随机访问数据。

1.1 Java IO的分类

流的分类主要可以分为以下几类(抽象基类不能实例化)
在这里插入图片描述

操作数据单位不同:

字节流(8 bit):如:inputStream , outputStream

字符流(16 bit): 如:Reader , Writer

数据流的流向不同:

  • 输入流(InputStream 或者Reader:从文件中读到程序中;)
  • 输出流(OutputStream或者Writer:从程序中输出到文件中;)

流的角色不同:

  • 节点流(直接与数据源相连,读入或读出。但是不利于读写)
  • 处理流(加快读写文件的速度)

1、输入流和输出流
流的输入输出是相对于内存来说的。
在这里插入图片描述
2、字节流和字符流

›字符流的由来

直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

字节流的数据单元是8-bits,字符流的数据单元为16-bits。(Java的Unicode编码中)

其他的应用定位:

  1. 字节流一般都用来处理图像、视频、音频、PPT 等类型文件。
  2. 字符流主要用来处理纯文本类型的文件,不能处理视频等非文本文件。所以字节流可以处理一切文件而字符流只能处理纯文本文件。
  3. 字符流本身存在缓冲区,所以缓冲流是相对于字节流的提升效率高。

话不多说看源码:
在这里插入图片描述
可以看出这里用到了一个缓冲区。

3、节点流和处理流

▲节点流:直接处理数据读写的流,比如:FileInputStream

▲处理流:对已经存在的流进行包装并且通过更强的功能,速度变快、灵活的读写功能 ,比如:BufferedInputStream (节点的缓冲流);

!注:处理流的使用加快了我们对文件操作的速度。
处理流主要是在节点流的中途进行包装、使节点流的输出操作减少。

2 Java IO 的对象

上面我们简单的介绍的一下Java IO 流的一下分类和特点,下面我们来详细介绍我们在写项目中出现频率比较高的类。

2.1 File类

▶这个类主要用来操作文件的类而不能操作文件中对数据的类,也是一个最基本的类。
路径的分类

▶绝对路径:一个完整的路径,以盘符开头,例如F://aaa.txt。
▶相对路径:一个简化的路径,不以盘符开头,例如//aaa.txt//b.txt。

2.1.1 File类的定义如下:

public class File implements Serializable, Comparable<File>

该类实现了接口Serializable、Comparable,说明了该类可以排序、序列化。

> File类的构造方法主要有:
> 1、File(File parent , String child)  通过抽象路径和child的路径创建File实例
> 
> 2、File(String pathName)  通过指定的抽象路径来创建新File实例
> 
> 3、File(String parent , String child) 通过两个字符串路径来创建新File

2.1.2 File的主要方法

以下为常见的File的方法
1、createNewFile()  // 当不存在该路径下的文件是不可分的创建一个空文件(.txt .docx)这一类文件
2、mkdir()  // 创建该路径下的指定目录的文件夹 
3、mkdirs()  // 可以通过路径来创建不存在的多级文件夹(使用更广泛)
4、delete() //删除指定路径下的文件或者目录
5、exists()  //测试该路径下的是否存在文件或者目录
6、getAbsoluteFile()  //返回抽象路径名的绝对路径名
7、getAbsoluteString()  //返回抽象路径的绝对路径的字符串
8、length() //返回抽象路径名的文件长都

2.1.3 File类的实例实现

public class IOJava {

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

        //实例化方法一:
        String path2 = "D:\\JieWen";//1文件下的文件夹
        File file = new File(path2);
        if (!file.exists()) {
            file.createNewFile();
        }

        //实例化方法二:(常用)
        File file1 = new File("D:\\JieWen");
        if (!file1.exists()) {
            file1.createNewFile();
        }

        System.out.println("文件的绝对路径:" + file1.getAbsolutePath());
        System.out.println("文件的大小:" + file1.length());

        file1.delete();//文件删除
    }
}

最后File类创建之后肯定还有遍历呀!!

- public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。

- public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。

实例操作实现一下:

public class FileFor {
    public static void main(String[] args) {
        File file= new File("D:\\JieWen");
      
      	//获取当前目录下的文件以及文件夹的名称。
		String[] names = file.list();
		for(String name : names){
			System.out.println(name);
		}
        //获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就可以获取更多信息
        File[] files = file.listFiles();
        for (File file1 : files) {
            System.out.println(file1);
        }
    }
}
/*
输出结果:
Team Tools
VB
VC
VC#
Web
Xml
D:\vs2019\asan_blacklist.txt
D:\vs2019\clang_rt.asan_dynamic-i386.dll
D:\vs2019\Common7
D:\vs2019\DesignTools
D:\vs2019\DIA SDK
D:\vs2019\ImportProjects
*/

▶以上就是基本的文件创建(File类的简单介绍),接下来就是我们本次文章的重点之一咯------节点流、字符流。

接下来就由博主带你们了解了解这两个基本的流……

2.2 字节流

InputStreamOutputStream分别是字节输入流与字节输出流的基类,它们的子类都是字节流,主要用在按字节来处理二进制数据。InputStreamOutputStream,Reader,writer都是抽象类。所以不能直接new。

两个基类的都继承了Object类,实现了接口 Closeable 类以下为类的定义

public abstract class OutputStream
extends Object
implements Closeable ,Flushable

OutputStream 类的实现子类,以下为常见的子类
在这里插入图片描述

在这里插入图片描述实现的接口Object类实现了以下方法
在这里插入图片描述
2.2.1 InputStream 类作为父类的实现

▶先建立一个标准,而FileInputStream 是其子类。他可以对文件进行数据流的转换。

使用流的转换

InputStream inputstream = new FileInputStream("D:\\JieWen");//然后对InputStream 进行读操作,为啥是读呢?可以把内存当作主体,这是某个网友说的,你从硬盘往内存里Input 东西就是读取数据咯。 另外这里因为FileInputStream继承InputStream 类//所以可以这样用

   byte[] by = new byte[8192];//此数字不唯一哦;

   int len ;

   while(  (len=inputStream.read(by))!=-1 ){ //len就是得出的字节流了。
   
         }
   inputStream.close();//最后别忘记关闭(关闭才能得到流),当然应该还有个if判断是否为空和try catch 的语句。

  File f = new File("F:\\……"); 

  if (!f.exists()) { 
  		System.out.println("creat " + f.toString()); 

  		f.createNewFile(); 
   }

  FileOutputStream fos = new FileOutputStream(f);//InputStream和OutputStream 一般辩证的来看,一个读,一个写,两者差不多的用法。
 fos.write(b, 0, len); 
 fos.flush();
 fos.close(); 

InputStream 和 OupStream 类来拷贝文件

/** 拷贝文件
 *	操作步骤如下:
 *	1、建立联系 File 对象   源头  目的地
 *	2、选择流    既然是文件的拷贝  那肯定包含了 读取跟写入  所以
 *		1)文件输入流   InputStream FileInputStream 
 *		2)文件输出流  OutputStream FileOutputStream 
 *	3、操作:拷贝  -->一边读取 一边写入  就可以完成拷贝了 
 *		byte flush[] = new byte[1024];
 *		int len = 0;
 *		while(-1=(len=输入流.read(flush))){
 *			输出流.write(flush,0,len);
 *		}
 *		输出流.flush;
 *	4、释放资源:关闭两个流
 */
public class Demo01 {
	public static void main(String[] args){
		String str = new String("e:/work/a.txt");
		String str1 = new String("e:/work/asdfg.txt");
		try {
		//调用拷贝的方法
			copyFile(str, str1);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.out.println("文件未找到!");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("文件拷贝失败!没事找找bug再来!!!");
		}
		
	}
	public static void copyFile(String srcPath,String datePath) throws FileNotFoundException,IOException{
		//源文件  必须是文件不能是文件夹  输入流是读取不了文件夹的  
		File src = new File(srcPath);
		//目的地  文件可以不存在  
		File date = new File(datePath);
		//打开流
		InputStream it = new FileInputStream(src);
		OutputStream os = new FileOutputStream(date);
		//文件拷贝    
		byte flush[]  = new byte[1024];//字节的开辟空间
		int len = 0;
		while(0<=(len=it.read(flush))){
			os.write(flush, 0, len);
		}
		//关闭流的注意 先打开的后关
		os.close();
		it.close();
	}
}

InputStream 类的子类的方法的如下

2.2.2 FileInputStream 类的实例

 ▶该类的子类的常见实现
 
 BufferedInputStream, DataInputStream

方法的摘要

 int available() 
          返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 
 void close() 
          关闭此输入流并释放与此流关联的所有系统资源。 
 void mark(int readlimit) 
          在输入流中的当前位置上作标记。 
 boolean markSupported() 
          测试此输入流是否支持 mark 和 reset 方法。 
 int read() 
          从此输入流中读取下一个数据字节。 
 int read(byte[] b) 
          从此输入流中将 byte.length 个字节的数据读入一个 byte 数组中。 
 int read(byte[] b, int off, int len) 
          从此输入流中将 len 个字节的数据读入一个 byte 数组中。 
 void reset() 
          将此流重新定位到对此输入流最后调用 mark 方法时的位置。 
 long skip(long n) 
          跳过和丢弃此输入流中数据的 n 个字节。 

FileInputStream 类的构造方法

1、 FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。

2、 FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名name命名。(开发中比较常用)

实例: FileInputStream inputStream = new FileInputStream("a.txt");
//该构造方法最推荐使用

注意:当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出 FileNotFoundException

使用输入流(从文件中读取到 FileInputStream 流中)

public class FISRead {
    public static void main(String[] args) throws IOException{
      	// 使用文件名称创建流对象
       	FileInputStream fis = new FileInputStream("read.txt");//read.txt文件中内容为ab
      	// 读取数据,返回一个字节
      	//方法一:单个读取
        int read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        //当流最后读取的是空则返回-1
        //方法二:循环读取
        /* int b ;
        // 循环读取
        while ((b = fis.read())!=-1) {
            System.out.println((char)b);
            //则不会输出-1
        }
		方法三:增加读取速度
		// 定义变量,作为有效个数
        int len ;
        // 定义字节数组,作为装字节数据的容器   
        byte[] b = new byte[1024];
        // 循环读取
        while (( len= fis.read(b))!=-1) {
           	// 每次读取后,把数组变成字符串打印
            System.out.println(new String(b));
            注意:这里数组的长度为1024如果最后一次的长度没有1024,最后面输出可能会和前面的的重复;
            原因:当数组前一次读取的数据为:1 2 3 4.。。。。1000,那后一次只有一个数据a b c,
            则只有下标0,1,2会替换,最后一次数组的数据为a,b,c,4,5,.....1000;所以重复
            使用以下方法避免该bug
            System.out.println(new String(b,0,len));//  len 每次读取的有效字节个数
        }
*/
		// 关闭资源
		// 关闭资源
        fis.close();
    }
}
输出结果:
a
b
-1

2.2.3 FileOutputStream 类的构造方法

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

该类实现的方法如下:
在这里插入图片描述多了实现刷新流的效果,就不需要将流关闭才能得到数据,刷新一样将缓冲区中的数据拿到。

方法和 FileInputStream 的方法相似;
主要方法区别就是以下方法的实现

public void write(int b)
public void write(byte[] b)
public void write(byte[] b,int off,int len) //从off索引开始,len个字节

以下就不介绍那些和文件输入流相同的方法了,直接上案例;

public class IOWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// 字符串转换为字节数组
      	byte[] b = "abcd".getBytes();
      	// 写出字节数组数据
      	fos.write(b);
      	//fos.write(b,2,2);
      	
      	// 关闭资源
        fos.close();
    }
}
输出结果:
abcd
//cd注意基本上的范围都是左闭右开的;

▶对于FileOutputStream 类的追加问题

先列出构造方法

1、public FileOutputStream(String name, boolean append)
2、public FileOutputStream(File file, boolean append)

当创建一个流的时候,该路径存在时,如果默认为则追加是为false 会清空该文件的数据,
如果指定为true则表示为追加数据,文件中的数据不会覆盖。如下实例:

public class IOWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("a’.txt",true);     
      	// 字符串转换为字节数组
      	byte[] b = "abcde".getBytes();
		// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
        fos.write(b);
      	// 关闭资源
        fos.close();
    }
}
文件操作前:cd
文件操作后:cdabcde

文件操作追加为数据尾部追加,如果要换行追加呢?
大家也都知道文件数据操作不能直接使用\n换行吧,在windows系统中文件数据操作可以使用\r\n进行换行操作的,接下来就去敲代码实现一下吧!!!

public class IOWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");  
      	// 定义字节数组
      	byte[] words = {97,98,99,100,101};
      	// 遍历数组
        for (int i = 0; i < words.length; i++) {
          	// 写出一个字节
            fos.write(words[i]);
          	// 写出一个换行, 换行符号转成数组写出
            fos.write("\r\n".getBytes());
        }
      	// 关闭资源
        fos.close();
    }
}

输出结果:
a
b
c
d
e

那你知道为什么windows系统要使用这个换行符嘛。。。

简单的说一下:
首先 \r 是将该行数据退格到该行的行首,然后使用换行符换下一行首;主要还是对原来打印机的继承
一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。

上面有对文件数据的复制,接下来我们来一起实现对非文本数据的复制吧,举个复制照片的实例吧,如下:

public class Copy {
    public static void main(String[] args) throws IOException {
        // 1.创建流对象
        // 1.1 指定数据源,不存在源,则会报错
        FileInputStream fis = new FileInputStream("D:\\a.jpg");
        // 1.2 指定目的地
        //如果不存在该文档,自动创建文档
        FileOutputStream fos = new FileOutputStream("a_copy.jpg");

        // 2.读写数据
        // 2.1 定义数组
        byte[] b = new byte[1024];
        // 2.2 定义长度
        int len;
        // 2.3 循环读取
        while ((len = fis.read(b))!=-1) {
            // 2.4 写出数据
            fos.write(b, 0 , len);
        }

        // 3.关闭资源
        fos.close();
        fis.close();
    }
}

对于流最后关闭问题博主和你们来探讨一下:
为什么流一定要关闭呢?

一个流绑定了一个文件句柄(或网络端口),如果流不关闭,该文件(或端口)将始终处于被锁定(不能读取、写入、删除和重命名)状态,占用大量系统资源却没有释放。

对流关闭的实现:

OutputStream out = null;
OutputStream out2 = null;
try {
	out = new FileOutputStream("");
	out2 = new FileOutputStream("");
	// ...操作流代码
} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		if (out != null) {
			out.close();// 如果此处出现异常,则out2流也会被关闭
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	try {
		if (out2 != null) {
			out2.close();
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

注:如果流是有关系的流,则需要按流的外围流开始向里关闭,互不相干的流则不需要按顺序关闭。
例:

out1 = new FileOutputStream("");
out2 = new FileOutputStream("");
//这里的out1和out2谁先关谁后关都一样,没有任何影响

FileOutputStream fos = new FileOutputStream("d:\\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("java IO close test");

// 从外到内顺序关闭ok
bw.close();
osw.close();
fos.close();
//如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b

注意的是:如果用到了缓冲区的流必须将流关闭或者需要刷新缓冲区才能输出流哦!!

字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的

字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容

现在,对节点流就告一段落了,也把主要的类简要介绍了一遍,接下来就是对字符流(ReaderWriter)的介绍咯

2.3 字符流

首先博主和你们说一下,为什么会有字符流呢,字节流不是可以直接操作所有的流嘛!

原因原来是这个…
由于我们汉字基本上都是3个字节,而字节流本身就是2个字节 所以呢!会将汉字拆开导致
出现乱码。

如下:

 FileOutputStream fileOutputStream = new FileOutputStream("D:\\JieWen\\a.txt");
        FileInputStream fileInputStream = new FileInputStream("D:\\JieWen\\a.txt");

        fileOutputStream.write("高文文".getBytes());
        byte[] bt = new byte[2];
        int len ;
        while((len = fileInputStream.read(bt))!=-1){
            System.out.println(new String(bt,0,len));
        }
        fileInputStream.close();
        fileOutputStream.close();
        //�
		//��
        //输出会出现乱码

所以就引进了字符流(WriterReader),接下来就是字符流的主场咯!!

2.3.1 字符流基类

字符流抽象基类:

● Reader:字符输入流的抽象类

● Writer:字符输出流的抽象类

属于抽象类,说明了不能直接实例化 就是不能new 一个对象。

简单介绍一下字符流的基类吧

输入流和输出类似。。。。。。。

输出流的主要方法会比输入流多结果如下:

1、void write(int c) 写入单个字符。
2、void write(char[] cbuf)写入字符数组。
3、abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,
	len写的字符个数。
4、void write(String str)写入字符串。
5、void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
6、void flush()刷新该流的缓冲。
7、void close() 关闭此流,但要先刷新它。

WriterReader 类属于抽象超类,所以不能实例化只能利用子类进行实例化。

接下来就是字符流的子类了

首先我们还是来聊聊文件类的字符流吧

2.3.2 FileWriter 类

首先文件字符流包括输入、输出流呗,构造方法肯定是第一要做的。。。。。

 FileWriter fw = new FileWriter("fw.txt");     
 // 写出数据
 fw.write(97); // 写出第1个字符
 fw.write('b'); // 写出第2个字符
 fw.write('C'); // 写出第3个字符

 //关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
 // fw.close();
 FileWriter(File file, boolean append);
 FileWriter(String path, boolean append)
//给出文件名的情况下构造 FileWriter 对象,它具有指示是否挂起写入数据的 boolean 值。默认为false;

【注意】关闭资源时,与 FileOutputStream 不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。

接下来就是对该流 的实例

import java.io.*;
 
public class FileRead {
    public static void main(String args[]) throws IOException {
        File file = new File("Hello1.txt");
        // 创建文件
        file.createNewFile();
        // creates a FileWriter Object
        FileWriter writer = new FileWriter(file);
        // 向文件写入内容
        writer.write("This\n is\n an\n example\n");
        writer.flush();
        writer.close();
        // 创建 FileReader 对象
        FileReader fr = new FileReader(file);
        char[] a = new char[50];
        fr.read(a); // 从数组中读取内容
        for (char c : a){
            System.out.print(c); // 一个个打印字符
        }
        fr.close();
    }
}

输出结果:
This
is
an
example

字符流的使用有一个非常重要的特点就是需要刷新缓冲区。。。

关闭close和刷新flush
原因:
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush
方法了。

flush :刷新缓冲区,流对象可以继续使用。
close: 先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了

所以 。。。close的方法中自带flush()方法的功能,使用关闭流就刷新了一次。

【注意】关闭资源时,与 FileOutputStream 不同(使用了字符流就需要将缓冲区刷新)。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。这个时候反应过来了吧,可见实践例子的重要性,编程就是这样,不去敲,永远学不会!!!

博主带你们看看flush方法的用法。

public class FlushDemo {
    public static void main(String[] args) throws Exception {
        //源   也就是输入流【读取流】 读取a.txt文件
        FileReader fr=new FileReader("a.txt");  //必须要存在a.txt文件,否则报FileNotFoundException异常
        //目的地  也就是输出流
        FileWriter fw=new FileWriter("b.txt");  //系统会自动创建b.txt,因为它是输出流!
        //fw.flush();
        int len;
        while((len=fr.read())!=-1){
           fw.write(len);
        }
        
   	   //注意这里是没有使用close关闭流,开发中不能这样做,但是为了更好的体会flush的作用所以没有将数据
 	   // 存入
    }
}

2.3.3 FileReader 类

输入流 Reader 主要的方法如下:

1、 public void close() :关闭此流并释放与此流相关联的任何系统资源。
2、 public int read(): 从输入流读取一个字符,返回一个int 型的数。
3、 public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中

java.io.Reader 抽象类是字符输入流的所有类的超类(父类),可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

该类和 FileReader 很相似,简单介绍一下。

// 使用文件名称创建流对象
       	FileReader fr = new FileReader("a.txt");
      	// 定义变量,保存数据
        int b ;
        // 循环读取
        while ((b = fr.read())!=-1) {
            System.out.println((char)b);
        }
		// 关闭资源
        fr.close();

FileWriter 类 和 FileReader类的结合使用案例

public class IOJava {

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

        FileWriter fileWriter = new FileWriter("D:\\JieWen\\a.txt");
        FileReader fileReader   = new FileReader("D:\\JieWen\\a.txt");

        fileWriter.write("你是猪嘛!!!丫丫");
        fileWriter.flush();
        char [] chars = new char[1024];
        int len = 0;
        while((len = fileReader.read(chars)) != -1){
            System.out.println(new String(chars,0,len));
        }
        fileWriter.close();
        fileReader.close();
    }

注意:如果会报错需要try catch的使用。

 try {
        if (fw != null) {
              fw.close();
          }
      } catch (IOException e) {
          e.printStackTrace();
      }
      //方法就是这样try报错

接下来就是我们IO流中比较重要一点!!!!

2.4 缓冲流

使用节点流和字符流的速度比较慢,所以推出了缓冲流(高效流),使用缓冲流的速度真的快!!
先来了解了解缓冲流吧

基本原理:
先上图
在这里插入图片描述
就是给那个配送员加了一个后备箱可以放更多的字符,减少运输次数来节省时间,更加高速!!!

缓冲流主要包括四个流Bufferxxxx…所以接下来就是对这4个缓冲流的介绍

首先就是字节缓冲流咯

2.4.1 字节缓冲流

上面我们简单了解了 InputStream 流,这是一个字节基类,但是速度慢!
还是这样,先上构造方法(永远是第一步哦)

  • public BufferedInputStream(InputStream in) :创建一个新的缓冲输入流,注意参数类型InputStream
  • public BufferedOutputStream(OutputStream out):: 创建一个新的缓冲输出流,注意参数类型为OutputStream

来个常用的实例吧

//构造方式一: 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));

///构造方式二: 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));

现在知道缓冲流的用法,我猜你肯定在想基本流和缓冲流到底差距有多大,满足你,看!
我们通过复制一个文件用两种流来实现看看速度哦

首先就是我们普通的字节流咯

public class IOJava {

    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        FileInputStream fileInputStream = new FileInputStream("D:\\下载程序\\ideaIU-2020.1.1.exe");
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\copy.exe");
        // 读写数据
        int b;
        while ((b = fileInputStream.read()) != -1) {
            fileOutputStream.write(b);
        }
        fileInputStream.close();
        fileOutputStream.close();
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("普通流复制时间:" + (end - start) + " 毫秒");
    }
}
//结果就是:博主等了很久很久,不想等了!!!!

然后接下来就是缓存字节流的主场,他来了

public class IOJava {

    public static void main(String[] args) throws IOException {
        // 记录开始时间
        long start = System.currentTimeMillis();
        // 创建流对象
        try (
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\下载程序\\ideaIU-2020.1.1.exe"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy1.exe"));
        ){
            // 读写数据
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");

在这里插入图片描述
最近出了速9都飞到太空了,这速度也太快了,那这缓冲流能追上嘛
答案肯定是可以的呀,把缓冲区的内存开辟大一点就OK咯

public class IOJava {

    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        // 创建流对象
        try (
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\下载程序\\ideaIU-2020.1.1.exe"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy1.exe"));
        ){
            // 读写数据
            int len = 0;
           byte [] bytes  = new byte[8024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");
    }
}

在这里插入图片描述
你瞧,快了17945/951倍吧,可以比上速10了。
缓冲流的字节流的介绍就结束咯,接下来就是那个那个字符缓冲流。。。。。

2.4.2 字符缓冲流

字符缓冲流从字面来看就是字符的一个高效流。
还是老规矩 —先构造方法

  • public BufferedReader(Reader in) :创建一个新的缓冲输入流,注意参数类型为Reader。
  • public BufferedWriter(Writer out): 创建一个新的缓冲输出流,注意参数类型为Writer。

还是来演示一条实例吧

// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("b.txt"));

我们知道字符流一般独特的方法,字符缓冲流也吧例外

接下来就是特有的方法

BufferedReader:public String readLine(): 读一行数据。 读取到最后返回null
BufferedWriter:public void newLine(): 换行,由系统属性定义符号。

博主还是得多敲代码给你们演示一遍

public class IOJava {

    public static void main(String[] args) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\JieWen\\a.txt"));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意一下哈什么博主有些抛异常是直接throws,但是实际上我们最好用try catch更加好。

还有一个方法就是newLine(),下面就介绍这个方法

public class BufferedWriterDemo throws IOException {
  public static void main(String[] args) throws IOException  {
    	// 创建流对象
  	BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
    	// 写出数据
      bw.write("你");
    	// 写出换行
      bw.newLine();
      bw.write("是");
      bw.newLine();
      bw.write("猪");
      bw.newLine();
      bw.write("丫丫");
      bw.newLine();
  	// 释放资源
      bw.close();
  }
}
输出效果:
你
是
猪
丫丫

缓冲流就已经简单的介绍完了,接下来呢,就是转换流了

2.5 转换流

转换流,听着听着就是流之间的相互转换咯。。
而转换呢,那就是字节流和字符流之间的转换,简单介绍一下编码和解码

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

那我们的问题是如何编码、如何解码呢?????
博主来给一个实例吧

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

接下来就是转换流的类了

2.5.1 OutputStreamWriter 类

首先就是介绍该类的实现

java.io.OutputStreamWriter 继承Writer类
就是一个字符输出流,写文本文件
write()字符,字符数组,字符串    
字符通向字节的桥梁,将字符流转字节流
OutputStreamWriter 构造方法:
    OutputStreamWriter(OuputStream out)接收所有的字节输出流
    字节输出流:  FileOutputStream       
    OutputStreamWriter(OutputStream out, String charsetName)
    String charsetName 传递编码表名字 GBK  UTF-8 
    OutputStreamWriter 有个子类,  FileWriter

默认的字符输出流的编码表是UTF-8
然后介绍完构造方法,就是直接敲代码实现方法

    	// 创建流对象,默认UTF8编码
      OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\JieWen\\a.txt"));
      // 写出数据
     osw.write("丫丫"); // 保存为6个字节
     osw.close();
   	// 创建流对象,指定GBK编码
    OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("D:\\JieWen\\a.txt"),"GBK");
    // 写出数据
    osw2.write("丫丫");// 保存为4个字节
    osw2.close();

上面就是对输入流的简单介绍,接下来就是对输出流的介绍

2.5.2 InputStreamReader 类

还是介绍该类的用法

java.io.InputStreamReader 继承 Reader
字符输入流,读取文本文件
字节流向字符的敲了,将字节流转字符流
读取的方法:read() 读取1个字符,读取字符数组
技巧 (1)OuputStreamWriter写了文件 (2)InputStreamReader读取文件
OutputStreamWriter(OutputStream out)所有字节输出流
InputStreamReader(InputStream in) 接收所有的字节输入流
可以传递的字节输入流: FileInputStream
InputStreamReader(InputStream in,String charsetName) 传递编码表的名字和上面的输出流编码一样

接下来就是实操咯

public class ReaderDemo2 {
    public static void main(String[] args) throws IOException {
      	// 定义文件路径,文件为gbk编码
        String FileName = "D:\\JieWen\\a.txt";
      	// 创建流对象,默认UTF8编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
      	// 创建流对象,指定GBK编码
        InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
		// 定义变量,保存字符
        int read;
      	// 使用默认编码字符流读取,乱码
        while ((read = isr.read()) != -1) {
            System.out.print((char)read); // ��ʺ      
        }
        isr.close();
      
      	// 使用指定编码字符流读取,正常解析
        while ((read = isr2.read()) != -1) {
            System.out.print((char)read);//丫丫
        }
        isr2.close();
    }
}

以上就是转换流了,那你们还了解序列化流嘛!!!

2.6 序列化流

说到序列化流,首先,何为序列化呀所以呢,我们要先了解什么是序列化

序列化:

将内存中保存的对象变为二进制数据流的形式进行传输,或者是将其保存在文本中.
实现序列化与反序列化的对象操作:
ObjectOutputStreamObjectInputStream
Serializable默认会将对象中所有属性进行序列化保存,如果现在某些属性不希望被保存了,那么就可以 使用transient关键字。

反序列化:

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化:

然后我们再来看序列化和反序列化的构造方法吧

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“employee.txt”));

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“exployee.txt”));

然后博主给你们奉上方法
在这里插入图片描述
来一个示例:

	  FileOutputStream fos = new FileOutputStream("t.tmp");
      ObjectOutputStream oos = new ObjectOutputStream(fos);

      oos.writeInt(12345);//int型序列化
      oos.writeObject("Today");//
      oos.writeObject(new Date());//object序列化一个对象

      oos.close();

然后反序列化的方法和序列化相似,只是将write改为read

直接来示例吧

FileInputStream fis = new FileInputStream("t.tmp");
ObjectInputStream ois = new ObjectInputStream(fis);

 int i = ois.readInt();//什么类型序列化就什么类型反序列化
 String today = (String) ois.readObject();
 Date date = (Date) ois.readObject();

 ois.close();

我们在上面已经了解了序列化和反序列化,下面我们直接上案例来序列化流操作

class person implements Serializable{//Serializable必须要实现
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
 //序列化
 ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("a.txt"));
 oos.writeObject((Object)new person("fay",18));
 oos.close();
 //反序列化
 ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt"));
 Person person = ois.readObject();
 ois.close();

序列化就已经介绍完了,还有最后一个打印流

2.7 打印流

平时我们在控制台打印输出,是调用print方法和println方法完成的,各位用了这么久的输出语句肯定没想过这两个方法都来自于 java.io.PrintStream 类吧,哈哈。该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

打印流的两种类

字节打印流 PrintStream,字符打印流 PrintWriter

为什么又要引出打印流这个东西呢??
打印流

1、 通过定义的构造方法可以发现,有一个构造方法可以直接接收OutputStream类的实例,与OutputStream相比起来,PrintStream可以更方便的输出数据,相当于把OutputStream类重新包装了一下,使之输出更方便。打印流提供了非常方便的打印功能,可以打印任何类型的数据信息,例如:小数,整数,字符串。所以才提出打印流。
2、直接操作文件。
3、API和他们的父类基本一致。

在这里插入图片描述
接下来就是对他们直接上实例了
1、打印到控制台桌面

import java.io.PrintWriter;
public class Test {
  public static void main(String args[]){
      // 通过System.out为PrintWriter实例化
      PrintWriter out = new PrintWriter(System.out);
      // 向屏幕上输出
      out. println("Hello World!");
      out.close();    //如果此句不写,则没有内容,跟PrintStream有区别
      }
}

2、向文件中输出

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Test {
  public static void main(String args[]) {
      PrintWriter out = null;
      File f = new File("d:\\a.txt");
      try {
          // 由FileWriter实例化,则向文件中输出
          out = new PrintWriter(new FileWriter(f));
      } catch (IOException e) {
          e.printStackTrace();
      }
      out.print("Hello World!" + "\r\n");
      out.close();
  }
}

3、还可以使用%d等使用

		PrintStream ps = null ;		// 声明打印流对象
		// 如果现在是使用FileOuputStream实例化,意味着所有的输出是向文件之中
		ps = new PrintStream(new FileOutputStream(new File("d:" + File.separator + "test.txt")));
		ps.print("%d",1);

4、使用PrintStream时自动转为字节

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
 
public class Main {
    public static void main(String[] args) throws IOException {
        PrintStream p=new PrintStream(new FileOutputStream(new File("d:\\a.txt")));
        String str="hello world";
        int i=5;
        double d=7.9;
        p.println(str);
        p.println(i);
        p.println(d);
        p.close();
    }
}
//结果
hello world
5
7.9//各种类型

最后就是文件的复制咯
PrintStream 流的复制类

		BufferedReader br=new BufferedReader(new FileReader("copy.txt"));
        PrintStream ps=new PrintStream("printcopy.txt");
        String line;
        while((line=br.readLine())!=null) {
            ps.println(line);//获取了缓冲流,然后转为打印流输出到文件里
        }
        br.close();
        ps.close();

3 Java IO流的面试题

(1)java中有几种类型的流?

字符流和字节流。字节流继承inputStreamOutputStream,字符流继承自InputSteamReaderOutputStreamWriter

(2) 解释一下java.io.Serializable接口(面试常考)

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。

(3)Java IO体系中有太多的对象,到底用哪个呢?

明确操作的数据设备。 数据source对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据destination对应的设备:硬盘(File),内存(数组),控制台(System.out)。

最后吧,博主把Java IO流中主要的流都大致介绍了一下,博主这次的IO流就到此结束,待 IO 流更新做相应的更新文章。谢谢大家!!!



这篇关于Java IO流的那些事的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程