您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

JAVA字节流、字符流、缓冲流、转换流、内存流、字符编码

时间:2020-05-03 10:34:45  来源:  作者:

「JAVA」字节流、字符流、缓冲流、转换流、内存流、字符编码

文件流、字节流、字符流、缓冲流、转换流、内存

文件流: 在JAVA 中,文件流负责操作文件,包括读取和写入;

FileInputStream        // 文件的字节输入流;
FileOutputStream     // 文件的字节输出流;
FileReader                 // 文件的字符输入流;
FileWriter                  // 文件的字符输出流;

FileInputStream 和 FileOutputStream的父类分别是InputStream和OutputStream,都是字节流,以面向字节的形式提供IO操作。FileReader 和 FileWriter的父类则是Reader和Writer,都是字符流,字符流以面向字符提供IO操作,同时,字符流兼容Unicode,能够提供更好的国际化支持。

在读取数据时,字节流的数据存储单位是字节,在接收字节流数据时,会新建一个字节类型的数组(byte[])来接收读取的数据;字符流的数据存储单位是字符,在接收字符流数据时,会新建一个字符类组(char[])的数据来接收读取的数据。

InputStream 和 OutputStream

InputStream:

InputStream及其子类的作用是用来表示不同数据源输入数据的类,其中封装了用于从数据源中读取数据的操作。这些数据源包括:

  • 字节数组:byte[];
  • String对象;
  • 资源文件:File;
  • 资源管道;
  • 其他流组成的流序列;
  • 其他数据源,如网络资源;

InputStream子类:

  • ByteArrayInputStream:将内存的缓冲区当作数据源输入;
  • StringBufferInputStream:将String转换成InputStream;
  • FileInputStream:从本地文件中读取数据;
  • PipedInputStream:资源管道的输入端,也是PipedOutputStream的输入源;
  • SequenceInputStream:能够将2个或者以上的InputStream合并成一个单一的InputStream;
  • FileterInputStream:为文件资源读取提供筛选功能;

 

OutputStream:

OutputStream及其子类的作用是用来表示数据输出将要去向的目标,比如:文件、资源管道、字节数组等。OutputStream也是因不同的目标有不同的子类:

  • ByteArrayOutputStream:在内存中创建缓冲区,所有输出的数据都会暂存在缓冲区中;
  • FileOutputStream:将数据写出到文件中;
  • PipedOutputStream:作为PipedInputStream的输出端,与PipedInputStream一起实现资源的“管道化”;
  • FileterOutputStream:为文件资源写出提供筛选功能;

 

Reader 和 Writer

字符流有着和字节流近乎一样的功能,每个字节流api都能在字符流中找到与之相对应的,而且,字符流比字节流有着更好的性能;尽管如此,字符流的设计并不是为了取代字节流,相反,二者同时存在,相辅相成。

Reader 和 Writer及其子类都提供了对Unicode的支持,这是他们和InputStream与OutputStream系拓展最大的不同,详细如下图所示:

「JAVA」字节流、字符流、缓冲流、转换流、内存流、字符编码

字节流和字符流的api对应

 

文件过滤器

Java IO中提供了过滤器:FilterInputStream和FilterOutputstream,用于筛选特定的输入流和输出流。因为FilterInputStream和FilterOutputstream也是自InputStream与OutputStream继承而来,所以他们都可以读写来自InputStream与OutputStream的数据。

FilterInputStream子类:

  • DataInputStream:用于从流中读取基本类型的数据,比如:char、int、long等,往往与DataOutputStream配合使用;
  • BufferedInputStream:代表“缓冲区”,可以将数据暂存在缓冲区,使用它可以防止每次读取都进行实际的写操作;
  • LineNumberInputStream:追踪输入流中的行号,可做调试用;
  • PushbackInputStream:能弹出一个字节的缓冲区,能够将文件的最后一个字符返回(操作系统使用-1表示磁盘文件的结尾标记),通常作为编译器的扫描器,往往被编译器调用;

 

FilterOutputStream子类:

  • DataOutputStream:用于从流中写出基本类型的数据,比如:char、int、long等,往往与DataInputStream配合使用;
  • PrintStream:用于格式化输出,比如:java 的运行控制台打印;能够与DataOutputStream相互配合使用:DataOutputStream负责数据的存储,PrintStream负责数据的显示;
  • BufferedOutputStream:代表“缓冲区”,可以将数据暂存在缓冲区,使用它可以防止每次都进行实际的写操作;可以使用flush()方法将缓冲区数据一次性写出;

同样,在Reader 和 Writer中也提供了与之对应的类,详细如下:

「JAVA」字节流、字符流、缓冲流、转换流、内存流、字符编码

文件过滤器 子类

 

字节流

字节流又分字节输入流(InputStream)和字节输出流(OutputStream),分别对应读和写;

字节输入流:FileInputStream,字节输入流的使用案例代码如下:

import java.io.*;

/**
 * 案例:使用字节流完成文件拷贝任务。
 */
public class StreamCopyDemo {

    public static void main(String[] args) throws Exception {

        // 分别创建文件的源对象和目标对象
        File src = new File("stream.txt");
        File target = new File("stream_copy.txt");
        // 创建输入和输出管道
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(target);
        // 读取/写出数据
        byte[] buffer = new byte[1024]; // 设置每次读取的字节数
        int len = -1; // 读取到的字节总数
        while ((len = in.read(buffer)) != -1) {

            // 写出数据
            out.write(buffer, 0, len);
        }
        // 关闭输入/输出管道
        in.close();
        out.close();

    }

}

字节输入流在读取数据时使用的是read()方法,该方法还有另外的重载方法:

void read(int b); 
// 读取单个字节

void read(byte[] b); 
// 读取多个字节,并存储到(byte[] b)中

void read(byte[] b, int off, int len); 
// 读取多个字节,并存储到(byte[] b)中,从数组b的off索引为止开始存储

 

字节输出流:FileOutputStream,字节输出流的使用案例代码如下:

import java.io.*;

/**
 * 案例:使用字节流完成文件拷贝任务。
 */
public class StreamCopyDemo {

    public static void main(String[] args) throws Exception {

        // 分别创建文件的源对象和目标对象
        File src = new File("stream.txt");
        File target = new File("stream_copy.txt");
        // 创建输入和输出管道
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(target);
        // 读取/写出数据
        byte[] buffer = new byte[1024]; // 设置每次读取的字节数
        int len = -1; // 读取到的字节总数
        while ((len = in.read(buffer)) != -1) {

            // 写出数据
            out.write(buffer, 0, len);
        }
        // 关闭输入/输出管道
        in.close();
        out.close();

    }

}

与字节输入流在读取数据时使用的read()方法相对应的,字节输出流也提供了对应的写出数据的方法:write(),该方法也有一些重载方法:

out.write(int b); 
// b 写入文件的字节

out.write(byte[] b); 
// b 要写入文件的数据,类型为byte数组

out.write(byte[] b, int off, int len); 
// 把字节数组中的从off索引开始的len个字节写到文件中

注意:字节流读写中的数据类型是字节数据类型(byte[])。

 

案例1,使用字节流完成文件拷贝任务。

import java.io.*;

/**
 * 案例:使用字节流完成文件拷贝任务。
 */
public class StreamCopyDemo {

    public static void main(String[] args) throws Exception {

        // 分别创建文件的源对象和目标对象
        File src = new File("stream.txt");
        File target = new File("stream_copy.txt");
        // 创建输入和输出管道
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(target);
        // 读取/写出数据
        byte[] buffer = new byte[1024]; // 设置每次读取的字节数
        int len = -1; // 读取到的字节总数
        while ((len = in.read(buffer)) != -1) {
            // 写出数据
            out.write(buffer, 0, len);
        }
        // 关闭输入/输出管道
        in.close();
        out.close();
    }
}

 

字符流

字符流又分字符输入流(Reader)和字符输出流(Writer),分别对应文件读和写;需要注意的是:字符流读写中的数据类型是字符数据类型(char[]),区别于字节流中的字节类型(byte[])。

字符输入流,FileReader,字符输入流的使用案例代码如下:

import java.io.File;
import java.io.FileReader;
import java.io.Reader;

/**
 * 文件的字符输入流案例
 */
public class FileReaderDemo {

    public static void main(String[] args) throws Exception {
        // 创建资源对象
        File src = new File("file_reader.txt");
        // 创建文件字符输入流
        Reader reader = new FileReader(src);
        char[] buffer = new char[1024]; // 每次读取1024个字符
        int len = -1; // 当前读取到的字符长度
        while ((len = reader.read(buffer))!= -1) {
          	String line = new String(line, 0, len);
            System.out.println(line);
        }
        // 关闭字符输入流
        reader.close();
    }
}

字符输入流在读取数据时使用的是read()方法,该方法还有另外的重载方法:

int read(); 
// 每次读取一个字符,直至文件末尾,返回-1为止int read(char[] buffer); // 每次都能读物多个字符,并把读取到的字符存储到buffer中

 

字符输出流,FileWriter,字符输出流的使用案例代码如下:

import java.io.*;

/**
 * 文件的字符输出流案例
 */
public class FileWriterDemo {

    public static void main(String[] args) throws Exception {
        // 创建目标对象
        File target = new File("file_writer.txt");
        // 创建文件字符输出流
        Writer writer = new FileWriter(target);
        writer.write("dkgfkdsfowmsdfmowemfsdmfkemfkmfd");
        writer.flush();
        // 关闭字符输出流
        writer.close();
    }

}

字符输出流在读取数据时使用的是write()方法,该方法还有另外的重载方法:

void write(int c); 
// 向文件中写出一个字符

void write(char[] buffer);
// 向文件中写出多个字符(char[] buffer)

void write(char[] buffer, int off, int len);
// 向文件中写出多个字符(char[] buffer),从off位置开始,长度为len

void write(String str);
// 向文字中写出一个字符串

 

案例2,使用字符流完成文件拷贝任务

import java.io.*;

/**
 * 案例:使用字符流完成文件拷贝任务。
 */
public class FileCopyDemo {

    public static void main(String[] args) throws Exception {
        // 创建文件源对象和目标对象
        File src = new File("file_reader.txt");
        File target = new File("file_reader_copy.txt");
        // 创建输入流和输出流
        Reader reader = new FileReader(src);
        Writer writer = new FileWriter(target);
        // 文件读和写
        char[] buffer = new char[1024];
        int len = -1;
        while((len = reader.read(buffer)) != -1) {
            writer.write(buffer, 0, len);
        }
        // 关闭输入流和输出流
        reader.close();
        writer.close();
    }

}
「JAVA」字节流、字符流、缓冲流、转换流、内存流、字符编码

文件拷贝

 

案例3,使用流完成Java 代码动态编译、运行

import java.io.*;

/**
 * 案例3,使用流完成Java 代码动态编译、运行
 */
public class EvalDemo {

    public static void main(String[] args) {
        String code = "System.out.println("Eval Java");";
        try {
						// 构建java代码
            buildJavaCode(code);
						// 编译Java 代码
						compileJavaCode();
						// 运行Java 代码
						runJavaCode();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	
	/**
	 * 构建java代码
	 */
	private static void buildJavaCode(String code) throws Exception {
				// 拼接Java 主函数
        StringBuilder builder = new StringBuilder(80);
        builder.Append("public class Eval {");
        builder.append("public static void main(String[] args) {");
        builder.append(code);
        builder.append("}");
        builder.append("}");
        // 保存拼接好的java代码到文件中
        OutputStream out = new FileOutputStream("Eval.java");
        out.write(builder.toString().getBytes("UTF-8"));
        out.close();
	}

	/**
	 * 编译Java 代码
	 */
    private static void compileJavaCode() throws Exception {
        
        // 调用javac 编译保存的java文件
        Process javacProcess = Runtime.getRuntime().exec("javac Eval.java");
        // 读取编译错误结果,并输出
        InputStream error = javacProcess.getErrorStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = error.read(buffer)) != -1) {
            String msg = new String(buffer, 0, len);
            System.out.println("Eval 编译:" + msg);
        }
        // 关闭javac 编译进程
        error.close();

    }
	
	/**
	 * 运行Java 代码
	 */
    private static void runJavaCode() throws Exception {

        // 调用java 运行编译好的java字节码文件
        Process javaProcess = Runtime.getRuntime().exec("java Eval");
        // 读取java字节码文件的运行信息。并打印输出
        InputStream info = javaProcess.getInputStream();
        len = -1;
        buffer = new byte[1024];
        while ((len = info.read(buffer)) != -1) {
            String msg = new String(buffer, 0, len);
            System.out.println("Eval 运行: " + msg);
        }
        info.close();
        // 删除java问价和class文件
        new File("Eval.java").delete();
        new File("Eval.class").delete();
    }

}

 

缓冲区

在上述的字符输出流的使用案例中,使用了flush()方法,该方法有何作用呢?

因为计算机访问外部设备(磁盘文件)时,要比直接访问内存慢很多,如果每次调用write()方法都要直接把数据写出到磁盘文件中,CPU会花更多的时间,计算机执行性能会大大降低。

因此,我们可以准备一个内存缓冲区,程序每次调用write()方法时,会将数据先写到内存缓冲区中,暂存于缓冲区中;当内存缓冲区满后,系统才把缓冲区中暂存的数据一次性写出到磁盘文件中。这样就能减少直接操作本地文件系统的频率,使得系统性能得以提升。

 

flush()方法

flush(),刷新操作,输出流中都有flush方法。该方法的作用正是把缓冲区中暂存的数据一次性写出到磁盘文件中。

 

使用缓冲区的好处:

  1. 提高CPU使用率,提高系统的执行性能;
  2. 有机会回滚写入的数据,能避免一定的脏数据;
  3. 缓冲区大小一般使用容量整数倍,可以提高IO性能;

 

对于字节流,flush方法不是都有作用(部分字节流才有作用),对于字符流、缓冲流则都起作用。如果我们调用close方法关闭系统资源,那么,系统在关闭资源前,会先调用flush方法。

.

资源关闭

资源关闭分为手动关闭和自动关闭两种:

  • 手动关闭:手动调用流的close()方法;
  • 自动关闭:不需要手动调用close方法,不过,前提是资源对象实现了AutoClose接口;
import java.io.*;

/**
 * 资源关闭案例
 */
public class StreamCloseDemo {

    public static void main(String[] args) {
		
				// 资源手动关闭
				closeManual();
		
				// 资源自动关闭
				closeAuto();
    }
	
	/**
	  * 资源手动关闭
	  */
    private static void closeManual() {

        InputStream in = null;
        OutputStream out = null;
        try {
            // 分别创建文件的源对象和目标对象
            File src = new File("stream.txt");
            File target = new File("stream_copy.txt");
            // 创建输入和输出管道
            in = new FileInputStream(src);
            out = new FileOutputStream(target);
            // 读取/写出数据
            byte[] buffer = new byte[1024]; // 设置每次读取的字节数
            int len = -1; // 读取到的字节总数
            while ((len = in.read(buffer)) != -1) {

                // 写出数据
                out.write(buffer, 0, len);
            }

        } catch (Exception e ) {
            e.printStackTrace();
        } finally {
            // 关闭输入/输出管道
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

	/**
	  * 资源自动关闭
	  */
    private static void closeAuto() {
        // 分别创建文件的源对象和目标对象
        File src = new File("stream.txt");
        File target = new File("stream_copy.txt");

        try (// 创建输入和输出管道
             InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(target);
        ) {
            // 读取/写出数据
            byte[] buffer = new byte[1024]; // 设置每次读取的字节数
            int len = -1; // 读取到的字节总数
            while ((len = in.read(buffer)) != -1) {

                // 写出数据
                out.write(buffer, 0, len);
            }

        } catch (Exception e ) {
            e.printStackTrace();
        }
    }

}

 

包装类/缓冲流

字节缓冲输入流,BufferedInputStream,案例代码如下:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 字节缓冲输入流
 */
public class BufferedInputStreamDemo {

    public static void main(String[] args) throws Exception {
        // 字节输入流
        InputStream in = new FileInputStream("stream.txt");
        // 字节缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(in);
        // 写出到文件的内容
        String content = "BufferedOutputStream";
        // 通过字节缓冲输入流读取数据
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = bis.read(buffer)) != -1) {
            String line = new String(buffer, 0, len);
            System.out.println(line);
        }
        in.close();
        bis.close();
    }

}

 

字节缓冲输出流,BufferedOutputStream,案例代码如下:

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
 * 字节缓冲输出流
 */
public class BufferedOutputStreamDemo {

    public static void main(String[] args) throws Exception {
        // 字节输出流
        OutputStream out = new FileOutputStream("stream.txt", true);
        // 字节缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(out);
        // 写出到文件的内容
        String content = "BufferedOutputStream";
        // 通过字节缓冲输出流写出数据
        bos.write(content.getBytes());
        out.close();
        bos.close();
    }

}

 

字符缓冲输入流,BufferedReader,案例代码如下:

import java.io.*;

/**
 * 字符缓冲输入流
 */
public class BufferedReaderDemo {

    public static void main(String[] args) throws Exception {
        // 字符输入流
        Reader reader = new FileReader("stream.txt");
        // 字符缓冲输入流
        BufferedReader br = new BufferedReader(reader);
        // 读取文件内容
        String line = null;
        while ((line = br.readLine()) != null) {

            System.out.println(line);
        }
        // 关闭流
        reader.close();
        br.close();
    }

}

 

字符缓冲输出流,BufferedWriter,案例代码如下:

import java.io.*;

/**
 * 字符缓冲输出流
 */
public class BufferedWriterDemo {

    public static void main(String[] args) throws Exception {
        // 字符输出流
        Writer writer = new FileWriter("stream.txt");
        // 字符缓冲输出流
        BufferedWriter bw = new BufferedWriter(writer);
        bw.write("这世界很美好");
        bw.newLine();
        bw.write("世界也很美好");
        // 关闭流
        writer.close();
        bw.close();
    }

}

 

字节流、缓冲流性能测试比较:

import java.io.*;

/**
 * 字节流、缓冲流性能测试
 */
public class BufferedDemo {
	
      public static void main(String[] args) {

        File src = new File("stream.txt");
        File target = new File("stream_copy.txt");

        try {
            // 字节流性能测试
            testSingleByteStream(src, target);
            testSingleBufferedStream(src, target);
            // 缓冲流性能测试
            testMultiByteStream(src, target);
            testMultiBufferedStream(src, target);
        } catch (Exception e) {
          e.printStackTrace();
        }
    }

    /**
     * 字节流性能测试
     * 每次读写都是一个字节
     *
     */
    private static void testSingleByteStream(File src, File target) throws Exception {
        long begin = System.currentTimeMillis();
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(target);
        int len = -1;
        while ((len = in.read()) != -1) {
            out.write(len);
        }
        in.close();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("SingleByteStream 耗费:" + (end - begin) + "ms");
    }
    
    /**
     * 缓冲流性能测试
     * 每次读写都是一个字节
     *
     */
    private static void testSingleBufferedStream(File src, File target) throws Exception {
        long begin = System.currentTimeMillis();
        // 创建文件流
        InputStream srcStream = new FileInputStream(src);
        OutputStream targetStream = new FileOutputStream(target);
        // 穿甲缓冲流
        InputStream in = new BufferedInputStream(srcStream);
        OutputStream out = new BufferedOutputStream(targetStream);
        int len = -1;
        while ((len = in.read()) != -1) {
            out.write(len);
        }
        // 关闭流
        in.close();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("SingleBufferedStream 耗费:" + (end - begin) + "ms");
    }

    /**
     * 字节流性能测试
     * 使用多字节读写
     *
     */
    private static void testMultiByteStream(File src, File target) throws Exception {
        long begin = System.currentTimeMillis();
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(target);
        int len = -1;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("MultiByteStream 耗费:" + (end - begin) + "ms");
    }

    /**
     * 缓冲流性能测试
     * 使用多字节读写
     *
     */
    private static void testMultiBufferedStream(File src, File target) throws Exception {
        long begin = System.currentTimeMillis();
        // 创建文件流
        InputStream srcStream = new FileInputStream(src);
        OutputStream targetStream = new FileOutputStream(target);
        // 穿甲缓冲流
        InputStream in = new BufferedInputStream(srcStream);
        OutputStream out = new BufferedOutputStream(targetStream);
        int len = -1;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        // 关闭流
        in.close();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("MultiBufferedStream 耗费:" + (end - begin) + "ms");
    }
}

上述测试运行结果如下:

SingleByteStream 耗费:3610ms
SingleBufferedStream 耗费:31ms
MultiByteStream 耗费:16ms
MultiBufferedStream 耗费:0ms

性能孰高孰低,测试结果已经很能说明问题了。

 

转换流

由于字节流和字符流同时存在,有时候会把二者结合起来使用,甚至在二者之间实现转换;为了实现这样的功能,Java IO中提供了字节流和字符流的适配器:

  • InputStreamReader:可以把InputStream转换为Reader;
  • OutputStreamWriter:可以把OutputStream转换为Writer;

以下便是这个适配器—“转换流”的使用案例,案例通过文件的拷贝操作来体现,在读写的过程中,均使用了特定编码。案例代码实现如下:

import java.io.*;

/**
 * 转换流案例
 */
public class StreamConvertDemo {

    public static void main(String[] args) throws Exception {
        // 创建文件对象
        File src = new File("stream.txt");
        File target = new File("stream_copy.txt");
        // 创建文件流
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(target);
        // 创建字节/字符转换流
        Reader reader = new InputStreamReader(in, "UTF-8");
        Writer writer = new OutputStreamWriter(out, "UTF-8");
        // 操作字符流完成文件读写
        char[] buffer = new char[1024];
        int len = -1;
        while ((len = reader.read(buffer)) != -1) {

            writer.write(buffer, 0, len);
        }
        // 关闭流
        in.close();
        out.close();
        reader.close();
        writer.close();
    }

}

为什么只有字节转字符流,却没有字符转字节流呢?原因也很简单:

  • 字节流可以操作一切文件,包括纯文本文件、二进制文件;
  • 字符流是对字节流的增强,是用来操作纯文本使用的;

字节流和字符流之间并不对等,无法实现转换,就比如C++程序可以运行C程序,但C程序却不能运行C++程序。

数组流/内存流

面向字节的数组流/内存流:ByteArrayInputStream和ByteArrayOutputStream;该“流”会把数据暂存到内存中,以便实时读写,读写时使用的字节类型(byte[]);

  • ByteArrayInputStream:把数据从程序输出到内存中;
  • ByteArrayOutputStream:把数据从内存读取到程序中;

代码案例如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * 面向字节的数组流/内存流
 */
public class ByteArrayDemo {

    public static void main(String[] args) throws Exception {

        String content = "dfjdslkfjdlsfjld";
        // 字节数组输出流:程序 -> 内存
        ByteArrayOutputStream bas = new ByteArrayOutputStream();
        bas.write(content.getBytes());

        // 缓存字节区
        byte[] data = bas.toByteArray();

        // 字节数组输入流:内存 -> 程序
        ByteArrayInputStream bai = new ByteArrayInputStream(data);
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = bai.read(buffer)) != -1) {
            String line = new String(buffer, 0, len);
            System.out.println(line);
        }
				// 关闭流
        bas.close();
        bai.close();
    }

}

 

面向字符的数组流/内存流,CharArrayReader和CharArrayWriter,该“流”会把数据暂存到内存中,以便实时读写,读写时使用的字符类型(char[]);

代码案例如下:

import java.io.CharArrayReader;
import java.io.CharArrayWriter;

/**
 * 面向字符的数组流/内存流
 */
public class CharArrayDemo {

    public static void main(String[] args) throws Exception {
        // 字符数组输出流
        CharArrayWriter writer = new CharArrayWriter();
        writer.write("这世界很美好");

        char[] data = writer.toCharArray();
        // 字符数组输入流
        CharArrayReader reader = new CharArrayReader(data);
        char[] buffer = new char[1024];
        int len = -1;
        while ((len = reader.read(buffer)) != -1) {
            String line = new String(buffer, 0, len);
            System.out.println(line);
        }
        // 关闭流
        writer.close();
        reader.close();
    }

}

 

面向字符串的内存流,StringReader/StringWriter,把数据临时存储到字符串中,也同样是在内存中,但数据的类型是字符串;

案例代码如下:

import java.io.StringReader;
import java.io.StringWriter;

/**
 * 面向字符串的内存流
 */
public class StringWriterReaderDemo {

    public static void main(String[] args) throws Exception {
        // 字符串输出流
        StringWriter stringWriter = new StringWriter();
        stringWriter.write("这世界很美好");
        stringWriter.write("社会也很美好");
        System.out.println(stringWriter.toString());

        // 字符串输入流
        StringReader stringReader = new StringReader(stringWriter.toString());
        char[] buffer = new char[1024];
        int len = -1;
        while ((len = stringReader.read(buffer)) != -1) {
            String line = new String(buffer, 0, len);
            System.out.println(line);
        }

        // 关闭流
        stringReader.close();
        stringWriter.close();
    }

}

 

合并流

合并流,SequenceInputStream:能够将2个或者以上的InputStream合并成一个单一的InputStream,进而进行合并操作;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.SequenceInputStream;

/**
 * 合并流
 */
public class SequenceInputStreamDemo {

    public static void main(String[] args) throws Exception {
        // 创建输入队列流
        SequenceInputStream seqIn = new SequenceInputStream(
                new FileInputStream("stream1.txt"),
                new FileInputStream("stream2.txt"),
								new FileInputStream("stream3.txt"));
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = seqIn.read(buffer)) != -1) {
            String line = new String(buffer, 0, len);
            System.out.println(line);
        }
        seqIn.close();
    }

}

 

字符编码

字符编码的发展历程:

阶段1:计算机只认识数字0和1,计算机里的一切数据都是以数字来表示;因为英文符号有限,所以规定使用的字节的最高位是0,每一个字节都是以0~127之间的数字来表示,比如:A对应65,a对应97..这就是美国标准信息交换码-ASCII

阶段2:随着计算机在全球的普及,很多国家和地区都把自己的字符引入了计算机中,比如汉字;但是ASCII中的一个字节能表示数字范围太小,不能包含所有的中文汉字,所以就规定使用两个字节来表示一个汉字;

所以决定,原有的ASCII字符的编码保持不变,仍然使用一个字节表示;为了区别一个中文字符与两个ASCII码字符:

  • 中文字符的每个字节最高位规定为1(中文的二进制是负数),这个规范就是GB2312编码;
  • 后来在GB2312的基础上增加了更多的中文字符,比如简体中文,GBK应运而生;

阶段3:新的问题产生了,在中国是认识汉字的,支持GBK编码,但是如果把GBK编码的汉字传递给其他国家,该国家的编码表中没有收录该汉字的字符,故不能正常显示,很容易就出现了乱码。

为了解决各个国家因为本地化字符编码带来的影响,把全世界所有的符号统一进行编码-Unicode编码;此时某一个字符在全世界任何地方都是固定的,比如字符:'哥',在任何地方都是以十六进制的54E5来表示。Unicode的每个编码字符都占有2个字节大小。

 

常见的字符集:

  • ASCII:每个字符占一个字节,只能包含128个符号,不能表示汉字;
  • ISO-8859-1:每个字符占一个字节,收录西欧语言,不能表示汉字;
  • ANSI:每个字符占两个字节,在简体中文的操作系统中,ANSI 就指的是 GB2312;
  • GB2312/GBK/GB18030:每个字符占两个字节,中文专用编码;
  • UTF-8:是一种针对Unicode的可变长度字符编码,又称万国码,是Unicode的实现方式之一,目前的应用率最高;
  • UTF-8 BOM:是微软搞出来的编码,默认占3个字节,使用率并不高;

编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。

互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。

 

存储字母,数字和汉字

存储字母和数字时无论是什么字符集都只占1个字节(因为和ASCII兼容),但是存储汉字:GBK家族占两个字节,UTF-8家族占3个字节。

存储中文时,GBK和UTF-8都是OK的。

import java.util.Arrays;

/**
 * 字符编码
 */
public class EncodingDemo {

    public static void main(String[] args) throws Exception {
        String str = "老夫不正经";
        // 以特定编码格式编码字符串
        byte[] data = str.getBytes("UTF-8");
        System.out.println(Arrays.toString(data));
        // 解码字符串
        str = new String(data, "ISO-8859-1");
        System.out.println(str);
        // 此时,字符串str是乱码:老夫不正经
        // 解决方案便是,使用ISO-8859-1来读取字节数据,然后使用UTF-8重新编码
        data = str.getBytes("ISO-8859-1");
        System.out.println(Arrays.toString(data));
        str = new String(data, "UTF-8");
        System.out.println(str);
    }

}

 

小结

  1. InputStream 和 OutputStream
  2. Reader 和 Writer
  3. 文件过滤器
  4. 字节流
  5. 字符流
  6. 缓冲区
  7. 资源关闭
  8. 缓冲流
  9. 转换流
  10. 内存流
  11. 合并流
  12. 字符编码

完结,老夫虽不正经,但老夫一身的才华。



Tags:JAVA   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  Tags: JAVA  点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  Tags: JAVA  点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Tags: JAVA  点击:(5)  评论:(0)  加入收藏
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === 'a') { b = true} else { b = false}// goodb = a === 'a'2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: JAVA  点击:(6)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  Tags: JAVA  点击:(11)  评论:(0)  加入收藏
传统游戏项目一般使用TCP协议进行通信,得益于它的稳定和可靠,不过在网络不稳定的情况下,会出现丢包严重。不过近期有不少基于UDP的应用层协议,声称对UDP的不可靠进行了改造,这意...【详细内容】
2021-12-23  Tags: JAVA  点击:(12)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  Tags: JAVA  点击:(12)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  Tags: JAVA  点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  Tags: JAVA  点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  Tags: JAVA  点击:(19)  评论:(0)  加入收藏
▌简易百科推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  顶顶架构师    Tags:面向对象   点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Java架构师之路    Tags:JAVA   点击:(5)  评论:(0)  加入收藏
大家好!我是老码农,一个喜欢技术、爱分享的同学,从今天开始和大家持续分享JVM调优方面的经验。JVM调优是个大话题,涉及的知识点很庞大 Java内存模型 垃圾回收机制 各种工具使用 ...【详细内容】
2021-12-23  小码匠和老码农    Tags:JVM调优   点击:(11)  评论:(0)  加入收藏
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  dingle    Tags:JDBC   点击:(13)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  JAVA小白    Tags:Java   点击:(11)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  小西学JAVA    Tags:JAVA并发   点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  唯一浩哥    Tags:Java基础   点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  软件老王    Tags:logback   点击:(19)  评论:(0)  加入收藏
本篇文章我们以AtomicInteger为例子,主要讲解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe对象。我们先从一个例子开始吧。假设现在我们...【详细内容】
2021-12-14  小西学JAVA    Tags:JAVA   点击:(21)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条