什么是IO流
io流就是内存和硬盘上完成对数据的读和写
I:Input
O:Output
通过IO可以完成硬盘文件的读和写
往内存中去,叫做输入(Input),或者叫做读(Read)
从内存中出来,叫做输出(Output),或者叫做写(Write)
字节和字符:
字节(Byte)是一种计量单位,表示数据量多少,它是计算机信息技术用于计量存储容量的一种计量单位
字符就是键盘上所有的符号,包括英文,汉字等
针对于每一个字符,都有固定的Ascll码与之对应,!占了一个字节,然后根据字节流读取数据的时候,读取的是!这个字符所对应的AscII码
像特殊符号、英文在java中占了一个字节,汉字你在java中占了两个字节
有的流时按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位
按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的
字节流在读取数据的时候,会遍历文件中的字符,然后读取的是每个字符所对应的asII码
字节流可以读取任意类型的文件(普通文本,视频,录音,图片...)
在读取数据的时候,就是一个字符一个字符的进行读取,字符流的效率比较高,但是只能读取普通文本
输入和输出:
java.io.InputStream 字节输入流
读取硬盘数据到内存
java.io.OutputStream 字节输出流
存储内存数据到硬盘
java.io.Reader 字符输入流
java.io.Writer 字符输出流
所有的流都实现了java.io.Closeable接口,都是可关闭的,都有close()方法
所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法
xxxxxxxxxx201//创建一个字节流对象2FileInputStream fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");3
4//调用读取数据的方法,一次读取一个字符5//fileInputStream.read() 这个方法的返回值是指定字符的ascii码,在文件中数据都读取完的情况下会返回-16int read = fileInputStream.read();7
8//输出一次读取的字符的Ascii码9System.out.println(read);10
11//读取数据12int read = fileInputStream.read();13
14//通过循环的方式来读取数据15do {16
17 System.out.println(read);18
19 read = fileInputStream.read();20}while (read !=-1);
fileInputStream.read() 如果传了字符数组,那么返回的是读取的字符个数
xxxxxxxxxx81//通过字节数组的方式可以一次性读取多个字节2//1、创建一个字节数组3byte[] bytes = new byte[10];4
5int read = fileInputStream.read(bytes);6
7//read是读取字符的个数8System.out.println(read);FileInputStream这个字节流如何将ascII转成对应的字符
可以根据String这个类来完成
xxxxxxxxxx61//创建一个String类2//参数:读取数组,读取起始位置,读取字符数量3//默认从0开始读,读取长度为bytes.length4String s = new String(bytes);5
6System.out.println(s);
注:第二次读取会对之前数组中已经存储的数据进行覆盖
xxxxxxxxxx521package com.os467.io;2
3import java.io.FileInputStream;4import java.io.FileNotFoundException;5import java.io.IOException;6
7public class IoTest03 {8
9 public static void main(String[] args) {10
11 //创建输入流对象12 FileInputStream fileInputStream = null;13
14 try {15
16 fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");17
18 //创建字节数组 1kb=1024b19 byte[] bytes = new byte[1024];20
21 int read = 0;22
23 //遍历字节数组,读取数据24 while ((read = fileInputStream.read(bytes)) != -1){25
26 //根据字符串将读取到的字节转换成字符串,读取到多少个就转换多少个27 System.out.print(new String(bytes,0,read));28
29 }30
31
32 } catch (FileNotFoundException e) {33 e.printStackTrace();34 } catch (IOException e) {35 e.printStackTrace();36 } finally {37
38 try {39 //关闭流对象40 fileInputStream.close();41 } catch (IOException e) {42 e.printStackTrace();43 }44
45 }46
47 }48
49
50
51
52}
方法2:只适合小文件
xxxxxxxxxx101fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");2
3//获取指定文件的字节总个数4int available = fileInputStream.available();5
6byte[] bytes = new byte[available];7
8int read = fileInputStream.read(bytes);9
10System.out.println(new String(bytes,0,read));xxxxxxxxxx101//创建字节输出流2fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt");3
4//写入数据到硬盘上5fileOutputStream.write(97);6
7//创建一个字节数组8byte[] bytes = {98,99,100,101};9
10fileOutputStream.write(bytes);将字符串数据写入
xxxxxxxxxx111//创建一个字符串2String str = "你好,Tom";3
4//将字符串转成对应的字节数组5byte[] bytes1 = str.getBytes();6
7//写入数据8fileOutputStream.write(bytes1);9
10//刷新11fileOutputStream.flush();开启写入追加(默认是false,覆盖写入)
xxxxxxxxxx11fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt",true);
字符输入输出流
xxxxxxxxxx811package com.os467.io;2
3import java.io.*;4
5/**6 * 通过字符输入流和字符输出流来完成一个普通文本文件的拷贝7 */8public class IoTest06 {9 public static void main(String[] args){10
11 //文件字符输入流12 FileReader fileReader = null;13
14 //文件字符输出流15 FileWriter fileWriter = null;16
17
18 //创建文件字符输入流19 try {20
21 //创建文件字符输入流22 fileReader = new FileReader("C:\\Users\\tly20\\Desktop\\test.txt");23
24 fileWriter = new FileWriter("C:\\Users\\tly20\\Desktop\\test_copy.txt",true);25
26 //创建字符数组 一次读取1024字符27 char[] chars = new char[100];28
29 //读数据30 int readCount = 0;31
32 //一边读,一边写33 while ((readCount = fileReader.read(chars) )!= -1){34
35 //数据的转换36 String str = new String(chars,0,readCount);37
38 //数据的写入,可以直接写入字符串39 fileWriter.write(str);40
41 }42
43 //刷新输出流44 fileWriter.flush();45
46 } catch (FileNotFoundException e) {47 e.printStackTrace();48 } catch (IOException e) {49 e.printStackTrace();50 } finally {51
52 //关闭资源53 if (fileReader != null){54
55 try {56 fileReader.close();57 } catch (IOException e) {58 e.printStackTrace();59 }60
61 }62
63 if (fileWriter != null){64
65 try {66 fileWriter.close();67 } catch (IOException e) {68 e.printStackTrace();69 }70
71 }72
73 }74
75
76 }77
78
79
80
81}
注意点:
close()方法关闭资源flush()方法进行刷新
缓冲流会将字节先写到一个缓冲区中,然后通过flush()或者关闭这个缓冲流来实现将缓冲流中的数据回显
特点:不需要创建数组
xxxxxxxxxx301package com.os467.io;2
3import java.io.*;4
5/**6 * 缓冲流:7 * 自带缓冲区的流,在一次读取多个字符的时候,可以不用创建数组8 */9public class IoTest08 {10 public static void main(String[] args) throws Exception{11
12 String readLine = null;13
14 //创建缓冲流对象15 BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\tly20\\Desktop\\test.txt"));16
17 //调用读取数据的方法18 //读取一行数据19
20 while ((readLine = bufferedReader.readLine()) != null){21
22 System.out.println(readLine);23
24 }25
26
27 bufferedReader.close();28 }29
30}
可以将字符流转换成字节流,也可以将字节流转换成字符流 InputStreamReader
包装流和节点流: new 流(new 流()) 外层是包装流,里面的是节点流
在关闭流对象的时候,只需要关闭最外层的包装流即可
xxxxxxxxxx51//创建转换流 将字节流转换成字符流2 InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\tly20\\Desktop\\test.txt"));3
4//关闭流的时候只需要关闭最外层的包装流即可5inputStreamReader.close();
数据流在写入数据到文件中的时候,要根据数据类型来写入,而且写入之后的文件是加密的,是我们看不懂的文件,如果想读取这些数据的话,只能通过数据输入流来读取
数据输入流在读取数据的时候,是要按照写入的顺序来读取,而且需要按照指定的数据类型来读取
存入数据流数据
xxxxxxxxxx241public static void main(String[] args) throws Exception {2
3 //创建一个数据输出流4 DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("C:\\Users\\tly20\\Desktop\\test_copy.txt"));5
6 //指定数据7 boolean b = false;8 float f = 10f;9 int i = 20;10 double d = 3.14;11 12 //调用写入的方法13 dataOutputStream.writeBoolean(b);14 dataOutputStream.writeFloat(f);15 dataOutputStream.writeInt(i);16 dataOutputStream.writeDouble(d);17 18 //刷新输出流19 dataOutputStream.flush();20 21 //关闭流22 dataOutputStream.close();23 24}按照存储的顺序读取加密数据
xxxxxxxxxx231public static void main(String[] args) throws Exception {2
3 //创建一个数据输出流4 DataInputStream dataInputStream = new DataInputStream(new FileInputStream("C:\\Users\\tly20\\Desktop\\test_copy.txt"));5
6 //读取数据要根据写入的顺序来读取7 boolean b = dataInputStream.readBoolean();8
9 float v = dataInputStream.readFloat();10
11 int i = dataInputStream.readInt();12
13 double v1 = dataInputStream.readDouble();14
15 System.out.println(b);16 System.out.println(v);17 System.out.println(i);18 System.out.println(v1);19 20 dataInputStream.close();21
22
23}
默认情况是我们经常使用的控制台打印,我们可以去改变输出流的输出方向
标准输出流的默认输出方向就是控制台
标准输出流以后适用的场景:
可以去收集日志
比如说程序在某个时间点发生了某某异常,然后可以根据这个流来收集异常信息
或者去收集模块域模块之间的调用次数
xxxxxxxxxx51//创建输出流对象,使用相对路径2PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));3
4//打印hello world在data.txt文件中5printStream.print("hello world");xxxxxxxxxx41//创建输出流对象,不设置路径会默认打印在控制台2PrintStream printStream = System.out;3
4printStream.print("Hello world");xxxxxxxxxx131//设置输出的方向2PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));3
4System.setOut(printStream);5
6//打印在data.txt中7System.out.println("hello world");8
9//刷新输出流10printStream.flush();11
12//关闭资源13printStream.close();
xxxxxxxxxx661package com.os467.utils;2
3import java.io.FileNotFoundException;4import java.io.FileOutputStream;5import java.io.ObjectOutputStream;6import java.io.PrintStream;7import java.text.Format;8import java.text.SimpleDateFormat;9import java.util.Date;10
11/**12 * 日志收集工具类13 */14public class LoggerUtils {15
16 private LoggerUtils(){17
18 }19
20 /**21 * 收集日志的方法22 * @param message23 */24 public static void logger(String message){25
26 PrintStream printStream = null;27
28 try {29
30 //创建标准输出流对象31 printStream = new PrintStream(new FileOutputStream("logger",true));32
33 //设置输出路径34 System.setOut(printStream);35
36 //创建日期对象37 Date date = new Date();38
39 //创建日期格式化对象40 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");41
42 //将日期转成字符串43 String time = simpleDateFormat.format(date);44
45 //记录日志46 System.out.println(time +" : "+message );47
48 //刷新输出流对象49 printStream.flush();50
51 } catch (FileNotFoundException e) {52 e.printStackTrace();53 }finally {54
55 if (printStream != null){56
57 printStream.close();58
59 }60
61 }62
63
64 }65
66}
java.io.ObjectInputStream
java.io.ObjectOutput
可序列化接口:
写入的对象必须实现序列化的接口Serializable
序列化:
序列化就是将java对象写入到硬盘上,写入之后的文件也是加密的格式
反序列化:
将文件中我们看不懂的加密数据读取出来,读取的就是java对象
Serializable序列化接口,这个接口没有任何的方法,只是一个标识性接口
因为我们在执行加载某一个类的时候会生成该类对应的字节码文件,这个时候jvm虚拟机会检查该类是否有Serializable这个接口
如果有,则赋予该类序列化的权限,如果没有则不能进行序列化
对User类的对象进行序列化操作
xxxxxxxxxx181public static void main(String[] args) throws Exception {2
3 //创建输出流对象(序列化对象)4 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("users.txt"));5
6 //创建user对象7 User user = new User();8
9 //将java对象写到文件中10 objectOutputStream.writeObject(user);11
12 //刷新输出流13 objectOutputStream.flush();14
15 //关闭流16 objectOutputStream.close();17
18}
对文件中存储的user数据进行反序列化操作
xxxxxxxxxx221public static void main(String[] args) throws Exception {2
3 //创建输入流对象4 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("users.txt"));5
6 //读取数据7 Object object = objectInputStream.readObject();8
9 //判断数据类型10 if (object instanceof User){11
12 User user = (User)object;13
14 System.out.println(user);15
16 }17
18 //关闭流19 objectInputStream.close();20
21
22}
对多个对象进行序列化与反序列化(通过ArrayList来完成)
xxxxxxxxxx441序列化多个对象2-----------------------------------------------------------------------------------------3//创建输出流对象(序列化对象)4ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("users.txt"));5
6//创建user对象7User user1 = new User(1,"张三1","男");8User user2 = new User(2,"张三2","男");9User user3 = new User(3,"张三3","男");10User user4 = new User(4,"张三4","男");11
12//创建集合对象13ArrayList<User> users = new ArrayList<>();14
15users.add(user1);16users.add(user2);17users.add(user3);18users.add(user4);19
20//将java对象写到文件中21objectOutputStream.writeObject(users);22
23//刷新输出流24objectOutputStream.flush();25
26//关闭流27objectOutputStream.close();28------------------------------------------------------------------------------------------29反序列化多个对象30------------------------------------------------------------------------------------------31//创建输入流对象32ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("users.txt"));33
34//用一个数组来接收35ArrayList<User> arrayList = (ArrayList<User>)objectInputStream.readObject();36
37for (User user : arrayList) {38
39 System.out.println(user);40
41}42
43//关闭流44objectInputStream.close();
关于序列化版本号的问题:
如果在反序列化之前修改了类中的代码,在类加载的时候会重新生成字节码文件,那么jvm给该类生成的序列化版本号就会改变,在反序列化时,旧的序列化版本号与新生成的版本号不对应,就无法完成反序列化
解决方案:
我们可以在类中写死一个序列化版本号
xxxxxxxxxx11private static final long serialVersionUID = 8683452581122892189L;
transient可以使得某一属性不参与序列化的操作,在反序列化后会得到null值
JAVA I/O读取文件
读取文本文件
new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"UTF-8"))