Java IO流

2021/6/6 14:23:03

本文主要是介绍Java IO流,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

IO流

1. IO流概述

1.1 什么是IO流

  • I:Input
  • O:Output
  • 通过IO可以完成硬盘文件的读写

1.2 IO流的分类

  • 分类方式
    • 按照流的方向进行分类
      • 以内存作为参照物
        • 输入流:
          • 往内存中去叫做输入(Input )或读(Read)
        • 输出流:
          • 从内存中取出浇输出(Output)或写(Write)
    • 按照读取数据的方式不同分类
      • 字节流:
        • 按照字节的方式读取数据,一次读一个字节(byte)即8个二进制位
        • 这种流是万能的,可以读取任何文件(文本文件,图片,声音等)
      • 字符流:
        • 按照字符的方式读取,一次读取一个字符(为了方便读取普通文本文件而存在)
        • 只能读取纯文本文件(能用普通记事本编辑的文件)
  • 综上,流分为
    • 输入,输出流
    • 字符流,字节流

1.3 Java中的IO流

  • 所有的流都在java.util.*下
  • 只需要学习如何new对象,每个对象的功能即可
  • IO过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHMm5aL1-1622954450105)(/Users/mac/Library/Containers/com.tencent.qq/Data/Library/Caches/Images/1785058FC34814F73BD7D6E40F560262.jpg)]

1.3.1 Java中IO流的四大家族

  • java.io.InputStream 字节输入流

  • java.io.OutputStream 字节输出流

  • java.io.Reader 字符输入流

  • java.io.Writer 字符输出流

四大家族的首领都是抽象类(abstract class)

  • 所有流都实现了

    • java.io.Closeable接口,都是可关闭的,都有clos()方法
    • 流是一个管道,用完需要关闭,这样就不会占用资源
  • 所以的输出流都实现了

    • java.io.Flushable接口,都是可刷新的,都有flush()方法
    • 流是一个管道,输出后管道内会有残留,需要flush()方法来冲刷,强行输出完,否则数据会部分丢失
  • 注意

    • Java中类名以Stream结尾的都是字节流
    • 以Reader/Writer结尾的都是字符流

1.3.2 java.io包下需要掌握的16个流

  • 文件专属

    • java.io.FileInputStream(重点掌握)
    • java.io.FileOutputStream(重点掌握)
    • java.io.FileReader
    • java.io.FileWriter
  • 转换流:(将字节流转换为字符流)

    • java.io.InputStreamReader
    • java.io.OutputStreamWriter
  • 缓冲流专属

    • java.io.BufferedReader
    • java.io.BufferedWriter
    • java.io.BufferedInputStream
    • java.io.BufferedOutputStream
  • 数据流专属

    • java.io.DataInputStream
    • java.io.dataoutputStream
  • 标准输出流

    • java.io.PrintWriter
    • java.io.PrintStream
  • 对象专属流

    • java.io.ObjectInputStream(重点掌握)
    • java.io.ObjectOutputStream(重点掌握)

2. FileInputStream的使用

  • java.io.FileInputStream
    • 文件字节输入流,万能的,任何类型的文件都可以读
    • 字节的方式完成输入的操作,完成读的操作
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Main {
    /*
        java.io.FileInputStream:
     */
    public static void main(String[] args) {
        //创建文件字节输入流对象
        FileInputStream fis=null;
        try {
            fis=new FileInputStream("/Users/mac/Desktop/draft/hello.txt");
            while (true){
                int readData=fis.read();//返回值为读到的字节本身,读之前会移动到下一个字节(移动,读,移动,读)
              	if(readData==-1){//如果没有内容会返回-1,退出循环
                    break;
                }
                System.out.println(readData);
            }
          /*
          	改造while循环
          	
          	int readData=0;
            while ((readData=fis.read())!=-1){
                System.out.println(readData);
            }
          */
          
        } catch (FileNotFoundException e) {
            e.printStackTrace();
          
        } catch (IOException e) {
            e.printStackTrace();
          
        } finally {
            if (fis != null) {//空的流不用关,避免空指针异常
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
  • 往byte数组中读数据
fis=new FileInputStream("/Users/mac/Desktop/draft/hello.txt");//文件内容:abcdef
byte[] bytes=new byte[4];//准备一个byte数组,一次最多读四个字节(自定义的)
int readCount=fis.read(bytes);//返回的是读取的长度
System.out.println(readCount);//4  
System.out.println(new String(bytes));//abcd
System.out.println(new String(bytes,0,readCount));//abcd,从0开始读readCount个字节

readCount=fis.read(bytes);
System.out.println(readCount);//2
System.out.println(new String(bytes));//efcd 把原来的ab覆盖,cd还在
System.out.println(new String(bytes,0,readCount));//ef

readCount=fis.read(bytes);
System.out.println(readCount);//-1 读不到返回-1
  • FileInputStream最终版
fis=new FileInputStream("/Users/mac/Desktop/draft/hello.txt");
byte[] bytes=new byte[4];
int readCount=0;
while((readCount=fis.read(bytes))!=-1){//读一次转一次,如果没读到(返回-1)就退出循环
   System.out.print(new String(bytes,0,readCount));//最终读完文件所有数据
}
  • 其它方法
    • available()获取目前可读的字节数
    • skip() 跳过几个字节不读
fis=new FileInputStream("/Users/mac/Desktop/draft/hello.txt");
byte[] bytes=new byte[fis.available()];//创建一个和目前文件大小一样的数组,一次性读完
int readCount=fis.read(bytes);
System.out.println(new String(bytes,0,readCount));

3. FileOutputStream的使用

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

public class Main {
    public static void main(String[] args) {
        FileOutputStream fos=null;
        try {
            //在append参数上加true,不会清空原来文件,否则会清空原来的文件在写入
            //文件不存在会新建
            fos=new FileOutputStream("/Users/mac/Desktop/draft/hh.txt",true);
            byte[] bytes={97,98,99,100};
            //将byte数组全部写出
            fos.write(bytes);//abcd
            //将byte数组部分写出
            fos.write(bytes,0,2);//ab

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

    }
}

3.1 文件的复制

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

public class Main {
    public static void main(String[] args) {
        FileInputStream fis=null;
        FileOutputStream fos=null;
        try {
            //创建一个输入流,读文件
            fis=new FileInputStream("/Users/mac/Desktop/draft/hh.txt");
            //创建一个输出流,写文件
            fos=new FileOutputStream("/Users/mac/Desktop/draft/hhh.txt");
            

            //复制就是一边读一边写
            byte[] bytes=new byte[1024];//一次读1KB
            int readCount=0;
            //边读边写
            while ((readCount=fis.read(bytes))!=-1){
                fos.write(bytes,0,readCount);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //分开try,否则上面关闭错误,进入catch语句块,导致下面读没关
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4. FileReader和FileWriter

4.1 FileReader

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

public class Main {
    public static void main(String[] args) {
        FileReader reader=null;
        try {
            reader=new FileReader("/Users/mac/Desktop/draft/hhh.txt");
            char[] chars=new char[4];//一次读取四个字符

            /*reader.read(chars); 读入数组,不接收返回值,用foreach输出
            for(char c:chars){
                System.out.println(c);
            }*/
            
            int readCount=0;
            while ((readCount=reader.read(chars))!=-1){//边读边输出
                System.out.print(new String(chars,0,readCount));
            }


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

    }
}

4.2 FileWriter

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

public class Main {
    public static void main(String[] args) {
        FileWriter out=null;
        try {
            out=new FileWriter("/Users/mac/Desktop/draft/hhh.txt");
            //创建char数组
            char[] chars={'我','是','中','国','人'};
            out.write(chars);//全部写出
            out.write(chars,2,3);//从2开始写出三个字符
            out.write("我是一名Java软件工程师!");//写出字符串
            out.write("\n");//写出换行符
            out.write("hello world");

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

    }
}

4.3 普通文本文件的复制

import java.io.*;

public class Main {
    public static void main(String[] args) {
        FileReader in=null;
        FileWriter out=null;
        try {
            //创建字符流
            in=new FileReader("/Users/mac/Desktop/draft/hh.txt");
            out=new FileWriter("/Users/mac/Desktop/draft/copy.txt");

            //创建缓冲数组
            char[] chars=new char[1024*512];//一次读1MB

            int readCount=0;
            while ((readCount=in.read(chars))!=-1){//读入数组,返回读入字节数
                out.write(chars,0,readCount);//数组中数据写出到文件
            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } 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();
                }
            }
        }



    }
}

5. 带有缓冲区的字符流

  • 不需要定义byte或char数组,自带缓冲
  • 传入一个字符流,BufferedReader用完会自动把流关闭
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class Main {
    public static void main(String[] args) throws Exception {
        //创建字符输入流
        FileReader reader=new FileReader("/Users/mac/Desktop/draft/hh.txt");
        //创建自带缓冲的字符输入流
        //这里传入构造方法的字符输入流(FileReader),称为节点流
        //而负责包装的这个流称为包装/处理流(BufferedReader)
        BufferedReader br=new BufferedReader(reader);

        String s=null;
        //一次读一行,不带换行符,读完后,没有内容,返回null
        while ((s=br.readLine())!=null){
            System.out.println(s);
        }

    }
}

5.1 节点流和包装流

  • 两个节点流,两个包装流
  • FileInputStream相对于InputStreamReader,FileInputStream是节点流,InputStreamReader是包装流
  • InputStreamReader相对于BufferedReader,InputStreamReader是节点流,BufferedReader是包装流
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        //创建字节流
        FileInputStream fis=new FileInputStream("/Users/mac/Desktop/draft/hh.txt");
        //转换流
        InputStreamReader isr=new InputStreamReader(fis);
        //缓冲字符流
        BufferedReader br=new BufferedReader(isr);

        String line=null;
        while ((line=br.readLine())!=null){
            System.out.println(line);
        }
    }
}

6. 数据流

6.1 DataOutputStream

  • 可以将数据连同数据类型一起写入文件
  • 不是普通文件,是加密后的文件(记事本打不开),读的顺序必须和写入顺序一致
import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class Main {
    public static void main(String[] args) throws Exception {
        //创建数据流
        DataOutputStream dos=new DataOutputStream(new FileOutputStream("data"));
        //写数据
        byte b=100;
        short s=200;
        int i=300;
        long l=400;
        float f=500;
        double d=600;
        boolean bo=true;
        char c='a';
        
        dos.writeByte(b);
        dos.writeShort(s);
        dos.writeInt(i);
        dos.writeLong(l);
        dos.writeFloat(f);
        dos.writeDouble(d);
        dos.writeBoolean(bo);
        dos.writeChar(c);
        
        //刷新
        dos.flush();
        //关闭流
        dos.close();
    }
}

6.2 DataInputStream

import java.io.DataInputStream;
import java.io.FileInputStream;

public class Main {
    public static void main(String[] args) throws Exception{
        DataInputStream dis=new DataInputStream(new FileInputStream("data"));
        
        //读数据
        byte b=dis.readByte();
        short s=dis.readShort();
        int i=dis.readInt();
        long l=dis.readLong();
        float f=dis.readFloat();
        double d=dis.readDouble();
        boolean bo=dis.readBoolean();
        char c=dis.readChar();
        
        //输出
        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
        System.out.println(l);
        System.out.println(f);
        System.out.println(d);
        System.out.println(b);
        System.out.println(c);
    }
}

7. 标准字节输出流

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Main {
    public static void main(String[] args) throws Exception {
        //合起来写
        System.out.println("hello");
        //分开写
        //System.out返回一个PrintStream对象
        PrintStream ps=System.out;
        ps.println("hello");

        //标准输出流指向文件,不指向控制台
        PrintStream printStream=new PrintStream(new FileOutputStream("/Users/mac/Desktop/draft/hhhh.txt"));
        //修改输出方向为文件
        System.setOut(printStream);
        //输出到文件中
        System.out.println("hello");

    }
}

7.1 利用标准字节输出流编写的日志工具

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class LogTool {
    public static void log(String msg) {
        try {
            //创建标准输出流,指向文件,使用追加方式,否则第二次调用该函数会覆盖重写
            PrintStream ps = new PrintStream(new FileOutputStream("/Users/mac/Desktop/draft/log.txt",true));
            //设置输出方向
            System.setOut(ps);
            //获取当前时间
            Date nowdate=new Date();
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss-SSS");
            String strTime=sdf.format(nowdate);

            System.out.println(strTime+":"+msg);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }
}
  • 测试
public class Main {
    public static void main(String[] args) {
        //测试工具
        LogTool.log("调用了System类的gc()方法,建议启动垃圾回收");
        LogTool.log("调用了UserService的doSome()方法");
    }
}

8. File类

8.1 File

  • File类不能完成读和写
  • File是文件和目录路径的抽象表示形式
  • File可能文件也可能是对应的目录

8.2File类的常用方法

import java.io.File;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        //创建一个File对象
        File f1=new File("/Users/mac/Desktop/draft/a/b/c");
        //判断是否存在
        System.out.println(f1.exists());

        //如果不存在,新建
        if (!f1.exists()) {
            //以文件的方式新建
            //f1.createNewFile();

            //以目录(文件夹)的方式新建
            //f1.mkdir();

            //创建多重目录
            //f1.mkdirs();
        }

        File f2=new File("/Users/mac/Desktop/draft/hhhh.txt");
        //获取文件的父路径
        String parentPath=f2.getParent();
        System.out.println(parentPath);
        //获取父文件
        File parentFile=f2.getParentFile();
        System.out.println("获取 parentFile 的绝对路径:"+parentFile.getAbsolutePath());
        System.out.println("获取 f2 的绝对路径:"+f2.getAbsolutePath());
    }
}
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        File f1=new File("/Users/mac/Desktop/draft");
        //获取文件名
        System.out.println(f1.getName());//copy.txt

        //判断是否是一个目录
        System.out.println(f1.isDirectory());//false

        //判断是否是一个文件
        System.out.println(f1.isFile());

        //获取文件最后一次修改时间
        long haoMiao=f1.lastModified();
        System.out.println(haoMiao);//1621843065077 1970年到现在的总毫秒数
        //将毫秒转换为日期
        Date time=new Date(haoMiao);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy 年 MM 月 dd 日 hh 时 mm 分 ss 秒 SSS 毫秒");
        String modifytime=sdf.format(time);
        System.out.println(modifytime);//2021 年 05 月 24 日 03 时 57 分 45 秒 077 毫秒(不一定是现在的时间)

        //获取文件的大小
        System.out.println(f1.length());//953字节

        //获取当前目录下所有子文件
        File[] files=f1.listFiles();
        //打印目录下所有文件名
        for(File file:files){
            System.out.println(file.getName());
        }
    }
}

9. 序列化与反序列化

9.1 序列化

  • 参与序列化和反序列化的对象必须实现Serializable接口

  • Serializable接口起标志作用,内容是空的

    • 这个接口是给java虚拟机参考的
    • Java虚拟机看到这个接口,自动生成一个序列化版本号
  • 序列化版本号

    • Java根据类名区分类,如果类名相同,用序列化版本号来区分

    • 自动生成序列化版本号的缺陷:

      • 一旦代码确定后,不能进行后续的修改
      • 如果修改,编译后生成全新的 序列化版本号,虚拟机会认为是一个全新的类
    • 改进方法

      • 凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号

      • private static final long serialVersionUID=1L;
        
      • 即使修改代码,序列化版本号不变,虚拟机认为是同一个类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDkesVsA-1622954450111)(/Users/mac/Library/Containers/com.tencent.qq/Data/Library/Caches/Images/844F0189B9B64FED831607C2F6AD9982.jpg)]

  • 序列化的实现
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class Main2 {
    public static void main(String[] args) throws Exception {
        //创建Student对象
        Student s=new Student(1111,"zhangsan");
        //序列化
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Student"));
        //序列化对象
        oos.writeObject(s);
        //刷新
        oos.flush();
        //关闭
        oos.close();
    }
}
import java.io.Serializable;
//实现Serializable接口
public class Student implements Serializable {
    private int no;
    private transient String name;//游离的不参与序列化

    public Student(){

    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 序列化多个对象
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

public class Main2 {
    public static void main(String[] args) throws Exception {
        List<Student> slist=new ArrayList<>();
        slist.add(new Student(1111,"zhangsan"));
        slist.add(new Student(1112,"lisi"));
        slist.add(new Student(1113,"wangwu"));
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Student"));
        
        //序列化一个集合 
        oos.writeObject(slist);
        
        oos.flush();
        oos.close();
    }
}

9.2 反序列化

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class Main2 {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Student"));
        //开始反序列化,读
        Object obj=ois.readObject();//返回一个student对象
        System.out.println(obj);
        //关闭流
        ois.close();
    }
}
  • 反序列化多个对象
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class Main2 {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Student"));

        List<Student> studentlist=(List<Student>) ois.readObject();
      
        for(Student s:studentlist){
            System.out.println(s);
        }
        
        ois.close();
    }
}

10. IO和Properties联合使用

  • 配置文件
    • 经常改变的数据写到一个文件中,用程序动态获取
  • 属性配置文件
    • 配置文件中的格式为 key=value 的格式,称为属性配置文件
    • 建议以.properties结尾(不是必须的,只是这样写比较规范)
    • Properties是专门存放属性配置文件的一个类
    • 属性配置文件中#是注释
    • key 重复 value 自动覆盖
    • key和value中的值不要加空格
import java.io.FileReader;
import java.util.Properties;


public class Main {
    public static void main(String[] args) throws Exception {
        //创建字符输入流
        FileReader fr=new FileReader("/Users/mac/IdeaProjects/333/src/userinfo.properties");
        //创建一个Map集合
        Properties pro=new Properties();
        //调用Properties集合中的load方法将文件中的数据加载到Map集合中
        pro.load(fr);
        //通过key获取value
        String s1=pro.getProperty("username");
        System.out.println(s1);
        String s2=pro.getProperty("password");
        System.out.println(s2);

        fr.close();
    }


这篇关于Java IO流的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程