Java基础复习-IO流

本文详述了Java中IO流的基础知识,包括File类的使用,流的分类,操作步骤,以及各种流的实例,如字符流、字节流、缓冲流、转换流、对象流。此外,还深入讲解了NIO(非阻塞IO)的通道、缓冲区、直接缓冲区和非直接缓冲区的概念,并涉及RandomAccessFile和序列化机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java基础复习-IO流

本文仅对所学java知识的查缺补漏

File

File类的使用
1.File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
2.File类声明在java.io包下
3.路径分隔符与操作系统:
     window和DOS系统默认使用 \ 表示
     UNIX和URL使用 / 表示
4.Java程序支持跨平台运行,因此路径分隔符要慎用
5.为了解决这个隐患,File类提供了一个常量:
     public static final String separator:根据操作系统,动态提供分隔符
     File file = new File("C:"+File.separator+"test.txt");
6.File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,
     但并未涉及到写入或读取文件内容的操作,如果需要读取或写入文件内容,必须使用IO流完成

创建对象

File(String filePath);
File(String parentPath, String childPath);
File(File parentFile, String childPath);

常用方法

  • public String getAbsolutePath():获取绝对路径
  • public String getPath():获取路径
  • public String getName():获取名称
  • public String getParent():获取上层文件目录路径。若无,返回null
  • public long length():获取文件长度(即:字节数)。不能获取目录长度
  • public long lastModified():获取最后一次的修改时间,毫秒数
  • public String[] list():获取指定目录下的所有文件或者文件目录的名称数组(适用目录)
  • public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组(适用目录)
  • public boolean reanmeTo(File dest):把文件重命名为指定的文件路径。
    • 比如:file1.renameTo(file2),要想保证返回true,需要file1在硬盘中存在,而且file2在硬盘中不存在
  • public boolean isDirectory():判断是否是文件目录
  • public boolean isFile():判断是否是文件
  • public boolean exists():判断是否存在
  • public boolean canRead():判断是否可读
  • public boolean canWrite():判断是否可写
  • public boolean isHidden():判断是否隐藏
  • public boolean createNewFile():创建文件,若文件存在,则不创建,返回false
  • public boolean mkdir():创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层不存在,则创建失败
  • public boolean mkdirs():创建文件目录。如果上层目录不存在,一并创建
  • public boolean delete():删除文件或者文件夹(注意:Java中的删除不走回收站)

IO流

流的分类

  • 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流(节点流的包装)
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

流体系

流的操作步骤(固定)

  1. 实例化File对象
  2. 实例化流对象
  3. 要进行流的操作
  4. 关闭流

例子:文本复制(字符流)

public class FileReaderWriterTest {

    @Test
    public void test(){
        FileReader fileReader = null;
        try {
            //1.实例化File类对象,指明要操作的文件
            File file = new File("src\\javabasic\\day26","hello.txt");
            System.out.println(file.getAbsolutePath());
            //2.提供具体的流
            fileReader = new FileReader(file);

            //3.数据的读入
            //read():返回读入的一个字符。如果到达文件末尾,则返回-1
            //read(char[] cbuf):返回每次读入cbuf数组中的字符个数。如果到达文件末尾,返回-1。这种方式,如果读入的个数比char[]数组的长度小,那么char[]数组中还会保留部分上一次读入的内容
            int data;
            while((data = fileReader.read()) != -1){
                System.out.print((char)data);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //4.关闭流,因为对于物理连接,比如:数据库连接,IO流,Socket的连接,JVM是关闭不了的,需要手动关闭
            try {
                if (fileReader != null)
                    fileReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

例子:图片复制(字节流)

/**
* 图片复制:使用FileInputStream和FileOutputStream
*/
@Test
public void test3(){
    File file = new File("src\\javabasic\\day26", "hello.jpg");
    File file1 = new File("src\\javabasic\\day26", "hello1.jpg");

    FileInputStream inputStream = null;
    FileOutputStream outputStream = null;

    try {
        inputStream = new FileInputStream(file);
        outputStream = new FileOutputStream(file1);
        byte[] bytes = new byte[5];
        int len;
        while((len = inputStream.read(bytes)) != -1){
            outputStream.write(bytes, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意

  • 对于文本文件(.txt,.java,.c,.cpp),使用**字符流(16bit)**处理
  • 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt......),使用**字节流(8bit)**处理
  • 使用字节流处理文本文件可能会出现乱码(复制文件可以用字节流,相当于数据的搬运工)

缓冲流

BufferedInput / OutputStream

提高了字节流的读取和写入速度,

原因:底层有一个8k大小的byte缓冲区:即byte[8192]

/**
 * 处理流之一:缓冲流的使用
 * @Author: fxx
 * @Date: 2020/12/28 14:26
 */
public class BufferedTest {

    @Test
    public void test(){
        //1.实例化File
        File file1 = new File("src\\javabasic\\day26", "hello.jpg");
        File file2 = new File("src\\javabasic\\day26", "hello1.jpg");

        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            //2.实例化节点流
            inputStream = new FileInputStream(file1);
            outputStream = new FileOutputStream(file2);
            //3.实例化处理流:包装节点流的流
            bufferedInputStream = new BufferedInputStream(inputStream);
            bufferedOutputStream = new BufferedOutputStream(outputStream);

            //4.操作流
            byte[] bytes = new byte[5];  //每次读进来的流字节,一般文件比较大的话会设置为1024
            int len;
            long start = System.currentTimeMillis();
            while((len = bufferedInputStream.read(bytes)) != -1){
                bufferedOutputStream.write(bytes, 0, len);
            }
            long end = System.currentTimeMillis();
            System.out.println("耗时:"+(end - start));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5.关闭流
            //关闭流的操作是:先关闭外层处理流,再关闭内层节点流
            //说明:关闭外层流的同时,内层流也会自动关闭,所以这里只需要关闭外层流
            try {
                if(bufferedInputStream != null)
                    bufferedInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bufferedOutputStream != null)
                    bufferedOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
BufferedReader / Writer

提高了字符流的读取和写入速度,

原因:底层有一个8k大小的char数组缓冲区:即char[8192];

转换流

作用:提供字节流与字符流之间的转换

属于字符流(看名字后缀)

  • InputStreamReader:将一个字节的输入流转换为字符的输入流,相当于解码
  • OutputStreamWriter:将一个字符的输出流转换为字节的输出流,相当于编码
  • 可以解码后重新编码
例子
@Test
public void test(){
    FileInputStream fileInputStream = null;
    InputStreamReader inputStreamReader = null;
    try {
        fileInputStream = new FileInputStream("src\\javabasic\\day26\\hello.txt");
        //如果不指定字符集,用系统默认字符集
        //解码的字符集使用什么,取决于原来的文件使用了什么字符集
        inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");

        char[] chars = new char[20];
        int len;
        while((len = inputStreamReader.read(chars)) != -1){
            String str = new String(chars, 0, len);
            System.out.println(str);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            inputStreamReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

标准输入输出流

* 标准的输入,输出流
 * System.in:标准输入流,默认从键盘输入
 * System.out:标准输出流,默认从控制台输出
 * System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流:流重定向
 *
 * 练习:
 * 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直到输入
 * e或exit时,退出程序。
 * 方式一:用Scanner实现,调用next()返回一个字符串
 * 方式二:用System.in实现。System.in   ----> 转换流   ----->BufferedReader的readLine()
 *      InputStreamReader inputStream = new InputStreamReader(System.in);
 *      BufferedReader bf = new BufferedReader(inputStream);
 *     下面操作和之前的一样

打印流

实现将基本数据类型的数据格式转化为字符串输出

  • PrintStreamPrintWriter的输出不会抛出IOException异常
  • PrintStreamPrintWriter有自动flush功能
  • PrintStream打印的所有字符都会使用平台默认字符编码转换为字节,如果需要写入字符而不是字节,应使用PrintWriter
  • System.out类返回的是PrintStream实例

数据流

用于将内存中的基本数据类型存到磁盘中,或者从磁盘中读取对应的基本数据类型的数据到内存中

/**
* 数据流
* DataInputStream 和 DataOutputStream
* 作用:用于读取或写出基本数据类型的变量或字符串
* 		需要注意的一点:读取的时候要按照存入的时候的顺序去读取,不能随机读取
*/
@Test
public void test(){

    DataOutputStream outputStream = null;
    DataInputStream inputStream = null;
    try {
        outputStream = new DataOutputStream(new FileOutputStream(new File("src\\javabasic\\day26\\hello.txt")));
        inputStream = new DataInputStream(new FileInputStream(new File("src\\javabasic\\day26\\hello.txt")));
        outputStream.writeUTF("小明");
        outputStream.writeInt(90);
        outputStream.writeBoolean(false);

        System.out.println(inputStream.readUTF());
        System.out.println(inputStream.readInt());
        System.out.println(inputStream.readBoolean());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

对象流

  • 用于存储和读取基本数据类型对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来;
  • 序列化:用ObjectOutputStream保存基本数据类型或对象的机制
  • 反序列化:用ObjectInputStream读取基本数据类型或对象的机制
  • ObjectInputStreamObjectOutputStream不能序列化statictransient修饰的成员变量
对象的序列化
  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
  • 序列化是RMI ( Remote Method Invoke -远程方法调用)过程的参数和返回值都必须实现的机制,而RMIJavaEE的基础。因此序列化机制是JavaEE平台的基础。
  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSerializableException异常
    • Serializable
    • Externalizable
自定义对象序列化
  • 需要实现接口:Serializable

  • 当前类提供一个全局常量:serialVersionUID

    • public static final long serialVersionUID = ...L;
      
  • 除了当前的Person类需要实现Serializable接口之外,还必须保证其内部所有属性也是可以序列化的(即:属性里可能含有其他类,需要保证这些类都可以序列化),默认情况下,基本数据类型是可序列化的。

  • statictransient修饰的成员变量不可序列化

为什么要提供serialVersionUID

serialVersionUID是一个版本号,相当于你那个类的版本:

  • 如果提供了这个版本号,那么在序列化的时候,他会记下这个版本号,然后假如你的类的结构变了(比如加多了其他属性或方法,即修改了类的结构了),那么在反序列化的时候,因为有版本号,所以他会在反序列化的时候去找有相同版本号的类,将之前的数据赋给之前有的属性,而没有的属性会赋默认值,这样就不会发生序列化错误。
  • 如果没有提供这个版本号,那么系统会自动生成一个版本号,序列化的时候也会记下这个版本号。
    • 如果没有改动类的情况下,在反序列化的时候,反序列化时候的版本号是可以在内存中找到的,所以可以将属性都重新赋值,即:反序列化是成功的;
    • 如果改动了类的话,在反序列化的时候,因为改动了类,所以系统生成了一个新的序列号了,此时新序列号可能与之前存储的序列号不同,导致找不到对应的类,此时反序列化失败;

RandomAccessFile

  • 直接继承于java.lang.Object,实现了DataInputDataOutput接口。
  • RandomAccessFile既可以作为一个输入流,也可以作为一个输出流。
  • 如果RandomAccessFile作为输出流,写出到的文件如果不存在,则在执行过程中自动创建;如果写出到的文件存在,则会对原有文件内容进行覆盖(从头开始覆盖)。
  • 构造器
    • public RandomAccessFile(File file, String mode)
    • public RandomAccessFile(String name, String mode)
  • 创建RandomAccessFile类实例需要指定一个mode参数,该参数指定RandomAccessFile的访问模式:
    • r:以只读方式打开
    • rw:打开以便读取和写入
    • rwd:打开以便读取和写入;同步文件内容的更新
    • rws:打开以便读取和写入;同步文件内容和元数据的更新
  • 如果模式为只读r。则不会创建文件,而是会去读取-一个已经存在的文件,如果读取的文件不存在则会出现异常。如果模式为rw读写。如果文件不存在则会去创建文件,如果存在则不会创建。

NIO

(非阻塞IO)

通道和缓冲区

Java NIO系统的核心在于:通道(channel)和缓冲区(Buffer)。通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。

简而言之:Channel负责传输,Buffer负责存储

缓冲区(Buffer)
/**
 * 一、缓冲区(buffer):在java nio中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
 *
 * 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
 * ByteBuffer
 * CharBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * FloatBuffer
 * DoubleBuffer
 *
 * 上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
 *
 *二、缓冲区存取数据的两个核心方法:
 *  put():存入数据到缓冲区中
 *  get():获取缓冲区中的数据
 *
 * 三、缓冲区中的四个核心属性:
 *  capacity:容量,表示缓冲区中最大存储数据的容量。一旦声明就不能改变
 *  limit:界限,表示缓冲区中可以操作数据的大小。(limit以后的数据不能进行读写)
 *  position:位置,表示缓冲区中正在操作数据的位置。
 *  mark:标记,表示记录当前position的位置。可以通过reset()恢复到mark的位置
 *
 * 0 <= mark <= position <= limit <= capacity
 *
 *
 * hasRemaining():缓冲区中是否还有剩余可操作元素
 * remaining():缓冲区中剩余可操作元素数量
 *
 * 四:直接缓冲区和非直接缓冲区
 *      非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中
 *      直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。
 *                  可以提高效率
 *
 * @Author: fxx
 * @Date: 2021/1/2 16:15
 */
public class BufferTest {

    //显示NIO的属性
    private void show(Buffer buffer, String s){
        System.out.println("-----------"+s+"--------------");
        System.out.println("position:" + buffer.position());
        System.out.println("limit:" + buffer.limit());
        System.out.println("capacity:" + buffer.capacity());
    }

    @Test
    public void test(){
        String str = "abcde";
        //1.分配一个指定大小的缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        show(byteBuffer, "allocate");
        //2.利用put()存入数据到缓冲区
        byteBuffer.put(str.getBytes());
        show(byteBuffer, "put");
        //3.切换到读取数据的模式,上面的put是写数据模式
        byteBuffer.flip();
        show(byteBuffer, "flip");
        //4.开始读数据
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        System.out.println(new String(bytes, 0, bytes.length));
        show(byteBuffer, "get");
        //5.rewind():可重复读数据,即,position回到读数据之前的位置
        byteBuffer.rewind();
        show(byteBuffer, "rewind");
        //6.clear():清空缓冲区,其实只是NIO的属性值变为初始值了,
        // 缓冲区中依然还保留着原来的数据
        byteBuffer.clear();
        show(byteBuffer, "clear");

        byteBuffer.put(str.getBytes());
        //7.mark():标记position位置,可用reset()回到刚刚position位置
        byteBuffer.flip();
        byteBuffer.get(bytes, 0, 2);
        show(byteBuffer, "flip-get");
        System.out.println(bytes.length);
        //标记一下
        byteBuffer.mark();
        byteBuffer.get(bytes, 2, 2);
        System.out.println(bytes);
        show(byteBuffer, "mark-get");
        byteBuffer.reset();
        show(byteBuffer,"reset");

        //判断缓冲区中是否还有剩余的数据
        if (byteBuffer.hasRemaining()){
            //获取缓冲区中可以操作的数据容量
            System.out.println("缓冲区中还可以操作的数据容量:"+byteBuffer.remaining());
        }
    }

    @Test
    public void test1(){
        //开辟直接缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        //判断是不是开辟的直接缓冲区
        System.out.println(byteBuffer.isDirect());
    }
}
NIO缓冲区的属性

NIO缓冲区的属性

直接缓冲区

缓冲区直接建立在物理内存上,不放内存中

NIO直接缓冲区

非直接缓冲区

缓冲区建立在JVM内存中

NIO非直接缓冲区

通道(Channel)

通道:由java.nio.channels包定义的。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”。只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。

/**
 * 一、通道(Channel):用于源结点与目标结点的连接。在Java NIO中负责缓冲区中的数据传输。
 *      Channel本身不存储数据,因此需要配合缓冲区进行传输。
 *
 * 二、通道的主要实现类
 *  java.nio.channels.Channel 接口:
 *      1.FileChannel
 *      2.SocketChannel
 *      3.ServerSocketChannel
 *      4.DatagramChannel
 *
 * 三、获取通道
 *  1.Java针对支持通道的类提供了getChannel()方法
 *      本地IO:
 *      FileInputStream / FileOutputStream
 *      RandomAccessFile
 *
 *      网络IO:
 *      Socket
 *      ServerSocket
 *      DatagramSocket
 *
 *  2.在JDK1.7中的NIO.2针对各个通道提供了静态方法  open()
 *  3.在JDK1.7中的NIO.2的 Files 工具类的 newByteChannel()
 *
 * 四、通道之间的数据传输
 *  transferFrom()
 *  transferTo()
 *
 * 五、分散(Scatter)与聚集(Gather)
 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
 *
 *
 * @Author: fxx
 * @Date: 2021/1/2 17:54
 */
public class ChannelTest {

    @Test
    public void test4(){
        FileChannel channel = null;
        FileChannel channel1 = null;
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw");
            //1.获取通道
            channel = randomAccessFile.getChannel();
            //2.分配指定大小的缓冲区
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
            ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
            //3.分散读取
            ByteBuffer[] buffers = {byteBuffer1, byteBuffer2};
            channel.read(buffers);
            for (ByteBuffer buffer : buffers) {
                buffer.flip();
            }

            System.out.println(new String(buffers[0].array(), 0, buffers[0].limit()));
            System.out.println("---------------");
            System.out.println(new String(buffers[1].array(), 0, buffers[1].limit()));

            //4.聚集写入
            RandomAccessFile accessFile = new RandomAccessFile("2.txt", "rw");

            channel1 = accessFile.getChannel();

            channel1.write(byteBuffer2);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (channel != null){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (channel1 != null){
                try {
                    channel1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //通道之间的数据传输(直接缓冲区)
    @Test
    public void test3(){
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = FileChannel.open(Paths.get("src\\javabasic\\NIO\\2.jpg"), StandardOpenOption.READ);
            outChannel = FileChannel.open(Paths.get("src\\javabasic\\NIO\\2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            inChannel.transferTo(0, inChannel.size(), outChannel);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inChannel != null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outChannel != null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    //2.使用直接缓冲区完成文件复制(内存映射文件)
    @Test
    public void test2(){
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = FileChannel.open(Paths.get("src\\javabasic\\NIO\\2.jpg"), StandardOpenOption.READ);
            outChannel = FileChannel.open(Paths.get("src\\javabasic\\NIO\\2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

            //内存映射文件
            MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

            //直接对缓冲区进行数据的读写操作
            byte[] bytes = new byte[inMappedBuf.limit()];
            inMappedBuf.get(bytes);
            outMappedBuf.put(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inChannel != null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outChannel != null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //1.利用通道完成文件的复制(非直接缓冲区)
    @Test
    public void test1(){
        //1.拿到流
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        FileChannel fileInputStreamChannel = null;
        FileChannel fileOutputStreamChannel = null;
        try {
            fileInputStream = new FileInputStream(new File("src\\javabasic\\NIO\\1.gif"));
            fileOutputStream = new FileOutputStream(new File("src\\javabasic\\NIO\\2.gif"));

            //2.通过流拿到通道
            fileInputStreamChannel = fileInputStream.getChannel();
            fileOutputStreamChannel = fileOutputStream.getChannel();

            //3.拿到缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            //4.通过通道操作缓冲区
            while(fileInputStreamChannel.read(byteBuffer) != -1){
                //切换为读模式
                byteBuffer.flip();
                fileOutputStreamChannel.write(byteBuffer);
                //清空缓冲区
                byteBuffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStreamChannel != null){
                try {
                    fileInputStreamChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStreamChannel != null){
                try {
                    fileOutputStreamChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileInputStream != null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStream != null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
非阻塞式网络编程-TCP
/**
 * 非阻塞式IO:
 * 1.通道(Channel):负责连接
 * 2.缓冲区(Buffer):负责数据的存取
 * 3.选择器(Selector):是SelectableChannel的多路复用器。用于监控SelectableChannel的IO状况
 *
 * @Author: fxx
 * @Date: 2021/1/3 15:01
 */
public class NonBlockingNIOTest {

    @Test
    public void client(){
        SocketChannel socketChannel = null;
        try {
            //1.获取通道
            socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
            //2.切换成非阻塞模式
            socketChannel.configureBlocking(false);
            //3.分配指定大小的缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            //4.发送数据给服务端
            byteBuffer.put(new Date().toString().getBytes());
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socketChannel != null){
                try {
                    //5.关闭通道
                    socketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    //try-catch-finally篇幅太长,这里用Exception取代
    @Test
    public void server()throws Exception{
        //1.获取通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.切换成非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //3.绑定连接端口
        serverSocketChannel.bind(new InetSocketAddress(9898));
        //4.获取选择器
        Selector selector = Selector.open();
        /**
         * SelectionKey.OP_ACCEPT:接收数据操作
         * SelectionKey.OP_CONNECT:连接操作
         * SelectionKey.OP_READ:读操作
         * SelectionKey.OP_WRITE:写操作
         * 如果有多个事件,使用 | 连接,如:SelectionKey.OP_READ | SelectionKey.OP_WRITE
         */
        //5.将通道注册到选择器上,并且指定“监听接收事件”
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6.轮询式地获取选择器上已经“准备就绪”的事件
        while(selector.select() > 0){
            //7.获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while(iterator.hasNext()){
                //8.获取准备就绪的事件
                SelectionKey selectionKey = iterator.next();
                //9.判断具体是什么事件准备就绪
                if(selectionKey.isAcceptable()){    //如果接受就绪
                    //10.获取客户端连接
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //11.切换非阻塞模式
                    socketChannel.configureBlocking(false);
                    //12.将该通道注册到选择器上
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }else if(selectionKey.isReadable()){    //如果已经读就绪
                    //13.获取当前选择器上“读就绪”状态的通道
                    SocketChannel sChannel = (SocketChannel) selectionKey.channel();
                    //14.读取数据
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    while(sChannel.read(byteBuffer) != -1){
                        byteBuffer.flip();
                        System.out.println(new String(byteBuffer.array(), 0, byteBuffer.limit()));
                        byteBuffer.clear();
                    }
                }
                //15.取消选择键:SelectionKey
                iterator.remove();
            }
        }
    }
}
非阻塞式网络编程-UDP
/**
 * @Author: fxx
 * @Date: 2021/1/4 15:38
 */
public class NonBlockingNIOUDPTest {

    @Test
    public void send()throws Exception{
        DatagramChannel datagramChannel = DatagramChannel.open();
        datagramChannel.configureBlocking(false);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put("123".getBytes());
        byteBuffer.flip();
        datagramChannel.close();
    }

    @Test
    public void receive()throws Exception{
        DatagramChannel datagramChannel = DatagramChannel.open();
        datagramChannel.configureBlocking(false);
        datagramChannel.bind(new InetSocketAddress(9898));
        Selector selector = Selector.open();
        datagramChannel.register(selector, SelectionKey.OP_READ);
        while (selector.select() > 0){
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey next = iterator.next();
                if (next.isReadable()){
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    datagramChannel.receive(byteBuffer);
                    byteBuffer.flip();
                    System.out.println(new String(byteBuffer.array(), 0, byteBuffer.limit()));
                    byteBuffer.clear();
                }
            }
            iterator.remove();
        }
    }
}
管道
/**
 * 管道:单向数据发送
 *
 * @Author: fxx
 * @Date: 2021/1/4 21:24
 */
public class PipeTest {

    @Test
    public void test()throws Exception{
        //1.获取管道
        Pipe pipe = Pipe.open();
        //2.将缓冲区中的数据写入管道
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        Pipe.SinkChannel sinkChannel = pipe.sink();
        byteBuffer.put("通过单向管道发送数据".getBytes());
        byteBuffer.flip();
        sinkChannel.write(byteBuffer);
        //3.读取缓冲区中的数据
        Pipe.SourceChannel sourceChannel = pipe.source();
        byteBuffer.flip();
        int len = sourceChannel.read(byteBuffer);
        System.out.println(new String(byteBuffer.array(), 0, len));
        //关闭资源
        sourceChannel.close();
        sinkChannel.close();
    }
}

Files、Path、Paths等

后面有看到再补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值