IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java从入门到精通十一(javaIO流) -> 正文阅读

[Java知识库]Java从入门到精通十一(javaIO流)

Java从入门到精通十一(javaIO流)

IO引入

在这里插入图片描述

字节流和字符流(一些说明)

在java中io流分为字节流和字符流。字节流和字符流分别对应相应的读取和写入操作。整体的功能就是实现对输入输出的操作。

I/O就是input和output的缩写。而java之所以将此称之为流就是将其抽象化,来表示输入输出的功能。封装为对应的类,实现具体的功能,然后全部都存在io包当中。

用流来表示输入输出是也是非常形象的。可以想象数据传输信道中数据像流水一样进行传输。

字节流就是传输单位按为字节进行传输,字符流就是传输按照字符为单位进行传输

字节流没有用到缓冲区并不是没有用到内存

字节流与字符流的区别是什么呢?
从传输上面讲,字节流是字节在文件测层次上进行操作的,并没有用到缓冲区,字符流会用到缓冲区,然后通过缓冲区操作文件。我们是从传输上面讲,但是不要认为这个缓存区就是缓存,缓存是cache,缓冲区是buffer。这是两个不同的概念

buffer也就是我们的缓冲区是内存空间的一部分,内存空间是预留了一定的存储空间的,用来缓冲输入或者输出。这就是我们 的缓冲区。当然缓冲区根据输入设备或者是输出设备分文输入缓冲区和输出缓冲区。
但是缓存(cache)就是我们为了内存为了弥补速度上与cpu的差异而划分出来的。当然磁盘上也有缓存,cpu也有。但是我们这里只区分内存上的缓冲区与缓存。这是完全不一样的两个概念。

字节流本身没有用到缓冲区,但是也可以加入缓冲流来加快读取效率。那就是字节缓冲流了

缓冲区是为了缓冲,缓存是为了加快存取速度。

缓冲区不等于内存,没有用到缓冲区不是没有用到内存。如果认为没有用到内存,那计算机就白学了。

在处理或者说是传输数据上面,字节流基本是可以处理任何类型的数据类型,但是字符流只能处理的是字符或者是字符串。字节流是不能直接处理Unicode字符的,字符流是可以进行处理的。在涉及到一些编码的问题上,比如文件中存在汉字,我们可以去用字符流去处理。

为什么输入流是读数据,而输出流是写数据?

不知道你是否有这样的疑问。
为什么输入流是读取数据,而输出流却是写数据。
因为输入输出都是相对于内存来说的。input是将数据从磁盘读取到内存当中,而输出就是将数据从内存输出道磁盘。
我们程序在运行地时候也会从磁盘中被调入内存中,程序和其运行时数据都是会在内存中驻留,然后在真正执行的时候,cpu会从中读取到相应的数据指令,执行相应的指令。

字节流说明

字节输入流读数据的方法

public abstract class InputStreamextends InputStream

字节输入流InputStream是一个抽象类,直接继承于Object类。所以我们具体在应用功能的时候,最好还是去用到实现它的一些基本的类。

比较主要的读取方法在参数设定上会和字节输出流的写入方法相似。

public abstract int read()
throws IOException从输入流读取数据的下一个字节。 值字节被返回作为int范围0至255 。 如果没有字节可用,因为已经到达流的末尾,则返回值-1 。 该方法阻塞直到输入数据可用,检测到流的结尾,或抛出异常。
一个子类必须提供这个方法的一个实现。

public int read(byte[] b,
int off,
int len)
throws IOException从输入流读取len字节的数据到一个字节数组。 尝试读取多达len个字节,但可以读取较小的数字。 实际读取的字节数作为整数返回。
该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。
如果len为零,则不会读取字节并返回0 ; 否则,尝试读取至少一个字节。 如果没有字节可用,因为流是文件的-1则返回值-1 ; 否则,读取至少一个字节并存储到b 。
第一个字节读取存储在元素b[off] ,下一个字节存入b[off+1] ,等等。 读取的字节数最多等于len 。 令k为实际读取的字节数; 这些字节将存储在元素b[off]至b[off+ k -1] ,使元素b[off+ k ]至b[off+len-1]不受影响。
在每种情况下,元件b[0]至b[off]和元件b[off+len]至b[b.length-1]不受影响。
该read(b, off, len)类方法InputStream简单地调用该方法read()反复。 如果第一个这样的呼叫产生一个IOException ,那个异常从呼叫返回到read(b, off, len)方法。 如果任何后续调用read()导致IOException ,则异常被捕获并被视为文件的结尾; 读取到该点的字节被存储到b ,并且返回异常发生前读取的字节数。 该方法的默认实现将阻塞,直到所请求的输入数据len已被读取,文件结束被检测到或异常被抛出为止。 鼓励子类提供更有效的方法实现。

public int read(byte[] b)
throws IOException从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。

字节输入流是用来进行读取数据的,不过读取数据的方式可以有两种,基本的是这样。

package io_demo;
//字节流读取数据
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class IoDrmo_2 {
    public static void main(String args[]) throws IOException {
        FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        //int read() 从该输入流读取一个字节的数据
//        int by = fis.read();
//        System.out.println(by);
//        System.out.println((char)by);
//        fis.close();
          int by = fis.read();//读取一个字节
          while(by!=-1)
          {
              System.out.print((char)by);
              by = fis.read();
          }
          //优化一下读取
          while((by = fis.read())!=-1)
          {
              System.out.print((char)by);
          }
            fis.close();

    }
}


当然我们也可以按照字节数组去读取,我们可以让一次读取多个字节。这样读取的话,就快一些。

package io_demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class IoDemo_4 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        byte[] bytes = new byte[1024];

//        System.out.println(len);
        int len;
        //采用循环
        while((len= fis.read(bytes))!=-1)
        {
            System.out.print(new String(bytes,0,len));
        }
        fis.close();

    }
}

一定要记得在操作完毕之后,关闭资源。

字节缓冲输入流

public class BufferedInputStream
extends FilterInputStream

提供的主要的构造方法

BufferedInputStream(InputStream in)
创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
BufferedInputStream(InputStream in, int size)
创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。

从构造方法可知。我们使用它的时候需要传入一个字节缓冲输入流对象。

  FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
  BufferedInputStream bis = new BufferedInputStream(fis);

同样我们可以用这样的对象去进行读取操作。

字节输出流写数据的方法

public abstract class OutputStreamextends OutputStream

字节输出流就是进行写入数据,我们可以进行向文件中写入数据。基本的抽象类还是需要去实现类中实现它的基本功能。


可以这样去实现,输出流的时候可以这样传入参数。

package java_practice;

import java.io.*;

public class FileDemo__ {
    public static void main(String args[]) throws IOException {

        File file = new File("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(97);
        fos.close();


    }
}

将这个文件对象传入,当然你也可以向文件输入流一样直接传入文件路径。但是需要注意的是,如果文件不存在的话 输入流是会报错的。因为输入流是读取文件的,被读取的文件不存在,那就会报错。输出流会自动创建文件,输入流不会自动创建文件。

写入数据的时候,比如我们写入一个97,
就会转换为字符a。这就涉及到对应的编码。

对照ASCII码表,我们可以看到十进制97对应的字符为a。这个write()方法会把对应的十进制数字97按照ASCII码进行转换。

0到97的输入,会看到ASCII表进行转换,其余的编码默认会查询系统编码GBK。关于编码的问题,我们在后面说。

当然write()方法也可以接收一个数组类型。一个字节数组。去对应的源码里面一看便知。
在这里插入图片描述

在这里插入图片描述
当然还可以有不同的参数列表
在这里插入图片描述
当然你如果不是很想去查看源码,也可以在javaapi里面找到对应的方法说明。

write
public void write(int b)
throws IOException将一个 integer(数组长度)写入此流。

public void write(byte[] b)
throws IOException将b.length字节从指定的字节数组写入此输出流。 write(b)的一般合约是应该具有与电话write(b, 0, b.length)完全相同的效果。

public void write(byte[] b,
int off,
int len)
throws IOException从指定的字节数组写入len字节,从偏移off开始输出到此输出流。 write(b, off, len)的一般合同是数组b中的一些字节按顺序写入输出流; 元素b[off]是写入的第一个字节, b[off+len-1]是此操作写入的最后一个字节。
该write的方法OutputStream调用写出在每个字节中的一个参数的写入方法。 鼓励子类覆盖此方法并提供更有效的实现。
如果b是null ,则抛出NullPointerException 。
如果off为负数,或len为负数,或off+len大于数组b的长度,则抛出IndexOutOfBoundsException 。

与此相关的方法,都可以查阅文档得到。


            byte[] by = {97,98,99,100};
            //取范围写入
            fos.write(by,0,by.length);
            fos.write(by);

当然你不能直接给write()中直接传入字符或者是字符串,我们的直接传入类型中是直接接收的是int类型,或者是可以传入一个byte数组。

既然是字节流,我们可以尝试将字符串或者字符转换为对应的byte类型。用到的一个方法是getBytes()。查看一下源码,看看是否转换为了数组类型,你可以验证一下。

 public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);
    }

我们也可以直接用代码进行操作。

 fos.write("hello".getBytes());

字节缓冲输出流

public class BufferedOutputStream
extends FilterOutputStream

提供的主要的两个构造方法

BufferedOutputStream(OutputStream out) 
//创建一个新的缓冲输出流,以将数据写入指定的底层输出流。  
BufferedOutputStream(OutputStream out, int size) 
//创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。  

提供的一些具体的方法

void flush() 
//主要是进行刷新用的
void write(byte[] b, int off, int len) 
//从指定的字节数组写入 len个字节,从偏移 off开始到缓冲的输出流。 
void write(int b)
//将指定的字节写入缓冲的输出流。

**从构造方法可知,在使用这个缓冲输出流的时候,我们可以传一个输出流的对象进去。**

```javascript
    FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
    BufferedOutputStream bos = new BufferedOutputStream(fos);

当然也可以直接这样进行传

  BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));

注意这样的做法叫做匿名对象,而不是匿名内部类

然后后面的写入操作基本还是一样的。可以按照字节进行写入,也可以写入字节数组。但是加入缓冲区是一定会比较快的,

用输入输出实现数据的复制

可以进行文件到文件的复制

package io_demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

//用输入输出流进行文件的复制
public class IoDemo_3 {
    public static void main(String args[]) throws IOException {
        //字节输入流对象
        FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        //创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_01");
        int  by;
        while((by = fis.read())!=-1)
        {
            fos.write(by);
        }
        fos.close();
        fis.close();
    }
}

这样可以简单的实现复制。
前面已经说过,读取文件返回-1表示到达文件结尾。
上边这段代码也只是一次读取一个字节,其实你也可以自己加入一个字节数组。来加快读取写入的速度。
当然我们可以用缓冲区,缓冲流。进行相关的操作。可以用字节缓冲流一次读取一个字节的数据,也可以一次读取一个字节数组大小数据。

package io_demo;

import java.io.*;

public class IoDemo_06 {
    public static void main(String args[]) throws IOException {
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        BufferedOutputStream bos = new BufferedOutputStream(fos);

        FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        BufferedInputStream bis = new BufferedInputStream(fis);
        int bu;
        while((bu=bis.read())!=-1)
        {
            System.out.println((char)bu);
        }
        //一次读取一个字节数组的数据
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes))!=-1)
        {
        	bos.write(bytes,0,len);
            //System.out.println(new String(bytes,0,len));
        }
        bis.close();
        fis.close();
    }


}

但是其实你也会发现,假如文本中有中文的话,其实字节流处理的话是不太方便的,会有乱码的问题。
单单从文本中读取中文的话,如果再转换为char类型是绝对会出现乱码,但是如果从文件复制到文件的话,是不会出现问题的。因为复制到文件的时候,底层会自动进行拼接
这样处理的话,其实在后面的字符流中可以得到非常方便的处理。

我们采用的字节流一般是默认utf-8编码的,这样的编码是会让一个汉字占用三个字节,但是如果是gbk编码的话,就会占用两个字节。但是无论采取何种编码,汉字的第一个字节总会是负数。这样的话,在数据复制的时候,就可以根据第一个字节是否是负数进行判断是汉字还是一般的字符。如果是第一个字节是负数的话,就会进行拼接。

当然是也是可以实现对视频数据地复制的。选一个视频的话。然后比较一下种复制的效率。

提供一段代码,方法仅供参考,可以自己进行优化。

package io_demo;

import java.io.*;

public class IoDemo_07 {
    public static void main(String args[]) throws IOException {
        //四种方式实现复制视频
//        1:基本字节流一次读取一个字节
//        2:基本字节流一次读取一个字节数组
//        3:字节缓存流一次读取一个字节
//        4:字节缓存流一次读取一个字节数组
        //记录开始时间
        long startTime = System.currentTimeMillis();
        //复制视频
        method();
        method_2();
        method3();
        method4();

        //纪录结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("共耗时:" + (endTime - startTime) + "毫秒秒");
    }

    private static void method() throws IOException {
        //读取字节
        FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\1.mv");
        int by;
        while ((by = fis.read()) != -1) {
            fos.write(by);

        }
        fos.close();
        fis.close();
    }

    private static void method_2() throws IOException {
        //一次读取一个字节数组
        FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\1.mv");
        byte[] bytes = new byte[1024];
        int len;
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }
        fos.close();
        fis.close();
    }

    private static void method3() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo");
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        int by;
        while ((by = bis.read()) != -1) {
            bos.write(by);

        }
        bos.close();
        bis.close();
        fis.close();
        fos.close();

    }

    private static void method4() throws IOException {
        FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo");
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos);

        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes))!=-1)
        {
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
        fis.close();
        fos.close();

    }
}

事实其实字节缓冲流加上一次读取一个字节数组的话,就非常快了。

复制单级文件夹(文件夹中只含有文件,不包含其它文文件夹)

使用字节流来进行复制

这样的单级文件夹得话,复制的时候主要还是需要对文件夹下面的文件进行一个遍历。

package io_demo;

import java.io.*;

public class Only_doc {
    public static void main(String args[]) throws IOException {
        //创建数据源
        File srcFolder = new File("D:\\c_doc");
        String srcFolderName = srcFolder.getName();
        //创建目的地
        File destFolder = new File("E:\\java_doc\\src\\io_demo", srcFolderName);
        if(!destFolder.exists())
        {
            destFolder.mkdir();
        }
        File[] listFiles = srcFolder.listFiles();
        for(File  srcFile:listFiles)
        {
            String srcFileName = srcFile.getName();
            File destFile = new File(destFolder, srcFileName);
        copyFile(srcFile,destFile);

        }
    }

    private static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
        byte[] bytes = new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1)
        {
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
    }

}

需要注意的是,复制到源文件的指定父级路径一定要存在。如果不存在的话,会报错。子级别路径可以自己设定。

复制多级文件夹(文件夹中包含文件夹)

package io_demo;

import java.io.*;

public class Mult_doc
{
    //复制多级文件夹
    public static void main(String args[]) throws IOException {
        File srcfile = new File("D:\\BaiduNetdiskWorkspace");
        File destFile = new File("E:\\java_doc\\src\\io_demo");
        copyFolder(srcfile,destFile);
    }
    //复制文件夹
    private static void copyFolder(File srcfile, File destFile) throws IOException {
        //判断数据源是否是目录
        if(srcfile.isDirectory())
        {
            //在目的地下创建和数据源File名称一样的目录
            String srcFileName = srcfile.getName();
            File new_Folder = new File(destFile,srcFileName);
            if(!new_Folder.exists())
            {
                new_Folder.mkdir();
            }
            File[] filearry = srcfile.listFiles();
            for(File file:filearry)
            {
                copyFolder(file,new_Folder);
            }
        }else {
            File file = new File(destFile,srcfile.getName());
            copyFile(srcfile,file);
        }
    }

    private static void copyFile(File srcFile,File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
        byte[] bytes = new byte[1024];
        int len;
        while((len= bis.read(bytes))!=-1)
        {
            bos.write(bytes,0,len);
        }
    }
}

这里面有一个基本的应用点就是采用了递归调用,基于判断是否遍历出来的是文件还是文件夹来采用是否进行继续遍历。

一个编码与解码的过程

这是一段简单的程序

package io_demo;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class IoDemo_09 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //进行编码解码的演示
        String s  = "中国";
        //使用默认的字符集将String编码为一系列字节,将结果存储到新的数组当中
        //编码
        byte[] bytes = s.getBytes();//使用默认的字符utf-8编码
        System.out.println(Arrays.toString(bytes));
        byte[] bytes1 = s.getBytes("UTF-8");
        System.out.println(Arrays.toString(bytes1));
        //解码过程
        String ss = new String(bytes);
        System.out.println(ss);
        //平台的默认编码和平台默认的解码是一样的,所以可以正确输出


    }
}

不要疑惑为什么getBytes()可以进行编码,给String()传入这个byte数组却又可以进行解码。直接区看看源码就好啦。可以进行跟进。

好,首先getBytes()。

在这里插入图片描述
然后我们跟进它的encode方法

在这里插入图片描述
可以看到它这里是指定了编码的。

然后String里面的解码时怎么回事呢?继续跟进。
在这里插入图片描述
然后要知道如何区跟进,我们跟进this。

在这里插入图片描述
从单词字面意思也可以看出这是一个解码的过程,但是我们可以继续进行跟进。
在这里插入图片描述
这里时一段解码的过程。所有的调用都可以进行溯源。

字符流说明

字符输入流读数据的方法

Reader是一个抽象类

public abstract class Readerextends Objectimplements Readable, Closeable

用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

我们主要演示的实现类就是InputStreamReader

public class InputStreamReaderextends Reader

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));

当然这个缓冲流我们在后面举例

InputStreamReader的主要构造方法有这么几个

构造方法摘要
1:InputStreamReader(InputStream in)
创建一个使用默认字符集的 InputStreamReader。
2:InputStreamReader(InputStream in, Charset cs)
创建使用给定字符集的 InputStreamReader。
3:InputStreamReader(InputStream in, CharsetDecoder dec)
4: 创建使用给定字符集解码器的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName)
创建使用指定字符集的 InputStreamReader。

主要的方法

void close()
关闭该流并释放与之关联的所有资源。
String getEncoding()
返回此流使用的字符编码的名称。
int read()
读取单个字符。
int read(char[] cbuf, int offset, int length)
将字符读入数组中的某一部分。
boolean ready()
判断此流是否已经准备好用于读取。

从提供的构造方法的·参数我们可以了解到这个实现类基本的底层还是有字节流的实现。或者可以跟进源码。

在这里插入图片描述
我们可以认为这个字符输入流实现类基本实现的就是字节流加上编码的效果。

现在我们可以去看看它的读取数据的方法。(到源码中)
在这里插入图片描述
在这里插入图片描述
我们可以这样用字符流进行简单读取

package io_demo;

import java.io.*;

public class IODemo_10 {
    public static void main(String args[]) throws IOException {

        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
        

      
        int ch ;
        while((ch=isr.read())!=-1)
        {
            System.out.println((char)ch);
        }
        isr.close();

    }
}

当然还是可以进行一次读取一个字符数组的方法

package io_demo;



import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

public class IoDemo_12 {
    public static void main(String args[]) throws IOException {
        //字符流读数据
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
//        int ch;
//        while((ch=isr.read())!=-1)
//        {
//            System.out.println((char)ch);

        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1) {
            System.out.println(new String(chs,0,len));

        }
        isr.close();
    }
}

字符缓冲输入流

我们照样可以使用缓冲流
在这里插入图片描述
这个缓冲流也继承了Reader类,基本上也提供了父类有的read()等方法,另外还有readline()方法。我们在后面的例子中使用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package io_demo;



import java.io.*;

public class DemoPratice01 {
    public static void main(String args[]) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_01"));
        BufferedReader br= new BufferedReader(isr);
        int ch;
        while((ch=br.read())!=-1)
        {
            System.out.print((char)ch);
        }
        char[] chars = new char[1024];
        int len;
        while ((len = br.read(chars))!=-1)
        {
            System.out.println(new String(chars,0,len));
        }

    }
}

使用字符缓输入流可以实现一行一行读取的操作。

BufferedReader br = new BufferedReader(new FileReader("bw.newLine();"));
         String line;
         while((line = br.readLine())!=null)
         {
             System.out.println(line);
         }

字符输出流写数据的方法

public abstract class Writerextends Objectimplements Appendable, Closeable, Flushable

写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

还是首先选一个实现类开始一些说明

public class OutputStreamWriterextends Writer

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:
Writer out
= new BufferedWriter(new OutputStreamWriter(System.out));

主要的构造方法

1:OutputStreamWriter(OutputStream out)
创建使用默认字符编码的 OutputStreamWriter。
2:OutputStreamWriter(OutputStream out, Charset cs)
创建使用给定字符集的 OutputStreamWriter。
3:OutputStreamWriter(OutputStream out, CharsetEncoder enc)
创建使用给定字符集编码器的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName)
创建使用指定字符集的 OutputStreamWriter。

常用的方法

void close()
关闭此流,但要先刷新它。
void flush()
刷新该流的缓冲。
String getEncoding()
返回此流使用的字符编码的名称。
void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str, int off, int len)
写入字符串的某一部分。

很明显这样的设计和输入流还是十分对称的。
下面还是简单的去使用它。

package io_demo;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class IoDemo__ {
    public static void main(String args[]) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice1"));
        osw.write("hello");
        char[] chars = new char[]{'a','b','c'};
        osw.write(chars,0,chars.length);

    }
}

字符缓冲输出流

同样可以使用缓冲流

public class BufferedWriter extends Writer {

    private Writer out;

    private char cb[];
    private int nChars, nextChar;

    private static int defaultCharBufferSize = 8192;

缓冲流使用到的一些方法

主要的构造方法
在这里插入图片描述
在这里插入图片描述
一些普通方法

BufferedWriter bw = new BufferedWriter(osw);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
同样还有进行读入行的方法
在这里插入图片描述
使用的时候这样将字符输出流对象进行传入就可以

BufferedWriter bw = new BufferedWriter(osw);

用输入输出实现数据的复制

package io_demo;

import java.io.*;

public class IoDemo13 {
    //字符流进行复制文件
    public static void main(String args[]) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\IoDemo_12.java"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\Demo_Copy.java"));
        int ch;
//        while((ch = isr.read())!=-1)
//        {
//            osw.write(ch);
//        }
        //一次读取一个字符数组
//        char[] chars = new char[1024];
//        int len;
//        while((len= isr.read(chars))!=-1)
//        {
//            osw.write(chars,0,len);
//        }
        osw.close();
        isr.close();
    }
}

但是其实我们还可以使用这个两个类下的子类,更方便的进行操作。

package io_demo;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class IoDemo_14 {
    public static void main(String args[]) throws IOException {
        //使用下面的子类(可以简化书写长度)
        FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice");
        FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_01");
        int ch;
        while((ch = fr.read())!=-1)
        {
            fw.write(ch);
        }
        char[] chars = new char[1024];
        int len;
        while((len= fr.read(chars))!=-1)
        {
            fw.write(chars,0,len);
        }
        fw.close();
        fr.close();
    }

}

同样该子类也可以结合字符缓冲流

package io_demo;



import java.io.*;

public class IoDemo15 {
    public static void main(String args[]) throws IOException {
        //字符缓冲流
        FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("Hello");
        bw.close();
        BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_01"));
        // 一次读一个字符数据
        int ch;
        while((ch=br.read())!=-1)
        {

            System.out.println((char)ch);

        }
        //一次读取一个字符数组
        char[] chars = new char[1024];
        int len;
        while((len=br.read(chars))!=-1)
        {
          System.out.println(new String(chars,0,len));
        }
    }
}

做一个随机点名器

在这里插入图片描述
假设文件中有这些名字,然后我们从中读取信息,并随机输出。这样就时一个随机点名器了。

package io_demo;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

//从文件中读取数据,做一个随机点名器
public class IoDemo18 {
    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice"));
        //创建ArrayList对象
        ArrayList<String> array = new ArrayList<String>();
        String line;
        while((line= br.readLine())!=null)
        {
            array.add(line);
        }
        br.close();
        Random r = new Random();
        int i = r.nextInt(array.size());
        String name = array.get(i);
        System.out.println("幸运者是"+name);

    }
}

在这里插入图片描述

集合到文件(将对象信息拼接输入到文件)

首先我们创建一个学术类

package io_demo;

public class Student {
    private String sid;
    private String name;
    private int age;
    private String address;
    public Student()
    {

    }

    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

测试代码

package io_demo;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class IoDemo19 {
    //集合到文件,改进版
    public static void main(String args[]) throws IOException {
        //创建ArrayList集合
        ArrayList<Student> array = new ArrayList<>();
        Student s1 = new Student("001", "jgdabc", 19, "山西");
        Student s2 = new Student("002", "兰舟千帆", 20, "山西");
        Student s3 = new Student("003", "潇潇", 18, "湖北");
        Student s4 = new Student("004", "小雨", 21, "河北");
        Student s5 = new Student("005", "叶灵芸", 22, "浙江");
        array.add(s1);
        array.add(s2);
        array.add(s3);
        array.add(s4);
        array.add(s5);
        //创建字符流输出对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\\\java_doc\\\\src\\\\io_demo\\\\Student.txt"));
        for (Student s : array) {
            StringBuilder sb = new StringBuilder();
            sb.append(s.getSid()).append(",").append(s.getName())
                    .append(",").append(s.getAge()).append(",").append(s.getAddress());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        //释放资源
        bw.close();

    }
}

在这里插入图片描述

文件到集合(将文件中的人物属性通过对象存放于集合中,然后进行遍历)

还是基本的学术类

package io_demo;

public class Student {
    private String sid;
    private String name;
    private int age;
    private String address;
    public Student()
    {

    }

    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

测试类

package io_demo;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

//将文本文件当中的数据督导集合当中
public class IoDemo_20 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student.txt"));
        ArrayList<Student> array = new ArrayList<>();
        //调用字符缓冲输入流对象的方法读取数据
        String line;
        while ((line = br.readLine()) != null) {
            String[] strArray = line.split(",");
            Student s = new Student();
            //将字符串数组的每一个元素取出来对应的复制给学生对象的成员变量值
            s.setSid(strArray[0]);
            s.setName(strArray[1]);
            s.setAge(Integer.parseInt(strArray[2]));
            s.setAddress(strArray[3]);
            array.add(s);
        }
        br.close();
        for (Student s : array) {
            System.out.println(s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
        }
        


    }

}

在这里插入图片描述

将学生成绩排序,并将数据写入文件中(排序采用多级条件)

我们可以先创建一个学生类

package io_demo;

public class Student01 {
    private String name;
    private int chinese;
    private int math;
    private int english;

    public Student01() {
        super();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getEnglish() {
        return english;
    }

    public void setEnglish(int english) {
        this.english = english;
    }

    public Student01(String name, int chinese, int math, int english) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }

    public int getSum() {
        return this.chinese + this.english + this.math;
    }
}

创建学生类的唯一作用就是来获取对象,存放相关的属性。

然后我们再创建一个测主类

package io_demo;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;

public class Student01_Demo {
    public static void main(String[] args) throws IOException {
        TreeSet<Student01> ts = new TreeSet<>(new Comparator<Student01>() {
            @Override
            public int compare(Student01 s1, Student01 s2) {
                int num = s2.getSum() - s1.getSum();
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
                int num4 = num3 == 0 ? s1.getName().compareTo(s2.getName()) : num3;
                return num4;
                //return 0;
            }
        });
        for (int i = 0; i < 5; i++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请录入第" + (i + 1) + "个学生信息");
            System.out.println("姓名:");
            String name = sc.nextLine();
            System.out.println("语文成绩:");
            int chinese = sc.nextInt();
            System.out.println("数学成绩:");
            int math = sc.nextInt();
            System.out.println("英语成绩:");
            int english = sc.nextInt();
            Student01 s = new Student01();
            s.setName(name);
            s.setChinese(chinese);
            s.setMath(math);
            s.setEnglish(english);
            ts.add(s);
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\Student01.txt"));
        for (Student01 s : ts) {
            StringBuilder sb = new StringBuilder();
            sb.append(s.getName()).append(",").append(s.getChinese())
                    .append(",").append(s.getMath()).append(",").append(s.getEnglish()).append(",").append(s.getSum());

            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

在这里插入图片描述

标准输入输出流

无论是在api中查看,还是在Ststem中查看,我们都可以了解到System有标准输入和输出流的定义,并且声明为static,于是肯定是可以直接进行按照类名调用的。

api对System类有以下说明

在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。

作为了System类的两个成员变量。这两个流的主要是进行键盘和内存的交互。

标准输入流

public abstract class InputStreamextends Objectimplements Closeable此抽象类是表示字节输入流的所有类的超类。

这个抽象类唯一的构造方法就是InputStream()这个无参构造方法。

简单了解下

package io_demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;

public class IoDemo_21 {
    public static void main(String args[]) throws IOException {
       //字节流
        InputStream in = System.in;
        int by;
        while((by=in.read())!=-1)
        {
            System.out.print((char)by);
        }
        //使用字符流
        InputStreamReader isr = new InputStreamReader(in);
        //使用字符流实现一次读取一次数据(使用字符缓冲输入流)
        BufferedReader br = new BufferedReader(isr);
        System.out.println("请输入一个字符串:");
        String line = br.readLine();
        System.out.println("你输入的字符串是"+line);
        System.out.println("请输入一个整数:");
        int i = Integer.parseInt(br.readLine());
        System.out.println("你输入的整数是:"+i);
        //java提供了一个类实现键盘录入
        Scanner sc = new Scanner(System.in);//既可以接收整数也可以接收字符串

    }
}

平常我们都会使用Scanner这个下面的方法去进行键盘输入数据,但是弄清楚它的本质也是一件非常有意义的事情。
在这里插入图片描述
它的底层还是使用了字节输入流

在这里插入图片描述
这样就与流的知识衔接起来了。

标准输出流

public class PrintStreamextends FilterOutputStreamimplements Appendable, Closeable

PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 (’\n’)。
PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。

所具有的一些构造方法

PrintStream(File file)
创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(File file, String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
PrintStream(OutputStream out)
创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush)
创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush, String encoding)
创建新的打印流。
PrintStream(String fileName)
创建具有指定文件名称且不带自动行刷新的新打印流。
PrintStream(String fileName, String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

这样我们得到的信息是,我么可以在参数中按照布尔值确定是否进行自动刷新,另外还可以进行指定编码。

这个和标准输入流在System类中定义的格式是一样的。底层话,其实同样也是字节流。简单看下怎么使用。

 //标准输出流
        PrintStream out = System.out;
        out.print("hello");//输出在控制台

但是你其实也可以向文件中进行输出,也就是将数据输入到文件中。

  //字节打印流
        PrintStream ps = new PrintStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        ps.write(97);//使用父类方法
        ps.print(97);//使用自己特有方法
        ps.close();
       //字符打印流
        PrintWriter pW = new PrintWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        pW.write("hello");
        pW.flush();
        pW.println("Hello");
        pW.flush();
        PrintWriter pw = new PrintWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt"),true);//会自动刷新
        pw.println("Hello");
        pw.println("world"

对象序列化流于反序列化流

什么是序列化与反序列化

=java序列化,就是指吧java对象转换为字节序列的过程。而反序列自然就是将字节对象恢复为java对象。==

这样做的意义在哪呢?对象进行序列化,会转换为字节流,这样在网络上传输,或者是进行保存为本地文件都是非常方便的。反序列很明显就是进行对象的重构。

其实你可以和通信联系在一起。网络上的文本,图片,视频,音频都是通过二进制进行传输的,我们的java所创建的对象在传输的时候也应该进行序列化,转换为字节流,然后通过网络,io传入,当我们的对象序列传输完成后,对方进行反序列化,就可以读取到数据内容。

对象序列化流( ObjectOutputStream)

api对这个类有很多的说明,主要说明

public class ObjectOutputStreamextends OutputStreamimplements ObjectOutput, ObjectStreamConstants

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

看一下简单使用

首先我们需要一个对象类

package io_demo;

import java.io.Serializable;

public class Student implements Serializable {

    private String sid;
    private String name;
    int age;

    private String address;
    public Student()
    {

    }

    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

然后测试主类

package io_demo;

import java.io.*;

public class IoDemo_23 {
    //对象序列化流
//    构造方法
   // ObjectOutputStream(OutputStream out)
    public static void main(String args[]) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
        Student s = new Student("001","刘备",28,"汉中");
        oos.writeObject(s);//将对象序列化()
        oos.close();
    }
}

为什么Student需要实现Serializable,api有说明

只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。

为什么没有具体实现?
在这里插入图片描述
因为这个接口本身没有具体实现,我们不要要实现什么,这个只是作为一个标识而已。

对象反序列化流(.ObjectInputStream)

public class ObjectInputStream
extends InputStream
implements ObjectInput, ObjectStreamConstants

说明如下

ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。
ObjectOutputStream和ObjectInputStream可以分别为与FileOutputStream和FileInputStream一起使用的对象图提供持久性存储的应用程序。 ObjectInputStream用于恢复先前序列化的对象。 其他用途包括使用套接字流在主机之间传递对象,或者在远程通信系统中进行封送和解组参数和参数。

首先还是原来的Student对象类

package io_demo;

import java.io.Serializable;

public class Student implements Serializable {

    private String sid;
    private String name;
    int age;

    private String address;
    public Student()
    {

    }

    public Student(String sid, String name, int age, String address) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

对的,你虽然是从文件中读取,但是如果你不使用构建的对象的话。是绝对无法成功进行反序列化的。

主要的测试代码

package io_demo;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

//反序列化ObiectreadObject() 进行读取来反序列化
public class IoDemo_24 {
    public static void main(String args[]) throws IOException, ClassNotFoundException {
        //实现对象反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
        Object o = ois.readObject();
        Student s = (Student)o;
        System.out.println(s.getName()+","+s.getAge());
        ois.close();


    }
}

在这里插入图片描述
这样感觉有时候很像加密与解密的样子。

如果在反序列话的时候原来对象类有修改?

我现在就修改一个属性。我让age属性权限有所变化,变为私有属性
在这里插入图片描述

**结果反序列化就报错了,为什么?你看报错 **

在这里插入图片描述
再清晰一点就是

local class incompatible: stream classdesc serialVersionUID = -8624680451455869930, local class serialVersionUID = 6474701452341618882

serialVersionUID是什么?(为什么修改对象类会反序列化失败?)

serialVersionUID是序列化的时候会生成的一个的版本标识,。当我们对对象进行修改后,那么这个序列化的版本标识的值就会与之前的不一样了。这样会导致字节流的版本标识与本地的版本标识不一样,不一样就会反序列化失败。

解决修改对象类修改反序列化失败的问题

我们就自己定义一个常量。让这个版本标识符不再变化
在这里插入图片描述
注意,一定是在对象类中,不是在测试类。

不想让某些属性被序列化?

这样就可以
在这里插入图片描述

集合(Properties)

介绍这个集合的原因就是这个集合可以和流结合使用。
这个集合的具体说明就不在本文详细说明了,具体的在之前的集合文中再补充吧。简单说明一下和其用法。

这个集合首先是Map下的子类,所以也是双列集合。但是不支持泛型。用代码来看看具体使用。简单演示。

package io_demo;

import java.util.Properties;
import java.util.Set;

public class IoDemo_25 {
    public static void main(String[] args) {
        //创建集合对象
        Properties pro = new Properties();
        //存储元素
        pro.put(001,"jgdabc");
        pro.put(002,"兰舟千帆");
        pro.put(002,"lym");
        //遍历集合
        Set<Object> KeySet = pro.keySet();
        for(Object key:KeySet)
        {
            Object value = pro.get(key);
            System.out.print(key+","+value);
        }
        pro.setProperty("000","jgdabc");
        pro.getProperty("000");
        //获取键值
        Set<String> names = pro.stringPropertyNames();//获取到键集合
        //于是可以这样遍历
        for(String key:names)
        {
            String value = pro.getProperty(key);
            System.out.println(key+value);
        }


    }
}

==它的put()方法键是Object类型的,setProperty()方法的键必须是String类型。==这个需要注意。

集合到文件,文件到集合

package io_demo;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class IoDemo_26 {
    public static void main(String[] args) throws IOException {
        //把集合中的数据保存到文件
        myStore();
        //文件数据加载到集合
        myLoad();
    }

    private static void myLoad() throws IOException {
        Properties prop = new Properties();
        FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        prop.load(fr);
        fr.close();
        System.out.print(prop);
    }

    private static void myStore() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("000","jack");
        prop.setProperty("001","jgdabc");
        prop.setProperty("002","john");
        FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
        prop.store(fw,null);//null代表不描述信息
    }
}

做一个猜数字游戏

文件中给一个count=0,然后用这个集合读取到数据(需要转换为int类型,然后根据值得限制,限制玩的次数。当然没每次运行完毕都会count+1,运行三次就不能玩了。这个和一直在交互是不一样的。)
首先一个猜数字游戏类

package java_practice;

import java.util.Scanner;

public  class GuessNumber {

    public static  void start()
    {
        Scanner input = new Scanner(System.in);
        int number = (int) (Math.random() * 100)+1; // 产生随机数
        int guess;// 用户猜的数字
        int count = 0;// 猜测次数
        System.out.println("我心里有一个0到100之间的整数,你猜是什么?");
        // 用户猜测随机数
        do {
            guess = input.nextInt();
            if (number < guess) {
                System.out.println("大了点,再猜!");
                count++;
            } else if (number > guess) {
                System.out.println("小了点,再猜!");
                count++;
            } else {
                count++;
                break;
            }
        } while (true);
        System.out.println("这个数字是"+number);
        System.out.println("您猜的次数是"+count);
        // 根据猜测次数给出评价
        if (count == 1) {
            System.out.println("你太聪明了!");
        } else if (count >= 2 && count <= 5) {
            System.out.println("不错,再接再厉!");
        } else {
            System.out.println("要努力啊!");
        }


    }
}



然后一个测试主类

package io_demo;

import java_practice.GuessNumber;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLOutput;
import java.util.Properties;

public class IoDemo_27 {


    public static void main(String args[]) throws IOException {
        GuessNumber guessNumber = new GuessNumber();
        Properties prop = new Properties();
        FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\count.txt");
        prop.load(fr);
        fr.close();
        String count = prop.getProperty("count");
        int number = Integer.parseInt(count);
        if(number>=3)
        {
            System.out.println("游戏试玩结束,想玩请充值");
        }else
        {
           guessNumber.start();
            prop.setProperty("count",String.valueOf(number));
            number++;
            FileWriter fw =  new FileWriter("E:\\java_doc\\src\\io_demo\\count.txt");
            prop.store(fw,null);
            fw.close();
        }


    }
}

注意一定要自己建立一个文件,里面存一个标记值。这样功能就可以实现了。

IO异常处理

可以根据自己的需要选择将数据输入到文件中还是将信息输出到控制台。

抛出异常

package io_demo;

import java.io.*;

public class IODemo_10 {
    public static void main(String args[]) throws IOException {
      
        FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
        //

        osw.write("中国");
        osw.close();
        int ch ;
        while((ch=isr.read())!=-1)
        {
            System.out.println((char)ch);
        }
        isr.close();

    }
}

标准捕获

package io_demo;

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

public class IoDemoException {
    public static void main(String args[])
    {
        FileReader fr=null;
        FileWriter fw=null;
        try {
             fr= new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
             fw= new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
            char[] chs = new char[1024];
            int len;
            while((len = fr.read())!=-1)
            {
                fw.write(chs,0,len);
            }
        }catch (IOException e)
        {
            e.printStackTrace();
        }finally {
            if(fw!=null)
            try {
                fw.close();
            }catch (IOException e)
            {
                e.printStackTrace();
            }
           if(fr!=null){
               try {
                   fr.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }

        }


    }
}

但是这样的处理还是比较麻烦的

捕获处理改进版(jdk7特性)

package io_demo;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//jdk7的改进
public class IoDemoException01 {
    public static void main(String args[])
    {
        try (  FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
               FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\Student.txt");) {

            char[] chars = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chars, 0, len);
            }
        }catch (IOException e)
        {
            e.printStackTrace();
        }
        }
           ;
        }

//自动释放资源


这样的处理还是会自动释放资源,不需要人为关闭。

就先介绍这么多。之后有问题会继续更改补充。

我的主页

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-27 10:57:09  更:2022-02-27 10:58:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:59:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码