Java六十五: IO流

IO流

引用
  1. IO流概念

    用于设备间的数据传输,传输操作以“ 流 ” 的形式进行。此处的数据是指永久性存储数据(硬盘中的数据)。

  2. IO流的分类

    在这里插入图片描述

  3. 基本概念

    ① 字节流:以字节为单位(8bit)的非文本格式文件进行数据传输,多用于图片、视频
    ② 字符流:以字符为单位(16bit)的文件格式进行数据传输,多用于文本
    ③ 输入流:数据从文件(外存)流向程序(内存)
    ④ 输出流:数据从程序(内存)流向文件(外存)
    ⑤ 节点流:直接操作文件的流
    ⑥ 处理流:操作节点流或者其它处理流的流

  4. java.io包

    提供了各种类的接口,通过标准方法输入输出数据

  5. IO流体系表

    在这里插入图片描述

  6. 重点掌握

    ① 抽象基类

    ② 访问文件中流的操作

    其余流的操作与上面类似

一、节点流 - - > 文件中流的操作
  1. 将文件中的内容读取到程序中

    ① 实例化File类的对象,指明要操作的文件

    File file = new File("Hello1.txt");
    

    ② 提供具体的流(会因文件找不到而抛出异常)

    FileReader fr = new FileReader(file);
    

    ③ 数据的读入(运行时会因阻塞而抛出异常中断程序)

        // 读入单个字符
    	int data ;
        while ((data = fr.read(cbuf)) != -1){
            System.out.print((char)data);           
         }
    

    ④ 流的关闭(会因空指针而抛出异常)

    	stream.close();
    

    完整的读入程序(考虑异常)

    /*
    	多个语句会抛异常的情况,需要用try-catch-finally结构,确保流操作可以关闭
    */
    @Test
        public void test1() {
            File file = new File("Hello.txt");
            // 在try-catch-finally结构前,声明流
            FileReader fr = null;        
            try {  
                if (!file.exists()) {
                    file.createNewFile();
                }
                FileReader fr = new FileReader("Hello2.txt");
                char[] cbuf = new char[5];
                int len ;
                // read(char[] cbuf):返回每次读入cbuf数组中的字符个数。
                // 如果剩余字符不足数组长度,返回实际读入个数。直到文件末尾,返回-1
                while ((len = fr.read(cbuf)) != -1){
                    for (int i = 0; i < len; i++) {
                        System.out.print(cbuf[i]);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fr != null) {
                        fr.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
  2. 从内存中写出数据的到硬盘的文件中

    ① 创建文件对象

    File e_n = new File("e_nanxu");
    

    ② 创建文件写入操作流

    FileWreiter fw = new FileWriter(e_n);
    

    ③ 数据写入

    fw.write("hellojava");
    

    ④ 关闭流

    fw.close();
    

    完整程序(考虑异常情况)

    @Test
    public void fileWriterTest() {
        FileWriter fw = null;
        try {
            File e_n = new File("e_nanxu");
            fw = new FileWriter(e_n);
            fw.write("hello");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    注:输出操作,对应的文件可以不存在,不会抛异常

    • 文件不存在,在输出操作时会自动创建
    • 文件存在
      • File.Write( ) / File . Write( file , false )会对原文件覆盖
      • File . Write( file , true ) 不会对原文件覆盖,会在原文件末端追加内容
  3. 图像文件的复制(读取后写入)

    ① 创建两个File对象

    ② 创建字节输入流和字节输出流

    ③ 调用字节数组byte[]

    ④ 进行数据的读取和写入,实现复制

    ⑤ 流关闭

    @Test
        public void copyTest() {
        
    		//第1步:创建两个File对象
            File file = new File("C:\\Users\\NLC\\Desktop\\《新程序员》海报.png");
            File file1 = new File("C:\\Users\\NLC\\Desktop\\《新程序员》杂志海报.png");
            
            // 第2步:创建字节输入流和字节输出流
            FileInputStream fi = null;
            FileOutputStream fo = null;
            try {
                fi = new FileInputStream(file);
                fo = new FileOutputStream(file1);
                
                // 第3步 调用字节数组byte[]
                byte[] bytes = new byte[5];
                int len;
                
                // 第4步 进行数据的读取和写入,实现复制
                while ((len = fi.read(bytes)) != -1) {
                    fo.write(bytes,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
            
            	// 第5步  关闭流
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fo.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    

    在这里插入图片描述

  4. 复制视频,测试复制所需时间(随后与缓冲流进行对比)

    步骤于复制图片一样,此次使用方法传参的方式

    @Test
    	// 测试复制时间的方法
        public void timeTest() {
            String s = "C:\\Users\\NLC\\Download\\《灵笼》.mp4";
            String d = "F:\\影视收藏\\热漫\\国漫之光\\《灵笼》.mp4";
            long start = System.currentTimeMillis();
            videoCopyTest(s,d);
            long  end  = System.currentTimeMillis();
            System.out.println(end-start);
        }
        
    @Test
    	// 复制非文本文件方法
        public void videoCopyTest(String srcPath,String dest) {
            File file = new File(srcPath);
            File file1 = new File(dest);
            FileInputStream fi = null;
            FileOutputStream fo = null;
            try {
                fi = new FileInputStream(file);
                fo = new FileOutputStream(file1);
                byte[] bytes = new byte[1024];
                int len;
                while ((len = fi.read(bytes)) != -1) {
                    fo.write(bytes,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fo.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    

    灵笼

    在这里插入图片描述

  5. 读取写入方法总结
    • read()

      第1种:读取单个字符,用于字节流操作

      第2种:读取字字节数组 byte[ ] , 用于字节流操作

      第3种:读取字符数组 char[ ] ,用于字符流操作

    • write()

      第1种:覆盖写入Write( ) / Write( file , false )

      第2种:末尾写入Write( file , true )

      第3种:将file文件中的len个字符写入新的文件种 Write( file , 0, len )

  6. 两种错误的写法

    ① 错误写法

    		// 第一种错误写法
    		char[] cbuf = new char[5];
    		int len;
    		while((len = fr.read(cbuf)) != -1) {
                // i < cbuf.lengtn 应该写为  i < len
    	        for(i = 0; i < cbuf.length; i++) {
    	        	System.out.println(cbuf[i]);
    	    	}	
    	    }
    	
    		// 第二种错误写法
    		char[] cbuf = new char[5];
    		int len;
    		while((len = fr.read(cbuf)) != -1) {
    	        // 字符数组转变为字符串,,同样可能会在最后一次读取中,把上一次读取的内容未完全覆盖引发错误
    	        String str = new String(cbuf);
    	        System.out.println(str);        	
    	    }
    

    ② 错误原因图示

    在这里插入图片描述

二、处理流之一 - - > 缓冲流(Buffered)
  1. 缓冲流的作用:

    套接在流的基础上,提升流的读取、写入速度(流:可以是字节流,也可以是处理流)

    ① 缓冲流内部提供了一个缓冲区,8192是213

    		private static int defaultCharBufferSize = 8192;
    

    ② 提供了一个缓冲区刷新方法flush Buffer(),write( ) 方法内会自动调用

  2. 使用缓冲流复制文本文件

    ① 创建文本对象

    ② 创建节点流

    ③ 创建缓冲流

    ④ 读写操作

    读操作有一个新的方法readLine(),一行行的读入,返回字符串类型,空值是返回null;
    write( )方法不提供换行服务,可手动加上“ \n ”,这样按行读入的数据就可以按行写入,保持原文档格式。

    ⑤ 流关闭

    @Test
        public void bufferedReanerTest() {
            BufferedReader br= null;
            BufferedWriter bw = null;
            try {
                // 1.创建文件对象、创建节点流合并
                FileReader fr  = new FileReader(new File("e_nanxu"));
                FileWriter fw = new FileWriter(new File("e_n.txt"));
                
                // 2.创建缓冲流
                br = new BufferedReader(fr);
                bw = new BufferedWriter(fw);
    
                // 3.读写操作
                String line;
                while ((line = br.readLine()) != null) {
                    bw.write(line + "\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                
                // 4.流关闭
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (bw != null) {
                        bw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
  3. 文件加密与解密
    • 加密:利用异或 ^ 运算,在写入数据的时候,将每个字符进行异或运算,就使原来字符发生变化,再去读取时,看到的就是乱码
    • 解密:对加密文件再进行异或运算,就得到了正确的字符
     @Test
    	// 加密过程
        public void secretTest() {
            FileReader fr = null;
            FileWriter fw = null;
            try {
                File e_nanxu = new File("e_nanxu");
                File secret_e = new File("secret_e");
                fr = new FileReader(e_nanxu);
                fw = new FileWriter(secret_e);
    
                char[] cbuf = new char[5];
                int len;
                while ((len = fr.read(cbuf)) != -1) {
                    for (int i = 0; i < len; i++) {
                        fw.write(cbuf[i] ^ 5);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fr != null) {
                        fr.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fw != null) {
                        fw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    	// 解密:将里面的File对象更改一下即可
    			File decrypt = new File("secret_e");
                File e_n = new File("e_n");
                
    
  4. 获取文件中每个字符出现的次数
     @Test
    /**
     *  遍历文中的每一个字符:读取
     *  字符出现的次数都保存在Map中
     *  将Map中的数据写入文件
     * */
        public void countTest() {
            FileReader fr = null;
            FileWriter fw = null;
            try {
                // 1.创建文件对象
                File min = new File("min.txt");
                File count = new File("count.txt");
                // 2.创建节点流
                fr = new FileReader(min);
                fw = new FileWriter(count);
                
                // 3.创建Map对象,并将文件读入Map集合中
                Map<Character, Integer> map = new HashMap<>();
                int ch;
                while ((ch = fr.read()) != -1) {
                   char c = (char)ch;
                   if (!map.containsKey(c)) {
                       map.put(c,1);
                   }
                   if (map.containsKey(c)) {
                       map.put(c,map.get(c)+1);
                   }
                }            
                // 4.将Map中的键值对存入Set集合中(无序、不可重复)
                Set<Entry<Character, Integer>> set = map.entrySet();
                
                // 5.遍历Set集合,将其写入文件中,文件中有一些换行符、空格等无法显示,所以分情况标以文本
                for (Entry<Character, Integer> e : set) {
                    switch (e.getKey()) {
                        case ' ' :
                            fw.write("空格 = " + e.getValue() + '\n');
                            break;
                        case '\t':
                            fw.write("tab键=" + e.getValue() + '\n');
                            break;
                        case '\r':
                            fw.write("回车=" + e.getValue() + '\n');
                            break;
                        case '\n':
                            fw.write("换行=" + e.getValue() + '\n');
                            break;
                        default:
                            fw.write(e.getKey() + "=" + e.getValue() + "\n");
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fr != null) {
                        fr.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fw != null) {
                        fw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
三、处理流之二 - - - > 转换流
  1. 本质:字符流

    InputStreamReader: 解码,将一个字节的输入流转换字符的输入流

    OutputStreamWriter:编码,将一个字符的输出流转换字节的输出流

    在这里插入图片描述

  2. 转换流参数

    ① 第1个参数:需要转换的文件

    ② 第2个参数:指定字符集,如果没有,使用系统默认字符集

       @Test
           public void inputStreamReaderTest() {
               InputStreamReader reader = null;
               OutputStreamWriter wr = null;
               try {
                   // 1.创建文件对象、节点流
                   File e_nanxu = new File("e_nanxu");
                   File code_nanxu = new File("code_nanxu.txt");
                   FileInputStream fi = new FileInputStream(e_nanxu);
                   FileOutputStream fo = new FileOutputStream(code_nanxu);
                   // 2.造转换流(处理流)
                   reader = new InputStreamReader(fi);
                   wr = new OutputStreamWriter(fo,"UTF-8");
       			// 3.读取和写入
                   char[] cbuf = new char[10];
                   int len;
                   while ((len = reader.read(cbuf)) != -1) {
                       for (int i = 0; i < len; i++) {
                           wr.write(cbuf[i]);
                       }
                   }
               } catch (IOException e) {
                   e.printStackTrace();
               } finally {
                   // 4.流关闭
                   try {
                       if (reader != null) {
                           reader.close();
                       }
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
                   try {
                       if (wr != null) {
                           wr.close();
                       }
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
           }
       ```
    
       
    
    
  3. UTF-8的二进制表示原理

    在这里插入图片描述

  4. 字符集的演变及应用

    在这里插入图片描述

四、处理流之三 - - - > 标准输入输出流
  1. 规范

    System.in :标准输入流,默认设备键盘
    System.out :标准输出流,默认从控制台输出(显示器)

  2. 两个属性

    in

    	public final static InputStream in = null;
    

    setIn:重新设置输入流的作用对象,InputStream类型

    	public static void setIn(InputStream in) 
    

    out

    	public final static PrintStream out = null;
    

    setOut:重新设置输出流的作用对象,PrintStream类型

    	public static void setOut(PrintStream out)
    
  3. 测试程序

    public class IOTest1 {
        public static void main(String[] args) {
            IOTest1 iot = new IOTest1();
            iot.systemInOutTest1();
        }
        
        /**
         * 用System.in从键盘输入字符串,
         * 将读取到的整行字符串变为大写,在继续输入下一行
         * 直到输入'e' 或 'exit' 退出程序
         */
        public void systemInOutTest1() {
            InputStreamReader isr = null;
            BufferedReader br = null;
            try {
                // 1.将System.in的InputStream类型转换为Reader类型
                isr = new InputStreamReader(System.in);     
                // 2.利用缓冲流处理,可以使用readLine()方法整行读入数据
                br = new BufferedReader(isr);            
                String data;
                String dataUpper;
                while (true) {
                    data = br.readLine();
                    if ("e".equalsIgnoreCase(data) 
                        			|| "exit".equalsIgnoreCase(data)) {
                        break;
                    }
                    dataUpper = data.toUpperCase();
                    System.out.println();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (isr != null) {
                        isr.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
五、处理流之四 - - - > 打印流
  1. 作用:

    ① 实现将基本数据类型的数据格式转换为字符串的输出
    System.out.printn() == (new PrintStream).println()

  2. 方法及特性

    ① 提供了一系列重载的print() 和 println() 方法,用于多种数据类型的输出
    ② PrintStream和PrintWriter的输出不会抛出IOException异常
    ③ 自带有自动flush功能
    ④ PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况,应使用PrintWrite类
    ⑤ System.out返回的是PrintStream实例

  3. 创建PrintStream对象,改变System.out的默认值,使输出平台改变

       @Test
           public void printStreamTest() {
               PrintStream ps = null;
               try {
                   // 1.创建文件对象和节点流
                   File file = new File("F:\\Java\\规范代码1.0\\IO流\\outt.txt");
                   FileOutputStream fos = new FileOutputStream(file);
                   // 2.利用打印流和标准输出流处理节点流
                   ps = new PrintStream(fos);
                   if (ps != null) {
                       System.setOut(ps);
                   }
                   // 下面的System.out. 都可以替换为  ps.
                   for (int i = 0; i < 256; i++) {
                       System.out.print((char)i);
                       if (i % 50 == 0) {
                           System.out.println();
                       }
                   }
               } catch (FileNotFoundException e) {
                   e.printStackTrace();
               } finally {
                   try {
                       if (ps != null) {
                           ps.close();
                       }
                   } catch (Exception e) {
                       e.printStackTrace();
                   }
               }
           }
       ```
    
六、处理流之五 - - - > 数据流
  1. 作用:

    方便操作基本数据类型和字符串,套接在InputStream和OutputStream子类流上

  2. 数据流中的方法
    boolean readBoolean( )byte readByte( )write方法类同,
    char readChar( )float readFloat( )只要把read
    double readDouble( )short readShort( )换成write即可
    long readLong( )int readInt( )
    String readUTF( )void readFully(byte[] b)
  3. DataOutputStream

    将基本数据类型和字符串写入到文件中

    @Test
        public void dataOutputStreamTest() {
            DataOutputStream dos = null;
            try {
                File file = new File("wdata.txt");
                FileOutputStream fos = new FileOutputStream(file);
                // 下面写入到的wdata文件不能打开查看,需要用DataInputStream读取到控制台看
                dos = new DataOutputStream(fos);
                dos.writeInt(123);
                dos.writeBoolean(true);
                dos.writeUTF("hijava");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (dos != null) {
                        dos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
  4. DataInputStream

    将用DataOutputStream写入文件中的内容读出到控制台上

    @Test
        public void dataInputStreamTest() {
            DataInputStream dis = null;
            try {
                File file = new File("wdata.txt");
                FileInputStream fis = new FileInputStream(file);
                dis = new DataInputStream(fis);
                // 读取数据的顺序需要和写入文件中的数据顺序相同
                int r = dis.readInt();
                boolean b = dis.readBoolean();
                String s = dis.readUTF();
                System.out.println(r);
                System.out.println(b);
                System.out.println(s);;
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (dis != null) {
                        dis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
七、处理流之六 - - - > 对象流
  1. 作用

    用于存储和读取基本数据类型数据或对象的处理流。

    强大之处是可以把Java中的对象写入数据源中,也能把对象从数据源中还原回来

  2. 对象序列化机制

    2.1 机制作用和实现条件

    ① 可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输后可被还原
    ② 序列化是RMI(Remote Method Invoke - - - 远程方法调用)过程的参数和返回值都必须实现的机制,RMI是JavaEE的基础。故序列化机制是JavaEE平台的基础
    ③ 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable Externalizable

    2.2 序列化:ObjectOutputStream

    ① 允许把内存中的Java对象转换成成与平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络这种二进制流传输到另一个网络节点。

       	@Test
       	    public void objectOutputStreamTest() {
       	        ObjectOutputStream dat = null;
       	        try {
       	            dat = new ObjectOutputStream(new FileOutputStream(new File("oos.dat")));
       	            dat.writeObject(new String("Java全系列学习"));
       	        } catch (IOException e) {
       	            e.printStackTrace();
       	        } finally {
       	            try {
       	                if (dat != null) {
       	                    dat.close();
       	                }
       	            } catch (IOException e) {
       	                e.printStackTrace();
       	            }
       	        }
       	    }
       	
    

    2.3 反序列化:ObjectInputStream

    当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。

    		 @Test
    		    public void objectInputStreamTest() {
    		        ObjectInputStream ois = null;
    		        try {
    		            ois = new ObjectInputStream(new FileInputStream("oos.dat"));
    		            Object o = ois.readObject();
    		            String s = (String)o;
    		            System.out.println(s);
    		        } catch (IOException e) {
    		            e.printStackTrace();
    		        } catch (ClassNotFoundException e) {
    		            e.printStackTrace();
    		        } finally {
    		            try {
    		                if (ois != null) {
    		                    ois.close();
    		                }
    		            } catch (IOException e) {
    		                e.printStackTrace();
    		            }
    		        }
    		    }
    
  3. 依次实现的各接口

    Serializable 标识接口,什么方法都没有

    	public interface Serializable {}
    

    Externalizable

    	public interface Externalizable extends java.io.Serializable {
    		void writeExternal(ObjectOutput out) throws IOException;
    		void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    	}
    

    ObjectInput/ObjectOutput

    	public interface ObjectInput extends DataInput, AutoCloseable {
    

    ObjectInputStream/ObjectOutputStream

    	public class ObjectOutputStream
    	    extends OutputStream implements ObjectOutput, ObjectStreamConstants
    	{
    
  4. 自定义类实现Serializable接口的序列化和反序列化

    4.1 凡是实现Serializable接口的类都有一个表示序列化版本表示的静态变量

       	private static final long serialVersionUID = -754667710L;
    

    ① serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化是否兼容

    ② 如果类没有显示定义这个静态常量,它的值时Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化,故建议显示声明

    ③ 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为一致,可以进行反序列化,否则就会出现序列化版本不一致的异常InvalidCastException

    4.2 内部属性

    必须可序列化:基本数据类型本身时可序列化,引用类型数据需保证可序列化
    不可以是static、transient类型

    4.3 程序实例

    	 @Test
    		// 自定义类的序列化
    	    public void customizeOutputTest() {
    	        ObjectOutputStream coos = null;
    	        try {
    	            coos = new ObjectOutputStream(new FileOutputStream("coos.dat"));
    	            Customize c1 = new Customize("詹姆斯·高斯林 ", 65);
    	            Customize c2 = new Customize("James Gosling", 66);
    	            coos.writeObject(c1);
    	            coos.writeObject(c2);
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        } finally {
    	            try {
    	                if (coos != null) {
    	                    coos.close();
    	                }
    	            } catch (IOException e) {
    	                e.printStackTrace();
    	            }
    	        }
    	    }
    
    	@Test
    		// 自定义类的反序列化
    	    public void customizeInputTest() {
    	        ObjectInputStream cois = null;
    	        try {
    	            cois = new ObjectInputStream(new FileInputStream("coos.dat"));
    	            Object o3 = cois.readObject();
    	            Object o4 = cois.readObject();
    	            Customize c3 = (Customize)o3;
    	            Customize c4 = (Customize)o4;
    	            System.out.println(c3);
    	            System.out.println(c4);
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        } catch (ClassNotFoundException e) {
    	            e.printStackTrace();
    	        } finally {
    	            try {
    	                if (cois != null) {
    	                    cois.close();
    	                }
    	            } catch (IOException e) {
    	                e.printStackTrace();
    	            }
    	        }
    	    }
    
    	// 自定义类
    	class Customize implements Serializable {
    	    private static final long serialVersionUID = -754667710L;
    	    private String name;
    	    private int age;
    	    public Customize(String name, int age) {
    	        this.name = name;
    	        this.age = age;
    	    }  
    	
    	    public String getName() {
    	        return name;
    	    }
    	
    	    public void setName(String name) {
    	        this.name = name;
    	    }
    	
    	    @Override
    	    public String toString() {
    	        return "Person{" +
    	                "name='" + name + '\'' +
    	                ", age=" + age +
    	                '}';
    	    }
    	}
    	```
    
八、处理流之七 - - - > 随机存取文件流
  1. RandomAccessFile声明在java.io包下,直接继承于java.lang.Object类。并且它实现了DataInput、DataOutput两个接口,也意味着这个类既可以读也可以写(即使输入流,也是输出流)。

    public class RandomAccessFile implements DataOutput, DataInput, Closeable {
    
  2. RandomAccessFile类支持“随机访问”的方式,程序可以直接跳到文件的任意位置读、写文件

    ① 支持只访问文件的部分内容
    ② 可以向已存在的文件后追加内容

  3. RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置;也可以自由移动记录指针

    long getFilePointer( ) :获取文件记录指针的当前位置
    void seek( long pos): 将文件记录指针定位到 pos 位置

  4. 构造器

    	public RandomAccessFile(String name, String mode)
            throws FileNotFoundException
        {
            this(name != null ? new File(name) : null, mode);
        }
    	public RandomAccessFile(File file, String mode)
            throws FileNotFoundException
        {
    

    ① 第一各参数,可以是文件名,也可以是文件对象,但最终执行的构造器是参数为文件对象

    ② 第二个参数:mode参数

    r: 以只读方式打开
    rw: 打开以便读取和写入
    rwd:打开以便读取和写入;同步文件内容的更新
    rws:打开以便读取和写入;同步文件内容和元数据的更新。

    ③ 如果模式为只读 r ,则不会创建文件,而是会去读取一个已经存在的文件,读取的文件不存在则会异常。

    ④ 如果模式为读写 rw ,文件不存在则回去创建文件;存在则不会创建,并对原文件进行覆盖,覆盖长度取决于写入文件的多少

  5. 可以通过相关操作,实现数据的插入

    ① 将文件中插入位置后的字符全部保存到一个StringBuffer中

    ② 插入相关数据后,再将StringBuffer中的内容写到文件中

    	@Test
    	    public void randomInsertText() {
    	        RandomAccessFile insert = null;
    	        try {
    	            File file = new File("hi.txt");
    	            insert = new RandomAccessFile(file, "rw");
    	            // 1.调后指针,将文件中该指针后的余部存入StringBuffer
    	            insert.seek(3);
    	            // File类中的length()返回long类型整数
    	            StringBuffer sb = new StringBuffer((int) file.length());
    	            byte[] cbuf = new byte[20];
    	            int len;
    	            while ((len = insert.read(cbuf)) != -1) {
    	                sb.append(new String(cbuf,0,len));
    	            }
    	            
    	            // 2.插入后,继续将复制到StringBuffer中的数据写入文件
    	            insert.seek(3);
    	            // write()内的参数只有byte[],需要用getBytes()将String转变为byte[]
    	            // 写入后指针指向了插入数据后的位置
    	            insert.write("xyz".getBytes(StandardCharsets.UTF_8));
    	            insert.write(sb.toString().getBytes(StandardCharsets.UTF_8));
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        } finally {
    	            try {
    	                if (insert != null) {
    	                    insert.close();
    	                }
    	            } catch (IOException e) {
    	                e.printStackTrace();
    	            }
    	       }
    	    }
    	```
    
    
  6. 特殊之处
    • 提供了指针偏移功能 - - - > seek( int pos );
    • 可以用RandomAccessFile类,实现多线程断点下载。下载中有两个临时文件,一个临时文件是与被下载文件大小相同的空文件,一个用来记录文件指针的位置文件,每次暂停,都会保存上一次的指针。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

e_nanxu

感恩每一份鼓励-相逢何必曾相识

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值