前言:以前学习Java的时候对IO接触很少,所以总觉得这一块知识很复杂,所以就找个时间,总结一下流的处理、用法,巩固一下知识
java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。
1.IO流
IO流可以分为字符流、字节流两大类。 字节流一个字节占8位(bit),字符流根据不同字符所占的字节不同而读取的大小不同,常见中文一个字符占两个字节(16bit)。
IO流很巧妙的使用了设计模式《装饰模式》,外层的处理流包裹着内层的节点流,外层的处理流方法对内层的节点流方法调用,然后做对应的扩展
流的体系因为功能不同,但是有共性内容,形成继承体系。该体系一共有四个基类,而且都是抽象类。:
字节流:InputStream OutputStream 字符流:Reader Writer
类型 | 说明 |
---|---|
文件流 | 对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream |
缓冲流 | 在读入或写出时,数据可以基于缓冲批量读写,以减少I/O的次数:BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream |
内存流 | 1.从/向内存数组读写数据: CharArrayReader、 CharArrayWriter、ByteArrayInputStream、ByteArrayOutputStream 2.从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream |
转换流 | 按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader,Writer的转换类):InputStreamReader、OutputStreamWriter |
对象流 | 字节流与对象实例相互转换,实现对象的序列化 :ObjectInputStream、ObjectOutputStream 注意: 1.读取顺序和写入顺序一定要一致,不然会读取出错。 2.在对象属性前面加transient 关键字,则该对象的属性不会被序列化 |
打印流 | 只有输出,没有输入,在整个IO包中,打印流是输出信息最方便的类,分为 PrintWriter(字符打印流)、PrintStream(字节打印流) |
DataConversion数据流 | 按基本数据类型读/写,可以字节流与基本类型数据相互转换:DataInputStream、DataOutputStream |
过滤流 | 在数据进行读或写时进行过滤:FilterReader、FilterWriter、FilterInputStream、FilterOutputStream |
合并流 | 把多个输入流连接成一个输入流 :SequenceInputStream |
操作ZIP包流 | ZipInputStream可以读取zip格式的流,ZipOutputStream可以把多份数据写入zip包 |
操作JAR包流 | JarInputStream/JarOutputStream,派生自ZipInputStream/ZipOutputStream,它增加的主要功能是直接读取jar文件里面的MANIFEST.MF文件。因为本质上jar包就是zip包,只是额外附加了一些固定的描述文件。 |
管道流 | 实现管道的输入和输出(进程间通信): PipedReader、PipedWriter、PipedInputStream、PipedOutputStream |
Counting计数 | 在读入数据时对行记数 :LineNumberReader、LineNumberInputStream |
推回输入流 | 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream |
接收和响应客户端请求流 | servletinputstream:用来读取客户端的请求信息的输入流 servletoutputstream:可以将数据返回到客户端 |
随机读取写入流 | RandomAccessFile 既可以读取文件内容,也可以向文件输出数据,RandomAccessFile 对象包含一个记录指针,标识当前读写处的位置,可以控制记录指针从IO任何位置读写文件 |
加密流 | 对流加密/解密 CipherOutputStream 由一个 OutputStream 和一个 Cipher 组成 ,write() 方法在将数据写出到基础 OutputStream 之前先对该数据进行处理(加密或解密) , 同样CipherInputStream是由InputStream和一个Cipher组成,read()方法在读入时,对数据进行加解密操作. |
数字签名流 | DigestInputStream : 最大的特点是在读取的数据的时候已经调用MessageDigest实例的update方法,当数据从底层的数据流中读取之后就只可以直接调用MessageDigest实例的digest()方法了,从而完成对输入数据的摘要加密 DigestOutputStream :最大的特点是在向底层的输出流写入数据的时候已经调用MessageDigest实例的update方法,并作为MessageDigest的输入数据,之后就可以直接调用MessageDigest实例的digest()方法完成加密过程;同样的,是否对数据加密也是由该流的on(boolean b)方法进行控制的,如果设置成false,那么在写出数据的过程中便不会将数据传给update方法,那么此时它跟普通的输出流就没有任何区别了。 |
2.操作方法
不同的操作,我们可以选择对应的流来进行处理,下面是总结的操作方法和代码演示。
1.字节流
/**
* @author :endwas
* @description:io流test
* @date :Created in 2021/3/25 14:22
*/
public class IOStream {
private static void byteStream(String inputUrl, String outUrl) {
//新建一个文件目录
File file = new File(outUrl);
try (FileInputStream in = new FileInputStream(inputUrl);
FileOutputStream out = new FileOutputStream(file)) {
//通过逐个读取,存入字节,实现文件拷贝
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
System.out.println("传输完成!");
} catch (IOException e) {
// log
}
}
public static void main(String[] args) {
byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
}
}
2.字符流
在这里插入代码片/**
* @author :endwas
* @description:io流test
* @date :Created in 2021/3/25 14:22
*/
public class IOStream {
private static void charStream(String url, String outrul) {
File f = new File(outrul);
try (FileReader r = new FileReader(url);
FileWriter w = new FileWriter(outrul)) {
// 切记这个数字不是指读的数量而是读取的字符相对应的数字,一次两个字节
int k;
while ((k = r.read()) != -1){
w.write(k);
}
} catch (IOException e) {
// log
}
}
public static void main(String[] args) {
// byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
charStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
}
}
3.缓冲流
本质只是套了层管道,让IO流能得到缓冲,提高效率
/**
* @author :endwas
* @description:io流test
* @date :Created in 2021/3/25 14:22
*/
public class IOStream {
private static void bufferStream(String url, String outUrl) {
//新建一个文件目录
File file = new File(outUrl);
try (FileInputStream in = new FileInputStream(url);
FileOutputStream out = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(out)) {
//通过逐个读取,存入字节,实现文件拷贝
int c;
while ((c = bis.read()) != -1) {
bos.write(c);
}
System.out.println("传输完成!");
} catch (IOException e) {
// log
}
}
public static void main(String[] args) {
// byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
// charStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
bufferStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
}
}
BufferedWriter and BufferedReader 注:这种方法读取会将读取的行末尾换行或者空格删去;
private static void bufferReader(String url, String outUrl) {
File f = new File(url);
File f2 = new File(outUrl);
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(f2))) {
String line;
while ( (line = bufferedReader.readLine())!= null){
bufferedWriter.write(line);
}
} catch (IOException e) {
// log
}
}
public static void main(String[] args) {
// byteStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
// charStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
// bufferStream("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
bufferReader("D:\\11\\input\\byteStream.txt", "D:\\11\\output\\out.txt");
}
4.对象流
一般作用于对象序列化,要注意实现Serializable 接口 对象流输入流:,不仅可以读取/写入基本类型,也可以写入对象 ,读取的顺序与写出保持一致否则报错 下列代码转自:https://blog.csdn.net/qq877728715/article/details/103844382
@Data
@Getter
@Setter
@ToString
@AllArgsConstructor
class User implements Serializable {
private static final long serialVersionUID = -2119298670197329888L;
private Integer id;
private String userName;
private String passWord;
}
/**
* 对象流: 不仅可以读取/写入基本类型,也可以写入对象 ,读取的顺序与写出保持一致否则报错java.io.EOFException
* 1、写出后读取
* 2、读取的顺序与写出保持一致
* 3、不是所有的对象都可以序列化Serializable,必须要实现Serializable接口
* <p>
* ObjectOutputStream ObjectInputStream
*/
public class ObjectStream0 {
@Test
public void tesObjectStream() throws IOException, ClassNotFoundException {
// 写出 -->序列化
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("obj.ser")));
// 操作数据类型 +数据
oos.writeUTF("编码辛酸泪");
oos.writeInt(18);
oos.writeBoolean(false);
oos.writeChar('a');
//对象
oos.writeObject("谁解其中味");
oos.writeObject(new Date());
Employee emp = new Employee("马云", 400);
oos.writeObject(emp);
oos.flush();
oos.close();
// 读取 -->反序列化
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("obj.ser")));
//序列化写出和反序列化读取读取顺序必须一致
String msg = ois.readUTF();
int age = ois.readInt();
boolean flag = ois.readBoolean();
char ch = ois.readChar();
System.out.println(flag);
// 对象的数据还原
Object str = ois.readObject();
Object date = ois.readObject();
Object employee = ois.readObject();
if (str instanceof String) {
String strObj = (String) str;
System.out.println(strObj);
}
if (date instanceof Date) {
Date dateObj = (Date) date;
System.out.println(dateObj);
}
if (employee instanceof Employee) {
Employee empObj = (Employee) employee;
System.out.println(empObj.getName() + "-->" + empObj.getSalary());
}
ois.close();
}
/**
* 序列化集合到文件中
* @throws IOException
* @throws ClassNotFoundException
*/
@Test
public void testSerializeObject() throws IOException, ClassNotFoundException {
String path = "user.ser";
List<User> userList = new ArrayList<>();
for (int i = 0 ;i<100000 ;i++) {
userList.add(new User(i,"admin"+i,"123"+i));
}
serializationObjectToFile(Arrays.asList(userList), path);
}
/**
* 反序列化文件中集合对象
* @throws IOException
* @throws ClassNotFoundException
*/
@Test
public void testDeSerializeObject() throws IOException, ClassNotFoundException {
String path = "user.ser";
List<User> userList = deserializationObjectToFile(path);
System.out.println(userList);
}
/**
* 序列化对象到指定文件
*
* @param list 对象
* @param path 文件路径
*/
public static <T> void serializationObjectToFile(List<T> list, String path) {
try (
//1.选择流
// 写出->序列化
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(path)));
) {
oos.writeObject(list);
oos.flush();
System.out.println("序列化成功");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 反序列指定文件中的集合
*
* @param path 文件路径
*/
public <T> List<T> deserializationObjectToFile(String path) {
try (
//1.选择流
// 读取->反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
) {
System.out.println("反序列化成功");
List<T> list = (List<T>) ois.readObject();
return list;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
注意点:
- 当调用read等无参方法时候切记这个数字不是指读的数量而是读取的字符/字节相对应的数字,当使用read(bytes[], 0, length) 等方法时候返回的是读取读取数组长度;
- BufferedWriter and BufferedReader的字符流会将读取的行末尾换行或者空格删去;
- 对象流输入流:,不仅可以读取/写入基本类型,也可以写入对象 ,读取的顺序与写出保持一致否则报错
参考: https://blog.csdn.net/qq_41097354/article/details/90574770
https://blog.csdn.net/qq877728715/article/details/103844382
评论