Java_IO流

引言

        其实 IO 流的学习让我头疼的是对编码集的不了解,以下是一些基于class文件的理解,如果理解了编码格式,那么理解 class 文件将对你更有帮助(主要是其经典的编码格式应用思想)

  1. Java 的 IO流很有意思,首先我们要知道 java.exe 会自动识别 Java 文件里的代码是因为Java 文件会以 Unicode 格式编码并加密为 class 文件;
  2. 当 javap 一个 class 文件会发现有 field、class、interface、method、constant_pool 等 unicode 格式信息;
  3. java.exe 启动 JVM 进程执行栈帧时需要 class 的内容,比如一个方法的执行,需要去找对应的 class,然后找对于的 method,method 里会有ldc、iconst、bipush、cipush、invokevirtual、invokestatic、invokeinterface、invokespecial 等命令,命令执行需要引入参数,参数又要去 constant_pool 找,总而言之 class 文件会被作为缓存存入 metaspace (JDK8以前是PormGen),需要调用什么就去对应的class缓存按址查找;
  4. 之前纳闷为啥用 unicode 做 JVM 的编码,我的理解如下
    1. unicode 仅仅提供了编码表,并未真正落地,也就是未实现二进制的存储,这个可以去看些相关文章了解
    2. 所以说 unicode 是一个未实现字节存储的编码,非常适合面向对象这种语言的使用
    3. 编译时只需要把字符编码匹配到对应的 unicode 编码即可,比如 utf-8。这时会将 utf-8 字符编译为对应的 unicode 编码并加密。命令格式:javac -encoding utf-8 xx.java
    4. JVM 执行 class 文件时方法区存储的 unicode 格式内容就可以通过各种指定的编码格式正确输出。命令格式:java -Dfile.encoding=gbk xx.java
    5. 可以发现3、4的字符编码虽然不同但并不影响class文件正常应用
    6. 基于3、4的优势,JVM执行引擎就把 unicode 作为内置的解析编码
    7. 好文推荐,class详细讲解

正文

一、概念

        流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象——Java语言程序设计教程。

        大白话:实现设备间的数据传输功能的类就是流,其中数据以字节形式存储在集合中。

二、分类

根据数据传输特性将流抽象为各种类,方便更方便操作数据。

按读写:Reader 和 Writer

按输入输出:InputStream 和 OutputStream

按类别:节点流 和 处理流(比如缓冲流、转换流、数据流)

三、常用的流

要知道流有四十多个类型,截取常用的说明就够

分类(红字重要)字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
打印流PrintStreamPrintWriter
数据流DataInputStreamDataOutputStream

s s 

是 

四、代码实现

1、文件流

访问文件FileInputStreamFileOutputStreamFileReaderFileWriter

 

  1. FileInputStream和FileOutputStream实现读写,就两个方法,一个字节读写,一个字节数组读写
    package com.cx.base.day13_io.io_02_fileStream;
    
    import java.io.*;
    
    /**
     * @Author: robert.song
     * @Date: Created in 2022/4/22
     */
    public class FileInput2OutStreamTest {
        public static void main(String[] args) {
    //        基于字节的读写
            baseByte();
    //        基于字节数组的读写
            baseBytesArray();
        }
    
        private static void baseByte() {
            FileInputStream fileInputStream = null;
            FileOutputStream fileOutputStream = null;
            long start = 0;
            try {
    
                String separator = System.getProperty("file.separator");
    //            文件在根项目下
                File file1 = new File("."+separator+"test0.txt");
                File file2 = new File("."+separator+"test1.txt");
                file1.createNewFile();
                file2.createNewFile();
                fileInputStream =  new FileInputStream(file1);
                fileOutputStream = new FileOutputStream(file2,true);
                boolean flag = true;
                int asciiOfByte = 0;
                start = System.currentTimeMillis();
                while (flag){
                    //        读取数据,一次只读一个字节
                    asciiOfByte = fileInputStream.read();
                    if(asciiOfByte==-1){
                        flag = false;
                        break;
                    }
                    //        写数据
                    fileOutputStream.write(asciiOfByte);
                    //            write会自动适时刷新会磁盘,要想直接刷新执行flush()
                    fileOutputStream.flush();
                }
                System.out.println("复制完成");
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            finally{
                try {
                    if(fileInputStream!=null){
                        fileInputStream.close();
                    }
                    if(fileOutputStream!=null){
                        fileOutputStream.close();
                    }
                    System.out.println("耗时"+(System.currentTimeMillis()-start));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void baseBytesArray(){
            FileInputStream fileInputStream = null;
            FileOutputStream fileOutputStream = null;
            long start = 0;
            try {
                String separator = System.getProperty("file.separator");
    //            文件在根项目下
                File file1 = new File("."+separator+"test2.txt");
                File file2 = new File("."+separator+"test3.txt");
                file1.createNewFile();
                file2.createNewFile();
                fileInputStream =  new FileInputStream(file1);
                fileOutputStream = new FileOutputStream(file2,true);
                byte[] bytes = new byte[fileInputStream.available()];
                boolean flag = true;
                int byteCount = 0;
                start = System.currentTimeMillis();
                while (flag){
                    //        读取数据
                    byteCount = fileInputStream.read(bytes);
                    if(byteCount==-1){
                        flag = false;
                        break;
                    }
                    //        写数据
                    fileOutputStream.write(bytes,0,byteCount);
                    //            write会自动适时刷新会磁盘,要想直接刷新执行flush()
                    fileOutputStream.flush();
                }
                System.out.println("复制完成");
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            finally{
                try {
                    if(fileInputStream!=null){
                        fileInputStream.close();
                    }
                    if(fileOutputStream!=null){
                        fileOutputStream.close();
                    }
                    System.out.println("耗时"+(System.currentTimeMillis()-start));
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  2. FileReader和FileWriter实现读写
    //    读写流
        public static void fileReaderWriterTest() throws IOException {
            FileWriter fileWriter = null;
            FileReader fileReader = null;
            char[] chars = new char[1024];
            int lenth = 0;
            int i;
            try {
                fileReader = new FileReader("D:\\project\\200921\\srb_log\\core\\info\\0.txt");
                fileWriter = new FileWriter("D:\\project\\200921\\srb_log\\core\\info\\0.txt",true);
                while ((i = fileReader.read(chars))!=-1){
                    System.out.println(i);
                    System.out.println(new String(chars,0,i).length());
                    fileWriter.write(new String(chars,0,i));
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fileWriter!=null){
                    fileWriter.close();
                }
                if(fileReader!=null){
                    fileReader.close();
                }
            }
        }

2、缓冲流

3.数据流

DataInputStream和DataOutputStream
DataInputStream 是数据输入流,它继承于FilterInputStream。
DataOutputStream 是数据输出流,它继承于FilterOutputStream。
二者4.BufferedInputStream 和 BufferedOutputStream
BufferedInputStream是带缓冲区的输入流,它继承于FilterInputStream。默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能。

BufferedOutputStream是带缓冲区的输出流,它继承于FilterOutputStream,能够提高文件的写入效率。

它们提供的“缓冲功能”本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据。

    public static void readAndWrite(String[] args) {    
        try {
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f:/a.mp3"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:/b.mp3"));
            byte[] b=new byte[1024];
            int len=0;
            while(-1!= (len = bis.read(b, 0, b.length))) {
                bos.write(b, 0, len);
            }
 
        } catch (FileNotFoundException e) {
            System.out.println("文件找不到");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if (null! = bos){
                bos.close();
            }
            if (null! = bis){
                bis.close();
            }
        }
    }

5. ByteArrayInputStream 和 ByteArrayOutputStream
该类从内存中的字节数组中读取数据,它的数据源是一个字节数组,它们分别继承自InputStream 和 OutputStream。

ByteArrayInputStream类的构造方法包括: 
ByteArrayInputStream(byte[] buf)--------参数buf指定字节数组类型的数据源。 
ByteArrayInputStream(byte[] buf, int offset, int length)-----参数buf指定字节数组类型数据源,参数offset指定从数组中开始读取数据的起始下标位置,length指定从数组中读取的字节数。 

    private static byte[] readWithByteArray(byte[] dataSource) {
        ByteArrayInputStream in = null;
        ByteArrayOutputStream out = null;
        
        try {
            in = new ByteArrayInputStream(dataSource);
            out = new ByteArrayOutputStream();
            
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = in.read(buffer, 0, buffer.length)) != -1){
                out.write(buffer, 0, len);
            }
            return out.toByteArray();
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e1) {
            }
            try {
                out.close();
            } catch (IOException e1) {
            }
        }
    }

二、字符流
1. InputStreamReader 和 OutputStreamWriter
 InputStreamReader 和 OutputStreamWriter为各种输入输出字符流的基类,所有字符流都继承这两个基类。实际上,这两个类内部各自持有一个inputStream 和 outputStream对象,相当于是对inputStream 和 outputStream进行了包装,将输入字节流转换成字符流,便于读写操作。

    /**
     * 以字符为单位读取文件,常用于读文本,数字等类型的文件
     */
    public static void readFileByChars(String fileName) {
        File file = new File(fileName);
        Reader reader = null;
        try {
            System.out.println("以字符为单位读取文件内容,一次读一个字节:");
            //1. 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file));//可以是任意的InputStream类,不一定必须是FileInputStream
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                if (((char) tempchar) != '\r') {
                    System.out.print((char) tempchar);
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
 
        try {
            System.out.println("以字符为单位读取文件内容,一次读多个字节:");
            //2. 一次读多个字符
            char[] tempchars = new char[30];
            int charread = 0;
            reader = new InputStreamReader(new FileInputStream(fileName));
            while ((charread = reader.read(tempchars)) != -1) {
                for (int i = 0; i < charread; i++) {
                    if (tempchars[i] != '\r') {
                        System.out.print(tempchars[i]);
                    }
                }
            }
 
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }

2. FileReader 和 FileWriter
FileReader 和 FileWriter分别继承自 inputStreamReader 和 outputStreamWriter。它是对读取文件操作系统的封装,所有的读写都是直接操作文件系统。因此如果是频繁读写操作,不建议使用FileReader 和 FileWriter,性能将会非常低,这时你需要使用BufferedReader。

(1)FileWriter类
构造方法:
FileWriter fw = new FileWriter(String fileName);//创建字符输出流类对象和已存在的文件相关联。文件不存在的话,并创建。
FileWriter fw = new FileWriter(String fileName,boolean append);//创建字符输出流类对象和已存在的文件相关联,并设置该该流对文件的操作是否为续写。
主要方法: 

write(char[] buffer, int offset, int count) //将字符数组写入,offset为数组的起始地址,count为需要写入的字符数
void write(String str)  //写入字符串。当执行完此方法后,字符数据还并没有写入到目的文件中去。此时字符数据会保存在缓冲区中。
viod flush() //刷新该流中的缓冲。将缓冲区中的字符数据保存到目的文件中去。
viod close() //关闭此流。在关闭前会先刷新此流的缓冲区。在关闭后,再写入或者刷新的话,会抛IOException异常。

(2)FileReader类
构造方法:
FileReader fr = new FileReader(String fileName);  //使用带有指定文件的String参数的构造方法。创建该输入流对象。并关联源文件。
主要方法:
int read();   // 读取单个字符。返回作为整数读取的字符,如果已达到流末尾,则返回 -1。
int read(char []cbuf);  //将字符读入数组。返回读取的字符数。如果已经到达尾部,则返回-1。
void close();   //关闭此流对象。释放与之关联的所有资源。

    public static void readAndWrite() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("C:\\my.txt");
            fw = new FileWriter("D:\\you.txt");
            //1.读一个字符,写一个字符方法
            int ch = 0;  
            while ((ch = fr.read()) != -1) {  
                fw.write(ch);  
            } 
            
            //2.读一个数组大小,写一个数组大小方法。
            char []buf = new char[1024];
            int len = 0;
            while((len = fr.read(buf)) != -1){
                fw.write(buf, 0, len);              
            }
            
            //3.直接写一个字符串
            fw.write("hello world!");
        } catch (Exception e) {
            System.out.println(e.toString());
        } finally {
            if (fr != null)
                try {
                    fr.close();
                } catch (Exception e2) {
                    throw new RuntimeException("关闭失败!");
                }
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    throw new RuntimeException("关闭失败!");
                }
            }
        }
    }

3. BufferedReader 和 BufferedWriter
(1)BufferedReader和BufferedWriter类各拥有8192字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并置入缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。
(2)从标准输入流System.in中直接读取使用者输入时,使用者每输入一个字符,System.in就读取一个字符。为了能一次读取一行使用者的输入,使用了BufferedReader来对使用者输入的字符进行缓冲。readLine()方法会在读取到使用者的换行字符时,再一次将整行字符串传入

    /**
     * 以行为单位读取文件,常用于读面向行的格式化文件
     */
    public static void readWithBufferedReader(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 1;
            // 一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                System.out.println("line " + line + ": " + tempString);
                line++;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }

System.in是一个位流,为了转换为字符流,可使用InputStreamReader为其进行字符转换,然后再使用BufferedReader为其增加缓冲功能。

    public static void readAndWrite() {
        try {
            //缓冲System.in输入流
            //System.in是位流,可以通过InputStreamReader将其转换为字符流
            BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in));
            //缓冲FileWriter
            BufferedWriter bufWriter = new BufferedWriter(new FileWriter("/sdcard/log/test.txt"));
 
            String input = null;
            //每读一行进行一次写入动作
            while(!(input = bufReader.readLine())) {
                bufWriter.write(input);
                //newLine()方法写入与操作系统相依的换行字符,依执行环境当时的OS来决定该输出那种换行字符
                bufWriter.newLine();
            }
            bufReader.close();
            bufWriter.close();
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("没有指定文件");
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

三、 RandomAccessFile 
RandomAccessFile不属于InputStream和OutputStream类系的。实际上,它和这两个类系毫不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;它是一个完全独立的类。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。
RandomAccessFile的基本功能有:定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )、skipBytes()跳过多少字节数。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数。实际它和C的fopen()一模一样,都是直接对文件句柄操作。

  /**
     * 随机读取文件内容
     */
    public static void readFileByRandomAccess(String fileName) {
        RandomAccessFile randomFile = null;
        try {
            // 打开一个随机访问文件流,按只读方式
            randomFile = new RandomAccessFile(fileName, "rw");
            long fileLength = randomFile.length();
 
            // 设置读写文件的起始位置
            randomFile.seek(0);
 
            // 一次读取多个数据
            byte[] bytes = new byte[10];
            int byteread = 0;
            while ((byteread = randomFile.read(bytes)) != -1) {
                System.out.write(bytes, 0, byteread);
            }
            //一次写入多个数据
            randomFile.write(bytes);
            
            // 一次读取单个数据
            randomFile()
            // 一次写入单个数据
            randomFile.writeDouble(47.0001);  
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (randomFile != null) {
                try {
                    randomFile.close();
                } catch (IOException e1) {
                }
            }
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值