Java 总结第一弹

Java IO

IO流

1. 传统方式划分
1.1 简介
  • 传统方式将流划分为两种:字节流与字符流

    字节流:用来处理二进制文件,例如图片、MP3 、视频等

    字符流:用来处理文本文件,文本文件可以看作是一种特殊的二进制文件,经过编码,便于人们阅读

  • 字节流可以处理一切文件,而字符流只能处理文本

  • IO 核心 4 个抽象类:InputStream、OutputStream、Reader、Writer

  • InputStream 和 Reader 是所有输入流的基类

  • OutputStream和 Writer是所有输出流的基类

1.2 核心类

InputStream 类

  • 典型实现:FileInputStream (用于读取非文本数据之类的原始字节流)
  • 方法:
    • int read():读取数据 - 从输入流中读取数据的下一个字节
    • int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个byte 数组
    • int read(byte b[], int off, int len):从第 off 位置开始读,读取 len 长度的字节,然后放入数组 b 中
    • int available():返回可读的字节数
    • void close():关闭流,释放资源

OutputStream 类

  • 方法:
    • void write(int b): 将指定的字节写入
    • void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入
    • void write(byte b[], int off, int len): 将数组 b 中的从 off 位置开始,长度为 len 的字节写入
    • void flush(): 强制刷新,将缓冲区的数据写入
    • void close():关闭流,释放资源

Reader 类

  • 读取字符流,需要使用 FileReader
  • 方法:
    • int read():读取单个字符
    • int read(char[] ch):将字符读入数组 - 如果已经达到流的末尾,将返回 -1
    • int read(char[] ch, int off, int len):从第 off 位置开始读,读取 len 长度的字符,然后放入数组 b 中
    • int ready():判断是否可以读
    • void close():关闭流,释放资源

Writer 类

  • 方法:
    • void write(int c): 写入一个字符
    • void write(char[] ch): 写入字符数组
    • void write( char cbuf[], int off, int len): 将数组 cbuf 中的从 off 位置开始,长度为 len 的字符写入
    • void flush(): 强制刷新,将缓冲区的数据写入
    • void close():关闭流,释放资源

文件 - File 类

  • 新建、删除、重命名文件和目录,但 File 不能访问文件内容本身

  • 构造方法:

    • public File(String pathname)

      以pathname为路径创建File对象,可以是绝对路径或者相对路径

      如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储

      • 绝对路径:是一个固定的路径,从盘符开始
      • 相对路径:是相对于某个位置开始
    • public File(String parent, String child)

      以parent为父路径,child为子路径创建File对象

    • public File(File parent, String child)
      根据一个父File对象和子文件路径创建File对象

  • 常用方法:

    • 获取
      • getAbsolutePath() :获取绝对路径
      • getPath() :获取路径
      • getName() :获取名称
      • getParent() :获取上层文件路径地址
      • length() :获取文件长度
      • lastModified() :获取最后一次修改时间 - 毫秒级
      • list() :获取指定文件目录下的所有文件/文件目录的名称数组
      • listFiles() :获取指定文件目录下的所有文件/文件目录的File数组
    • 重命名:renameTo(File dest):把文件重命名为指定的文件路径
    • 判断:
      • isDirectory():是否为文件目录
      • isFile():是否为文件
      • exists():是否存在
      • canRead():能否读取
      • canWrite():能否写入
      • isHidden():是否隐藏
    • 创建:
      • createNewFile():创建文件
      • mkdir():创建文件目录 - 上层文件目录不存在会执行失败,返回 false
      • mkdirs():创建文件目录 - 同时创建该文件所在路径的所有缺失的父目录
    • 删除:delete():删除文件/文件目录
2. 操作对象划分
2.1 简介
  • IO IO,就是输入输出(Input/Output)

  • Input:将外部的数据读入内存,比如说把文件从硬盘读取到内存,从网络读取数据到内存等等

  • Output:将内存中的数据写入到外部,比如说把数据从内存写入到文件,把数据从内存输出到网络等等

  • IO 可以分类为:文件、数组、管道、基本数据类型、缓冲、打印、对象序列化/反序列化,以及转换等

2.2 分类
2.2.1 文件
  • 文件流也就是直接操作文件的流,可以细分为字节流(FileInputStream 和 FileOuputStream)和字符流(FileReader 和 FileWriter)

  • FileInputStream 实例

    int b;
    FileInputStream fileIn = new FileInputStream("fileIn.txt");
    // 循环读取
    while ((b = fileIn.read())!=-1) {
        System.out.println((char)b);
    }
    // 关闭资源
    fileIn.close();
    
  • FileOuputStream 实例

    String str = "我是文件输出流";
    FileOutputStream fileOut = new FileOutputStream("fileOut.txt");
    fileOut.write(str.getBytes());
    fileOut.close();
    
  • FileReader 实例

    int b = 0;
    FileReader fileReader = new FileReader("read.txt");
    // 循环读取
    while ((b = fileReader.read())!=-1) {
        // 自动提升类型提升为 int 类型,所以用 char 强转
        System.out.println((char)b);
    }
    // 关闭流
    fileReader.close();
    
  • FileWriter实例

    String str = "我是文件写入流";
    FileWriter fileWriter = new FileWriter("writer.txt");
    char[] chars = str.toCharArray();
    fileWriter.write(chars, 0, chars.length);
    fileWriter.close();
    
2.2.2 数组
  • 为了提升效率,频繁地读写文件并不是太好,因此就出现了数组流,有时候也称为内存流

  • ByteArrayInputStream

    String str = "我是数组输入流";
    InputStream is = new BufferedInputStream(
            new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)));
    //操作
    byte[] flush = new byte[1024];
    int len = 0;
    while(-1 != (len = is.read(flush))) {
        System.out.println(new String(flush, 0, len));
    }
    //释放资源
    is.close();
    
  • ByteArrayOutputStream

    String str = "我是数组输出流";
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    byte[] info = str.getBytes();
    bos.write(info, 0, info.length);
    //获取数据
    byte[] dest = bos.toByteArray();
    //释放资源
    bos.close();
    
2.2.3 管道
  • Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的进程之间可以通过管道来通信,但 Java 中,通信的双方必须在同一个进程中,也就是在同一个 JVM 中,管道为线程之间的通信提供了通信能力

  • 一个线程通过 PipedOutputStream 写入的数据可以被另外一个线程通过相关联的 PipedInputStream 读取出来

    final PipedOutputStream pipedOutputStream = new PipedOutputStream();
    final PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
    
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String str = "我是管道流";
                pipedOutputStream.write(str.getBytes(StandardCharsets.UTF_8));
                pipedOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    
    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                byte[] flush = new byte[1024];
                int len =0;
                while(-1 != (len = pipedInputStream.read(flush))){
                    System.out.println(new String(flush, 0, len));
                }
    
                pipedInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    });
    thread1.start();
    thread2.start();
    
2.2.4 基本数据类型
  • 基本数据类型输入输出流是一个字节流,该流不仅可以读写字节和字符,还可以读写基本数据类型

  • DataInputStream 提供了一系列可以读基本数据类型的方法

    DataInputStream dataIn = new DataInputStream(new FileInputStream(“dataIn.txt”)) ;
    byte b = dataIn.readByte() ;
    short s = dataIn.readShort() ;
    int i = dataIn.readInt();
    long l = dataIn.readLong() ;
    float f = dataIn.readFloat() ;
    double d = dataIn.readDouble() ;
    boolean bool = dataIn.readBoolean() ;
    char ch = dataIn.readChar() ;
    
  • DataOutputStream 提供了一系列可以写基本数据类型的方法

    DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(“dataOut.txt”));
    dataOut.writeByte(10);
    dataOut.writeShort(100);
    dataOut.writeInt(1000);
    dataOut.writeLong(10000L);
    dataOut.writeFloat(12.34F);
    dataOut.writeDouble(12.56);
    dataOut.writeBoolean(true);
    dataOut.writeChar('A');
    
2.2.5 缓冲
  • 为了减少程序和硬盘的交互,提升程序的效率,就引入了缓冲流,也就是类名前缀带有 Buffer 的那些,比如说 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

  • 缓冲流在内存中设置了一个缓冲区,只有缓冲区存储了足够多的带操作的数据后,才会和内存或者硬盘进行交互

    简单来说,就是一次多读/写点,少读/写几次,这样程序的性能就会提高

2.2.6 对象序列化/反序列化
  • 序列化本质上是将一个 Java 对象转成字节数组,然后可以将其保存到文件中,或者通过网络传输到远程

    String str = "我是序列化流";
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
        output.writeUTF(str);
    }
    System.out.println(Arrays.toString(buffer.toByteArray()));
    
  • 反序列化,将字节数组转成 Java 对象的过程

    try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(
            new File("file.txt")))) {
        String s = input.readUTF();
    }
    
2.2.7 转换
  • InputStreamReader 是从字节流到字符流的桥连接,它使用指定的字符集读取字节并将它们解码为字符

    InputStreamReader insr = new InputStreamReader(
            new FileInputStream("demo.txt"));
    char[] ch = new char[1024];
    int len = insr.read(cha);
    System.out.println(new String(ch, 0, len));
    isr.close();
    
  • OutputStreamWriter 将一个字符流的输出对象变为字节流的输出对象,是字符流通向字节流的桥梁

    File file = new File("test.txt") ;
    // 字节流变为字符流  
    Writer out = new OutputStreamWriter(new FileOutputStream(file));
     // 使用字符流输出  
    out.write("hello world!!");
    out.close();
    
2.2.8 打印
  • System.out 其实返回的就是一个 PrintStream 对象,可以用来打印各式各样的对象

    System.out.println("我是打印输出流");
    
  • PrintStream 最终输出的是字节数据,而 PrintWriter 则是扩展了 Writer 接口,所以它的 print()/println() 方法最终输出的是字符数据

    StringWriter buffer = new StringWriter();
    try (PrintWriter pw = new PrintWriter(buffer)) {
        pw.println("我是打印输出流");
    }
    System.out.println(buffer.toString());
    
3. AIO、BIO、NIO
3.1 BIO
  • BIO 是一种同步且阻塞的通信模式 - 一个连接一个线程

  • 是一个比较传统的通信方式,模式简单,使用方便,但并发处理能力低,通信耗时,依赖网速

  • 适用场景:

    连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中 ,但程序直观简单易理解 - JDK1.4 以前的唯一选择

  • 文件的读取与写入

    //初始化实体类
    User user = new User();
    user.setName("Joker");
    user.setAge(23);
    System.out.println(user);
    
    // 将内容写入到指定文件
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(new FileOutputStream("tempFile.txt"));
        oos.writeObject(user);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        IOUtils.closeQuietly(oos);
    }
    
    // 从指定文件读取信息
    File file = new File("tempFile.txt");
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream(file));
        User1 newUser = (User1) ois.readObject();
        System.out.println(newUser);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        IOUtils.closeQuietly(ois);
        try {
            FileUtils.forceDelete(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
3.2 NIO
  • NIO 是一种非阻塞同步的通信模式,以块的方式处理数据 - 一个请求一个线程

  • 按块处理数据比按(流式的)字节处理数据要快得多,但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性

  • 适用场景

    连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂 - JDK1.4 开始支持

  • 文件的读取和写入

    static void readNIO() {
        String pathname = "E:\SpringNote\mdAndpdf\notebook\Java\visio\TreeMap.png";
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(new File(pathname));
            FileChannel channel = fin.getChannel();
    
            // 字节
            int capacity = 100;
            ByteBuffer bf = ByteBuffer.allocate(capacity);
            int length = -1;
    
            while ((length = channel.read(bf)) != -1) {
    
                bf.clear();
                byte[] bytes = bf.array();
                System.out.write(bytes, 0, length);
                System.out.println();
            }
    
            channel.close();
    
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    static void writeNIO() {
        String filename = "out.txt";
        FileOutputStream fos = null;
        try {
    
            fos = new FileOutputStream(new File(filename));
            FileChannel channel = fos.getChannel();
            ByteBuffer src = Charset.forName("utf8").encode("你好你好你好你好你好");
            int length = 0;
    
            while ((length = channel.write(src)) != 0) {
                System.out.println("写入长度:" + length);
            }
    
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
3.3 AIO
  • AIO 是一种异步非阻塞的通信模式 - 一个有效请求一个线程

  • 在 NIO 的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现

  • 适用场景

    连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持

  • 文件的读取与写入

    public class ReadFromFile {
      public static void main(String[] args) throws Exception {
        Path file = Paths.get("E:\SpringNote\mdAndpdf\notebook\Java\txt\a.txt");
        AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
    
        ByteBuffer buffer = ByteBuffer.allocate(100_000);
        Future<Integer> result = channel.read(buffer, 0);
    
        while (!result.isDone()) {
          ProfitCalculator.calculateTax();
        }
        Integer bytesRead = result.get();
        System.out.println("Bytes read [" + bytesRead + "]");
      }
    }
    class ProfitCalculator {
      public ProfitCalculator() {
      }
      public static void calculateTax() {
      }
    }
    
    public class WriteToFile {
    
      public static void main(String[] args) throws Exception {
        AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
            Paths.get("E:\SpringNote\mdAndpdf\notebook\Java\txt\asynchronous.txt"), StandardOpenOption.READ,
            StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        CompletionHandler<Integer, Object> handler = new CompletionHandler<Integer, Object>() {
    
          @Override
          public void completed(Integer result, Object attachment) {
            System.out.println("Attachment: " + attachment + " " + result
                + " bytes written");
            System.out.println("CompletionHandler Thread ID: "
                + Thread.currentThread().getId());
          }
    
          @Override
          public void failed(Throwable e, Object attachment) {
            System.err.println("Attachment: " + attachment + " failed with:");
            e.printStackTrace();
          }
        };
    
        System.out.println("Main Thread ID: " + Thread.currentThread().getId());
        fileChannel.write(ByteBuffer.wrap("Sample".getBytes()), 0, "First Write",
            handler);
        fileChannel.write(ByteBuffer.wrap("Box".getBytes()), 0, "Second Write",
            handler);
    
      }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值