【IO流】

I/O流

流分类

数据流向:

  • 输入流
  • 输出流

数据类型:

  • 字节流:byte(8位)为单位"读写的数据为视屏啊其他等等"
  • 字符流:char(16位)为单位"读写的数据时文本的时候一般"

四个抽象父类:

  1. InputStream;

  2. OutputStream;

    字符流

  3. Reader;

  4. Write;

字节流

适合读任何类型的数据

InputStream:所有字节输入流的抽象父类**

  • ByteArrayInputStream:是以字节的形式从byte数组中读取数
  • FileInputStream:以字节的形式从文件中读取数据

OutputStream所有字节输出流的抽象父类

基本步骤:

  1. 声明流;
  2. 创建流;
  3. 使用流;
  4. 关闭流;

基础使用:

package com.chapter11.Case;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

public class FileStream {
    public static void main(String[] args) {
        //从控制台获取数据:输入流
        //1.创建流
        InputStream in=System.in;//获取到一个从控制台输入的流
//        System.out.println("in...");
//        System.out.println(in);//输出地址

        OutputStream out=System.out;



        try {
            //读数据
//            int read = in.read();//输入a 读出97
//            System.out.println(read);
            //写数据
//            out.write(read);
//            out.flush();//清空缓冲区

//            int i=0;
//            while ((i=in.read())!=-1){
//                System.out.println(i);
//            }


//            byte[] buf=new byte[8];
//            int read = in.read(buf);//返回读到的字节数,
//            //buf中放的是读到的字节
//            System.out.println(read);
//            System.out.println(Arrays.toString(buf));
            /*输出结果:a
                      2  //a:97 回车键:10
                    [97, 10, 0, 0, 0, 0, 0, 0]*/

//            out.write(buf);
//            out.flush();

//            String str="hello world";
//            byte[] bytes = str.getBytes();
//            out.write(bytes);


           /* int read = in.read(buf,1,3);//从哪个位置开始写,写几个
            System.out.println(read);
            System.out.println(Arrays.toString(buf));
*/
//           out.write(buf);

           int len=0;
            byte[] buf = new byte[8];
           while ((len=in.read(buf))!=-1){
//               len=in.read(buf);
               out.write(buf,0,len);//当len>8数组长度时,会写多次
               out.flush();
           }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (in!=null) {
                try {
                    //关闭资源
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (out!=null) {
                    try {
                        //关闭资源
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

package com.chapter11.Case;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

public class byteStream {
    public static void main(String[] args) {
        //1.声明流
        InputStream in = null;
        OutputStream out = null;

        //2.创建流,这里使用System中已经创建好的流对象
        in = System.in;
        out = System.out;

        //3 使用流
        int data=-1;
        try {
            /*data=in.read();//一个一个读入,字节
            out.write(data);
            */

           /* byte[] buf = new byte[8];
            data=in.read(buf);//
            out.write(buf);
            System.out.println(data);//data记录写入的字节数,data的最大值是数组长度
            out.write(buf,0,3);
            System.out.println(Arrays.toString(buf));//输出数组,每个字符对于的int数字
            System.out.println(new String(buf));//输出字符*/

            int len = -1;
            byte[] buf = new byte[1024];
            while((len=in.read(buf))!=-1) {
//                len=in.read(buf);
                out.write(buf,0,len);//本次读了多几个字节,那么就写出几个字节
            }

                out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流
            if (in!=null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out!=null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }


}

读写方法:

  • read()read(byte[] arr) read(byte[] arr,offset,len);有int类型的返回值
  • write() write(byte[] arr) write(byte[] arr,offset,len)没有f返回值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7NB2Qtg-1638150567201)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210628141159636.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A3nOKTpE-1638150567204)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210628141217969.png)]

字节数组

java.io.ByteArrayInputStream 负责从字节数组中读取数据 java.io.ByteArrayOutputStream 负责把数据写入到字节数组中

数据来源:字节数组;

数据去向:另一个字节数组

管道

使用字节流,可以从管道中读取数据,以及向管道中写数据。

java.io.PipedInputStream 负责从管道中读取数据

java.io.PipedOutputStream 负责将数据写入到管道中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aw26IPu4-1638150567208)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210629092840366.png)]

  • 一定要先建立连接;可以用connect()方法,也可以直接连接;
  • 一般都要用到两个线程;
  • 可以实现边写边读;

案例

package com.chapter11.Case;

import java.io.*;

public class PipTest {
    public static void main(String[] args) {
        PipedInputStream in;
        PipedOutputStream out;

        in=new PipedInputStream();
        out=new PipedOutputStream();

        try {
            //管道对接
            in.connect(out);

            Thread t1=new  WriteThread(out);
            Thread t2 = new ReadThread(in);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println();
        System.out.println("程序运行结束!");
    }
}
class WriteThread extends Thread{
    private OutputStream out;

    public WriteThread(OutputStream out) {
        this.out = out;
    }

    @Override
    public void run() {
        byte[] arr="hello world".getBytes();


            try {
                for (int i=0;i<arr.length;i++){
                out.write(arr[i]);
                out.flush();
                sleep(1000);
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }finally {
                if (out!=null){
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

    }
}
class ReadThread extends Thread {
    private InputStream in;

    public ReadThread(InputStream in) {
        this.in = in;
    }

    @Override
    public void run() {
        int data = -1;
        try {
            while ((data = in.read()) != -1) {
                System.out.print("-"+data+"-");
                System.out.write(data);
                System.out.flush();
            }
            System.out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

文件流

package com.chapter11.Case;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

public class FileStreamTest {
    public static void main(String[] args) throws IOException {
        File file=new File("file");
        System.out.println(file.exists());
        System.out.println(file.canRead());
        System.out.println("绝对路径"+file.getAbsolutePath());
//        System.out.println("相对路径"+file.getCanonicalPath());
        System.out.println(file.getPath());

        String[] list = file.list();
        System.out.println(Arrays.toString(list));

    }

}
//运行结果
false
false
绝对路径E:\java.sql.jdk\IDEA\IdeaProjects\Briup_java\test\file
file
null

相对路径:相对于项目包[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pEgNCWMV-1638150567212)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210628150640221.png)]

数据的来源和数据的去向都是文件;

所以文件的路径非常重要,一般都是用相对路径;

注意,文件不存在不会报异常

  • 例如“file”,不存在则会在当前相对路径下床架该文件,即src/file

但是如果文件带了目录就会抛文件不存在异常FileNotFoundException:

  • 例如"zyz/file",这就不会自动创建,会报异常;

    以下是案例:

package com.chapter11.Case;

import java.io.*;
import java.util.Arrays;

public class FileStreamTest {
    public static void main(String[] args) throws IOException {
//            fileReaderTest();
        fileWriteTest("zyz");
    }

    public static  void fileReaderTest() throws IOException {
        FileInputStream in = null;
        //读数据
        try {
           in=new FileInputStream(
                    "C:/Users/ZYZ/Desktop/briup.txt");
            byte[] buf=new byte[1024];
            int len=-1;
            while ((len=in.read(buf))!=-1){
                System.out.println(new String(buf));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(in!=null){
                in.close();
            }
        }
    }

    public static void fileWriteTest(String content) throws IOException {
        FileOutputStream fos=null;
        try {
            fos=new FileOutputStream("file",true);
            //true即追加内容,不加则每次运行就覆盖之前的内容
            fos.write(content.getBytes());
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(fos!=null){
                fos.close();
            }
        }
    }

}

字符流

一次读16位,相当于两个字节

ReaderWriter下的方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m5zfXMDN-1638150567214)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210629092207337.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-koG3Z01l-1638150567216)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210629092252573.png)]

管道

文件

package com.chapter11.Case;


import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;

public class FileRW {
    public static void main(String[] args)   {
        //读取一个txt的文件,把文件的内容写入当前路径下的一个叫data的文件;
        FileReader fileReader= null;
        FileWriter fileWriter= null;
        try {
            fileReader = new FileReader("C:/Users/ZYZ/Desktop/briup.txt");
            fileWriter = new FileWriter("data");
            int len=-1;
            char[] ch=new char[1024];
            while ((fileReader.read(ch))!=-1){
                fileWriter.write(Arrays.toString(ch));
                fileWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fileReader!=null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }if (fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

节点流

以上介绍并使用的字节流和字符流,都属于节点流

它们的特点是,可以【直接】读取某一个地方的数据,或者【直接】把数据写入到某一个地方。 除此之外,下面介绍的一些流,都【不是】节点流,它们并不能【直接】从某一个地方读数据或写数 据,它们每一种都有一些特殊的功能,例如有的可以自动转换基本类型数据为字节,有的可以提高其他 流的读写效率,有的可以将字节流转为字符流,有的可以把对象转为字节等

数据流

主要适用于基本数据类型

字节流操作数据的时候,以字节文件单位,一个个的进行读写,很多时候这样并不方便。

我们希望读出来的若干个字节,自动转换为指定类型的数据,例如int、float、char等。

类似的,我们也希望每次能直接把一个数据,自动转成字节再写出去。

例如,把一个long类型数据,写入到一个字节数组中

long a = 1;
//int是642位,8个字节,在内存中表示为
//00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
//写入字节数组后,具体的值应该如下:
byte[] arr = {0,0,0,0,0,0,0,1};

我们需要一个流,可以将long类型的数据1,自动转换为字节后,再写出到数组中、文件中或者其 他地方

输出

注意1, DataOutputStream 必须要“包裹”一个字节流,增强这个字节流的功能,一次可以写出去一个 具体类型的数据

注意2,通过 writeLong 方法可以看出,内部是通过移位操作,拿到每一个字节的值,放到数组中,再 写出去

package com.chapter11.Case;

import java.io.*;
import java.util.Arrays;

public class DataStream {
    public static void main(String[] args) {
        //声明流
        DataOutputStream dos=null;
        ByteArrayOutputStream baos=null;
        

        //创建流
        baos=new ByteArrayOutputStream();
        dos=new DataOutputStream(baos);
        
        try {
            dos.writeLong(1000L);
            dos.flush();
            byte[] byteArray = baos.toByteArray();
            System.out.println(Arrays.toString(byteArray));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (dos!=null){
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (baos!=null){
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//运行结果:
[0, 0, 0, 0, 0, 0, 3, -24]
输入

DataOutputStream 功能类似, java.io.DataInputStream 可以将读取到的字节自动转化为指定 类型的数据,但一般都需要和 DataOutputStream 配合使用。

DataOutputStream 负责把指定类型的数据,转化为字节并写出去 DataInputStream 负责把读取到的若干个字节,转化为指定类型的数据java

public static void main(String[] args) {
    DataOutputStream dos=null;
    DataInputStream dis=null;

    File file=new File("filedata");
    try {
        dos=new DataOutputStream(new FileOutputStream(file));
        dos.writeLong(1000L);
        dos.writeDouble(8.8d);
        dos.writeUTF("中国");
        dos.writeChar('a');
        dos.flush();

        FileInputStream fis=new FileInputStream(file);
        dis=new DataInputStream(fis);

        System.out.println(dis.readLong());
        System.out.println(dis.readDouble());
        System.out.println(dis.readUTF());
        System.out.println(dis.readChar());

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (dos!=null){
            try {
                dos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (dis!=null){
            try {
                dis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

缓冲流

字节缓冲流

概念:缓冲流,可以在创建流对象时,设置一个默认大小的缓冲区数组,通过缓冲区进行读写,减少系统磁盘 的IO次数,从而提高读写的效率。

可以看出,字节缓冲流的构造器,要求一定要传入一个字节流对象,然后缓冲流就可以对这个字节 流的功能进行增强,提供缓冲数据的功能,从而提高读写的效率

注意,操作的文件内容越多,效果越明显

package com.chapter11.Case;

import java.io.*;

public class BufferIO {
    public static void main(String[] args) {
//1.声明流
        InputStream in = null;
        OutputStream out = null;
        try {
//2.创建流
            File file1 = new File("file");
            File file2 = new File("filecopy");
// in = new FileInputStream(file1);
// out = new FileOutputStream(file2);
//使用缓冲流增强文件字节流的功能,提供读写效率
//使用缓存流“包裹”其他的字节流即可
            in = new BufferedInputStream(new FileInputStream(file1));
            out = new BufferedOutputStream(new FileOutputStream(file2));
//3.使用流
            int data = -1;
            long start = System.currentTimeMillis();
            while ((data = in.read()) != -1) {
                out.write(data);
            }
            out.flush();
            long end = System.currentTimeMillis();
            System.out.println("使用缓冲流完成读写操作的时间为:" + (end - start) + "毫秒");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
//4.关闭流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

字符缓冲流

java.io.BufferedReader ,负责给字符输入流提供缓冲功能 java.io.BufferedWriter ,负责给字符输出流提供缓冲功能

  • 使用缓冲流来增强文件字符流的功能,提高读写效率
public class Test {
public static void main(String[] args) {
    //1.声明流
    Reader in = null;
    Writer out = null;
    try {
    //2.创建流
    File file1 = new File("src/main/java/com/briup/demo/背影.txt");
    File file2 = new File("src/main/java/com/briup/demo/背影copy.txt");
    // in = new FileReader(file1);
    // out = new FileWriter(file2);
    //使用缓冲流增强文件字符流的功能,提高读写效率
    in = new BufferedReader(new FileReader(file1));
    out = new BufferedWriter(new FileWriter(file2));
    //3.使用流
    int data = -1;
    long start = System.currentTimeMillis();
        while((data=in.read())!=-1){
    out.write(data);
    }
    out.flush();
    long end = System.currentTimeMillis();
    System.out.println("使用字符流完成读写操作的时间为:"+(end-start)+"毫秒");
    } catch (IOException e) {
    e.printStackTrace();
    }finally {
    //4.关闭流
    if(in!=null) {
    try {
    in.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    if(out!=null) {
    try {
    out.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
}
//运行结果:每次运行结果稍有差异
使用字符流完成读写操作的时间为:30毫秒

可以看出,字符流中使用缓冲流,也会有性能的提升,但是没有那么明显,因为 Writer 类中已经 提供了一个默认的缓冲区,也就是说其实 FileWriter 类其实默认已经使用了缓冲区了

常用方法:

BufferedReader 中有一个方法,会使用的比较多:

public class BufferedReader extends Reader{
//一次读取一行字符串,遇到换行符为止,算是一行
//该方法的返回值,【不会】包含回车换行(\r\n)
public String readLine() throws IOException {
//...
}
}

  • 如果下一行是空行, readLine 方法返回空字符串,也就是 String line = ""
  • 如果没有下一行数据了, readLine 方法返回 null

和一次读一行,相对于的,是一次写一行,写完之后自动加换行(如果是Windows系统还会加回车)

public class Test {
    public static void main(String[] args) {
        //1.声明流
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            //2.创建流
            File file1 = new File("src/main/java/com/briup/demo/背影.txt");
            File file2 = new File("src/main/java/com/briup/demo/背影copy.txt");
            in = new BufferedReader(new FileReader(file1));
            out = new PrintWriter(new FileWriter(file2));
            //3.使用流
            String line = null;
            while((line=in.readLine())!=null){
            out.println(line);
        }
        out.flush();
        } catch (IOException e) {
        	e.printStackTrace();
        }finally {
            //4.关闭流
            if(in!=null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
   	 		}
            if(out!=null) {
                //PrintWriter中重写的close方法没有声明抛出异常
                //重写的语法要求,抛出异常可以缩小,但是不能扩大
                out.close();
            }
    	}
    }
}

注意, PrintWriter 中重载了很次构造器,它可以“包裹”一个字符流对象、字节流对象、文件对象等

注意, PrintWriter 中重载了很次 println 方法,该方法可以再输出数据后,添加一个新行,【默 认】情况下在Windows系统中添加的是回车换行(\r\n),在Linux系统中添加的时候换行(\n

很多时候,我们都是 BufferedReader 和 PrintWriter ,进行数据的操作,刚好一个可以一次 读取一行数据,另一个可以一次写出一行数据

转换流

转换流,可以在把一个字节流转换为字符的同时,并指定转换的字符编码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnFpttK3-1638150567220)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210629091603759.png)]

输出
package com.chapter11.Case;

import com.company.B;

import java.io.*;

public class ChangeStream {
    public static void outStrean(){
        FileOutputStream fos=null;
        OutputStreamWriter osw=null;

//        byte[] bytes = "你好中国".getBytes();
        try {
            fos=new FileOutputStream("file");
            //将字节流转换成字符流 ISO-8859-1编码会乱码
            osw=new OutputStreamWriter(fos,"GBK");
//            fos.write(bytes);
//            fos.flush();
            osw.write("你好中国");//转换流继承了Writer,所以有该方法
            osw.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {//关闭流:先管里面的流
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }if (osw!=null){
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
    public  static void inTest(){
        FileInputStream fis=null;
        InputStreamReader isr=null;
        BufferedReader br=null;

        try {
            fis=new FileInputStream("file");
            //添加转换流
            isr=new InputStreamReader(fis,"GBK");
            //添加缓冲流
            br=new BufferedReader(isr);
//            byte[] buf=new byte[12];
//            fis.read(buf);
            
            char[] buf=new char[1024];
//            isr.read(buf);
            
            System.out.println(br.readLine());
//            System.out.println(new String(buf));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (isr!=null){
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
//        outStrean();
       inTest();
    }

}
  • 关流的时候,从里往外关;
  • 转换流的构造器都是有参的,而且里面都是放字节流的;

对象流

Java 提供了一种对象序列化的机制,可以将对象和字节序列之间进行转换:

  • 序列化:
    • 程序中,可以用一个字节序列来表示一个对象,该字节序列包含了对象的类型、对象中的数据等。 如果这个字节序列写出到文件中,就相当于在文件中持久保存了这个对象的信息
  • 反序列化:
    • 相反的过程,从文件中将这个字节序列读取回来,在内存中重新生成这个对象,对象的类型、对象 中的数据等,都和之前的那个对象保持一致。(注意,这时候的对象和之前的对象,内存地址可能 是不同的,因为反序列化之后重新再内存中给对象分配空间,但是内容是相同的)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CuAXDhV3-1638150567222)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\image-20210629110724545.png)]

完成对象的序列化和反序列化,就需要用到对象流了

序列化:

在java中,并非所有对象都可以进行序列化和反序列化,而是只有实现了指定接口的对象才可以进行。

java.io.Serializable 接口

public interface Serializable {
}

接口中没有抽象方法,这只是一个“标识”接口,实现它的的对象才可以进行序列化和反序列化操作

例如,不需要实现接口的抽象,因为这个接口中没有定义抽象

public class Student implements Serializable {
}

注意,如果不实现这个接口的,将来序列化操作会报错

transient关键字:

java中的关键字 transient ,可以修饰类中的属性,它是让对象在进行序列化的时候,忽略掉指定的属 性。

常用在一些敏感属性的修饰,例如对象中的password属性,我们并不想将这个敏感属性的值进行序列化 保存,那么就可以使用 transient 来对他进行修饰

transient 的单词含义就是短暂的、转瞬即逝。

public class Student implements Serializable {
private String name;
private transient int age;
//...
}

这时候,再进行序列化操作,对象中的age属性的值将会被忽略掉

序列化版本号

Serializable 接口给需要序列化的类,提供了一个序列版本号:serialVersionUID

该版本号的目的,是用于验证序列化的对象和对应类是否版本匹配

例如,没有声明序列化版本号的情况:

public class Student implements Serializable {
String name;
}

创建对象,并将对象进行序列化

public static void main(String[] args) {
    //1.声明流
    ObjectOutputStream out = null;
    try {
        //2.创建流
        //创建文件对象
        File file = new File("src/main/java/com/briup/demo/a.txt");
        //"包裹"一个文件字节输出流
        out = new ObjectOutputStream(new FileOutputStream(file));
        //3.使用流
        Student stu = new Student();
        stu.name = "tom";
        //序列化:对象->字节
        out.writeObject(stu);
        out.flush();
	} catch (IOException e) {
		e.printStackTrace();
	}finally {
		//4.关闭流
        if(out!=null) {
        	try {
        		out.close();
        	} catch (IOException e) {
        		e.printStackTrace();
        	}
		}
	}
}

修改Student类中的属性,加入一个age属性:

public class Student implements Serializable {
String name;
int age;
}

再进行反序列化操作:

public static void main(String[] args) {
    //1.声明流
    ObjectInputStream in = null;
    try {
        //2.创建流
        //创建文件对象
        File file = new File("src/main/java/com/briup/demo/a.txt");
        //"包裹"一个文件字节输出流
        in = new ObjectInputStream(new FileInputStream(file));
        //3.使用流
        Object obj = in.readObject();
        System.out.println(obj);
        } catch (IOException e) {
        	e.printStackTrace();
        } catch (ClassNotFoundException e) {
        	e.printStackTrace();
        } finally {
        	//4.关闭流
	        if(in!=null) {
    	    try {
	    	    in.close();
        	} catch (IOException e) {
		        e.printStackTrace();
        	}
        }
	}
}
//运行报错:
java.io.InvalidClassException: com.briup.demo.Student; local class incompatible:
stream classdesc serialVersionUID = 6548949808915977718, local class
serialVersionUID = -501096807619780610
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.briup.demo.Test.main(Test.java:22)

注意,这时候反序列化操作报错,因为我们在Student中添加的了一个新属性age

默认情况下,每次修改代码编译生成的新的class文件中,都会根据当前类的信息计算出一个序列 化版本号,之前序列化的字节中的保存的是之前老的版本号,和现在这个新的不一致,然后就报错 了

如果我们手动指定这个版本号之后,那么这个类的序列化版本号就固定下来了:

public class Student implements Serializable {
//属性的名字、类型、修饰符是固定的,值可以随意给一个
private static final long serialVersionUID = 1L;
String name;
}

此时,再重复上面的实验操作,就不会反序列化的时候报错了,因为版本号一直都是手动固定写死 的。

许多类都实现了序列化接口,所以可以直接使用对象流

随机访问流

Class RandomAccessFile主要适用于文件

特点:

  • 继承的是Object类;

  • 即可以做读的流,也可以是写的流;

  • 两个有参构造器:

      • RandomAccessFile(String name, String mode)

        创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件。

      • RandomAccessFile(File file, String mode)

        创建一个随机访问文件流从File参数指定的文件中读取,并可选地写入文件。

之前使用的每一个流,那么是读数据的,要么是写数据的,而这个随机访问流,它的对象即可用作文件 内容的读,又可以用作文件内容的写,同时它还可以任意定位到文件的某一个文件进行读或者写操作

  • 对象即可读也可以写
  • 随机定位文件中的任意字节位置进行读或写,并且可以前后反复定位

创建该类的对象时,需要指定要操作的文件和操作的模式:

  • "r” 模式,以只读方式来打开指定文件夹。如果试图对该RandomAccessFile执行写入方法,都将抛出 IOException异常。
  • “rw” 模式,以读写方式打开指定文件。如果该文件尚不存在,则试图创建该文件。
  • “rws” 模式,以读写方式打开指定文件。相对于”rw” 模式,还要求对文件内容或元数据的每个更新 都同步写入到底层设备。
  • “rwd” 默认,以读写方式打开指定文件。相对于”rw” 模式,还要求对文件内容每个更新都同步写入 到底层设备。

程序中,以r和rw模式最为常用,r模式表示只读,rw模式表示既能读又能写

例如,

package com.chapter11.Case;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomTest {
    public static void main(String[] args)  {
        RandomAccessFile raf=null;

        try {
            byte[] bytes = new byte[1024];
            raf=new RandomAccessFile("file","rw");
            System.out.println(raf.read(bytes));
            System.out.println(new String(bytes));
            raf.seek(4);
            raf.write("zyz".getBytes());
            raf.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//运行结果:
7
你好zyq 
    file文件中的内容位:
    你好zyz

file文件中"zyqzyq"替换成"zyqzyzzyq":

package com.chapter11.Case;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomTest {
    public static void main(String[] args)  {
        RandomAccessFile raf=null;
        ByteArrayOutputStream baos=null;

        try {
            //文件要插入数据的位置
            int insertP=3;
            //文件中要出入的内容
            String s="zyz";
            raf=new RandomAccessFile("file","rw");//读写模式
            baos=new ByteArrayOutputStream();

            byte[] buf=new byte[1024];
            //因为默认写入时会将对应位置上的数据给覆盖掉

            //把指定位置后的所有数据写入到buf数组保存起来
            int len=-1;
            raf.seek(insertP);//跳过了这些位置
            while ((len=raf.read(buf))!=-1){
                baos.write(buf,0,len);//0是insertP+1这个位置
            }
            System.out.flush();
            raf.seek(insertP);
            raf.write(s.getBytes());//写入内容
            raf.write(baos.toByteArray());//写入之前保存的数据
            System.out.flush();

            while ((raf.read(buf))!=-1){
                System.out.println(new String(buf));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Properties集合

util包下的一个集合,是Map接口的一个实现类

class Properties extends Hashtable<Object,Object> {
     private static final long serialVersionUID = 4112578634029874840L;
public Properties() {
        this(null);
    }
    public Properties(Properties defaults) {
        this.defaults = defaults;
    }
    public synchronized Object setProperty(String key, String value) {
        return put(key, value);
    }//加锁,保证线程安全
public synchronized void load(Reader reader) throws IOException {
        load0(new LineReader(reader));
    }//可以从流中读取数据
    public synchronized void load(InputStream inStream) throws IOException {
        load0(new LineReader(inStream));
    }//字节流也可以
    

由源码可知其有以下特点:

  • 继承与Hashtable,是线程安全的键值对存储结构;
  • 可以将数据保存在流中,或者从流中加载数据;
  • 只能保存字符串的键值对

构造器:

Properties()
创建一个无默认值的空属性列表。
Properties(Properties defaults)
创建一个带有指定默认值的空属性列表。

常用方法:

StringgetProperty(String key)
用指定的键在此属性列表中搜索属性。
StringgetProperty(String key, String defaultValue)
用指定的键在属性列表中搜索属性。
ObjectsetProperty(String key, String value)
调用 Hashtable 的方法 put。

案例:

package com.chapter11.Case;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import java.util.Set;

public class PropTest {
    public static void main(String[] args) {
        //属性集合类不支持泛型

        Properties prop = new Properties();
        //添加键值对

        prop.setProperty("name", "zhangsan");

        prop.setProperty("age", "10");

        prop.setProperty("gender", "male");

        PrintWriter pw=null;//声明流

        try {
            //创建自动刷新字符打印流对象
            pw=new PrintWriter(new FileWriter("prop.txt"));
            //拿到prop中所有的key值
            Set<Object> keySet = prop.keySet();
            //遍历
            for (Object o :keySet) {
                String key=(String)o;//强制转换
                //通过key值获取value值
                String value = prop.getProperty(key);
                pw.println(key+"="+value);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (pw!=null) pw.close();
        }

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值