1.什么是IO
IO流实际上是内存和硬盘(中的文件)之间的通道。
流都在java.io.*包下。
无论是输出还是输入,读还是写,都是针对内存而言的。
IO流的分类
按照流的方向进行分类(以内存为参照物):
输入流:往内存中去,叫输入(Input),或者叫读(Read)。
输出流:从内存中出来,叫输出(Output),或者叫写(Write)。
另一种方式是按照读取数据方式的不同进行分类的。
字节流:按照字节的方式读取数据,一次读取一个字节,这种流是万能的,什么文件都可以读。
字符流:按照字符的方式读取数据,一次读取一个字符,专为读取文本文件而存在的,而且只能读取文本文件。
注:windows操作系统中一个字符“a”占一个字节,用字节流读“a”就是一个“a”,因为.txt文件和java没有关系。
IO流的四大家族
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族都是abstract类。
所有类都实现了java.io.Closeable接口,都是可关闭的,其中有close()方法,用完流之后要进行对流的关闭。
所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法,输出流在输出结束后要进行刷新,用于将管道中的数据强行输出完(清空流管道),如果没有flush可能会导致数据的丢失。
java中主要学的16个类
2.FileInputStream
首先看看FileInputStream是如何定义的。
FileInputStream fis = null; // 在try之外进行定义,注意一定要赋值为null// 有tryfis = new FileInputStream("D:\\CODE\\java\\IO\\test.txt");// 这里会抛出异常,所以要进行异常的处理
read方法
int data = fis.read(); // 一次读取一个字符,以int形式返回System.out.println(data); // 读到字符“a”,然后返回97
通过while循环对read方法进行改进
int data = fis.read(); //while(data != -1){ // 这里不能写fis.read() != -1,因为文件指针会跳过一个字节,所以得用一个变量进行判断System.out.println(data);data = fis.read();}
或者可以这么写
int data = 0;while((data = fis.read()) != -1){ // 注意里面的括号System.out.println(data);}
但一次只读取一个字节,内存和硬盘交换太频繁。
我们可以使用byte数组+while循环去读文件。
fis = new FileInputStream("D:\\CODE\\java\\IO\\test.txt");byte [] bytes = new byte[4]; // 我们new出来一个4个长度(当然可以更多)的byte数组int datacount = 0; // datacount作为每次读到的byte数组的长度while((datacount = fis.read(bytes)) != -1){ // 如果没读完System.out.print(new String(bytes,0,datacount)); // 使用String类中的构造方法一并输出}
available方法,判断有流中有多少个字节没读
System.out.println("文件共多少个字节:"+fis.available()); // 可以在一开始就明确文件有多少个字节byte [] bytes = new byte[fis.available()]; // 字节数量作为byte数组的长度,可以只读一次System.out.println(new String(bytes));
skip方法,跳过多少个字节不读
3.FileOutputStream
FileOutputStream流把内存中的数据写出硬盘上。
fos = new FileOutputStream("myfile");String s = "i am Chanese";byte [] bs = s.getBytes(); // 将字符串转为byte数组的方法fos.write(bs);// fos.write(bs,0,2) 表示从byte数组的0位置写入,写两个字节// 注意写完之后一定要记得刷新一下fos.flush();
这种方法谨慎使用,因为会清空原文件原先的内容,FileOutputStream的重载构造方法中可以在后面加上true,表示写入的内容是追加而不是覆盖。
fos = new FileOutputStream("myfile" , true);
文件的拷贝,原理就是把硬盘上的东西输入到内存中,再从内存中把数据写到硬盘里。使用字节流的拷贝,可以使得要拷贝的文件类型是任意的,不局限于txt文件。
FileOutputStream fos = null;FileInputStream fis = null;fis = new FileInputStream("E:\\迷醉.Euphoria.S01E01.中英字幕.WEB-1080P-人人影视.mp4");fos = new FileOutputStream("D:\\迷醉1.mp4");byte [] bytes = new byte[1024*1024];// 一次最多可以拷贝1Mint count = 0;while ((count = fis.read(bytes)) != -1){// 读到多少个字节数,则拷贝多少个字节fos.write(bytes,0,count);}// 最后要记得刷新fos.flush();
finally {// 注意这里一定要分开处理异常,否则一个异常处理了会导致另外一个流没有关闭if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}
4.FileReader和FileWriter
文件字符输入输出流,只能读取不同的文本文件,不能读取媒体文件。
读取文本文件时,比较方便,快捷。
// 定义一个FileWriter对象out// 定义一个FileReader对象in// 字符的输入输出流不能用byte数组,只能用Sring数组。char [] chars = {'我','是','中','国','人'};out.write(chars);// 可以直接在write的参数中添加一个字符串数组形参System.out.println(chars,0,readcount); // 可以像读字节一样指定读几个字符out.write("我是中国人"); // 也可以直接读一个字符串(最方便)
5.BufferedReader和BufferedWriter
BufferedReader:带有缓冲区的字符输入流,使用这个流时不需要定义char数组(byte数组),自带缓冲。
BufferedReader br = null;FileReader reader = null;reader = new FileReader("test.txt");// 注意这里要传一个Reader类型的参数,但是Reader是abstra类型的,不能实例化,便传一个Reader的子类进去。br = new BufferedReader(reader);// readLine方法可以一次读一行// 但是注意readLine方法读一行时不带换行符String s = br.readLine();System.out.println(s);
finally {// 注意这里处理异常的时候只需要处理最外层的异常就行if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}
通常把里面的流叫节点流,把外面的流叫包装流。
readLine在读不到东西时会返回null,所以可以用while循环实现把文件中的东西全部读出来。
String s = null;while((s = br.readLine()) != null){System.out.println(s);}
在定义BufferedReader时,参数只能写Reader的子类,否则(比如写一个FileInputStream)会报错。
但是我们可以使用转换流把FileInputStream转换成Reader流。
// 先定义一个FileInputStream流FileInputStream in = new FileInputStream("test.txt");// 再用转换流把FileInputStream转换成Reader(相当于做了一个对象的转换)InputStreamReader reader = new InputStreamReader(in);// 最后在定义BufferedReader流的时候把转换后的对象传进去BufferedReader br = new BufferedReader(reader);// 只用关闭最外层的包装流就行了br.close();
当然我们可以简写
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt");
BufferedWriter:带有缓冲的字符输出流
// 创建一个自带缓冲的字节输出流BufferedWriter bw = new BufferedWriter(new FileWriter("test2.txt"));// 像FileWriter一样,可以在write方法里直接写字符串bw.write("我是中国人\n");bw.write("我骄傲!");// 最后注意流的刷新和关闭bw.flush();bw.close();
6.数据流
java.io.DataOutputStream:数据专属的流。
这个流可以将数据连通数据的类型一并写入文件。
注:这个文件不是普通的文本文件,使用记事本打不开。
java.io.DataInputStream:数据字节输入流。
DataOutputStream写的文件,只能使用DataInputStream读,而且读的时候需要提前知道写入的顺序。
读的顺序和写的顺序一直,才能正常取出数据。
7.标准输出流
PrintStream
// System.out是System类中的一个属性,初始值为空,类型是PrintStream的PrintStream ps = System.out;// println方法实际上实在PrintStream类中的。ps.println("123");
实际上,我们可以改变输出的方向(默认输出在控制台上),需要调用setOut方法
// 我们想将数据输出在log文件中// PrintStream类实例化时,参数需要一个OutputStream类型的节点流PrintStream ps = new PrintStream(new FileOutputStream("log"));// setOut方法是在System类中的// 调用这个函数,数据便会通过标准输出流输出到log文件中System.setOut(ps);System.out.println("123123");
8.File类
File类不属于四大家族的类,所以不能对文件进行读写。
File类的实例化对象是文件和目录路径的抽象表示。
File类常用的方法
import java.io.*;public class demo1 {public static void main(String[] args) throws Exception{File f = new File("log");// exists方法,用于判断对应路径有没有指定的文件System.out.println(f.exists());if(!f.exists()){// createNewFile方法,创建一个新的文件f.createNewFile();// mkdir方法,创建一个新的目录(文件夹)f.mkdir();}File f2 = new File("a/b/c/d/hello.txt");if(!f2.exists()){// 创建树结构的目录f2.mkdirs();}// 打印文件的上级目录System.out.println(f2.getParent());// 打印文件的绝对地址System.out.println(f2.getAbsolutePath());// 打印文件的字节数(大小)System.out.println(f2.length());File f3 = new File("D:\\CODE");// 获取当前目录下的所有文件,包装成一个数组File [] flist = f3.listFiles();// 遍历for(File file : flist){// 打印文件的名字System.out.println(file.getName());}}}
9.序列化和反序列化
在关闭程序的时候,程序中实例化的对象会被垃圾回收,有没有一种技术可以在程序结束之前把对象保存起来,我们可以使用序列化和反序列化。
