IO流
IO流概述
什么是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()
方法
文件专属流
- java.io.FileInputStream
- java.io.FileOutputStream
- java.io.FileReader
- java.io.FileWriter
//创建一个字节流对象
FileInputStream fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");
//调用读取数据的方法,一次读取一个字符
//fileInputStream.read() 这个方法的返回值是指定字符的ascii码,在文件中数据都读取完的情况下会返回-1
int read = fileInputStream.read();
//输出一次读取的字符的Ascii码
System.out.println(read);
//读取数据
int read = fileInputStream.read();
//通过循环的方式来读取数据
do {
System.out.println(read);
read = fileInputStream.read();
}while (read !=-1);
fileInputStream.read()
如果传了字符数组,那么返回的是读取的字符个数
//通过字节数组的方式可以一次性读取多个字节
//1、创建一个字节数组
byte[] bytes = new byte[10];
int read = fileInputStream.read(bytes);
//read是读取字符的个数
System.out.println(read);
FileInputStream这个字节流如何将ascII转成对应的字符
可以根据String这个类来完成
//创建一个String类
//参数:读取数组,读取起始位置,读取字符数量
//默认从0开始读,读取长度为bytes.length
String s = new String(bytes);
System.out.println(s);
注:第二次读取会对之前数组中已经存储的数据进行覆盖
读取数据
package com.os467.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class IoTest03 {
public static void main(String[] args) {
//创建输入流对象
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");
//创建字节数组 1kb=1024b
byte[] bytes = new byte[1024];
int read = 0;
//遍历字节数组,读取数据
while ((read = fileInputStream.read(bytes)) != -1){
//根据字符串将读取到的字节转换成字符串,读取到多少个就转换多少个
System.out.print(new String(bytes,0,read));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭流对象
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
方法2:只适合小文件
fileInputStream = new FileInputStream("D:\\pythonschool\\PY_lab\\File\\html.txt");
//获取指定文件的字节总个数
int available = fileInputStream.available();
byte[] bytes = new byte[available];
int read = fileInputStream.read(bytes);
System.out.println(new String(bytes,0,read));
写入数据
//创建字节输出流
fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt");
//写入数据到硬盘上
fileOutputStream.write(97);
//创建一个字节数组
byte[] bytes = {98,99,100,101};
fileOutputStream.write(bytes);
将字符串数据写入
//创建一个字符串
String str = "你好,Tom";
//将字符串转成对应的字节数组
byte[] bytes1 = str.getBytes();
//写入数据
fileOutputStream.write(bytes1);
//刷新
fileOutputStream.flush();
开启写入追加(默认是false,覆盖写入)
fileOutputStream = new FileOutputStream("C:\\Users\\tly20\\Desktop\\test.txt",true);
字符输入输出流
package com.os467.io;
import java.io.*;
/**
* 通过字符输入流和字符输出流来完成一个普通文本文件的拷贝
*/
public class IoTest06 {
public static void main(String[] args){
//文件字符输入流
FileReader fileReader = null;
//文件字符输出流
FileWriter fileWriter = null;
//创建文件字符输入流
try {
//创建文件字符输入流
fileReader = new FileReader("C:\\Users\\tly20\\Desktop\\test.txt");
fileWriter = new FileWriter("C:\\Users\\tly20\\Desktop\\test_copy.txt",true);
//创建字符数组 一次读取1024字符
char[] chars = new char[100];
//读数据
int readCount = 0;
//一边读,一边写
while ((readCount = fileReader.read(chars) )!= -1){
//数据的转换
String str = new String(chars,0,readCount);
//数据的写入,可以直接写入字符串
fileWriter.write(str);
}
//刷新输出流
fileWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (fileReader != null){
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileWriter != null){
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意点:
- 字节流要创建字节数组bytes[]接收读取的数据,字符流要创建字符数组chars[]接收读取的数据
- 输入流和输出流在最后都需要通过
close()
方法关闭资源 - 输出流需要通过
flush()
方法进行刷新
缓冲流
- java.io.BufferedReader
- java.io.BufferedWriter
- java.io.BufferedInputStream
- java.io.BufferedOutputStream
缓冲流会将字节先写到一个缓冲区中,然后通过flush()
或者关闭这个缓冲流来实现将缓冲流中的数据回显
特点:不需要创建数组
package com.os467.io;
import java.io.*;
/**
* 缓冲流:
* 自带缓冲区的流,在一次读取多个字符的时候,可以不用创建数组
*/
public class IoTest08 {
public static void main(String[] args) throws Exception{
String readLine = null;
//创建缓冲流对象
BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\tly20\\Desktop\\test.txt"));
//调用读取数据的方法
//读取一行数据
while ((readLine = bufferedReader.readLine()) != null){
System.out.println(readLine);
}
bufferedReader.close();
}
}
转换流
可以将字符流转换成字节流,也可以将字节流转换成字符流 InputStreamReader
包装流和节点流: new 流(new 流()) 外层是包装流,里面的是节点流
在关闭流对象的时候,只需要关闭最外层的包装流即可
//创建转换流 将字节流转换成字符流
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\tly20\\Desktop\\test.txt"));
//关闭流的时候只需要关闭最外层的包装流即可
inputStreamReader.close();
数据流
- java.io.DataInputStream
- java.io.DataOutputStream
数据流在写入数据到文件中的时候,要根据数据类型来写入,而且写入之后的文件是加密的,是我们看不懂的文件,如果想读取这些数据的话,只能通过数据输入流来读取
数据输入流在读取数据的时候,是要按照写入的顺序来读取,而且需要按照指定的数据类型来读取
存入数据流数据
public static void main(String[] args) throws Exception {
//创建一个数据输出流
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("C:\\Users\\tly20\\Desktop\\test_copy.txt"));
//指定数据
boolean b = false;
float f = 10f;
int i = 20;
double d = 3.14;
//调用写入的方法
dataOutputStream.writeBoolean(b);
dataOutputStream.writeFloat(f);
dataOutputStream.writeInt(i);
dataOutputStream.writeDouble(d);
//刷新输出流
dataOutputStream.flush();
//关闭流
dataOutputStream.close();
}
按照存储的顺序读取加密数据
public static void main(String[] args) throws Exception {
//创建一个数据输出流
DataInputStream dataInputStream = new DataInputStream(new FileInputStream("C:\\Users\\tly20\\Desktop\\test_copy.txt"));
//读取数据要根据写入的顺序来读取
boolean b = dataInputStream.readBoolean();
float v = dataInputStream.readFloat();
int i = dataInputStream.readInt();
double v1 = dataInputStream.readDouble();
System.out.println(b);
System.out.println(v);
System.out.println(i);
System.out.println(v1);
dataInputStream.close();
}
标准输出流
- java.io.PrintWriter
- java.io.PrintStream
默认情况是我们经常使用的控制台打印,我们可以去改变输出流的输出方向
标准输出流的默认输出方向就是控制台
标准输出流以后适用的场景:
可以去收集日志
比如说程序在某个时间点发生了某某异常,然后可以根据这个流来收集异常信息
或者去收集模块域模块之间的调用次数
//创建输出流对象,使用相对路径
PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));
//打印hello world在data.txt文件中
printStream.print("hello world");
//创建输出流对象,不设置路径会默认打印在控制台
PrintStream printStream = System.out;
printStream.print("Hello world");
//设置输出的方向
PrintStream printStream = new PrintStream(new FileOutputStream("data.txt"));
System.setOut(printStream);
//打印在data.txt中
System.out.println("hello world");
//刷新输出流
printStream.flush();
//关闭资源
printStream.close();
异常日志收集工具类
package com.os467.utils;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日志收集工具类
*/
public class LoggerUtils {
private LoggerUtils(){
}
/**
* 收集日志的方法
* @param message
*/
public static void logger(String message){
PrintStream printStream = null;
try {
//创建标准输出流对象
printStream = new PrintStream(new FileOutputStream("logger",true));
//设置输出路径
System.setOut(printStream);
//创建日期对象
Date date = new Date();
//创建日期格式化对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//将日期转成字符串
String time = simpleDateFormat.format(date);
//记录日志
System.out.println(time +" : "+message );
//刷新输出流对象
printStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (printStream != null){
printStream.close();
}
}
}
}
对象专属流
java.io.ObjectInputStream
java.io.ObjectOutput
可序列化接口:
写入的对象必须实现序列化的接口Serializable
- 对象专属流可以完成在一个内存和硬盘上对java对象进行序列化与反序列化
序列化与反序列化
序列化:
序列化就是将java对象写入到硬盘上,写入之后的文件也是加密的格式
反序列化:
将文件中我们看不懂的加密数据读取出来,读取的就是java对象
以后我们搭建的分布式项目,模块与模块间如果想完成数据的传输,需要通过序列化与反序列化来完成
序列化java对象的时候,该对象需要去实现一个序列化的接口
Serializable
序列化接口,这个接口没有任何的方法,只是一个标识性接口因为我们在执行加载某一个类的时候会生成该类对应的字节码文件,这个时候jvm虚拟机会检查该类是否有
Serializable
这个接口如果有,则赋予该类序列化的权限,如果没有则不能进行序列化
对User类的对象进行序列化操作
public static void main(String[] args) throws Exception {
//创建输出流对象(序列化对象)
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("users.txt"));
//创建user对象
User user = new User();
//将java对象写到文件中
objectOutputStream.writeObject(user);
//刷新输出流
objectOutputStream.flush();
//关闭流
objectOutputStream.close();
}
对文件中存储的user数据进行反序列化操作
public static void main(String[] args) throws Exception {
//创建输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("users.txt"));
//读取数据
Object object = objectInputStream.readObject();
//判断数据类型
if (object instanceof User){
User user = (User)object;
System.out.println(user);
}
//关闭流
objectInputStream.close();
}
对多个对象进行序列化与反序列化(通过ArrayList来完成)
序列化多个对象
-----------------------------------------------------------------------------------------
//创建输出流对象(序列化对象)
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("users.txt"));
//创建user对象
User user1 = new User(1,"张三1","男");
User user2 = new User(2,"张三2","男");
User user3 = new User(3,"张三3","男");
User user4 = new User(4,"张三4","男");
//创建集合对象
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
//将java对象写到文件中
objectOutputStream.writeObject(users);
//刷新输出流
objectOutputStream.flush();
//关闭流
objectOutputStream.close();
------------------------------------------------------------------------------------------
反序列化多个对象
------------------------------------------------------------------------------------------
//创建输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("users.txt"));
//用一个数组来接收
ArrayList<User> arrayList = (ArrayList<User>)objectInputStream.readObject();
for (User user : arrayList) {
System.out.println(user);
}
//关闭流
objectInputStream.close();
关于序列化版本号的问题:
如果在反序列化之前修改了类中的代码,在类加载的时候会重新生成字节码文件,那么jvm给该类生成的序列化版本号就会改变,在反序列化时,旧的序列化版本号与新生成的版本号不对应,就无法完成反序列化
解决方案:
我们可以在类中写死一个序列化版本号
private static final long serialVersionUID = 8683452581122892189L;
关键字transient
transient
可以使得某一属性不参与序列化的操作,在反序列化后会得到null值
JAVA I/O读取文件
读取文本文件
new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"UTF-8"))
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com