前言
IO流结构继承图:
InputStream 和 OutputStream 继承结构图:
Reader 和 Writer 继承结构图:
一、IO流概述
java中所有的流都是在:java.io.*;下。
通过IO可以完成硬盘文件的读和写。 (I : Input O : Output)
文件通常是由一连串的字节或字符构成,组成文件的字节序列称为字节流,组成文件的字符序列称为字符流。
Java 中根据流的方向可以分为输入流和输出流。输入流是将文件或其它输入设备的数据加载到内存的过程;输出流恰恰相反,是将内存中的数据保存到文件或其他输出设备
二、IO流的分类:
按照流的方向进行分类
以内存作为参照物,
往内存中去,叫做输入(Input)。或者叫做读(Read)。
从内存中出来,叫做输出(Output)。或者叫做写(Write)。
按照读取数据方式不同进行分类
1.按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等…
2. 按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
综上所述:流的分类:为输入流、输出流\字节流、字符流
Java IO流四大家族:
四大家族的首领:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
注意:
四大家族的首领都是抽象类。(abstract class)
所有的流都实现了:
java.io.Closeable接口,都是可关闭的,都有close()方法。
有的输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据。作用就是清空管道。没有flush()可能会导致丢失数据。
在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
四大家族的首领常用方法:
1.InputStream(字节输入流)
(1)void close() 关闭此输入流并释放与该流关联的所有系统资源。
(2)abstract int read() 从输入流读取下一个数据字节。
(3)int read(byte[] b) 从输入流中读取一定数量的字节并将其存储在缓冲 区数组 b 中。
(4)int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入字节数组。
2.OutputStream(字节输出流)
(1)void close() 关闭此输出流并释放与此流有关的所有系统资源。
(2)void flush() 刷新此输出流并强制写出所有缓冲的输出字节。
(3)void write(byte[] b) 将 b.length 个字节从指定的字节数组写入此输出 流。
(4)void write(byte[] b, int off, int len) 将指定字节数组中从偏移量 off 开始的 len 个字 节写入此输出流。
(5)abstract void write(int b)将指定的字节写入此输出流。
3.Reader(字符输入流)
(1)abstract void close() 关闭该流。
(2)int read() 读取单个字符。
(3)int read(char[] cbuf) 将字符读入数组。
(4)abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分
4.Writer(字符输出流)
(1) Writer append(char c) 将指定字符追加到此 writer。
(1)abstract void close() 关闭此流,但要先刷新它。
(1)abstract void flush() 刷新此流。
(1)void write(char[] cbuf) 写入字符数组。
(1)abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
(1)void write(int c) 写入单个字符
java.io包下重要的流有16个:
文件专属:
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream
对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
三、重要IO流详解
文件流:
FileInputStream(文件字节输入流)
FileInputStream其他常用方法:
1 int available() //返回流当中剩余的没有读到的字节数量
2 long skip(long n) 跳过几个字节不读
示例代码(1):
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class FileInputStreamText03 { public static void main(String[] args) { FileInputStream fis=null; try { fis=new FileInputStream("E:\\Day\\Temp.txt"); byte[] bytes=new byte[4]; /* while (true){ int readCount=fis.read(bytes); if(readCount==-1){ break; } System.out.print(new String(bytes,0,readCount)); }*/ //int available() //返回流当中剩余的没有读到的字节数量 System.out.println("总字节数量:"+fis.available()); /* //long skip(long n) 跳过几个字节不读 fis.skip(3);//跳过3个字节 System.out.println(fis.read());*/ int readCount=0; while ((readCount=fis.read(bytes))!=-1){ System.out.print(new String(bytes,0,readCount)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { try { //关闭流 fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
总字节数量:6 abcdef
FileOutputStream(文件字节输出流)
示例代码(2):
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileOutputStreamText01 { public static void main(String[] args) { FileOutputStream fos = null; try { //如果没有文件会新建一个文件 //这种方式会先将文件内容清空然后重新写入 //fos=new FileOutputStream("myfile"); //以追加的形式写入内容,不会删除原内容 fos = new FileOutputStream("myfile", true); //开始写 byte[] bytes = {97, 98, 99, 100}; fos.write(bytes); //写入一段内容!! fos.write(bytes, 0, 2); String s = "大家好,我是Java"; //将字符串转换为byte数组!! byte[] bytes1 = s.getBytes(); fos.write(bytes1); //写完之后最后一定要更新 fos.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
FileReader(文件字符输入流)
示例代码(3):
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /** * FileReader * 文件字符输入流,只能读取普通文本 * */ public class FileReadertext01 { public static void main(String[] args) { FileReader fr=null; try { fr=new FileReader("myfile"); /*char[] chars=new char[4]; int readCount=0; while ((readCount=fr.read(chars))!=-1){ System.out.print(new String(chars,0,readCount)); }*/ char[] chars=new char[4]; fr.read(chars); for (char c:chars){//一个一个读,只读取四个字符!! System.out.println(c); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fr!=null) { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
a b c d
FileWriter(文件字符输出流)
示例代码(4):
import java.io.FileWriter; import java.io.IOException; /** * FileWriter: * 文件字符输出流,写 * 只能输出普通文本 */ public class FileWriterText01 { public static void main(String[] args) { FileWriter fw=null; try { //创建文件输出流对象 fw=new FileWriter("myfile01",true);//写true,表示在内容后追加文本 char[] chars={'我','是','中','国','人'}; fw.write(chars); fw.write(chars,2,3); fw.write("我是一个Java攻城狮!!"); //换行 fw.write("\n"); fw.write("你好呀!"); //刷新 fw.flush(); } catch (IOException e) { e.printStackTrace(); }finally { if (fw != null) { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:
缓冲流:
带有缓冲区的字符流,不需要byte和char数组。
缓冲流主要是为了提高效率而存在的,减少物理读取次数
注意:
当一个流的方法中需要一个流的时候,被传进来的流叫节点流。外部需要包装的流叫包装流。(处理流)。
BufferedReader(缓冲字符输入流)
示例代码(5):
import java.io.*; public class BufferedReaderText02 { public static void main(String[] args) { BufferedReader bf= null; try { //字节流转换为字符流 bf = new BufferedReader(new InputStreamReader(new FileInputStream("Copy02.java"))); } catch (FileNotFoundException e) { e.printStackTrace(); } String s=null; while (true){ try { if (!((s=bf.readLine())!=null)) break; } catch (IOException e) { e.printStackTrace(); } System.out.println(s); } try { bf.close(); } catch (IOException e) { e.printStackTrace(); } } }
运行结果:、
文件内容:
BufferedWriter(缓冲字符输出流)
示例代码(6):
import java.io.*; public class BufferedWriterText01 { public static void main(String[] args) { try { //BufferedWriter out=new BufferedWriter(new FileWriter("copy")); BufferedWriter out=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("copy",true))); out.write("Hello"); out.write("\n"); out.write("我是"); out.write("java"); //更新 out.flush(); //关闭 out.close(); } catch (IOException e) { e.printStackTrace(); } } }
运行结果:
标准输出流:
标准输出流主要包含两个:PrintStream 和 PrintWriter,分别对应字节流和字符流
System.out 其实对应的就是 PrintStream,默认输出到控制台,我们可以重定向它的输出,可以
定向到文件,也就是执行 System.out.println(“hello”)不输出到屏幕,而输出到文件
这里重点说一下PrintStream就好啦!!!
PrintStream(标准字节输出流)
示例代码(7):
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; /** * PrintStream:标准字节输出流,默认输出到控制台 * 标准输出流不需要关闭! */ public class PrintStreamText01 { public static void main(String[] args) { System.out.println("hello world"); PrintStream ps=System.out; ps.println("ni hao"); ps.println("shi jie"); try { //标准输出流不在指向控制台,指向log文件 PrintStream p=new PrintStream(new PrintStream(new FileOutputStream("log"))); //修改输出方向,指向log文件 System.setOut(p); p.println("Hello Keity"); p.println("hello everyone"); System.out.println("hello zhangsan"); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
运行结果:
上面是打印到控制台,下面是写到了文件“log”里面
编写一个日志工具类:
代码如下:
日志工具类
import java.io.FileNotFoundException; 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) { try { PrintStream printStream = new PrintStream(new FileOutputStream("log.txt",true)); System.setOut(printStream); Date nowtime = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy MM-dd HH:mm:ss SS"); String strTime = simpleDateFormat.format(nowtime); System.out.println(strTime + ":" + msg); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
测试类:
public class LogText { public static void main(String[] args) { Logger.log("调用了System类的gc()方法,建议启动垃圾回收器"); Logger.log("用户尝试进行登录,验证失败!"); Logger.log("这个记录日志的非常好用,我很喜欢!"); } }
运行结果:
对象流:
序列化与反序列化
先进行序列化,在进行反序列化
序列化(Serialize):
java对象存储到文件中,将Java对象保存的过程
反序列化(DeSerialize):
将硬盘中的数据重新恢复到内存中,恢复成java对象
参与序列化和反序列化的对象,必须实现Serializable接口,并且手动写出来序列化版本号。(会发生java.io.NotSerializableException 不支持序列化异常,序列化版本号作用? 区分类!)
transient关键字,表示游离的,不参与序列卷
示例代码(8):
Student类:
import java.io.Serializable; public class Student implements Serializable { //IDEA生成的序列化版本号 private static final long serialVersionUID = -827425733984023668L; //手动定义序列化版本号! //private static final long serialVersionUID = 1L;//java虚拟机识别一个类的时候,先通过类名,如果类名一致再通过序列化版本号。 private int no; private String name; public Student() { } public Student(int no, String name) { this.no = no; this.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 String toString() { return "Student{" + "no=" + no + ", name='" + name + '\'' + '}'; } }
序列化:
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class ObjectOutputStreamText01 { public static void main(String[] args) { //创建对象 Student s=new Student(1111,"zhangsan"); try { //序列化 ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("student")); // 序列化对象 out.writeObject(s); //更新 out.flush(); //关闭 out.close(); } catch (IOException e) { e.printStackTrace(); } } }
反序列化:
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; /** * 反序列化 */ public class ObjectInputStreamText01 { public static void main(String[] args) { try { ObjectInputStream ois=new ObjectInputStream(new FileInputStream("student")); //反序列化,读文件 Object o=ois.readObject(); System.out.println(o); ois.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
运行结果:
Student{no=1111, name='zhangsan'}
如果想要一次序列化多个对象呢?
可以将对象放到集合中,序列化集合
注意:
参与序列化的ArrayList集合和User类都必须实现java.io.Serialable接口
示例代码(9):
User类:
import java.io.Serializable; //transient关键字,表示游离的,不参与序列卷 public class User implements Serializable { //序列化版本号 private static final long serialVersionUID = -5443252109301397065L; private int no; //name不参与序列化 //private transient String name; private String name; public User() { } public User(int no, String name) { this.no = no; this.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 String toString() { return "User{" + "no=" + no + ", name='" + name + '\'' + '}'; } }
序列化:
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; public class ObjectOutputText02 { public static void main(String[] args) { List<User> userList=new ArrayList<>(); userList.add(new User(1,"zhangsan")); userList.add(new User(2,"lisi")); userList.add(new User(3,"zhaosan")); try { ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("users")); oos.writeObject(userList); oos.flush(); oos.close(); } catch (IOException e) { e.printStackTrace(); } } }
反序列化:
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.List; public class ObjectInputStreamText02 { public static void main(String[] args) { try { ObjectInputStream ois=new ObjectInputStream(new FileInputStream("users")); /* Object o=ois.readObject(); System.out.println(o instanceof List);*/ List<User> userlist=(List<User>)ois.readObject(); for (User user:userlist){ System.out.println(user); } ois.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
运行结果:
User{no=1, name='zhangsan'} User{no=2, name='lisi'} User{no=3, name='zhaosan'}
IO + Properties联合使用
将经常修改的数据,单独写到一个文本中,进行动态获取,使用的时候只需要修改文件内容,代码不用修改。将这种机制的文件成为:配置文件。
当文件中的格式为:Key=Value的时候,将其称为属性配置文件。
java规范要求:属性配置文件以.properties结尾。
示例代码(10):
import java.io.*; import java.util.Properties; public class IOPropertiesText01 { public static void main(String[] args) { try { FileReader reader=new FileReader("src\\userFile.properties"); Properties po=new Properties(); //调用Properties对象的load()方法将文件中的数据加载到Map集合中 po.load(reader); //调用Key来获取Value String s=po.getProperty("user"); System.out.println(s); String s1=po.getProperty("password"); System.out.println(s1); reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
运行结果:
zhangsan 123123123
属性配置文件:
四、File类
File类常用方法
1.boolean exists() 检查文件或目录是否存在这种抽象路径。
2.boolean createNewFile() 自动创建一个新的空文件命名的抽象路径名的当且仅当该文件不存在。
3.boolean mkdir() 创建该目录下的抽象路径名命名。
4.boolean mkdirs() 多重目录新建出来
5.String getParent() 返回此抽象路径名的父路径名的字符串,或 null如果路径名不叫父目录。
6.File getParentFile()返回此抽象路径名的母抽象路径名,路径名或 null如果这不叫父目录。
7.File getAbsoluteFile() 返回此抽象路径名的绝对形式。
示例代码(11):
import java.io.File; import java.io.IOException; public class FileText01 { public static void main(String[] args) { //创建一个File对象 File file=new File("D:\\file"); //判断是否存在 System.out.println(file.exists()); /* if (!file.exists()){ try { //如果不存在以文件的形式创建出来 file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } }*/ if (!file.exists()){ //以目录形式创建 file.mkdir(); } /* //创建多重目录 File file1=new File("E:\\a\\b\\c\\d"); if (!file1.exists()){ file1.mkdirs(); }*/ File f3=new File("E:\\Day\\IO流\\log"); String parentPath=f3.getParent(); System.out.println(parentPath);//父路径 File parentFile=f3.getParentFile(); System.out.println("获取绝对路径:"+parentFile.getAbsoluteFile()); File f4=new File("copy"); System.out.println("绝对路径:"+f4.getAbsoluteFile()); } }
运行结果:
true E:\Day\IO流 获取绝对路径:E:\Day\IO流 绝对路径:E:\Day\IO流\copy
8.String getName() 返回的名称的文件或目录的路径名表示的抽象
9.boolean isDirectory() 测试文件是否通过这种抽象路径名表示是一个目录。
10.boolean isFile()测试文件是否通过这种抽象路径名表示的是一种正常的文件。
11.long lastModified()返回文件的抽象路径名表示上次修改时间。
12.long length()返回文件的抽象路径名表示的长度。
13.File[] listFiles()获取当前目录下所有的子文件
示例代码(12):
import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; public class FileText02 { public static void main(String[] args) throws Exception { File f=new File("E:\\Day\\IO流\\log"); //获取文件名 System.out.println("文件名:"+f.getName()); //判断是否为一个目录 System.out.println(f.isDirectory()); //判断是否为一个文件 System.out.println(f.isFile()); //获取文件最后一次修改的时间内 long lastTime=f.lastModified();//返回毫秒数 //将毫秒数转换为日期 Date time=new Date(lastTime); SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SS"); String strTime=sdf.format(time); System.out.println(strTime); //获取文件大小 System.out.println(f.length()); //获取当前目录下所有的子文件 File f1=new File("E:\\Day\\IO流"); File[] files=f1.listFiles(); for (File file1:files){ System.out.println(file1); } } }
运行结果:
文件名:log false true 2020-07-20 18:43:31 902 46 E:\Day\IO流\.idea E:\Day\IO流\copy E:\Day\IO流\Copy02.java E:\Day\IO流\data E:\Day\IO流\IO流.iml E:\Day\IO流\log E:\Day\IO流\log.txt E:\Day\IO流\myfile E:\Day\IO流\myfile01 E:\Day\IO流\out E:\Day\IO流\src E:\Day\IO流\student E:\Day\IO流\users Process finished with exit code 0