Java-BIO

概述

  • 全程为 Block Input/Output(同步阻塞式输入/输出), 指的就是传统 IO, 是用于处理设备之间的数据传输. 如 读写文件, 网络通讯等
  • 服务器实现模式为每次连接都会创建独立的线程
  • 相关的类和接口在 java.io包中
  • 数据的输入或输出操作是以流(Stream)的方式进行

IO流的分类

  1. 按照操作数据的单位: 字节流(8bit), 字符流(16bit)
  • 字节流: 非文本文件, 就是按照二进制形式进行读写操作. 如 .mp3,.avi,.rmvb,.mp4,.jpg,.doc,.ppt等
  • 字符流: 只能操作普通文本文件 如 .txt,.java,.c,.cpp等. 注 .doc,.excel,.ppt等文件不属于普通文本
  1. 按照流的角色: 节点流, 处理流
  • 节点流: 数据源与程序之间直接建立流连接, 进行读写数据
  • 处理流: 数据源与程序之间未直接连接, 而是建立在已存在的流(节点流或处理流)之上, 通过对数据的处理来提升了读写性能
  1. 按照数据流的流向: 输入流, 输出流
  • Java的 IO流共涉及40多个类, 都是派生于以下抽象基类
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

在这里插入图片描述

文件流(节点流)

  • 在文件写入时, 使用构造器 FileWriter(file)指定文件+路径, 如果已有相同名称的文件, 则会覆盖, 否则生成
  • 使用构造器 FileWriter(file, true), 则同名文件不会被覆盖, 而是内容末端会追加流

public class IOWriterApp {
    public static void main(String[] args) {
        FileWriter fw = null;
        try {
            // 创建用于写的流对象
            fw = new FileWriter(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "节点流01.txt", true
            );
            // 将数据写入流
            fw.write("写入文本!\nabc!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fw != null)
                try {
                    // 关闭流资源(IO 资源不属于内存资源, 所以 JVM垃圾回收机制不会自动回收, 因此只能显式关闭 IO资源)
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

}

public class IOReaderApp {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            // 创建用于读的流对象
            fr = new FileReader(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "节点流01.txt"
            );
            // 创建字符数组, 用于临时存放数据
            char[] buf = new char[1024];
            // 每次读取的字符个数
            int len;
            // 调用流对象的读取方法将流中的数据读入到数组中
            while ((len = fr.read(buf)) != -1) {
                System.out.print(new String(buf, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fr != null) {
                try {
                    // 关闭流资源
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}


缓冲流

  • 为了提高数据读写速度, Java API提供了缓冲流, 内部提供缺省 8kb的数组用于数据缓冲
  • 缓冲流是“套接”在已有字节或字符流之上的, 执行过程:
  • 读取数据时, 是将数据按块读入到缓冲区(每次默认一次性从文件中读取 8kb数据, 存在缓冲区中), 直到缓冲区装满, 使用时从缓冲区获取已缓冲的数据
  • 写入数据时, 不会直接写到文件, 而是先写到缓冲区中, 直到缓冲区满后, 会将缓冲区中的数据一次性写到文件中(也可以执行 flush()方法, 强制刷新缓冲区将内容写入到文件流中), 或最后关闭缓冲流时, 也会先刷新缓冲区, 再关闭流
  • 按照数据操作单位分为:
  • 字节缓冲流: BufferedInputStream& BufferedOutputStream
  • 字符缓冲流: BufferedReader& BufferedWriter

public class IOBufferedApp {
    public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            // 创建流对象
            br = new BufferedReader(new FileReader(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                    "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                    "io" +File.separator+ "缓冲流-src02.txt"
                )
            );
            bw = new BufferedWriter(new FileWriter(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                    "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                    "io" +File.separator+ "缓冲流-dest02.txt", true
                )
            );
            String str;
            // 读取一行, 不包含换行符
            while ((str = br.readLine()) != null) {
                // 将数据写入流
                bw.write(str);
                // 写入换行符
                bw.newLine();
                // 刷新缓冲区(清空)
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流对象
            try {
                if (bw != null) {
                    // 关闭缓冲流时, 会自动关闭内部被包装的流(节点流)
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

转换流

  • 转换流主要用于实现文件的编码和解码的功能

public class IOApp {
    public static void main(String[] args){
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            FileInputStream fis = new FileInputStream(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                    "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                    "io" +File.separator+ "转换流-src03.txt"
            );
            FileOutputStream fos = new FileOutputStream(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                    "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                    "io" +File.separator+ "转换流-dest03.txt", true
            );
            InputStreamReader isr = new InputStreamReader(fis, "UTF8");
            OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
            br = new BufferedReader(isr);
            bw = new BufferedWriter(osw);
            String str;
            // 读取一行, 不包含换行符
            while ((str = br.readLine()) != null) {
                bw.write(str);
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流对象
            try {
                if (bw != null) {
                    // 关闭缓冲流时, 会自动关闭内部被包装的流(节点流)
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

标准输入& 输出流

  • 通过 System.in(输入), System.out(输出). 默认输入设备是键盘, 输出是显示器
  • 重定向: 通过 System类的 setIn, setOut方法, 更变默认设备

public class IOApp {
    public static void main(String[] args) {
        System.out.println("请输入信息(退出输入 exit):");
        /** 1. 字节输入流(System.in), 赋给转换流, 再包装成缓冲流
         *  2. 通过 System.in, 监听键盘输入的信息*/
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s;
        try {
            // 读取用户输入的一行数据(阻塞程序)
            while ((s = br.readLine()) != null) {
                if ("exit".equalsIgnoreCase(s)) {
                    System.out.println("结束程序!");
                    break;
                }
                // 输出读取到的一行数据
                System.out.println("输出信息: " + s);
                System.out.println("继续输入信息:");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

打印流

  • 有2种打印流: PrintStream和 PrintWriter
  • PrintStream和 PrintWriter的输出不会抛出 IOException异常
  • PrintStream和 PrintWriter的第二个参数设置 true, 意为自动 flush

public class IOApp {
    public static void main(String[] args) {
        PrintStream ps = null;
        try {
            FileOutputStream fos = new FileOutputStream(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "打印流05.txt"
            );
            // 创建打印输出流, 第二个参数设置 true, 意为自动刷新模式(写入换行符时, 会自动刷新缓冲区并输出到文件)
            ps = new PrintStream(fos, true);
            if (ps != null) {
                // 将控制台输出改成输出到文件
                System.setOut(ps);
            }
            for (int i = 1; i <= 999; i++) {
                System.out.print(i + ", ");
                if (i % 50 == 0) {
                    // 每输出50个数后换行
                    System.out.println();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

}

数据流

  • 数据流是用于读写基本数据类型和 String类的流
  • 数据流有两个类: DataInputStream和 DataOutputStream. 分别“套接”在 InputStream和 OutputStream子类的流上

public class IODataOutputApp {
    public static void main(String[] args) {
        DataOutputStream dos = null;
        try {
            // 创建用于数据输出的流对象
            dos = new DataOutputStream(
                    new FileOutputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                    "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                    "io" +File.separator+ "数据流06.dat")
            );
            dos.writeUTF("测试 UTF");
            dos.writeBoolean(true);
            dos.writeLong(1234567890L);
            dos.writeDouble(123.123456789);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (dos != null) {
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

public class IODataInputApp {
    public static void main(String[] args) {
        DataInputStream dis = null;
        try {
            // 创建用于数据输入的流对象
            dis = new DataInputStream(
                    new FileInputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                    "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                    "io" +File.separator+ "数据流06.dat")
            );
            String utf = dis.readUTF();
            boolean _boolean = dis.readBoolean();
            long _long = dis.readLong();
            double _double = dis.readDouble();
            System.out.println(utf);
            System.out.println(_boolean);
            System.out.println(_long);
            System.out.println(_double);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

对象流

  • 对象流用于处理(存取)基本数据类型或对象
  • 对象流有两个类: ObjectInputStream和 OjbectOutputStream
  • 当对象存储时, 会将该对象序列化后写到磁盘(或进行网络传输)
  • 序列化时, 对象必须实现 如下接口之一 Serializable或 Externalizable, 否则, 会抛出 NotSerializableException异常
  • 反序列化时, 版本必须与序列化时的版本一致, 否则, 会抛出 InvalidCastException异常
  • static和 transient修饰的成员变量, 不会被序列化

public class Person implements Serializable {
    private static final long serialVersionUID = 1213831504600021305L;
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class IOObjectOutputApp {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(
                    new FileOutputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "对象流07.dat")
            );
            Person p = new Person("伟人1", 30);
            oos.writeObject(p);
            p = new Person("伟人2", 35);
            oos.writeObject(p);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

public class IOObjectInputApp {
    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(
                    new FileInputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "对象流07.dat")
            );
            Person p = (Person) ois.readObject();
            System.out.println(p);
            p = (Person) ois.readObject();
            System.out.println(p);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

随机存取文件流

  • RandomAccessFile类在 java.io包下, 但直接继承于 java.lang.Object类. 它实现了 DataInput和 DataOutput接口, 也就意味着它既可以读也可以写
  • RandomAccessFile类支持, 从任意指针(下标)开始读写文件:
  • long getFilePointer(), 获取记录指针的当前位置
  • void seek(long pos), 将指针定位到 pos位置
  • 构造器的第二个参数表示文件的访问模式:
  • r: 以只读方式打开
  • rw:打开以便读取和写入
  • rwd: 打开以便读取和写入; 同步文件内容的更新
  • rws: 打开以便读取和写入; 同步文件内容和元数据的更新

*注 当模式为 r, 且文件不存在时, 会抛异常. 或模式为 rw, 且文件不存在, 则会自动创建文件


public class IOWriterApp {
    public static void main(String[] args) {
        insert(0, "ABCDEFG!");
        insert(1, "HHH!");
    }

    public static void insert(int startPosition, String insertContent) {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "随机存取文件流08.txt", "rw");
            // 定位文件记录指针开始位置
            raf.seek(startPosition);
            // 开始输出(注 指定下标下已有内容会被覆盖)
            raf.write(insertContent.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                raf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

public class IOReaderApp {
    public static void main(String[] args) {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(
                    "src" +File.separator+ "main" +File.separator+ "java" +File.separator+
                            "org" +File.separator+ "example" +File.separator+ "base" +File.separator+
                            "io" +File.separator+ "随机存取文件流08.txt", "r");
            byte [] bytes = new byte[1024];
            int eachLength;
            while ((eachLength = raf.read(bytes)) != -1) {
                System.out.print(new String(bytes, 0, eachLength));
            }
            System.out.println();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                raf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

BIO网络编程实例

  • 服务器:
  1. 使用 BIO模型编写服务器端, 监听 6666端口
  2. 使用线程池机制, 连接多个客户端
  3. 可以接收客户端发送的数据
  • 客户端: telnet

    public static void main(String[] args) throws Exception {
        // 创建一个线程池
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        // 创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("服务器启动了");
        while (true) {
            System.out.println("线程信息 id:" + Thread.currentThread().getId() + ", name:" + Thread.currentThread().getName());
            // 监听, 等待客户端连接
            System.out.println("等待连接...");
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            // 创建一个线程
            newCachedThreadPool.execute(new Runnable() {
                public void run() {
                    // 可以和客户端通讯
                    handler(socket);
                }
            });
        }
    }

    // 编写一个handler方法,和客户端通讯
    public static void handler(Socket socket) {
        try {
            System.out.println("线程信息 id:" + Thread.currentThread().getId() + ", name:" + Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //通过socket 获取输入流
            final InputStream inputStream = socket.getInputStream();
            //循环的读取客户端发送的数据
            while (true) {
                System.out.println("线程信息 id:" + Thread.currentThread().getId() + ", name:" + Thread.currentThread().getName());
                System.out.println("read...");
                int read = inputStream.read(bytes);
                if (read != -1) {
                    // 输出客户端发送的数据
                    System.out.println(new String(bytes, 0, read));
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭和客户端的连接");
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值