什么是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()
方法
xxxxxxxxxx
201//创建一个字节流对象
2FileInputStream fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");
3
4//调用读取数据的方法,一次读取一个字符
5//fileInputStream.read() 这个方法的返回值是指定字符的ascii码,在文件中数据都读取完的情况下会返回-1
6int 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()
如果传了字符数组,那么返回的是读取的字符个数
xxxxxxxxxx
81//通过字节数组的方式可以一次性读取多个字节
2//1、创建一个字节数组
3byte[] bytes = new byte[10];
4
5int read = fileInputStream.read(bytes);
6
7//read是读取字符的个数
8System.out.println(read);
FileInputStream这个字节流如何将ascII转成对应的字符
可以根据String这个类来完成
xxxxxxxxxx
61//创建一个String类
2//参数:读取数组,读取起始位置,读取字符数量
3//默认从0开始读,读取长度为bytes.length
4String s = new String(bytes);
5
6System.out.println(s);
注:第二次读取会对之前数组中已经存储的数据进行覆盖
xxxxxxxxxx
521package 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=1024b
19 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:只适合小文件
xxxxxxxxxx
101fileInputStream = 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));
xxxxxxxxxx
101//创建字节输出流
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);
将字符串数据写入
xxxxxxxxxx
111//创建一个字符串
2String str = "你好,Tom";
3
4//将字符串转成对应的字节数组
5byte[] bytes1 = str.getBytes();
6
7//写入数据
8fileOutputStream.write(bytes1);
9
10//刷新
11fileOutputStream.flush();
开启写入追加(默认是false,覆盖写入)
xxxxxxxxxx
11fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt",true);
字符输入输出流
xxxxxxxxxx
811package 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()
或者关闭这个缓冲流来实现将缓冲流中的数据回显
特点:不需要创建数组
xxxxxxxxxx
301package 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 流()) 外层是包装流,里面的是节点流
在关闭流对象的时候,只需要关闭最外层的包装流即可
xxxxxxxxxx
51//创建转换流 将字节流转换成字符流
2 InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\tly20\\Desktop\\test.txt"));
3
4//关闭流的时候只需要关闭最外层的包装流即可
5inputStreamReader.close();
数据流在写入数据到文件中的时候,要根据数据类型来写入,而且写入之后的文件是加密的,是我们看不懂的文件,如果想读取这些数据的话,只能通过数据输入流来读取
数据输入流在读取数据的时候,是要按照写入的顺序来读取,而且需要按照指定的数据类型来读取
存入数据流数据
xxxxxxxxxx
241public 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}
按照存储的顺序读取加密数据
xxxxxxxxxx
231public 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}
默认情况是我们经常使用的控制台打印,我们可以去改变输出流的输出方向
标准输出流的默认输出方向就是控制台
标准输出流以后适用的场景:
可以去收集日志
比如说程序在某个时间点发生了某某异常,然后可以根据这个流来收集异常信息
或者去收集模块域模块之间的调用次数
xxxxxxxxxx
51//创建输出流对象,使用相对路径
2PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));
3
4//打印hello world在data.txt文件中
5printStream.print("hello world");
xxxxxxxxxx
41//创建输出流对象,不设置路径会默认打印在控制台
2PrintStream printStream = System.out;
3
4printStream.print("Hello world");
xxxxxxxxxx
131//设置输出的方向
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();
xxxxxxxxxx
661package 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 message
23 */
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类的对象进行序列化操作
xxxxxxxxxx
181public 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数据进行反序列化操作
xxxxxxxxxx
221public 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来完成)
xxxxxxxxxx
441序列化多个对象
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给该类生成的序列化版本号就会改变,在反序列化时,旧的序列化版本号与新生成的版本号不对应,就无法完成反序列化
解决方案:
我们可以在类中写死一个序列化版本号
xxxxxxxxxx
11private static final long serialVersionUID = 8683452581122892189L;
transient
可以使得某一属性不参与序列化的操作,在反序列化后会得到null值
JAVA I/O读取文件
读取文本文件
new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"UTF-8"))