文章目录
前言
本文不会对 Java IO 进行系统性介绍,因为这一块内容实在是够喝一壶的,但是对于 socket 编程来说,Java IO 的知识是必不可少的。本文就 Java IO 的基本内容进行介绍。
IO
简单来说就是输入输出数据流
Java IO
对于 Java IO ,需要了解到的是,Java IO 是一个很大的体系,但是究其根本依旧是读、写两个,此外Java IO 中有缓冲区一说,于是就有了 flush ,即清空缓冲区一说。
IO 是一个很占用资源的操作,尽管这些资源也可以被自身释放掉,但是为了保证代码的健壮性,务必养成用完主动关闭的释放资源的好习惯。
字节和字符
字节 Byte,一个字节占8位
在文本类内容中,我们会遇到各种编码格式,比如 GBK、UTF8 等
Java IO 将字节的处理和字符的处理分为了两类,但是在底层,都是按照字节来进行处理的
换而言之,字节和字符的处理是有转化方法的
字节流的两个抽象类
围绕字节输入输出流,Java IO 提供了两个抽象类,java.io.InputStream 和 java.io.OutputStream ,其下有诸多的子类,建议用 IDEA 查看这两个类的源码。
字节输入流
java.io.InputStream
inputStream.read();//返回 inputStream对象 一个字节的值
inputStream.read(byte[]);//将 inputStream 对象 写入字节数组 byte[] ,并返回写入的字节数
inputStream.close();//关闭
字节输出流
java.io.OutputStream
outputStream.write(byte[]);//将 字节数组 byte[] 写入 outputStream对象
outputStream.close();//关闭
字符流的两个抽象类
围绕字符输入流,比如文字,文本等,Java IO 提供了两个抽象类 java.io.Reader 和 java.io.Writer,对于文件的操作就是由这两个抽象类的子类完成的,建议使用 IDEA 查看源码
字符输入流
java.io.Reader
reader.read();//返回 reader 对象的一个字符
reader.read(char[]);//将 reader 对象写入 字符数组 char[] 并返回写入的字符数
reader.close();//关闭
字符输出流
java.io.Writer
writer.write(char[]);//将字符数组 char[] 写入到 writer 对象
writer.flush();//清空缓存
writer.close();//关闭
字符-Java 包含哪些种类的字符
Java 支持很多种类的字符,可以运行下面这段代码,获取直接去看源码
需要强调的是,这里并不包含 Unicode,Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,他只是一种标准,
Unicode标准提供了三种不同的编码格式,使用8位、16位和32位编码单元,分别为UTF-8、UTF-16、UTF-32,这三个是包含在 Java 支持的字符类型中的
public static void main(String [] args){
SortedMap<String,Charset> map= Charset.availableCharsets();
Collection<Charset> c=map.values();
Iterator iterator=c.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
字符流和字节流相互转化
在实际使用中,我们总是会遇到文本传输的问题,这个时候我们总是需要将字节流转化为字符流来进行处理,以便于解决乱码和读取信息等问题。
字节流通向字符流的桥梁
java.io.InputStreamReader
在该类的源码中我们可以看到如下内容,可以将 InputStream 转化为 具体 Reader 的子类对象
//使用举例
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//构造方法
public InputStreamReader(InputStream in) {··}
字符流通向字节流的桥梁
java.io.OutputStreamWriter
在该类的源码中我们可以看到如下内容,可以将 Writer 子类对象 写入 到 OutputSream对象
//使用举例
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
//构造方法
public OutputStreamWriter(OutputStream out) {··}
Java 中的换行符
如何在 Java 中表示换行符? \n ,\r 还是 \r\n?
由于不同操作系统中换行符不同,所以我们用源码中的换行符,他是根据操作自动来自动获取的
java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator"))
BufferedReader.readLine 和 PrintWriter.println的妙用
BufferedReader.readLine
readLine 方法是一个阻塞方法,只有读到末尾、遇到换行符、或者操作中断,而我们一般会采用下面的方法来进行读操作
试想,在一个线程中,服务器端等待客户端发送信息,而客户端在等服务器端返回信息,那么真是一件很糟糕的事情了。
在 socket 编程中我们就会遇到这个问题,不过别担心。我们一起见证解决方案。
String str="";
for(;(str=bufferedReader.readLine())!=null;){
System.out.println(str);
}
PrintWriter.println
println() 不同于 System.out.println(),它的内部有一个 flush()和换行,所以你应该想到,它可以在 IO 缓存没有写满就发送数据,提高效率,同时,换行也会有换行符。
此外需要注意,flush()默认不开启,需要在声明 PrintWriter()对象时调用相应的构造方法
PrintWriter(writer,true);
否则需要手动调用 flush()发送缓存区的数据
//autoFlush 为true,调用 printf 或者 format 时会自动调用 flush()方法
public PrintWriter(Writer out,
boolean autoFlush)
socket 获取 InputStream 和 OutputStream
一般而言,在socket 编程中,服务单获取客户端,客户端连接服务端,然后我们就对这个 socket 对象的输入输出流进行操作
获取 socket 客户端对象的输入流
//socket 表示客户端对象
socket.getInputStream();
获取 socket 客户端对象的输出流
//socket 表示客户端对象
socket.getOutputStream();
ByteBuffer
ByteBuffer.allocate 和 ByteBuffer.allocateDirect
ByteBuffer byteBuffer = ByteBuffer.allocate(16); | 在堆中,受 GC 影响 | 容量不可修改 |
ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(16); | 系统内存,不受 GC 影响,分配慢 | 容量不可修改 |
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
/**
* @Title:
* @Description:
* @Copyright: Copyright(c)2019
* @Company: bestcxx
* @Author: jie.wu
* @Version: v1.0
* @Date:2021-11-06 23:29
* @Updatedby:
*/
@Slf4j
public class DebugAllTest {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
byteBuffer.put(new byte[]{'a','b','c','d'});
byteBuffer.flip();//切换读模式 limit = position; position = 0; mark = -1;
while(byteBuffer.hasRemaining()){
log.info(String.valueOf((char) byteBuffer.get()));
byteBuffer.mark();//标记当前位置-用于重复读的场景
byteBuffer.reset();//恢复到mark位置
}
byteBuffer.rewind();//放到开头从新读
while(byteBuffer.hasRemaining()){
log.info(String.valueOf((char) byteBuffer.get()));
byteBuffer.mark();//标记当前位置-用于重复读的场景
byteBuffer.reset();//恢复到mark位置
}
byteBuffer.compact();//切换为写模式,把未读的向前移动,已读的从缓存去除
byteBuffer.clear();// 切换为写模式,从第一位置开始写 position = 0; limit = capacity; mark = -1;
}
}