Java IO

简介: Java IO

IO框架图:

623c9d21b4782cdbce87986fc8fbba89.pngJava 的 I/O 操作类在包 java.io 下,大概有将近 80 个类,但是这些类大概可以分成四组,分别是:


基于字节操作的 I/O 接口:InputStream 和 OutputStream


基于字符操作的 I/O 接口:Writer 和 Reader


基于磁盘操作的 I/O 接口:File


基于网络操作的 I/O 接口:Socket

前两组主要是根据传输数据的数据格式,后两组主要是根据传输数据的方式,虽然 Socket 类并不在 java.io 包下,但是我仍然把它们划分在一起,因为我个人认为 I/O 的核心问题要么是数据格式影响 I/O 操作,要么是传输方式影响 I/O 操作,也就是将什么样的数据写到什么地方的问题,I/O 只是人与机器或者机器与机器交互的手段,除了在它们能够完成这个交互功能外,我们关注的就是如何提高它的运行效率了,而数据格式和传输方式是影响效率最关键的因素了。我们后面的分析也是基于这两个因素来展开的。


不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符,所以 I/O 操作的都是字节而不是字符,但是为啥有操作字符的 I/O 接口呢?这是因为我们的程序中通常操作的数据都是以字符形式,为了操作方便当然要提供一个直接写字符的 I/O 接口,如此而已。我们知道字符到字节必须要经过编码转换,而这个编码又非常耗时,而且还会经常出现乱码问题,所以 I/O 的编码问题经常是让人头疼的问题。

1.流的概念

流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。

 一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。

 流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。


  实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。  

形象的比喻——水流 ,文件 ======程序 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出.便于理解,这么定义流: 流就是一个管道里面有流水,这个管道连接了文件和程序。

2.流的分类

字节流:一次读入或读出是8位二进制。

字符流:一次读入或读出是16位二进制。

字节流和字符流的原理是相同的,只不过处理的单位不同而已。后缀是Stream是字节流,而后缀是Reader,Writer是字符流。

节点流:直接与数据源相连,读入或读出。

0_1332494121hUHK.png

直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

处理流:与节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流

0_1332494112YBmp.png

Jdk提供的流继承了四大类:


InputStream(字节输入流),OutputStream(字节输出流): 主要用来表示 二进制文件(图片、视频、音频、文本文件)


Reader(字符输入流),Writer(字符输出流):表示文本文件(Windows 自带的记事本软件打开能看懂内容的文件)


Java的常用输入、输出流

java.io包中的stream类根据它们操作对象的类型是字符还是字节可分为两大类: 字符流和字节流。

  • Java的字节流
  • InputStream是所有字节输入流的祖先,而OutputStream是所有字节输出流的祖先。
  • Java的字符流
  • Reader是所有读取字符串输入流的祖先,而writer是所有输出字符串的祖先。

结合开始所说的输入/输出流 ,出现了个一小框架

                 字节流                         字符流

输入流        InputStream               Reader

输出流        OutputStream            Writer

实例1:向文件中写入数据

package cn.czbk.no9;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutTest {
    public static void main(String[] args) throws Exception {
        /**
         * 使用字节输入流的步骤
         * 1.打开文件(创建字节输出流对象)
         * 2.写出数据
         * 3.关闭文件(释放资源)
         */
        FileOutputStream fos=new FileOutputStream("src/test.txt", true);
        for(int i=0;i<9;i++){
            fos.write(("this is for test about fileoutstream i= "+i+"\r\n").getBytes());
        }
        fos.close();
    }
}

View Code

602e4c70cf7ffdfdf7bc08bdd13808f9.png

实例2:从文件中读取数据

package cn.czbk.no9;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class FileInptTest {
    public static void main(String[] args) throws Exception {
        /**
         * 使用字节输入流的步骤
         * 1.打开文件(创建字节输入流对象)
         * 2.读取数据
         * 2.1建立一个byte数组
         * 2.2将文件中的内容读取到byte数组中来,然后对其进行处理
         * 3.关闭文件(释放资源)
         */
        FileInputStream fis=new FileInputStream("src/test.txt");
        byte[] b=new byte[1000];
        int len=0;
        while((len=fis.read(b))!=-1){
            System.out.println(new String(b,0,len));
        }
        fis.close();
    }
}

View Code

61ca9086ca57bc2a6d3ea4dc5b5048f7.png

实例3:拷贝文件

package cn.czbk.no9;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileCopy {
    public static void main(String[] args) throws Exception{
        /**
         * 拷贝:先读在写
         */
        /**
        FileInputStream fis=new FileInputStream("src/test.txt"); 
        byte[] buffer=new byte[10000];
        int length;
        //read方法一次读完但是如果内容字节数大于定义的数组的长度的话就分多次读
        while((length=fis.read(buffer))!=-1){
            System.out.println(length);
            System.out.println("读取成功准备写入新文件。。。");
        }
        //在最后一次循环的时候读完文件后返回的length是-1所以你在取得时候只能按照buffer的长度去取
        for(int i=0;i<length;i++){
            System.out.println(length);
            System.out.println(buffer[i]);
        }
        fis.close();
        FileOutputStream fos=new FileOutputStream("copyfrombu.txt", true);
        fos.write(buffer, 0, buffer.length);
        System.out.println("写入成功");
        fos.close();
        */
        /**
         * 优化
         */
        FileInputStream fis=new FileInputStream("src/bu.txt");
        FileOutputStream fos=new FileOutputStream("bububu.txt",true);
        byte[] buffer=new byte[1000];
        int k;
        while((k=fis.read())!=-1){
            fos.write(k);
        }
        fis.close();
        fos.close();
    }
}

View Code840c4163ca9e4bd19640f3976b2db4af.png

实例4:字节缓冲输入输出流(处理流、过滤流)

package cn.czbk.no9;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class BufferOutPutTest {
    public static void main(String[] args) throws Exception{
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("src/bufferout.txt"));
        for(int i=0;i<5;i++){
            bos.write(("abcd"+i+"\r\n").getBytes());
        }
        bos.close();
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("src/bufferout.txt"));
        byte[] buf=new byte[1000];
        int length=0;
        while((length=bis.read(buf))!=-1){
            System.out.println(new String(buf, 0, length));
        }
        bis.close();
    }
}

View Code

a77228d199079387126e081d9a192e7b.png

实例5:字符串编码问题

解码: 把看不懂的数据解析成 能看懂的数据

例如:字节数组转换成 String 的操作

API: public String(byte[] bytes) 使用平台的默认字符集解码指定的 byte 数组

默认字符集: GBK编码: 把能看懂的转换成看不懂的

例如:String 转换成字节数组的操作

API: public byte[] getBytes()使用平台的默认字符集将此 String 编码为 byte

序列,并将结果存储到一个新的 byte 数组中。

默认字符集: GBK

public byte[] getBytes(Charset charset)使用给定的 charset 将此 String 编

码到 byte 序列,并将结果存储到新的 byte 数组。

注意:我们这里可能会抛出一个 UnsupportedEncodingException 不支持的编码异常

我们常见的码表:


* Unicode:用两个字节表示一个数据 java中就是使用的Unicode编码

* GB2312:简体中文, 一个数据用两个字节(16位)表示国标码

* GBK编码:在GB2312的基础之上对于中文数据进行了升级

* GB18030:简体中文,是在GBK的基础之上又做了升级

* UTF-8:表示Unicode数据的话需要使用1-6个字节来表示,一般情况1-3个字节即可以表示

* 表示一个汉字用3个字节来表示

* 能用1一个字节表示的数据就用1个字节来表示

* 能用2个字节来表示的数据就用2个字节来表示

package cn.czbk.no9;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class StringEncoding {
    public static void main(String[] args) throws Exception {
        /**
         * Unicode:用两个字节表示一个数据 java中就是使用的Unicode编码
         * GB2312:简体中文,    一个数据用两个字节(16位)表示国标码
         * GBK编码:在GB2312的基础之上对于中文数据进行了升级
         * GB18030:简体中文,是在GBK的基础之上又做了升级
         * UTF-8:表示Unicode数据的话需要使用1-6个字节来表示,一般情况1-3个字节即可以表示
         *            表示一个汉字用3个字节来表示
         *            能用1一个字节表示的数据就用1个字节来表示
         *                能用2个字节来表示的数据就用2个字节来表示
         */
        String s="李翎野";
        //将字符串转化为字节数组的形式
        //java中是Unicode编码
        byte[] arr=s.getBytes();
        //转码:转码都是已字节为基础进行转码的而不是字符串
        //Unicode转为GBK
        byte[] gbkarr=s.getBytes("GBK");
        //Unicode转为UTF-8
        byte[] u8arr=s.getBytes("UTF-8");
        /**
         * System.out.println("arr"+arr);
         * 不可以这样打印,这样打出来的是数组的引用,而不是对应的字节码
         */
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(gbkarr));
        System.out.println(Arrays.toString(u8arr));
        String s1=new String(arr);
        String s2=new String(gbkarr, "GBK");
        String s3=new String(u8arr,"UTF-8");
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }
}

View Code

实例6:转换流


问题:为什么需要转换流?

因为,字节流处理汉字 不方便, 所以出现了 转换流。转换流 = 字节流 + 编码表

常见的转换流(API 介绍):

OutputStreamWriter: 编码 输出流

InputStreamReader: 解码 输入流

package cn.czbk.no9;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class FileOutEncoding {
    public static void main(String[] args) throws Exception{
        //字符流
        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("src/unicodep.txt"));
        OutputStreamWriter gbk=new OutputStreamWriter(new FileOutputStream("src/gbkp.txt"), "GBK");
        OutputStreamWriter u8=new OutputStreamWriter(new FileOutputStream("src/u8p.txt"), "UTF-8");
        //字符流直接在流里面写汉字就可以了不需要转化为byte
        osw.write("中国");
        gbk.write("中国");
        /**
         * 用文档打开会出现乱码这是因为
         * 当我们向外输出时,用 UTF-8 读进行了编码,而我们的操作系统显示 txt 文档是用 GBK
         * 解码的,由于编码和解码码表不一致,所以就会出现乱码。
         */
        u8.write("中国");
        osw.close();
        gbk.close();
        u8.close();
    }
}

View Code

实例7:字符缓冲流

package cn.czbk.no9;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
public class BufferWriter {
    public static void main(String[] args) throws Exception{
        BufferedWriter bw=new BufferedWriter(new FileWriter("src/gbkp.txt"));
        bw.write("深圳今天下了很多雨");
        bw.close();
        BufferedReader br=new BufferedReader(new FileReader("src/gbkp.txt"));
        int ch=0;
        while((ch=br.read())!=-1){
            System.out.print((char)ch);
        }
        br.close();
    }
}

View Code

package cn.czbk.no9;
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferReaderTest {
    public static void main(String[] args) throws Exception{
        BufferedReader br=new BufferedReader(new FileReader("src/gbkp.txt"));
        //直接读字符
        int a=0;
        while((a=br.read())!=-1){
            System.out.println((char)a);
        }
        br.close();
    }
}

View Code

2aa3c9bae565ae5b8ba591bb473b4dd4.png

实例8:Properties集合

Java中有个比较重要的的类Properties(java.util.Properties),是代表一个持久的一套详细属性,

实现集合与 IO 进行交互

特点:

1: 它是一个 Map 集合, 是 Hashtable 的子类

2: 键和值 都是 String 类型

属性可以被保存到一个流或从流中加载的类。以下是关于属性的要点:

  • 属性列表中每个键及其对应值是一个字符串。
  • 一个属性列表可包含另一个属性列表作为它的“默认”,第二个属性可在列表中搜索,如果没有在原有的属性列表中找到的属性键。
  • 这个类是线程安全的;多个线程可以共享一个Properties对象,而不需要外部同步
package cn.czbk.no9;
import java.util.Properties;
import java.util.Set;
public class PropertiesTest {
    public static void main(String[] args) {
        Properties p=new Properties();
        p.setProperty("1", "小花");
        p.setProperty("2", "小明");
        p.setProperty("3", "小王");
        Set<String> set=p.stringPropertyNames();
        for(String key:set){
            String va=p.getProperty(key);
            System.out.println(key+" "+va);
        }
    }
}

View Code



package cn.czbk.no9;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Properties;
import java.util.Set;
public class PropertiesFileTest {
    private void storeFile(Properties p) throws Exception{
        p.setProperty("1", "腰疼");
        p.setProperty("2", "脖子疼");
        p.setProperty("3", "眼睛疼");
        p.store(new FileWriter("src/pro.txt"), "This is Test");
    }
    private void loadFile() throws Exception{
        Properties p=new Properties();
        p.load(new FileReader("src/pro.txt"));
        Set<String> ss=p.stringPropertyNames();
        for(String key:ss){
            String va=p.getProperty(key);
            System.out.println(va+key);
        }
    }
    public static void main(String[] args) throws Exception{
        Properties p=new Properties();
        PropertiesFileTest pft=new PropertiesFileTest();
        pft.storeFile(p);
        pft.loadFile();
    }
}

View Code

184ca86eb03c6e234282e30da9c87626.png

目录
相关文章
|
5月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
2月前
|
Java
java 中 IO 流
Java中的IO流是用于处理输入输出操作的机制,主要包括字节流和字符流两大类。字节流以8位字节为单位处理数据,如FileInputStream和FileOutputStream;字符流以16位Unicode字符为单位,如FileReader和FileWriter。这些流提供了读写文件、网络传输等基本功能。
56 9
|
3月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
95 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
4月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
|
3月前
|
Java 数据处理 开发者
揭秘Java IO流:字节流与字符流的神秘面纱!
揭秘Java IO流:字节流与字符流的神秘面纱!
49 1
|
3月前
|
自然语言处理 Java 数据处理
Java IO流全解析:字节流和字符流的区别与联系!
Java IO流全解析:字节流和字符流的区别与联系!
119 1
|
4月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
256 12
|
3月前
|
Java
Java 中 IO 流的分类详解
【10月更文挑战第10天】不同类型的 IO 流具有不同的特点和适用场景,我们可以根据具体的需求选择合适的流来进行数据的输入和输出操作。在实际应用中,还可以通过组合使用多种流来实现更复杂的功能。
71 0
|
3月前
|
存储 Java 程序员
【Java】文件IO
【Java】文件IO
43 0
|
4月前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践