1,IO 概念
IO 通常表示计算机与外界数据的输入与输出。I/O 流:IO流又叫输入输出流 ,输入和输出均是以内存作为参照物
分类:
1)按照流的方向
以内存为参考物,往内存中去,叫做输入,从内存中出来,叫做输出。
2)按照读取数据方式
字节流,一次读取一个字节 byte
字符流,一次读取一个字符,具体几个字节取决于编码,只能处理文本文件。
3)按功能
节点流:可以从一个特定数据源读写数据,如 FileInputStream, FileReader
处理流(包装流):链接已存在的流之上,为程序停工更为强大的读写功能。处理流只需要关闭自己,内部的流不用关。
2,BIO
1,四大家族:
- java.io.InputStream 字节输入流
- java.io.OutputStream 字节输出流
- java.io.Reader 字符输入流
- java.io.Writer 字符输出流
2,IO 流特征
1)所有的流都实现了 java.io.Colseable接口,都是可关闭的,都有close()方法. 流毕竟是一个管道,这个是内存和硬盘之间的通道,养成一个好习惯,用完流将其关闭
2)所有的输出流都实现了 java.io.Flushable接口,都是可刷新的,都有flush()方法,这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。如果没有flush()可能会导致数据丢失.
3)在java中只要"类名"以Stream结尾的都是字节流。以"Reader/Writer"结尾的都是字符流
3,16个流需要掌握
1)文件专属
java.io.FileinputStream (用得最多)
/**
* FileInputStream
* 文件字节输入流,万能的,任何类型的文件都可以采用这个流来读
* 字节的方式,完成输入的操作,完成读的操作(硬盘 —> 内存)
*
* 方法:
* read() 读取一个字节,返回的是读取到的内容的 int 表示,返回 -1 表示没有读到。
* read(byte[] b) 读取一个指定数组大小的内容,将读取内容放到 byte 数组中。返回 -1 说明没有读取到任何内容。
* read(byte[] b, int off, int len) 读取 len 长度的字节,从数组的 off 位置开始填充数组
* */
@Test
public void FileInputStream() throws IOException {
FileInputStream fis = new FileInputStream("D:\\gaox\\files\\test\\io_demo\\fileInputStream.txt");
byte[] by = new byte[4];
int length;
while((length = fis.read(by)) != -1){
System.out.println(new String(by, 0, length));
}
fis.close();
}
java.io.FileOutputStream (用得最多)
/**
* FileOutputStream
* 文件字节输出流,负责写,从内存到硬盘
* 构造方法:
* FileOutputStream(String name, boolean append) 第二个参数表示追加。如果根据文件路径找不到文件,就会自己创建一个文件
*
* 方法:
* write(int) 写入一个数字
* write(byte[] c) 写入一个字节数组
* <!---->write(byte[] c, int off, int len) 从数组 c 的 off 位置开始写入 len 个字节
* flush() 刷新
* */
@Test
public void FileOutputStream() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\gaox\\files\\test\\io_demo\\fileOutputStream.txt");
fos.write("yanqishiwode".getBytes(), 1, 10);
fos.flush();
fos.close();
}
java.io.FileReader
/**
* FileReader
* 文件字符输入流,以字符 char 为单位,只能读文本
*
* 方法:
* getEncoding():获取编码
* read():读取一个字符,返回 int 类型,可以强转成 char, -1 表示没读到
* read(char[] c):读取字符数组长度的字符,然后填充到数组 c 中, -1 表示没读到,返回值为实际读的数据
* read(char[] c, int off, int len) 读取 len 长度的字符,放到从 off 开始的数组 c 中,-1表示没读到返回值为实际读到的数据
* */
@Test
public void FileReader() throws IOException {
FileReader fr = new FileReader("D:\\gaox\\files\\test\\io_demo\\fileReader.txt");
System.out.println((char)fr.read());
System.out.println(fr.read());
char[] chars = new char[10];
fr.read(chars);
System.out.println(chars);
//读取部分,chars 数组其他位置仍然是上面读取的内容
fr.read(chars, 4, 3);
System.out.println(chars);
int len;
while((len = fr.read(chars)) != -1){
System.out.println(len);
}
fr.close();
}
java.io.FileWriter
/**
* FileWriter
* 文件字符输出流,以字符 char 为单位
*
* 方法:
* write():输出一个字符
* write(char[] c):输出整个字符数组
* write(char[] c, int off, int len):输入数组 c 中,从 off 开始共计 len 长度的字符
* write(String s):输出字符串
* write(String s, int off, int len):输出字符串的一部分
* flush() 刷新
* */
@Test
public void fileWriter() throws IOException {
FileWriter fw = new FileWriter("D:\\gaox\\files\\test\\io_demo\\fileWriter.txt", true);
fw.write(49);
char[] chr = new char[]{'张','非','又','要','大','人','了'};
fw.write(chr);
fw.write(chr, 1, 3);
fw.write("张三");
fw.write("woaiyanqi", 3, 5);
fw.flush();
fw.close();
}
2)转换流 (将字节流转换成字符流)
InputStreamReader
/**
* InputStreamReader
* 输入转换流:将字节流转换为字符流
*
* 构造方法
* InputStreamReader(InputStream in); 入参一个字节流,返回一个 inputStreamReader 转换流,该流是字符流
*
* read 三个 read 方法跟 reader 保持一致
*
*
* */
@Test
public void InputStreamReader() throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\gaox\\files\\test\\io_demo\\fileInputReader.txt"));
System.out.println((char)isr.read());
char[] chr = new char[10];
System.out.println(isr.read(chr));
System.out.println(chr);
chr = new char[10];
System.out.println(isr.read(chr, 3,7));
System.out.println(chr);
isr.close();
}
outputStreamWriter
/**
* outputStreamWriter
* 输出转换流
*
* write 五个方法与 fileWriter 五个 write 方法一致
* */
@Test
public void outputStreamWriter() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\gaox\\files\\test\\io_demo\\outputStreamWriter.txt", true));
//osw.write
osw.write(49);
osw.write(new char[]{'y','a','n','q','i'});
osw.write("不服来战");
osw.write(new char[]{'y','a','n','q','i'}, 1, 3);
osw.write("hello world", 1, 3);
osw.flush();
osw.close();
}
3)缓冲流专属: 缓冲流相比普通的流,多出一个缓冲区,可以读取比规定的多的数据,减少 io 次数,这类流构造方法都有个参数规定缓冲区大小
BufferedReader
/**
* BufferedReader
* 缓冲输入字符流
*
* 构造方法:
* BufferedReader(Reader r, int size) size 缓冲区大小
*
* 方法:
* read 常规三个方法
* readLine() 读一行
* */
@Test
public void bufferedReader() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("D:\\gaox\\files\\test\\io_demo\\bufferedReader.txt"),1);
System.out.println( (char)br.read());
char[] chr = new char[10];
br.read(chr);
System.out.println(chr);
chr = new char[10];
br.read(chr, 1, 5);
System.out.println(chr);
String s = br.readLine();
System.out.println(s);
br.close();
}
bufferedWriter
/**
* bufferedWriter
* 缓冲字符输出流
*
* 构造方法:
* BufferedWriter(Writer out)
*
* 方法:
* write 五个常规方法
* newLine() 创建新的一行
*
* */
@Test
public void bufferedWriter() throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\gaox\\files\\test\\io_demo\\bufferedWriter.txt"));
bw.write(49);
bw.write(new char[]{'1','2'});
bw.newLine();
bw.write("woaiyanqi");
bw.flush();
bw.close();
}
BufferedInputStream
/**
* BufferedInputStream
* 字节缓冲输入流
*
* 构造方法:
* BufferedInputStream(InputStream i);
*
* 方法:
* read() 的三个方法
* */
@Test
public void bufferedInputStream() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\gaox\\files\\test\\io_demo\\bufferInputStream.txt"));
System.out.println( bis.read() );
byte[] b = new byte[10];
bis.read(b);
System.out.println(b);
bis.read(b, 1, 9);
System.out.println(b);
bis.close();
}
BufferedOutputStream
/**
* BufferedOutputStream
*
* 构造方法
* BufferedOutputStream(OutputStream os)
*
* 方法
* write() 三个方法
* */
@Test
public void bufferedOutputStream() throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\gaox\\files\\test\\io_demo\\bufferedOutputStream.txt"));
bos.write(49);
String str = "wohao稀饭ya#@ &*qia";
bos.write(str.getBytes());
bos.write(str.getBytes(), 1, 10);
bos.flush();
bos.close();
}
4)数据流
DataOutputStream
/**
* DataOutputStream
* 数据输出流
*
* 构造方法
* DataOutputStream(OutputStream os)
*
* 方法:
* write() 三件套
* writeInt() 等八大类型
* */
@Test
public void dataOutputStream() throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:\\gaox\\files\\test\\io_demo\\dataInputStream.txt"));
dos.writeChar('燕');
dos.writeInt(112);
dos.writeDouble('燕');
dos.flush();
dos.close();
}
DataInputStream
/**
* DataInputStream
* 数据输入流:只能读 DataOutputStream() 写入的数据,并且需要知道顺序
*
* 构造方法:
* DataInputStream(InputStream is)
*
* 方法:
* read() 三件套
* readInt() 等八大类型
* */
@Test
public void dataInputStream() throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("D:\\gaox\\files\\test\\io_demo\\dataInputStream.txt"));
System.out.println( dis.readChar() );
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
dis.close();
}
5)标准输出流
PrintStream
/**
* printStream
* 标准输出流
*
* System.out、System.err 等返回的就是 printStream
* */
@Test
public void printStream() throws FileNotFoundException {
//默认输出到控制台
PrintStream out = System.out;
out.println("yanqi");
out.println("nidei");
out.println("shenmedeganh");
//输出到日志去
PrintStream ps = new PrintStream(new FileOutputStream("D:\\gaox\\files\\test\\io_demo\\log.txt"));
System.setOut(ps);
ps.println("张三");
System.out.println("你跑哪去了");
}
PrintWriter 不知道咋用
6)对象专属流
ObjectOutputStream
/**
* ObjectOutputStream
* 对象输出流,配合序列化
* */
@Test
public void objectOutputStream() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\gaox\\files\\test\\io_demo\\objectOutputStream.txt"));
oos.writeObject(new Cat("yanqi", 25, "white"));
oos.writeObject(new Cat("yanqi", 26, "white"));
oos.writeObject(new Cat("yanqi", 27, "white"));
oos.writeObject(new Cat("yanqi", 28, "white"));
oos.writeObject("张三");
oos.writeObject(new Cat("yanqi", 29, "white"));
oos.flush();
oos.close();
}
ObjectInputStream
/**
* ObjectInputStream
* 对象输入流,配合序列化
* */
@Test
public void ObjectInputStream() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\gaox\\files\\test\\io_demo\\objectOutputStream.txt"));
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println(ois.readObject());
ois.close();
}
4,文件操作 demo
字节流复制文件
/**
* 字节流复制文件
* */
@Test
public void fileCopy() {
try(
//卸载这里,会自动关闭流
FileInputStream fis = new FileInputStream("D:\\gaox\\files\\test\\io_demo\\bizhi.rar");
FileOutputStream fos = new FileOutputStream("D:\\gaox\\files\\test\\copy_io_demo\\bizhi.rar")
){
byte[] byt = new byte[1024];
int len;
while( (len = fis.read(byt)) != -1 ){
//这里因为最后可能数组都不满,所以每次只需要将有数据的写入文件就好了
fos.write(byt, 0, len);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
字符流复制文件:只能复制文本
/**
* 字符流复制文件:只能复制文本
* */
@Test
public void copyFileByChar(){
try( FileReader fr = new FileReader("D:\\gaox\\files\\test\\io_demo\\catalina.out");
FileWriter fw = new FileWriter("D:\\gaox\\files\\test\\copy_io_demo\\catalina.txt")
){
char[] chr = new char[10];
int len;
while( (len = fr.read(chr)) != -1 ){
fw.write(chr, 0, len);
}
fw.flush();
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
5,压缩流
ZipInputStream,ZipOutputStream,ZipFile,ZipEntry 等。
压缩流 demo
/**
* ZipOutputStream
* 压缩流:单个文件压缩
* */
@Test
public void zipFile() throws IOException {
//要压缩的文件
File file = new File("D:\\gaox\\files\\test\\io_demo\\catalina.out");
//压缩后的压缩包
File zipFile = new File("D:\\gaox\\files\\test\\io_demo\\catalina.zip");
FileInputStream fs = new FileInputStream(file);
ZipInputStream zis = new ZipInputStream(fs);
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
zos.putNextEntry(new ZipEntry(file.getName()));
byte[] byt = new byte[1024];
int len;
while( (len = fs.read(byt)) != -1 ){
zos.write(byt, 0, len);
}
zos.close();
zis.close();
}
/**
* ZipOutputStream
* 压缩流:文件夹压缩
* */
@Test
public void zipFiles() throws IOException {
//要压缩的文件夹
File file = new File("D:\\gaox\\files\\test\\io_demo");
//压缩后的压缩包
File zipFile = new File("D:\\gaox\\files\\test\\copy_io_demo\\io_demo.zip");
//定义文件输入流
InputStream is;
//定义输出压缩流
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
zos.setComment("yanqi");
recursionZip(zos, file, "");
zos.close();
}
//压缩方法抽出
private void recursionZip(ZipOutputStream zos, File file, String baseDir) throws IOException {
byte[] byt = new byte[1024];
int len;
if(file.isDirectory()){
//如果是文件夹,获取文件夹下所有文件
File[] files = file.listFiles();
for(File item : files){
recursionZip(zos, item, baseDir + File.separator + file.getName());
}
}else{
//对文件做压缩处理
FileInputStream is = new FileInputStream(file);
zos.putNextEntry(new ZipEntry(baseDir + File.separator + file.getName()));
while(( len = is.read(byt)) != -1){
zos.write(byt, 0, len);
}
is.close();
}
}
/**
* 解压缩
* ZipFile
* */
@Test
public void unzip() throws IOException {
//压缩包
File inFile = new File("D:\\gaox\\files\\test\\copy_io_demo\\io_demo.zip");
ZipFile zipFile = new ZipFile(inFile);
//解压后的文件路径
String path = inFile.getParent();
//压缩输入流
ZipInputStream zis = new ZipInputStream(new FileInputStream(inFile));
ZipEntry entity;
while( (entity = zis.getNextEntry()) != null ){
System.out.println("解压缩:" + entity.getName());
File outFile = new File(path + File.separator + entity.getName());
if( !outFile.getParentFile().exists() ){
outFile.getParentFile().mkdir();
}
outFile.createNewFile();
InputStream is = zipFile.getInputStream(entity);
FileOutputStream fos = new FileOutputStream(outFile);
byte[] byt = new byte[1024];
int len;
while( (len = is.read(byt)) != -1 ){
fos.write(byt, 0, len);
}
is.close();
fos.close();
}
}
3,NIO
1,NIO 简介
nio:new io,也称为非阻塞 io。与原来的 io 有同样的作用和目的,但是使用方式完全不同。
NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。NIO可以理解为非阻塞IO,传统的IO的read和write只能阻塞执行,线程在读写IO期间不能干其他事情,比如调用socket.read()时,如果服务器一直没有数据传输过来,线程就一直阻塞,而NIO中可以配置socket为非阻塞模式
2,NIO 特点
1)NIO相关类都被放在java.nio包及子包下,并且对原java.io包中的很多类进行改写。
2)NIO有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)
3)Java NlO的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
4)通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有1000个请求过来,根据实际情况,可以分配20或者80个线程来处理。不像之前的阻塞IO那样,非得分配1000个。
3,nio 和 io 比较
1)BIO以流的方式处理数据,而NIO以块的方式处理数据,块I/O的效率比流IO高很多
2)BIO是阻塞的,NIO则是非阻塞的(Java NIO 的非阻塞,是相对于连接的非阻塞,而不是指方法调用时的非阻塞)
3)BlO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道
4,NIO 三要素
1),Buffer 缓冲区
java.nio.Buffer 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。相比较直接对数组的操作,Buffer APl更加容易操作和管理。
nio 面向块,这个块就是 Buffer
2). Channel 通道
通道(Channel):由 java.nio.channels 包定义 的。Channel 表示 IO 源与目标打开的连接。 Channel 类似于传统的“流”。只不过 Channel 本身不能直接访问数据,Channel 只能与 Buffer 进行交互
3),Selector 选择器
选择器(Selector)是SelectableChannle对象的多路复用器,Selector可以同时监控多个SelectableChannel的IO状况,也就是说,利用Selector可使一个单独的线程管理多个Channel。Selector是非阻塞IO的核心
5,NIO demo
public class DemoMain {
/**
* nio 写本地文件
* */
@Test
public void write() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\gaox\\files\\日志\\copy.txt");
FileChannel channel = fos.getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put("yanqinidenalipaowl来两个中文ogaodingnil".getBytes());
buf.flip();
channel.write(buf);
buf.clear();
channel.close();
}
/**
* nio 读本第文件
* */
@Test
public void read() throws IOException {
FileInputStream fis = new FileInputStream("D:\\gaox\\files\\日志\\test.log");
FileChannel chl = fis.getChannel();
int bufLength = 1024;
ByteBuffer buf = ByteBuffer.allocate(bufLength);
byte[] bytes = new byte[bufLength];
int read;
while( (read = chl.read(buf)) != -1){
buf.flip();
buf.get(bytes, 0 , read);
System.out.println(new String(bytes));
buf.clear();
}
chl.close();
}
/**
* 使用 buffer复制
* */
@Test
public void bufferCopy() throws IOException {
FileChannel fic = new FileInputStream("D:\\gaox\\files\\日志\\catalina1231231241231312311312414.out").getChannel();
FileChannel foc = new FileOutputStream("D:\\gaox\\files\\日志\\bufferCopy.out").getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
while(fic.read(buf) != -1){
buf.flip();
foc.write(buf);
buf.clear();
}
fic.close();
foc.close();
}
/**
* transferFrom 复制
* */
@Test
public void transferFrom() throws IOException {
FileChannel fic = new FileInputStream("D:\\gaox\\files\\日志\\catalina1231231241231312311312414.out").getChannel();
FileChannel foc = new FileOutputStream("D:\\gaox\\files\\日志\\bufferCopy1.out").getChannel();
foc.transferFrom(fic, fic.position(), fic.size());
fic.close();
foc.close();
}
/**
* transferTo 复制
* */
@Test
public void transferTo() throws IOException {
FileChannel fic = new FileInputStream("D:\\mySoftWare\\jdk-17_windows-x64_bin.zip").getChannel();
FileChannel foc = new FileOutputStream("D:\\mySoftWare\\jdk-17_windows-x64_bi1n1.zip").getChannel();
long l = fic.transferTo(fic.position(), fic.size(), foc);
System.out.println("传输完成!");
fic.close();
foc.close();
}
/**
* 案例6-分散 (Scatter) 和聚集 (Gather)
* */
@Test
public void test() throws IOException{
RandomAccessFile raf1 = new RandomAccessFile("D:\\gaox\\files\\日志\\catalina1231231241231312311312414.out", "rw");
//1. 获取通道
FileChannel channel1 = raf1.getChannel();
//2. 分配指定大小的缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
//3. 分散读取
ByteBuffer[] bufs = {buf1, buf2};
channel1.read(bufs);
for (ByteBuffer byteBuffer : bufs) {
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
System.out.println("-----------------");
System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
//4. 聚集写入
RandomAccessFile raf2 = new RandomAccessFile("D:\\gaox\\files\\日志\\bufferCopy3.out", "rw");
FileChannel channel2 = raf2.getChannel();
channel2.write(bufs);
}
}
6,I/O 多路复用技术
在传统的IO编程中,每个socket连接都需要一个线程或进程来处理,这样就会导致系统资源的浪费和性能问题。而IO多路复用则可以通过一个线程或进程来同时管理多个socket连接,并且可以同时处理多个连接上的IO事件。
常用的IO多路复用技术有select、poll、epoll等。
以select为例,当一个进程调用select函数时,它会将多个文件描述符注册到一个监控集合中,并设置监听事件类型(如可读、可写、异常等)。然后,select函数会阻塞进程,直到监控集合中的任意一个文件描述符发生了监听事件(读、写)。此时,select函数就会返回,并告诉进程哪些文件描述符有事件发生,进程可以根据返回值来处理相应的IO事件。
7,Reactor 模式
Reactor 设计模式就是基于建立连接与具体服务之间线程分离的模式。在 Reactor 模式中,会有一个线程负责与所有客户端建立连接,这个线程通常称之为 Reactor。然后在建立连接之后,Reactor 线程 会使用其它线程(可以有多个)来处理与每一个客户端之间的数据传输,这个(些)线程通常称之为 Handler
4,AIO
1,简介
AIO,异步非阻塞 IO,又称 NIO2.0,最大的一个特性就是异步能力,这种能力对socket与文件I/O都起作用。AIO其实是一种在读写操作结束之前允许进行其他操作的I/O处理。
2,AIO demo
/**
*
* AsynchronousSocketChannel
* AsynchronousServerSocketChannel
* AsynchronousFileChannel
* AsynchronousDatagramChannel
* */
public class AioDemo {
@Test
public void test() throws IOException, ExecutionException, InterruptedException {
AsynchronousFileChannel channel = open(Paths.get("D:\\gaox\\files\\模型\\科艺楼.安装模型 - 副本.zip"), StandardOpenOption.READ);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024 * 1024);
Future<Integer> read = channel.read(byteBuffer, 0);
System.out.println("读了,但是不知道有没有读出来!");
read.get();
byteBuffer.flip();
System.out.println("读出来了:" + byteBuffer.get());
}
}