IO流详细

本文详细介绍了Java中的File类,包括如何创建、操作文件以及获取文件信息。接着,文章深入讲解了IO流的概念,分类和使用,如字符流和字节流的读写操作,并通过文件复制的例子展示了流的使用。此外,还涉及到了缓冲流、转换流以及System类对IO流的支持。最后,提到了数据流和对象流(序列化与反序列化)的相关知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习思路

  • 先学会使用File类定位文件及操作文件本身

  • 然后学习IO流读取文件

一、File类

File类的概述

  • File类在包java.io.File,代表操作系统的文件对象(文件、文件夹)

  • File提供了诸如:定位文件、获取文件本身信息、删除文件、创建文件(文件夹)等功能

File对象的创建:

方法名称

说明

public File(String pathname)

根据文件路径创建文件对象

public File(String parent,String child)

从父路径名字字符串和子路径名字字符串创建文件对象

public File(File parent,String child)

根据父路径文件对象和子路径文件名字创建对象

  • File封装的是一个路径名,这个路径可以是不存在的,也可以是存在的

public static void main(String[] args){
        //1、创建File对象(指定文件路径)
        //路径写法:C:\Users\lly\Pictures\1c7d6305b598922a4893a87b3b199ca1.jpg
        //        C:/Users\lly\Pictures\1c7d6305b598922a4893a87b3b199ca1.jpg
        //        File.separator
        //File f = new File("C:\\Users\\lly\\Pictures\\1c7d6305b598922a4893a87b3b199ca1.jpg");
        //File f = new File("C:/Users/lly\\Pictures\\1c7d6305b598922a4893a87b3b199ca1.jpg");
        File f = new File("C:" + File.separator + "Users" + File.separator + "lly\\Pictures\\1c7d6305b598922a4893a87b3b199ca1.jpg");
        long size = f.length();//文件的字节大小
        System.out.println(size);
        
        //2、File创建对象,支持绝对路径,支持相对路径(重点)
        File f1 = new File("C:\\Users\\lly\\Pictures\\======.jpg");
        System.out.println(f2.length());

        //相对路径,一般定义模块中的文件相对到工程下
        File f2 = new File("file-io-app/src/date.txt");
        System.out.println(f2.length());

        //File创建对象可以是文件夹
        File f3 = new File("C:\\Users\\lly\\Pictures);
        System.out.println(f3.exists());//判断这个文件是否存在
  • 总结:

  1. File类的作用?

  • 创建对象定位文件,可以删除、获取文件信息等。但是不能读写文件内容。

  1. File类的构建对象方式?

  • File file = new File("文件/文件/绝对路径/相对路径");

  1. 绝对路径和相对路径是什么样子的?

  • 绝对路径是带盘符的,依赖当前系统。

  • 相对路径是不带盘符的,默认相对到工程文件下开始寻找文件。

File类常用API:

方法名称

说明

public boolean isDirectory()

测试此抽象路径名表示的File是否为文件夹

public boolean isFile()

测试此抽象路径名表示的File是否为文件

public boolean exists()

测试此抽象路径名表示的File是否存在

public String getAbsolutePath()

返回系抽象路径名的绝对路径名字字符串

public String getPath()

将此抽象路径转换为路径名字符串

public String getName()

返回由此抽象路径表示的文件或文件夹的名称

public long lastModified()

返回文件最后修改时间毫秒值

public static void main(String[] args) {
        //1、绝对路径创造一个文件对象
        File f1 = new File("C:\\Users\\lly\\Pictures\\1c7d6305b598922a4893a87b3b199ca1.jpg");
        //a、获取它的绝对路径
        System.out.println(f1.getAbsoluteFile());
        //b、获取文件定义的时候使用的路径
        System.out.println(f1.getPath());
        //c、获取文件的名称,带后缀
        System.out.println(f1.getName());
        //d、获取文件的大小字节个数
        System.out.println(f1.length());
        //e、获取文件的最后修改时间
        long time = f1.lastModified();
        System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time));
 
        System.out.println("------------------------------------");
 
        File f2 = new File("file-io-app/src/date.txt");
        //a、获取它的绝对路径
        System.out.println(f2.getAbsoluteFile());
        //b、获取文件定义的时候使用的路径
        System.out.println(f2.getPath());
        //c、获取文件的名称,带后缀
        System.out.println(f2.getName());
        //d、获取文件的大小字节个数
        System.out.println(f2.length());
        //e、获取文件的最后修改时间
        long time1 = f2.lastModified();
        System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time1));
        //f、判断文件是文件还是文件夹
        System.out.println(f2.isFile());//true
        System.out.println(f2.isDirectory());//false
        System.out.println(f2.exists());//true
    }
C:\Users\lly\Pictures\1c7d6305b598922a4893a87b3b199ca1.jpg
C:\Users\lly\Pictures\1c7d6305b598922a4893a87b3b199ca1.jpg
1c7d6305b598922a4893a87b3b199ca1.jpg
448349
最后修改时间:2022/03/11 22:35:51
------------------------------------
E:\Java\code\javasepromax\file-io-app\src\date.txt
file-io-app\src\date.txt
date.txt
6
最后修改时间:2022/05/23 21:06:02
true
false

File类创建文件的功能

方法名称

说明

public boolean createNewFile()

创建一个新的空文件夹

public boolean mkdir

只能创建一级文件夹

public boolean mkdirs

可以创建多级文件夹

File类删除文件的功能

方法名称

说明

public boolean delete()

删除由此路径名表示的文件或空文件夹

  • delete方法直接删除不走回收站;如果删除的是一个文件,且文件没有被占用则直接删除

  • delete删除方法只能删除空文件夹

File类遍历文件的功能:

方法名称

说明

public String[] list()

获取当前目录下的”以及文件名称“到一个字符串数组中去返回

public File[] listFiles()(常用)

获取当前目录下所有的”一级文件对象“到一个文件对象数组中去返回(重点)

listFile注意事项:

  • 当调用者不存在时,返回null

  • 当调用者是一个文件时,返回null

  • 当调用者是一个空文件时,返回一个长度为0的数组

  • 当调用者是一个有内容的文件夹时,将文件夹里面所有文件文件夹的路径放在File数组中返回,包含隐藏内容

  • 只能遍历当前文件夹下的一级文件对象

字符集:

字符集基础知识:

  • 计算机底层是不可以直接存储字符的,只能存储二进制(0、1)

  • 二进制可以转换为十进制

总结:计算机底层可以表示十进制编号,也可以给人类字符进行比爱你好存储,这套规则就是字符集

ASCLL码字符集:

  • ASCLL(American Standard Code for Information Interchange,美国信息交换标准代码):包含数字、英文、符号

  • ASCLL使用1个字节存储一个符号,一个字节是8位,总共可以表示128个字符信息,对于英文、数字是够用的

GBK:

  • window系统默认编码,兼容ASCLL编码表,包含几万个汉字,并支持繁体汉字以及部分日文汉字

  • GBK是中国的码表,一个中文以两个字节的形式存储,不包含世界上所有的文字

Unicode码表:

  • 又称统一码、万国码、单一码,是计算机科学领域里的一项业界字符编码标准

  • 容纳世界上大多数国家的所有常见文字和符号

  • Unicode会先通过UTF-8,UTF-9,以及UTF-32的编码成二进制后再存储到计算机中,其中最为常见的就是UTF-8

注意:
  • Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储

  • UTF-8也兼容ASCLL编码表

  • 技术人员都应该使用UTF-8的字符集编码

  • 编码前和编码后的字符集需要一致,否则会出现中文乱码

字符集的编码解码操作:

String编码:

方法名称

说明

byte[] getBytes()

使用该平台默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中

byte[] getBytes(String charsetName)

使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

String编码:

方法名称

说明

String(byte[] bytes)

通过使用平台的默认字符集解码指定的字节字节数组来构造新的String

String(byte[] bytes,String charsetName)

通过指定的字符集解码指定的字节数组来构造新的String

二、IO流:

IO流概述:

  • I表示input,是数据从硬盘读取到内存的过程,称之为输入

  • O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之为输出,负责写

IO流的分类:

总结流的四大类:

  • 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读取到内存中去的流

  • 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流

  • 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读取到内存中去的流

  • 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络中去的流

可将各个流看作输送数据的管

下面通过对文件复制操作的描述来引入各个流

文件的复制:

字符流:

功能分解1:文件=》程序 FileReader
一个字符一个字符的读取:
public static void main(String[] args) throws IOException {
        //文件--》程序:
        //1.有一个文件:----》创建一个File类的对象
        File f = new File("d:\\Test.txt");
        //2.利用FileReader这个流,这个“管”怼到源文件上去   ---》创建一个FileReader的流的对象
        FileReader fr = new FileReader(f);
        //3.进行操作“吸”的动作  ---》读取动作
        /*下面的代码我们验证了:如果到了文件的结尾处,那么读取的内容为-1
        int n1 = fr.read();
        int n2 = fr.read();
        int n3 = fr.read();
        int n4 = fr.read();
        int n5 = fr.read();
        int n6 = fr.read();
        System.out.println(n1);
        System.out.println(n2);
        System.out.println(n3);
        System.out.println(n4);
        System.out.println(n5);
        System.out.println(n6);*/
        //方式1:
        /*int n = fr.read();
        while(n!=-1){
            System.out.println(n);
            n = fr.read();
        }*/
        //方式2:
        int n;
        while((n = fr.read())!=-1){
            System.out.println((char)n);
        }
        //4.“管”不用了,就要关闭  ---》关闭流
        //流,数据库,网络资源,靠jvm本身没有办法帮我们关闭,此时必须程序员手动关闭:
        fr.close();
    }
一次性读取多个字符(利用缓冲数组):
 public static void main(String[] args) throws IOException {
        //文件--》程序:
        //1.创建一个File类的对象
        File f = new File("d:\\Test.txt");
        //2.创建一个FileReader的流的对象
        FileReader fr = new FileReader(f);
        //3.读取动作
        //引入一个“快递员的小车”,这个“小车”一次拉5个快递:
        char[] ch = new char[5];//缓冲数组
        int len = fr.read(ch);//一次读取五个:返回值是这个数组中 的有效长度
        while(len!=-1){
            //System.out.println(len);
            //错误方式:
            /*for (int i = 0 ;i < ch.length;i++){
                System.out.println(ch[i]);
            }*/
            //正确方式:
            /*for (int i = 0 ;i < len;i++){
                System.out.println(ch[i]);
            }*/
            //正确方式2:将数组转为String:
            String str = new String(ch,0,len);
            System.out.print(str);
            len = fr.read(ch);
        }
功能分解2:程序=》文件 FileWriter
一个字符一个字符的向外输出:
public static void main(String[] args) throws IOException {
        //1.有个目标文件:
        File f = new File("d:\\demo.txt");
        //2.FileWriter管怼到文件上去:
        FileWriter fw = new FileWriter(f);
        //3.开始动作:输出动作:
        //一个字符一个字符的往外输出:
        String str = "hello你好";
        for (int i = 0 ;i < str.length();i++){
            fw.write(str.charAt(i));
        }
        //4.关闭流:
        fw.close();
    }
利用缓冲数组:
public static void main(String[] args) throws IOException {
        //1.有个目标文件:
        File f = new File("d:\\demo.txt");
        //2.FileWriter管怼到文件上去:
        FileWriter fw = new FileWriter(f,true);
        //3.开始动作:输出动作:
        //一个字符一个字符的往外输出:
        String str = "你好中国";
        char[] chars = str.toCharArray();
        fw.write(chars);
        //4.关闭流:
        fw.close();
    }
注意:

如果目标文件不存在的话,那么会自动创建此文件。

如果目标文件存在的话:

new FileWriter(f) 相当于对原文件进行覆盖操作。

new FileWriter(f,false) 相当于对源文件进行覆盖操作。不是追加。

new FileWriter(f,true) 对原来的文件进行追加,而不是覆盖。

一起用:
public static void main(String[] args) throws IOException {
        //1.有一个源文件
        File f1 = new File("d:\\Test.txt");
        //2.有一个目标文件:
        File f2 = new File("d:\\Demo.txt");
        //3.搞一个输入的管 怼到源文件上:
        FileReader fr = new FileReader(f1);
        //4.搞一个输出的管,怼到目标文件上:
        FileWriter fw = new FileWriter(f2);
        //5.开始动作:
        //方式1:一个字符一个字符的复制:
        /*int n = fr.read();
        while(n!=-1){
            fw.write(n);
            n = fr.read();
        }*/
        //方式2:利用缓冲字符数组:
        /*char[] ch = new char[5];
        int len = fr.read(ch);
        while(len!=-1){
            fw.write(ch,0,len);//将缓冲数组中有效长度写出
            len = fr.read(ch);
        }*/
        //方式3:利用缓冲字符数组,将数组转为String写出。
        char[] ch = new char[5];
        int len = fr.read(ch);
        while(len!=-1){
            String s = new String(ch,0,len);
            fw.write(s);
            len = fr.read(ch);
        }
        //6.关闭流:(关闭流的时候,倒着关闭,后用先关)
        fw.close();
        fr.close();
    }
注:
  • 文本文件:.txt .java .c .cpp 建议使用字符流操作

  • 非文本文件:.jpg, .mp3 , .mp4 , .doc , .ppt 建议使用字节流操作

  • 文件是utf-8进行存储的,所以英文字符 底层实际占用1个字符,但是中文字符,底层实际占用3个字节。

  • read方法底层做了处理,让返回的数据都是“正数”就是为了避免如果字节返回的是-1的话,那到底是读入的字节,还是到文件结尾

字节流:

功能分解1:文件=》程序 FileInputStream
一个字节一个字节的读取:
public static void main(String[] args) throws IOException {
        //功能:利用字节流将文件中内容读到程序中来:
        //1.有一个源文件:
        File f = new File("D:\\LOL.jpg");
        //2.将一个字节流这个管 怼  到 源文件上:
        FileInputStream fis = new FileInputStream(f);
        //3.开始读取动作
        int count = 0;//定义一个计数器,用来计读入的字节的个数
        int n = fis.read();
        while(n!=-1){
            count++;
            System.out.println(n);
            n = fis.read();
        }
        System.out.println("count="+count);
        //4.关闭流:
        fis.close();
    }
利用缓冲数组:
public static void main(String[] args) throws IOException {
        //功能:利用字节流将文件中内容读到程序中来:
        //1.有一个源文件:
        File f = new File("D:\\LOL.jpg");
        //2.将一个字节流这个管 怼  到 源文件上:
        FileInputStream fis = new FileInputStream(f);
        //3.开始读取动作
        //利用缓冲数组:(快递员的小车)
        byte[] b = new byte[1024*6];
        int len = fis.read(b);//len指的就是读取的数组中的有效长度
        while(len!=-1){
            //System.out.println(len);
            for(int i = 0;i<len;i++){
                System.out.println(b[i]);
            }
            len = fis.read(b);
        }
        //4.关闭流:
        fis.close();
    }
复制
一个字节一个字节的写出:
public static void main(String[] args) throws IOException {
        //功能:完成图片的复制:
        //1.有一个源图片
        File f1 = new File("d:\\LOL.jpg");
        //2.有一个目标图片:
        File f2 = new File("d:\\LOL2.jpg");
        //3.有一个输入的管道 怼 到 源文件:
        FileInputStream fis = new FileInputStream(f1);
        //4.有一个输出的管道 怼到  目标文件上:
        FileOutputStream fos = new FileOutputStream(f2);
        //5.开始复制:(边读边写)
        int n = fis.read();
        while(n!=-1){
            fos.write(n);
            n = fis.read();
        }
        //6.关闭流:(倒着关闭流,先用后关)
        fos.close();
        fis.close();
    }

利用缓冲字节数组:

public static void main(String[] args) throws IOException {
        //功能:完成图片的复制:
        //1.有一个源图片
        File f1 = new File("d:\\LOL.jpg");
        //2.有一个目标图片:
        File f2 = new File("d:\\LOL2.jpg");
        //3.有一个输入的管道 怼 到 源文件:
        FileInputStream fis = new FileInputStream(f1);
        //4.有一个输出的管道 怼到  目标文件上:
        FileOutputStream fos = new FileOutputStream(f2);
        //5.开始复制:(边读边写)
        //利用缓冲数组:
        byte[] b = new byte[1024*8];
        int len = fis.read(b);
        while(len!=-1){
            fos.write(b,0,len);
            len = fis.read(b);
        }
        //6.关闭流:(倒着关闭流,先用后关)
        fos.close();
        fis.close();
    }

缓冲字节流:

复制文件的普通方式(一个字节一个字节的来):

利用缓冲字节数组:

利用缓冲区:

要完成上面的功能,就需要映入新的流,即在FileInputStream,FileOutputStream外面再套一层流,即BufferedInputStream,BufferedOutputStream--->处理流

实现:

public static void main(String[] args) throws IOException {
        //1.有一个源图片
        File f1 = new File("d:\\LOL.jpg");
        //2.有一个目标图片:
        File f2 = new File("d:\\LOL2.jpg");
        //3.有一个输入的管道 怼 到 源文件:
        FileInputStream fis = new FileInputStream(f1);
        //4.有一个输出的管道 怼到  目标文件上:
        FileOutputStream fos = new FileOutputStream(f2);
        //5.功能加强,在FileInputStream外面套一个管:BufferedInputStream:
        BufferedInputStream bis = new BufferedInputStream(fis);
        //6.功能加强,在FileOutputStream外面套一个管:BufferedOutputStream:
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //7.开始动作 :
        byte[] b = new byte[1024*6];
        int len = bis.read(b);
        while(len!=-1){
            bos.write(b,0,len);
           /* bos.flush(); 底层已经帮我们做了刷新缓冲区的操作,不用我们手动完成:底层调用flushBuffer()*/
            len = bis.read(b);
        }
        //8.关闭流:
        //倒着关:
        //如果处理流包裹着节点流的话,那么其实只要关闭高级流(处理流),那么里面的字节流也会随之被关闭。
        bos.close();
        bis.close();
        /*fos.close();
        fis.close();*/
    }
}

缓冲字符流:

public static void main(String[] args) throws IOException {
        //1.有一个源文件:
        File f1 = new File("d:\\Test.txt");
        //2.有一个目标文件:
        File f2 = new File("d:\\Demo.txt");
        //3.需要一个管 怼到 源文件:
        FileReader fr = new FileReader(f1);
        //4.需要一根管怼到目标文件:
        FileWriter fw = new FileWriter(f2);
        //5.套一根管在输入字符流外面:
        BufferedReader br = new BufferedReader(fr);
        //6.套一根管在输出字符流外面:
        BufferedWriter bw = new BufferedWriter(fw);
        //7.开始动作:
        //方式1:读取一个字符,输出一个字符:
        /*int n = br.read();
        while(n!=-1){
            bw.write(n);
            n = br.read();
        }*/
        //方式2:利用缓冲数组:
        /*char[] ch = new char[30];
        int len = br.read(ch);
        while(len!=-1){
            bw.write(ch,0,len);
            len = br.read(ch);
        }*/
        //方式3:读取String:
        String str = br.readLine();//每次读取文本文件中一行,返回字符串
        while(str!=null){
            bw.write(str);
            //在文本文件中应该再写出一个换行:
            bw.newLine();//新起一行
            str = br.readLine();
        }
        //8.关闭流
        bw.close();
        br.close();
    }

转换流:

作用:将字节流和字符流进行转换

InputStreamReader:字节输入流->字符的输入流

OutputStreamWriter:字符输出流->字节的输出流

注:

转换流属于处理流

输入字节流转换为输入字符流示例:

public static void main(String[] args) throws IOException {
        //文件---》程序:
        //1.有一个源文件:
        File f = new File("d:\\Test.txt");
        //2.需要一个输入的字节流接触文件:
        FileInputStream fis = new FileInputStream(f);
        //3.加入一个转换流,将字节流转换为字符流:(转换流属于一个处理流)
        //将字节转换为字符的时候,需要指定一个编码,这个编码跟文件本身的编码格式统一
        //如果编码格式不统一的话,那么在控制台上展示的效果就会出现乱码
        //InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        //获取程序本身的编码--》utf-8
        InputStreamReader isr = new InputStreamReader(fis);
        //4.开始动作,将文件中内容显示在控制台:
        char[] ch = new char[20];
        int len = isr.read(ch);
        while(len!=-1){
            //将缓冲数组转为字符串在控制台上打印出来
            System.out.print(new String(ch,0,len));
            len = isr.read(ch);
        }
        //5.关闭流:
        isr.close();
    }

完成文件复制:

public static void main(String[] args) throws IOException {
        //1.有一个源文件
        File f1 = new File("d:\\Test.txt");
        //2.有一个目标文件:
        File f2 = new File("d:\\Demo.txt");
        //3.输入方向:
        FileInputStream fis = new FileInputStream(f1);
        InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        //4.输出方向:
        FileOutputStream fos = new FileOutputStream(f2);
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
        //5.开始动作:
        char[] ch = new char[20];
        int len = isr.read(ch);
        while(len!=-1){
            osw.write(ch,0,len);
            len = isr.read(ch);
        }
        //6.关闭流:
        osw.close();
        isr.close();
    }

System类对IO流的支持:

System.in:”标准“输入流。默认情况下,从键盘输入

public static void main(String[] args) throws IOException {
    InputStream in = System.in;
    //得到的是标准的输入流:从键盘输入
    //调用方法:
    int n = in.read();//read方法等待键盘的录入,所以这个方法是一个阻塞方法
    System.out.println(n);
    //形象的理解:System.in这根管怼到键盘上去了
    //Scanner的作用:扫描器,起扫描作用,扫描键盘这跟管出来的数据
}
对Scanner补充:
//既然Scanner是扫描的作用,不一定只能扫System.in进来的东西,还可以扫描其他的内容:
Scanner sc = new Scanner(new FileInputStream(new File("d:\\Test.txt")));
while(scs.hasNext()){
    System.out.println(sc.next())
}

System.out:”标准“的输出流。默认情况下,输出到控制台。

返回输出流、打印流(PrintStream)

public static void main(String[] args) {
        //写到控制台:
        PrintStream out = System.out;
        //调用方法:
        out.print("你好1");//直接在控制台写出,但是不换行
        out.print("你好2");
        out.print("你好3");
        out.print("你好4");
        out.println("我是中国人1");//直接在控制台写出,并且换行操作
        out.println("我是中国人2");
        out.println("我是中国人3");
        out.println("我是中国人4");
        System.out.println("你是");
        System.out.print("中国人");
    }

练习:键盘录入内容输出到文件中

思路:

实现:

public static void main(String[] args) throws IOException {
        //1.先准备输入方向:
        //键盘录入:
        InputStream in = System.in;//属于字节流
        //字节流--》字符流:
        InputStreamReader isr = new InputStreamReader(in);
        //在isr外面再套一个缓冲流:
        BufferedReader br = new BufferedReader(isr);
        //2.再准备输出方向:
        //准备目标文件
        File f = new File("d:\\Demo1.txt");
        FileWriter fw = new FileWriter(f);
        BufferedWriter bw = new BufferedWriter(fw);
        //3.开始动作:
        String s = br.readLine();
        while(!s.equals("exit")){
            bw.write(s);
            bw.newLine();//文件中换行
            s = br.readLine();
        }
        //4.关闭流:
        bw.close();
        br.close();
    }

数据流:

用来操作基本数据类型字符串

DataInputStream将文件中存储的基本数据类型和字符串写入内存变量中

DataOutputStream将内存中的基本数据类型和字符串的变量写出到文件中

利用DataOutputStream向外写出变量:

public static void main(String[] args) throws IOException {
        //DataOutputStream:  将内存中的基本数据类型和字符串的变量 写出  文件中
        /*File f = new File("d:\\Demo2.txt");
        FileOutputStream fos = new FileOutputStream(f);
        DataOutputStream dos = new DataOutputStream(fos);*/
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("d:\\Demo2.txt")));
        //向外将变量写到文件中去:
        dos.writeUTF("你好");
        dos.writeBoolean(false);
        dos.writeDouble(6.9);
        dos.writeInt(82);
        //关闭流:
        dos.close();
    }

我们可以发现,这个内容是我们看不懂的,是给程序看的

所以我们可以通过下面的程序来进行读取:

public static void main(String[] args) throws IOException {
        //DataInputStream:将文件中存储的基本数据类型和字符串  写入  内存的变量中
        DataInputStream dis = new DataInputStream(new FileInputStream(new File("d:\\Demo2.txt")));
        //将文件中内容读取到程序中来:
        System.out.println(dis.readUTF());
        System.out.println(dis.readBoolean());
        System.out.println(dis.readDouble());
        System.out.println(dis.readInt());
        //关闭流:
        dis.close();
    }

结果:

写出类型跟读入的类型必须要匹配

对象流:

用于存储和读取基本数据类型对象的处理流,它的强大之处在于可以把Java中的对象写入到数据源中,也能把对象从数据源中还原

序列化和反序列化:

ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。----》序列化

用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。----》反序列化

序列化实现:

public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo3.txt")));
        //将内存中的字符串写出到文件中:
        oos.writeObject("你好");
        //关闭流:
        oos.close();
    }

结果:

反序列化实现:

public static void main(String[] args) throws IOException, ClassNotFoundException {
        //将文件中保存的字符串 读入到 内存:
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo3.txt")));
        //读取:
        String s = (String)(ois.readObject());
        System.out.println(s);
        //关闭流:
        ois.close();
    }
}

控制台结果:

操作自己定义类的对象:

自定义Person:

public class Person {
    private String name;
    private int age;
    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 Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

测试:

public static void main(String[] args) throws IOException {
        //序列化:将内存中对象 ---》 文件:
        //有一个对象:
        Person p = new Person("lili",19);
        //有对象流:
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt")));
        //向外写:
        oos.writeObject(p);
        //关闭流:
        oos.close();
    }

运行时出现异常:

出现异常的原因:

想要序列化的那个对象的类,必须要实现一个接口

public interface Serializable{
}

接口内部什么都没有,这种借口叫标识接口。起标识作用。只有实现这个接口的对象才能序列化,否则不可以序列化

Person实现这个接口后:

public class Person implements Serializable {
    private String name;
    private int age;
    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 Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

序列化成功,Person具备了序列化的能力。

序列化后的二进制数据人无法识别,利用程序实现反序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo4.txt")));
        //读入内存:
        Person p = (Person)(ois.readObject());
        System.out.println(p/*.toString()*/);
        //关闭流:
        ois.close();
    }

结果:

反序列化成功,但是因为没有写toString方法,所以结果是这样的

serialVersionUID:

凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:

  • private static final long serialVersionUID;

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

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

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

现在在Person类中加入toString方法:

public class Person implements Serializable {
    private String name;
    private int age;
    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 Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

再次运行出现异常:

出现异常的原因序列化时的Person类没有toString方法,现在Person中加入了toString方法,然后进行反序列化,造成了这两个类的不匹配问题

解决:给这个类加一个序列号:serialVersionUID

private static final long serialVersionUID = 1223L;

IDEA中配置序列化版本号:

在Person类上:alt+enter即可生成

序列化注意事项:

  1. 被序列化的类的内部的所有属性1,必须是可序列化的(基本数据类型都是可序列化的)

  1. static、transient修饰的属性不可以被序列化

public class Person implements Serializable {
    private static final long serialVersionUID = 8027651838638826533L;
    private transient String name;
    private static int age;
    private Famaily f = new Famaily();
    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 Person() {
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", f=" + f + ",age=" + age +
                '}';
    }
}

结果:

Java中的IO分为输入和输出,其中输入用于读取数据,输出用于写入数据。我们可以通过使用Java IO来读取或写入文件、网络通信、内存缓冲区等。下面分别对Java IO中的输入和输出进行详细讲解。 1. Java输入 Java中的输入用于读取数据,分为字节输入和字符输入两种。 字节输入:InputStream InputStream是Java中所有字节输入的父类,主要提供了读取字节的方法,包括: - int read():读取一个字节的数据并返回,如果已经到达输入的末尾,则返回-1; - int read(byte[] b):读取b.length个字节的数据,并存储到数组b中,返回实际读取的字节数,如果已经到达输入的末尾,则返回-1; - int read(byte[] b, int off, int len):读取len个字节的数据,并存储到数组b中从off位置开始的地方,返回实际读取的字节数,如果已经到达输入的末尾,则返回-1。 字符输入:Reader Reader是Java中所有字符输入的父类,主要提供了读取字符的方法,包括: - int read():读取一个字符并返回,如果已经到达输入的末尾,则返回-1; - int read(char[] c):读取c.length个字符的数据,并存储到数组c中,返回实际读取的字符数,如果已经到达输入的末尾,则返回-1; - int read(char[] c, int off, int len):读取len个字符的数据,并存储到数组c中从off位置开始的地方,返回实际读取的字符数,如果已经到达输入的末尾,则返回-1。 2. Java输出 Java中的输出用于写入数据,分为字节输出和字符输出两种。 字节输出:OutputStream OutputStream是Java中所有字节输出的父类,主要提供了写入字节的方法,包括: - void write(int b):写入一个字节的数据; - void write(byte[] b):写入数组b中所有的字节数据; - void write(byte[] b, int off, int len):写入数组b中从off位置开始的len个字节数据; - void flush():刷新该的缓冲区,确保所有缓存的字节都被写出到目标设备中; - void close():关闭输出。 字符输出:Writer Writer是Java中所有字符输出的父类,主要提供了写入字符的方法,包括: - void write(int c):写入一个字符; - void write(char[] c):写入数组c中所有的字符数据; - void write(char[] c, int off, int len):写入数组c中从off位置开始的len个字符数据; - void flush():刷新该的缓冲区,确保所有缓存的字符都被写出到目标设备中; - void close():关闭输出。 以上就是Java IO详细讲解,希望对你有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值