概述
计算机内存与硬盘之间时常有交互行为,由于参考对象是内存,所以我们把数据从硬盘到内存传输的行为称为输入,也叫写;相反地,数据从内存到硬盘传输的行为称为输出,也叫读。由于数据传输的动作是像流水一样,因此传输的数据也被称为流,于是就有了输入流和输出流的概念。
IO流有2种分类,按照流的方向可分为输入流和输出流;按读取数据的方式可分为字节流和字符流,其中字节流是万能的,能传输各种格式的数据,而字符流只能传输纯文本文件,即能用记事本打开的文件。
Java的IO流有4个抽象类,分别是InputStream-OutputStream、Reader-Writer,以Stream结尾的文件称为字节流文件,以Reader或Writer结尾的文件被称为字符流文件。值得注意的是,只有数据传输时内存和硬盘的通道才会被打开,所以数据传输完毕后所有流都有关闭流的动作,因此所有流都实现了Closeable接口。同时,当内存向硬盘传输数据完成以后,是需要刷新的,因为这个刷新的动作表示将管道中剩余未输出的数据强行清空,所以所有的输出流都会实现Flushable接口,如果没有实现此接口,则有可能数据在传输过程中会丢失。
注:①路径中,\和/是等效的 ②IDEA目录中,默认的路径是当前工程下的根目录
先以FileInputstream类为例,介绍以下几个方法:
字节流
package IOStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class FileInputStreamTest03 {public static void main(String[] args) {FileInputStream fis = null;try {//创建文件字节输出流对象//此文件建在project的直接目录下fis = new FileInputStream("mytest1.txt");int readCount;byte[] bytes = new byte[1024];while ((readCount = fis.read(bytes))!= -1){System.out.println(readCount);//19//以byte[1024]大小的数组去读,然后数据留在了数组中,再将数组中的数据转成String类型//String类的构造方法,String(char[]),字符数组转成字符串System.out.println(new String(bytes,0,readCount));//heLLo,?? world...}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {//关闭流if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}}FileInputStream类的其他2个方法:package IOStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/*** FileInputStream类的其他常用方法* ①int available():其作用是避免了while()循环,不需要判断是不是读到头了。这个方法不适合大文件* ②long skip(long n):跳过几个字节不读*/public class FileInputStream04 {public static void main(String[] args) {testAvailMethod();testSkipMethod();}private static void testAvailMethod() {FileInputStream fis = null;try {fis = new FileInputStream("mytest1.txt");System.out.println("刚开始读的字节数:" + fis.available()); //刚开始读的字节数:14int readCount = fis.read();System.out.println("读完后的字节数:" + fis.available()); //读完后的字节数:13System.out.println(new String(new byte[fis.available()])); //} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}public static void testSkipMethod(){FileInputStream fis = null;File file = new File("mytest1.txt");try {fis = new FileInputStream(file);fis.skip(3);System.out.println(new String(new byte[fis.available() - 3]));//} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}
FileOutputStream类用法类似,比如我们将刚才从文件读取的内容写到另外一个文件中:
package IOStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class FileOutputStreamTest01 {public static void main(String[] args) {FileOutputStream fos = null;try {//以追加方式写入,英文字符和汉字。//注意:不追加的话每写一次就会清空之前的内容fos = new FileOutputStream("myoutfile.txt",true);byte[] bytes = {101,28,23,78,69};fos.write(bytes,0,bytes.length);String s = "今天天气不太好!!!";byte[] bytes1 = s.getBytes();fos.write(bytes1); //文件内容:eNE今天天气不太好!!!eNE今天天气不太好!!!fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}
字节流复制文件
文件复制:由于C、D盘并不能直接交互,需要以内存为中介进行操作,所有需要边读边写,具体代码如下:
package IOStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class CopyFileTest {public static void main(String[] args) {FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream("C:\\Users\\Simon\\Desktop\\List\\something convinient\\面试day726.md");fos = new FileOutputStream("D:\\面试day726.md");byte[] bytes = new byte[1024*1024];int readCount = 0;while ((readCount = fis.read(bytes,0,readCount))!= -1){fos.write(bytes,0,bytes.length);}fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}}
字符流
package IOStream;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;public class FileReaderTest {public static void main(String[] args) {FileReader fr = null;try {fr = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\src\\IOStream\\iotest1.txt");char[] chars =new char[20];int readCount = 0;while ((readCount = fr.read(chars))!=-1){//hello,wo rld//sdaj//kfsdghsdkbk我就偶返回数据拷贝//到方便快速//撒娇不看电视System.out.println(new String(chars, 0, readCount));}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fr != null) {try {fr.close();} catch (IOException e) {e.printStackTrace();}}}}}package IOStream;import java.io.FileWriter;import java.io.IOException;public class WriterTest {public static void main(String[] args) {FileWriter fw = null;try {fw = new FileWriter("D:\\Data\\temp.txt");char[] c = {'我','是','人'};fw.write(c);fw.write(c,0,2);fw.write("mytest.only you");//文件内容:我是人我是mytest.only youfw.flush();} catch (IOException e) {e.printStackTrace();}finally {if (fw != null) {try {fw.close();} catch (IOException e) {e.printStackTrace();}}}}}package IOStream;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class CopyFileTest2 {public static void main(String[] args) {FileReader in =null;FileWriter out = null;try {in = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\src\\IOStream\\iotest1.txt");out = new FileWriter("D:\\DataValue\\project\\MyData\\Data1\\iotest1.txt");//文件输出:// hello,wo rld//sdajkfsdghsdkbk我就偶返回数据拷贝到方便快速//撒娇不看电视char[] chars = new char[1024*1024];int readCount = 0;while ((readCount=in.read(chars))!=-1){out.write(chars,0,readCount);}out.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {in.close();} catch (IOException e) {e.printStackTrace();}try {out.close();} catch (IOException e) {e.printStackTrace();}}}}
缓冲流
package IOStream;import java.io.BufferedReader;import java.io.FileReader;/***带缓冲区的字符流:* ①由于带缓冲区,所以不需要再指定字节数组或者字符数组* ②包装流、节点流*本流优点是,一读就读一行*/public class BufferReaderTest{public static void main(String[] args) throws Exception{FileReader reader = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\iotest1.txt");//这里,FileReader称为节点流,外部的BufferedReader称为包装流//这里的参数要传一个Reader类的对象,但Reader类是抽象的,所以可以以FileReader传入BufferedReader br = new BufferedReader(reader);String s = null;while ((s=br.readLine())!=null){//这里并不自带换行符System.out.println(s);//读取文件内容如下:// hello,wo rld//sdajkfsdghsdkbk我就偶返回数据拷贝到方便快速//撒娇不看电视}//对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。br.close();}}//注意:文件字符流可以直接作为参数传入给缓冲流,但是字节流不行,它还必须经过转换流转成字符流//作为参数传入,所以会经历2次转换。package IOStream;import java.io.BufferedWriter;import java.io.FileOutputStream;import java.io.FileWriter;import java.io.OutputStreamWriter;public class BufferWriterTest {public static void main(String[] args) throws Exception{BufferedWriter bw = new BufferedWriter(new FileWriter("tetsBufferWriterTest.txt",true));BufferedWriter bw2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("testBufferWriterTest2.txt",true)));bw.write("hello"+"\n");bw.write(100);bw.write("test0000001",0,3);//文件内容如下:// hello//dteshello//dtesbw2.write("my test for the .java file");//文件内容输出如下:// my test for the .java filebw.flush();bw.close();bw2.flush();bw2.close();}}
读入和写出:以字节流为节点流
package sumup.iostream;import java.io.*;//以字节流作为缓冲流的节点流//运行成功public class IOStreamTest06 {public static void main(String[] args) {BufferedReader br = null;BufferedWriter bw = null;try {br = new BufferedReader(new InputStreamReader(new FileInputStream("myfile5")));String s = null;while ((s = br.readLine())!=null){System.out.println(s);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}}try {bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("myfile7",true)));bw.write("m买买买");char[] chars = {'z','o','o'};bw.write(chars);bw.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {try {bw.close();} catch (IOException e) {e.printStackTrace();}}}}
读入和写出:以字符流为节点流
package sumup.iostream;import java.io.*;//运行成功//用缓冲流读入和写出一个java文件,先以字符流作为节点流public class IOStreamTest05 {public static void main(String[] args) {FileReader fr =null;BufferedWriter bw = null;try {fr = new FileReader("printLog");BufferedReader br = new BufferedReader(fr);String s = null;while ((s=br.readLine())!=null){System.out.println(s);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fr.close();} catch (IOException e) {e.printStackTrace();}}try {FileWriter fw = new FileWriter("myfile5");bw = new BufferedWriter(fw);bw.write("这是一个秘密,请保密");bw.flush();} catch (IOException e) {e.printStackTrace();}finally {if (fr != null) {try {bw.close();} catch (IOException e) {e.printStackTrace();}}}}}
数据流
package IOStream;import java.io.DataOutputStream;import java.io.FileOutputStream;public class DataOutputStreamTest {public static void main(String[] args) throws Exception{DataOutputStream dos = new DataOutputStream(new FileOutputStream("dataOutputStream.txt"));//写数据byte b = 100;short s = 30;float f = 3.0f;double d = 3.0;int i = 3;char c ='c';//写:把数据及其类型写入文件,但由于不是普通文件,所以打开乱码dos.writeByte(b);dos.writeChar(c);dos.writeInt(i);dos.writeFloat(f);dos.flush();dos.close();}}package IOStream;import java.io.DataInputStream;import java.io.FileInputStream;/*** 按什么规则写,就按什么规则读*/public class DataInputStreamTest {public static void main(String[] args) throws Exception{DataInputStream dis = new DataInputStream(new FileInputStream("dataOutputStream.txt"));//开始读byte b = dis.readByte();char c = dis.readChar();float f = dis.readFloat();int i = dis.readInt();//b:100 c:c f:4.2E-45 i:1077936128System.out.println("b:" + b + "\tc:" + c + "\tf:" + f + "\ti:" + i);dis.close();}}
标准打印流
package IOStream;import java.io.FileOutputStream;import java.io.PrintStream;public class PrintStreamTest {public static void main(String[] args) throws Exception{PrintStream ps = System.out;ps.println("zs");ps.println("ls");ps.println("ww");//不再输出到控制台,而是输出到printLog文件中PrintStream pl = new PrintStream(new FileOutputStream("printLog"));System.setOut(pl);System.out.println("hello,world");System.out.println("hello,kitty");}}//一个案例package IOStream;import java.io.FileOutputStream;import java.io.PrintStream;import java.text.SimpleDateFormat;import java.util.Date;public class Logger {public static void log(String msg)throws Exception{//指向一个日志文件PrintStream out = new PrintStream(new FileOutputStream("loggerTest.txt", true));//改变输出方向System.setOut(out);//日期当前时间Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");String strTime = sdf.format(date);System.out.println(strTime + ":" + msg);}}package IOStream;public class LoggerTest {public static void main(String[] args) {try {Logger.log("zs做了一道奥数题");Logger.log("ls打了一桶水");Logger.log("ww完了一把游戏");} catch (Exception e) {e.printStackTrace();}}}新增文档内容:2021/10/12 10:36:37 864:zs做了一道奥数题2021/10/12 10:36:37 890:ls打了一桶水2021/10/12 10:36:37 890:ww完了一把游戏
关于File类
package IOStream;import java.io.File;import java.text.SimpleDateFormat;import java.util.Date;/*** File的直接父类是Object,不属于四大家族,所以File类是不能完成文件读和写的* File对象是文件和目录的路径名的抽象表示形式* Fiel类的常用方法*/public class FileTest {public static void main(String[] args) throws Exception{File file = new File("D:/file");System.out.println(file.exists()); //false//以文件形式新建/* if (!(file.exists())){file.createNewFile();}*///以目录形式新建,可创建多重目录--->mkdirsif (!(file.exists())){file.mkdir();}//获取父路径System.out.println(file.getParent()); //D:\//获取父路径的绝对路径System.out.println(file.getParentFile().getAbsolutePath()); //D:\//获取文件名System.out.println(file.getName()); //fileSystem.out.println(file.isDirectory()); //tureSystem.out.println(file.isFile()); //false//获取对象的最后一次修改时间long lm = file.lastModified();Date date = new Date(lm);SimpleDateFormat sdf = new SimpleDateFormat("yyyy - MM - dd HH:mm:ss SSS");System.out.println(sdf.format(date)); //2021 - 10 - 12 10:54:31 487//获取文件大小System.out.println(file.length()); //0//File中的listFiles方法File[] files = file.listFiles();for (File file1 : files) {System.out.println(file1.getName());//输出内容如下(是正确的):// 1//2//3.txt//4.txt}}}
对象流-序列化和反序列化
package IOStream.bean;import java.io.Serializable;//Serializable标志性接口public class Student implements Serializable {private int no;private String name;//若 private transient String name;则最终输出://Student{no=1111, name='null'}@Overridepublic String toString() {return "Student{" +"no=" + no +", name='" + name + '\'' +'}';}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Student(int no, String name) {this.no = no;this.name = name;}public Student() {}}package IOStream;import IOStream.bean.Student;import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.io.Serializable;/*** 对象的序列化和反序列化* 参与对象序列化和反序列的对象,必须实现Serializable接口* 仅有接口没有方法的称为标志性接口,作用是什么?* JVM看到这个接口后,会为这个类自动生成一个序列化版本号* 序列化版本号有什么用?每序列化一次就会生成一个序列化版本号,JVM怎么判断一个类有没有被改动?*①全类名是否变化 ②若①相同,比较2个序列化版本号是否一样,一样则表示为同一个类**/public class ObjectOutputStreamTest {public static void main(String[] args) throws Exception{//创建java对象Student s = new Student(1111, "zhangsan");//序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("studentTest"));//序列化对象oos.writeObject(s);oos.flush();oos.close();}}/***注意:如果改动了代码,然后没有序列化,直接点了反序列化操作,会报以下异常:*Exception in thread "main" java.io.InvalidClassException: IOStream.bean.Student; local class incompatible: stream classdesc serialVersionUID = 9082654830569486289, local class serialVersionUID = 3103404913107206989at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)at IOStream.ObjectInputStreamTest.main(ObjectInputStreamTest.java:9)Process finished with exit code 1*///自动生成序列化版本号有什么坏处?只要修改就必须重新编译。//所以凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号//比如ArrayList的:private static final long serialVersionUID = 8683452581122892189L;package IOStream;import java.io.FileInputStream;import java.io.ObjectInputStream;public class ObjectInputStreamTest {public static void main(String[] args) throws Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("studentTest"));Object o = ois.readObject();System.out.println(o); //Student{no=1111, name='zhangsan'}ois.close();}}
IO和Properties的联合使用
在idea写一个Properties文件,以key-value形式存储在硬盘中,我们创建一个userinfo.txt文件,内容写入:
username=admin
password=123
########################################这个#号表示注释##########################
package IOStream;import java.io.FileReader;import java.util.Properties;/*** Properties是一个Map集合,key和value都是String类型* 想将userinfo.txt文件中的数据加载到Properties对象中** 这是一个非常好的设计理念:* 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取,将来只需要修改文件的内容,* java代码不需要改动,不需要重新编译,服务器也不需要重启** 类似以上机制的这种文件称为配置文件,当配置文件的内容格式是:* key1=value;* key2=value;* 时,我们把这种配置文件称为属性配置文件** java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。* .properties是专门存放在属性配置文件内容的一个类*/public class IOPropertiesTest {public static void main(String[] args) throws Exception{//创建文件字符输入流对象FileReader fr = new FileReader("D:\\IDEA_Data\\Data5\\test\\src\\userinfo.txt");//创建Map对象Properties properties = new Properties();//Map的load方法,将字符流对象加载进来properties.load(fr);//读取String username = properties.getProperty("username");String password = properties.getProperty("password");//username:admin,password:123System.out.println("username:" + username + ",password:" + password);}}
