Java-数据输入输出流、内存操作流、打印流、序列化流、随机访问流、Properties

本文深入解析Java中的IO流概念,包括数据输入输出流、内存操作流、打印流、随机访问流、序列化流等,通过具体案例演示各种流的使用方法及特性。

在这里插入图片描述

数据输入输出流的概述和使用

  • 数据输入输出流的概述
    通过API查看
    数据输入和输出流:
    数据输入流: DataInputStream
    数据输出流: DataOutputStream
  • 特点: 可以写基本数据类型,可以读取基本数据类型
  • 案例演示: 数据输入输出流的使用
public class 数据输入输出流 {
    public static void main(String[] args) throws IOException {
        //数据输入输出流的特点,就是能够读写基本数据类型
        //怎么写的就怎么读,顺序不要乱
        DataInputStream in = new DataInputStream(new FileInputStream("a.txt"));
        boolean b = in.readBoolean();
        double v = in.readDouble();
        int i = in.readInt();
        String s = in.readUTF();
        System.out.println(b);
        System.out.println(v);
        System.out.println(i);
        System.out.println(s);
        in.close();
    }
    private static void writeData() throws IOException {
        DataOutputStream out = new DataOutputStream(new FileOutputStream("a.txt"));
        out.writeBoolean(true);
        out.writeDouble(3.14);
        out.writeInt(100);
        out.writeUTF("你好世界");
        out.close();
    }
}

内存操作流的概述和使用

  • 内存操作流的概述:
    (1):操作字节数组
    ByteArrayOutputStream
    ByteArrayInputStream
    此流关闭无效,所以无需关闭
    (2):操作字符数组
    CharArrayWrite
    CharArrayReader
    (3):操作字符串
    StringWriter
    StringReader
  • 内存操作流的特点:
    内存操作流,不关联任何文件,只是内存中对数据进行读写
  • 案例演示: 内存操作流的使用
    构造方法: public ByteArrayOutputStream()
操作字节数组
public class 内存操作流 {
    public static void main(String[] args) throws IOException {
        //内存操作流,不关联任何文件,只是内存中对数据进行读写
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        out.write("横眉冷对千夫指".getBytes());
        out.write("俯首甘为孺子牛".getBytes());
        //取出缓冲区中的数据
        byte[] allBytes = out.toByteArray();
        ByteArrayInputStream in= new ByteArrayInputStream(allBytes);
        byte[] bytes=new byte[1024*8];
        int len = in.read(bytes);
        String s1 = new String(bytes, 0, len);
        System.out.println(s1);
    }
}
--------------------------------------------------------------------------------------------
操作字符数组
public class 内存操作流2 {
    public static void main(String[] args) throws IOException {
        CharArrayWriter writer = new CharArrayWriter();
        writer.write("abc");
        writer.write("abc");
        writer.write("abc");
        writer.write("abc");
        writer.write("abc");
        //取出缓冲区中的数据
        char[] chars = writer.toCharArray();
        System.out.println(String.valueOf(chars));
        System.out.println(new String(chars));
        System.out.println(writer.toString());
    }
}
-------------------------------------------------------------------------------------------
操作字符串
public class 内存操作流3 {
    public static void main(String[] args) {
        StringWriter stringWriter = new StringWriter();
        stringWriter.write("abc");
        stringWriter.append("abc");
        String s = stringWriter.toString();
        System.out.println(s);
    }
}

打印流的概述和特点以及作为Writer的子类使用

  • 打印流的概述:
    通过API查看
    字节流打印流
    字符打印流

  • 打印流的特点
    (1): 打印流只能操作目的地,不能操作数据源(不能进行读取数据)
    (2): 可以操作任意数据类型的数据 调用print() 方法可以写任意数据类型
    (3): 如果我们启用自动刷新,那么在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新
    * public PrintWriter(OutputStream out, boolean autoFlush) 启动自动刷新
    * public PrintWriter(Writer out, boolean autoFlush) 启动自动刷新

    (4): 这个流可以直接对文件进行操作(可以直接操作文件的流: 就是构造方法的参数可以传递文件或者文件路径)

  • 案例演示: PrintWriter作为Writer的子类使用

public class 字符打印流 {
    public static void main(String[] args) throws IOException {
        //参数2:true 自动刷新
        //如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作
        PrintWriter writer = new PrintWriter(new FileOutputStream("c.txt"),true);
        writer.println("abc");
        writer.println("abc");
        writer.println("abc");
        writer.println("abc");
        writer.println("abc");
        writer.println("abc");
        writer.println("abc");
       writer.flush();
       writer.close();
    }
}

打印流复制文本文件

  • 案例演示: 打印流复制文本文件
    分析:
    • 这个打印流只能进行写数据,不能进行读取数据,
      那么我们应该找一个可以读取文本文件中的的数据的流对象进行读取操作.
    • 而我们非常喜欢高效的流对象,于是我们可以使用BufferedReader进行读取数据.
public class 打印流复制文本文件 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader("内存操作流.java"));
        PrintWriter printWriter = new PrintWriter(new FileOutputStream("aa.java"),true);
        String line=null;
        while ((line=reader.readLine())!=null){
          printWriter.println(line);
        }
        reader.close();
        printWriter.close();
    }
}

标准输入输出流概述和输出语句的本质

  • 标准输入输出流概述
    在System这个类中存在两个静态的成员变量:

    • public static final InputStream in: 标准输入流, 对应的设备是键盘

    • public static final PrintStream out: 标准输出流 , 对应的设备就是显示器
      System.in的类型是InputStream.
      System.out的类型是PrintStream是OutputStream的孙子类FilterOutputStream 的子类.

第二种方式实现键盘录入

  • Scanner
    • BufferedReader的readLine方法。
    • BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  • 案例演示
public class 键盘录入的第二种方式 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){
            System.out.println("请输入数据");
            String s = reader.readLine();
            //自定义一个结束标记
            if("886".equals(s)){
                break;
            }
            System.out.println(s);
        }
    }
}

输出语句用字符缓冲流改进

  • 案例演示: 输出语句用字符缓冲流改进

    /**
    * 获取System下的in成员变量
    */
    InputStream in = System.in ;

      /**
       * in是一个字节输入流对象,那么我们就可以通过这个字节输入流对象进行读取键盘录入的数据.
       * 那么我们既然要读取数据,之前我们讲解了两种读取数据的方式:
       * 	 1. 一次读取一个字节
       * 	 2. 一次读取一个字节数组
       * 那么我们在这个地方使用那种读取方式. 经过分析,这两种读取方式都不太合适.因为数据是客户通过键盘录入
       * 进来的,而我们希望直接读取一行数据. 而既然要读取一行数据,那么我们就需要使用readLine方法,而这个方法
       * 是属于BufferedReader的方法,而我们就需要创建一个BufferedReader对象进行读取数据.而我们这in有属于
       * 字节流,而创建BufferedReader对象的时候需要一个字符流,而我们就需要将这个字节流转换成字符流,那么既然
       * 要对其进行转换,那么就需要使用转换流. 需要使用InputStreamReader
       */
    
public class 键盘录入的第二种方式 {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){
            System.out.println("请输入数据");
            String s = reader.readLine();
            //自定义一个结束标记
            if("886".equals(s)){
                break;
            }
            System.out.println(s);
        }
    }
}

随机访问流概述和写出数据

  • 随机访问流概述
    RandomAccessFile概述 最大特点 能读能写
    RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能,支持对随机访问文件的读取和写入。
    RandomAccessFile的父类是Object , 这个流对象可以用来读取数据也可以用来写数据.可以操作任意数据类型的数据.
    我们可以通过getFilePointer方法获取文件指针,并且可以通过seek方法设置文件指针
  • 案例演示: 随机访问流写出数据
public class MyTest {
    public static void main(String[] args) throws IOException {
       // RandomAccessFile 随机访问流,此流的特点,能读能写,有一个文件指针,能够记录文件读写的位置
       // 此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;
        //rw 模式,可读可以写
       // writeData();
       //你怎么写的,就怎么读取 顺序不要乱
        RandomAccessFile ra = new RandomAccessFile(new File("e.txt"), "rw");
        boolean b = ra.readBoolean();
        //获取文件指针的位置
        long filePointer = ra.getFilePointer();
        System.out.println(filePointer);//1
        double v = ra.readDouble();
        filePointer = ra.getFilePointer();
        System.out.println(filePointer);//9
        int i = ra.readInt();
        filePointer = ra.getFilePointer();
        System.out.println(filePointer);//13
        char c = ra.readChar();
        filePointer = ra.getFilePointer();
        System.out.println(filePointer);//15
        String s = ra.readUTF();
        filePointer = ra.getFilePointer();
        System.out.println(filePointer);//
        System.out.println(b);
        System.out.println(v);
        System.out.println(i);
        System.out.println(s);
        //设置指针的位置
        ra.seek(15);
        String s1 = ra.readUTF();
        System.out.println(s1);
    }

    private static void writeData() throws IOException {
        RandomAccessFile ra = new RandomAccessFile(new File("e.txt"), "rw");
        ra.writeBoolean(true);
        ra.writeDouble(3.14);
        ra.writeInt(100);
        ra.writeChar('a');
        ra.writeUTF("字符串");
        ra.close();
    }
}

随机访问流读取数据和操作文件指针

  • 案例演示: 随机访问流读取数据和操作文件指针
public class MyTest2 {
    public static void main(String[] args) throws IOException {
        RandomAccessFile ra = new RandomAccessFile("许巍 - 曾经的你.mp3", "rw");
        File file = new File("曾经的你.mp3");
        RandomAccessFile ra2 = new RandomAccessFile(file, "rw");
        if(file.exists()){
            String s = new BufferedReader(new FileReader("临时.txt")).readLine();
            int i = Integer.parseInt(s);
            ra.seek(i);
           ra2.seek(i);
        }else{
            ra.seek(0);
            ra2.seek(0);
        }
        try {
            int len = 0;
            byte[] bytes = new byte[1];
            int index=1;
            while ((len = ra.read(bytes)) != -1) {
                    ra2.write(bytes,0,len);
            }
        }catch (Exception e){
            //一旦遇到异常,我得记录当前指针的位置
            long filePointer = ra.getFilePointer();
            System.out.println(filePointer);
            PrintWriter printWriter = new PrintWriter("临时.txt");
            printWriter.println(filePointer);
            printWriter.flush();
        }
    }
}

序列化流和反序列化流的概述和使用

  • 序列化流的概述
    所谓的序列化:就是把对象通过流的方式存储到文件中.注意:此对象 要重写Serializable 接口才能被序列化
    反序列化:就是把文件中存储的对象以流的方式还原成对象
    序列化流: ObjectOutputStream
    反序列化流: ObjectInputStream
    像这样一个接口中如果没有方法,那么这样的接口我们将其称之为标记接口(用来给类打标记的,相当于猪肉身上盖个章)
    一个对象可以被序列化的前提是这个对象对应的类必须实现Serializable接口
  • 案例演示: 对象序列化和反序列化的基本使用
public class Demo2 {
    public static void main(String[] args) throws IOException, CloneNotSupportedException, ClassNotFoundException {
        writeObj();
        readObj();
    }
//怎么写的怎么读
    private static void readObj() throws IOException, ClassNotFoundException {
        ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("file.txt"));
        Object o = objIn.readObject();
        Object o1 = objIn.readObject();
        Object o2 = objIn.readObject();
        Object o3 = objIn.readObject();
        System.out.println(o);
        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o3);
    }

    private static void writeObj() throws IOException {
        ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream("file.txt"));
        objOut.writeObject("既然钟情于玫瑰");
        objOut.writeObject("就勇敢地吐露真诚");
        objOut.writeObject("既然目标是地平线");
        objOut.writeObject("留给世界的只能是背影");
        objOut.close();
    }
}

如何解决序列化时候的黄色警告线问题

  • 我们的一个类可以被序列化的前提是需要这个类实现Serializable接口,就需要给这个类添加一个标记.
  • 在完成序列化以后,序列化文件中还存在一个标记,然后在进行反序列化的时候,
    会验证这个标记和序列化前的标记是否一致,如果一致就正常进行反序列化,如果
  • 不一致就报错了. 而现在我们把这个类做了修改,将相当于更改了标记,而导致这两个标记不一致,就报错了.
  • 解决问题: 只要让这个两个标记一致,就不会报错了吧
  • 怎么让这两个标记一致呢? 不用担心,很简单,难道你们没有看见黄色警告线吗? alt+enter, 生成出来
    private static final long serialVersionUID = -7602640005373026150L;

如何让对象的成员变量不被序列化

  • 使用transient关键字声明不需要序列化的成员变量
    private transient int age ;// 可以阻止成员变量的序列化使用transient

Properties的概述和作为Map集合的使用

  • Properties的概述
    查看API
    Properties 类表示了一个持久的属性集。
    Properties 可保存在流中或从流中加载。
    属性列表中每个键及其对应值都是一个字符串。
    Properties父类是Hashtable
  • 属于双列集合,这个集合中的键和值都是字符串 Properties不能指定泛型
  • 案例演示: Properties作为Map集合的使用
public class MyTest {
    public static void main(String[] args) {
       // Properties 属性集合,经常用它来读写配置文件 属于双列集合
        Properties properties = new Properties();//他规定了键值 是String类型
        properties.put("aaa","bbb");//当做map集合使用
        Object aaa = properties.get("aaa");
        System.out.println(aaa);
        //用它特有的方法,来存储键值
        properties.setProperty("陈羽凡","白百合");
        String value = properties.getProperty("陈羽凡");
        System.out.println(value);
        //参数2,默认值,如果键没有找到对应的值,就返回默认值
        String property = properties.getProperty("陈羽凡", "李小璐");
        System.out.println(property);
    }
}

Properties的特殊功能使用

  • Properties的特殊功能
    public Object setProperty(String key,String value)
    public String getProperty(String key)
    public Set stringPropertyNames()
  • 案例演示: Properties的特殊功能
public class MyTest {
    public static void main(String[] args) {
       // Properties 属性集合,经常用它来读写配置文件 属于双列集合
        Properties properties = new Properties();//他规定了键值 是String类型
        //用它特有的方法,来存储键值
        properties.setProperty("陈羽凡","白百合");
        String value = properties.getProperty("陈羽凡");
        System.out.println(value);
        //参数2,默认值,如果键没有找到对应的值,就返回默认值
        String property = properties.getProperty("陈羽凡", "李小璐");
        System.out.println(property);
    }
}

Properties的load()和store()功能

  • Properties的load()和store()功能
    Properties和IO流进行配合使用:
  • public void load(Reader reader): 读取键值对数据把数据存储到Properties中

  • public void store(Writer writer, String comments)把Properties集合中的键值对数据写入到文件中, comments注释

  • 案例演示
    Properties的load()和store()功能
public class Demo3 {
    public static void main(String[] args) throws IOException {
        Properties pro = new Properties();
        pro.setProperty("zhangsan","90");
        pro.setProperty("lisi","80");
        pro.setProperty("wangwu","85");
        if(pro.containsKey("lisi")){
            pro.setProperty("lisi","100");
        }
        pro.store(new FileOutputStream("file.txt"),null);
        //读取配置文件
        //要求配置文件键值用等号 =  连接
        properties.load(new FileReader("file.txt"));
        System.out.println(properties);
    }
}

判断文件中是否有指定的键如果有就修改值的

  • 案例演示
    需求:我有一个文本文件,我知道数据是键值对形式的,但是不知道内容是什么。
    请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其值为”100”

    • 分析:
      • a: 把文本文件中的数据加载到Properties集合中
      • b: 判断这个集合中是否有"lisi"这个键
      • 如果有直接修改其值为100
      • c: 把集合中的数据再次存储到文本文件中
public class Demo3 {
    public static void main(String[] args) throws IOException {
        Properties pro = new Properties();
        pro.setProperty("zhangsan","90");
        pro.setProperty("lisi","80");
        pro.setProperty("wangwu","85");
        if(pro.containsKey("lisi")){
            pro.setProperty("lisi","100");
        }
        pro.store(new FileOutputStream("file.txt"),null);
    }
}

SequenceInputStream

  • SequenceInputStream
    表示其他输入流的逻辑串联。
    它从输入流的有序集合开始,
    并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,
    依次类推,直到到达包含的最后一个输入流的文件末尾为止
    - a:构造方法
    SequenceInputStream(InputStream s1, InputStream s2)
    通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),
    以提供从此 SequenceInputStream 读取的字节。
    - b:构造方法
    SequenceInputStream(Enumeration<? extends InputStream> e)
    通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
  • 案例演示:
    • 将一个music.mp3文件,拆分成多个小文件,再将多个小文件,合并成一个mp3文件
public class Demo {
    public static void main(String[] args) throws IOException {
       heBing();
    }

    private static void heBing() throws IOException {
        File file = new File("E:\\music");
        File[] files = file.listFiles();
        Vector<FileInputStream> vector = new Vector<>();
        for (File f : files) {
            FileInputStream in = new FileInputStream(f);
            vector.add(in);
        }
        Enumeration<FileInputStream> elements = vector.elements();
        SequenceInputStream sequenceInputStream = new SequenceInputStream(elements);
        FileOutputStream out = new FileOutputStream(new File(file, "合并.mp3"));
        int len = 0;
        byte[] bytes = new byte[1024 * 1024];
        while ((len=sequenceInputStream.read(bytes))!=-1){
             out.write(bytes,0,len);
             out.flush();
        }
        out.close();
        sequenceInputStream.close();

        //合并完之后,把小文件删掉
        File file1 = new File("E:\\music");
        File[] files1 = file.listFiles();
        for (File ff : files1) {
            if(ff.length()<=1024*1024){
                ff.delete();
            }
        }
    }

    private static void chaiFen() throws IOException {
        FileInputStream in = new FileInputStream("歌曲串烧.mp3");
        //我们封装一个文件夹,放我们拆分的文件
        File file = new File("E:\\music");
        if(!file.exists()){
            file.mkdirs();
        }
        //拆分
        int len=0;
        byte[] bytes=new byte[1024*1024];
        int index=0;
        while ((len=in.read(bytes))!=-1){
            index++;
            FileOutputStream out = new FileOutputStream(new File(file, index + ".mp3"));
            out.write(bytes,0,len);
            out.close();
        }

        in.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值