Java中IO流分为几种?
Java IO流的划分可以根据多个纬度进行,包括数据流的方向(输入或输出),处理的数据单位(字节或字符),流的功能以及流是否支持随机访问等。
按照数据流方向如何划分?
输入流(Input Stream):从源(如文件,网络等)读取数据到程序
输出流(Output Stream):将数据从程序写出到目的地(如文件,网络,控制台等)
按处理数据单位如何划分?
字节流(Byte Stream):以字节为单位读写数据,主要用于处理二进制数据,如音频,图形文件等。
字符流(Character Streams):以字符为单位读写数据,主要用于处理文本数据
按功能如何划分?
节点流(Node Streams):直接与数据源或目的地相连,如FileInputStream,FileOutputStream
处理流(Processing Streams):对一个已存在的流进行包装,如缓存流BufferedInputStream
管道流(Piped Streams):用于线程之间的数据传输,如PipedInputStream,PipedOutputStream
IO流用到了什么设计模式?
其实,Java的IO流体系还用到了一个设计模式——装饰器模式
Java缓冲区溢出,如何预防
Java缓冲区溢出主要是由于向缓冲区写入的数据超过其能够存储的数据量。
可以采用这些措施来避免:
合理设置缓冲区大小:在创建缓冲区时应根据实际需求合理设置缓冲区的大小,避免创建过大或过小的缓冲区。
控制写入数据量:在向缓冲区写入数据时,应该控制写入的数据量,确保不会超过缓冲区的容量。
Java的ByteBuffer类提供了remaining()方法,可以获取缓冲区中剩余的可写入数据量
import java.noi.ByteBuffer;
public class ByteBufferExample{
public static void main(String[] args){
//模拟接收到的数据
byte[] receivedData={1,2,3,4,5};
int bufferSize = 1024; //设置一个合理的缓冲区大小
//创建ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
//写入数据之前检查容量是否足够
if (buffer.remaining() >=receiveData.length){
buffer.put(receivedData);
}else{
System.out.println("Not enough space in buffer to write data.");
}
//准确读取数据;将limit设置为当前位置,position设会0
buffer.flip();
//读取数据
while(buffer.hasRemaining()){
byte data = buffer.get();
System.out.println("Read data:"+data);
}
//清空缓冲区以便再次使用
buffer,clear();
}
}
既然有了字节流,为什么还要有字符流?
其实字符流是由Java虚拟机将字节转换得到的,问题就出在这个过程还比较耗时,并且,如果我们不知道编码类型就很容易出现乱码。
所以,I/O流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件,图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
文本存储是字节流还是字符流,视频文件呢?
在计算机中,文本和视频都是按照字节存储的,只是如果是文本文件的话,我们可以通过字符流的形式去读取,这样更方便我们进行直接处理
比如说我们需要再在一个大文本文件中查找某个字符串2,可以直接通过字符流来读取判断。
处理视频文件时,通常使用字节流(如Java中的fileInputStream,FileOutputStream)来读取或写入数据,并且会尽量使用缓冲流(如BufferedInputStream,BufferedOutputStream)来提高读写效率
在技术派实战项目项目中,对于文本,比如说文章和教程内容,是直接存储在数据库中的,而对于视频和图片等大文件,是存储在OSS中的。
因此,无论是文本文件还是视频文件,它们在物理存储层面都是以字节流的形式存在。
区别在于,我们如果通过Java代码来解释和处理这些字节流:作为编码后的字符还是作为二进制数据。
BIO,NIO,AIO之间的区别
Java提供了多种IO模型来处理输入和输出操作,包括传统的阻塞IO,非阻塞IO和异步IO.
BIO(Blocking I/O):采用阻塞式I/O模型,线程在执行I/O操作时被阻塞,无法处理其他任务,适用于连接脚手的场景。
NIO(New I/O 或Non-blocking I/O):采用非阻塞I/O模型,线程在等待I/O时可执行其他任务,,通过Selector监控多个Channel上的事件,适用于连接数多单连接时间短的场景
AIO(Asynchronous I/O):使用异步I/O模型,线程发起I/O请求后立即返回,当I/O操作完成时通过会调函数通知线程,适用于连接数多且连接时间长的场景。
简单说一下BIO?
BIO,也就是传统的IO,基于字节流或字符流(如FileInuptStream,BufferedReader等)进行文件读写,基于Socker和ServerSocket进行网络通信。
对于每个连接,都需要创建一个独立的线程来处理读写操作。
简单说下NIO?
NIO,JDK1.4时引入,放在Java.nio包下,提供了Channel,Buffer,Selector等新的抽象,基于RandomAccessFile,FileChannel,ByteBuffer进行文件读写,基于SockerChannel和ServerSockChannel进行网络通信。
实际上,“旧”的I/O包已经使用NIO重新实现过,所以在进行文件读写时,NIO并无法体现出比BIO更可靠的性能。
NIO的魅力主要体现在网络编程中,服务其可以用一个线程处理多个客户端连接,通过Selector监听多个Channel来实现多路复用,极大地提高了网络编程的性能
缓冲区Buffer也能极大提升一次IO操作的效率
简单说下AIO?
AIO是Java7引入的,放在java.nio.channels包下,提供了AsynchronsFiileChannel,AsynchronousSocketChannel等异步Channel.
它引入了异步通道的概念,使得I/O操作可以异步进行。
这意味着线程发起一个读写操作后不必等待其完成,可以立即进行其他任务。并且当读写操作真正完成时,线程会被异步地通知。
AsynchronousFileChannel fileChannel =AsynchronousFileChannel.open(Paths.get("test.txt"),StandardOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> result =fileChannel.read(buffer,0);
while (!result.isDone()){
// do something
}
什么是序列化?什么是反序列化?
序列化(Serialization)是指将对象转换为字节流的过程,以便能够将该对象保存到文件,数据库,或者进行网络传输。
反序列化(Deserialization)就是字节流转换回对象的过程,以便构建原始对象。
Serializable接口有什么用?
Serializable接口用于标记一个类可以被序列化
public class Person implements Serializable{
private String name;
private int age;
//省略getter和setter方法
}
serialVersionUID有什么用?
seralVersionUID是Java序列化机制中用于标识类版本的唯一标识符。
它的作用是确保在序列化和反序列化过程中,类的版本是兼容的。
import java.io.Serializable;
public class MyClass implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
//getters and setters
}
serialVersionUID被设置为1L是一种比较省事的做法,也可以使用IntelliJ IDEA进行自动生成。
但只要serialVersionUID在序列化和反序列化过程中保持一致,就不会出现问题。
如果不显示声明serialVersionUID,Java运行时会根据类的详细信息自动生成一个serialVersionUID.
那么当类的结构发生变化时,自动生成的serialVersionUID就会发生变化,导致反序列化失败。
Java序列化部包含静态变量吗?
是的,序列化机制只会保存对象的状态,而静态变量属于类的状态,不属于对象的状态?
如果有些变量不想序列化,怎么办?
可以使用transient关键字修饰不想序列化的变量。
public class Person implements Serializable{
private String name;
private transient int age;
//省略getter和setter方法
}
能解释一下序列化的过程和作用吗?
序列化过程通常涉及到以下几个步骤:
第一步,实现Serializable接口。
public class Person implements Serializable{
private String name;
private int age;
//省略构造方法,getter和setter
}
第二步,使用ObjectOutputStream来将对象写入到输出流中。
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));
第三步,调用ObjectOutputStream的writeObject方法,将对象序列化写入到输出流中。
Person person = new Person("沉默王二”,18);
out.writeObject(person);
说说有几种序列化方式?java序列化方式有很多,常见的有三种
Java对象序列化:Java原生序列化方法即通过Java原生流(InputStream和OutputSream之间的转化)
Json序列化:这个可能是我们最常用的序列化方式,Json序列化的选择很多,一般会使用Jackson包,通过ObjectMapper类来进行一些操作,比如将对象转化为byte数组或者json串转化为对象
ProtoBuff序列化:ProtocolBuffer是一种轻便高效的结构化数据存储格式,ProtoBuff序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提供系统性能。