IO流(Input/Output Stream)是Java中的一种输入输出机制,是处理输入输出流的一组类和接口的集合。在Java中,所有的输入输出都是通过流来实现的(即流指的是字符或字节流)。输入流用来从数据源中以某种方式读取数据,输出流用来向目标数据写入数据。IO流可以用来读写文件、网络连接、控制台输入输出等。Java中的IO流主要分为字节流和字符流两种类型。
根据数据的流向分为:
输入流和输出流。
其他设备
上读取到内存
中的流,通常使用FileInputStream或BufferedInputStream来实例化。内存
中写出到其他设备
上的流,通常使用FileOutputStream或BufferedOutputStream来实例化。格局数据的类型分为:
字节流和字符流。
字节流 :以字节为单位,读写数据的流。
字符流 :以字符为单位,读写数据的流。
字节流(Byte stream)是一种数据流的形式,它以字节(byte)为单位进行传输
以字节为单位,读(输入)、写(输出)数据
在Java语言中,字节流是通过InputStream和OutputStream两个类实现的
close方法
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用如果完成流的操作后没有释放系统资源,可能会导致以下问题:
因此,在完成流操作后,一定要及时释放资源,避免以上问题的出现。可以使用try-with-resources语句或手动调用close()方法来关闭流并释放资源。
注意:这是所以流操作都要注意的
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法
构造方法:
多态的手法
常用方法:
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b)
:将指定的字节输出流。注意:close方法,当完成流的操作时,必须调用此方法,释放系统资源。
//创建对象
OutputStream os=new FileOutputStream("D:\\MMM.txt");
//写数据 write(int b) 方法,每次可以写出一个字节数据
os.write(97);
os.write(98);
os.write(99);
os.close();
OutputStream os = new FileOutputStream("D:\\MMM.txt");
byte [] bytes="Java入门到精通".getBytes();
os.write(bytes);
os.close();
OutputStream os = new FileOutputStream("D:\\MMM.txt");
byte[] bytes="aabbccddeee".getBytes();
os.write(bytes,2,2);
os.close();
OutputStream fos = new FileOutputStream("D:\\MMM.txt",true);
String str="\nbcbc";
fos.write(str.getBytes());
fos.close();
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
构造方法:
多态的手法
常用方法
public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read()
: 从输入流读取数据的下一个字节。read()
:,读取字节,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1
read(byte[] b)
:每次读取b的长度个字节到数组中,返回读取到的有效字节个数public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。注意:close方法,当完成流的操作时,必须调用此方法,释放系统资源。
package 字节输入流;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo {
public static void main(String[] args) throws IOException {
//实例化InputStream是一个抽象类且是所以输入流类的超类
InputStream is= new FileInputStream("D:\\mmm.txt");
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
System.out.println((char) is.read());
is.close();
//因为-1在码表中没有对应的信息,所以打印方框
}
}
2.优化无数据问题
因为read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1
所以可以根据read 的无返回值是否为-1作为判断条件看文件是否还有数据
InputStream is= new FileInputStream("D:\\mmm.txt");
int a;
while((a=is.read())!=-1){
System.out.println((char) a);
}
is.close();
InputStream is=new FileInputStream("D:\\MMM.txt");
byte [] bytes =new byte[3];//定义数组装字节
while (is.read(bytes)!=-1){
//使用String构造方法把字节数组转换为字符串
System.out.print(new String(bytes));
}
is.close();
打印结构和实际结果不一样
分析原因:
最后一个数组没装满,使用只替换了两个个内容,而a没有被覆盖,使用也继续打印出来
InputStream is=new FileInputStream("D:\\MMM.txt");
int a;
byte [] bytes =new byte[3];//定义数组装字节
while ((a=is.read(bytes))!=-1){
//使用String构造方法把字节数组转换为字符串
System.out.print(new String(bytes,0,a));
}
is.close();
字符流是指按字符为单位进行输入输出操作的数据流,以字符为操作单元,在编程中常用于文本文件的读写。字符流以字符为基本单位,将数据读入或写出程序。字符流分为字符输入流和字符输出流两种。
在Java中,字符流主要由Reader和Writer类实现。
字符输入流:
Reader
字符输出流:
Writer
flush方法
:刷新缓冲区,流对象可以继续使用。刷新在一些特定场景下,IO流的刷新是非常必要的,比如:
总之,IO流的刷新虽然在一些场景下可能显得多余,但在一些特定的场景下是非常必要的,可以保证数据的有效性和完整性。
Writer 抽象类是表示用于写出字符流的所有类的超类
构造方法:
多态手法
常用方法:
write(char[] cbuf, int off, int len)
: 将字符数组的一部分写入输出流write(int c)
: 将单个字符写入输出流write(String str, int off, int len)
: 将字符串的一部分写入输出流write(String str)
: 将整个字符串写入输出流write(char[] cbuf)
: 将整个字符数组写入输出流Writer ow=new FileWriter("D:\\nnn.txt");
ow.write('中');
ow.write('国');
ow.close();
Writer ow=new FileWriter("D:\\nnn.txt",true);
//写入字符数组
char [] chars1={10,'中','国','牛','毕','啊'};
ow.write(chars1);
//写入字符串
String s="我也将很牛逼";
ow.write(s);
//写入字符串的某一部分,off字符串的开始索引,len写的字符个数
char [] chars2={'中','国','牛','毕','啊'};
ow.write(chars2,0,2);
ow.close();
Reader抽象类是表示用于读取字符流的所有类的超类
构造方法:
多态手法
常用方法:
read()
:从输入流中读取下一个字符,返回其Unicode值,如果已经读取到流的末尾,则返回-1read(char[] cbuf)
:从输入流中读取字符填充到指定的字符数组cbuf中,并返回实际读取的字符数read(char[] cbuf, int off, int len)
:从输入流中读取字符填充到指定的字符数组cbuf中,并从数组的off位置开始,最多读取len个字符 public static void main(String[] args) throws IOException {
//初始化
Reader ir=new FileReader("D:\\NNN.txt");
int a;
while ((a= ir.read())!=-1){
System.out.print((char)a);
}
ir.close();
Reader ir=new FileReader("D:\\NNN.txt");
int a;
char [] chars=new char[2];
while ((a= ir.read(chars))!=-1){
System.out.print(new String(chars,0,a));
}
ir.close();
}
此操作和InputStream相似,具体细节可参考如何使用InputStream
序列化是将对象转换为可存储或传输格式的过程。它的主要目的是将对象转换为字节流或文本格式,以便它可以被存储在磁盘上、传输到网络或其他程序中。反序列化则是将字节流或文本格式的序列化对象转换回对象本身的过程。
ObjectOutputStream 序列化流,将Java对象的原始数据类型写出到文件,实现对象的持久存储
构造方法:
public ObjectOutputStream(OutputStream out) :
创建一个指定OutputStream的ObjectOutputStream
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象
构造方法:
public ObjectInputStream(InputStream in) :
创建一个指定InputStream的ObjectInputStream。
Java中的Serializable接口是一个标记接口,它没有任何方法。该接口的主要作用是标记一个类的对象可以被序列化(serialization),即可以将对象保存到磁盘或者通过网络传输给其他进程。
当一个类实现Serializable接口之后,它的对象就可以被序列化,即可以将对象转换为字节序列,然后将字节序列保存到磁盘或者通过网络传输给其他进程。反之,如果一个类没有实现Serializable接口,那么该类的对象就不能被序列化。
需要注意的是,Serializable接口只是一个标记接口,它并不会自动地将一个类的所有属性都序列化。如果需要将一个类的所有属性都序列化,需要在该类中添加相应的方法,比如writeObject和readObject方法。
在Java中,每个实现了Serializable接口的类都有一个唯一的序列化ID,用来标识该类的对象进行序列化和反序列化的版本号。这个ID可以自动生成,也可以手动指定。
自动生成ID的方式是使用Java提供的序列化工具,在编译时自动生成,生成的ID根据类的结构、成员变量、方法等因素计算得出,可以通过反射的方式获取到。例如:
long serialVersionUID = ObjectStreamClass.lookup(MyClass.class).getSerialVersionUID();
System.out.println("MyClass的序列化ID为:" + serialVersionUID);
手动指定ID的方式是在类中添加一个名为serialVersionUID
的静态变量,并赋值为一个long型的数值。例如:
public class MyClass implements Serializable {
private static final long serialVersionUID = 123456789L;
// ...
}
需要注意的是,手动指定ID时必须确保该ID的唯一性,否则可能会导致序列化和反序列化时出现不兼容的情况。
若一个对象被序列化,他会默认得到一个id,但是每一次更改这个对象类,id都会发生改变,若想更改后任然可以被反序列化,就要设定固定的id。