Java IO流 知识点整理

        

1、IO流,什么是IO流?


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

2、IO流的分类?


   有多种分类方式:
    
    一种方式是按照流的方向进行分类:
       以内存作为参照物
        往内存中去,叫做输入(Input)。或者叫做读(Read)
        从内存中出来,叫做输出(Output)。或者叫做写(Write)
    
    一种方式是按照读取数据方式不同进行分类:
       有的流是按照字节的方式读取数组,一次读取1个字节byte,等同于一次读取8个二进制
       这种流是万能的,什么类型的文件都可以读取,包括:文本文件,图片,声音文件,视频文件,等等
           假设文件file1.txt,采用字符流的话是这样读的:
            a中国bc张三fe
            第一次读:一个字节,正好读到'a'
            第二次读:一个字节,读到'中'的一半
            第三次读:一个字节,读到'中'的另一半    
       有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本而存在的
       这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取
        假设文件file1.txt,采用字符流的话是这样读的:
            a中国bc张三fe
            第一次读:'a'字符('a'字符在windows系统中占用1个字节)
            第二次读:'中'字符('中'字符在windows系统中占用2个字节)

3、java IO流这块有四大家族:


    
    java.io.InputStream 字节输入流
    java.io.OutputStream 字节输出流

    java.io.Reader 字符输入流
    java.io.Writer 字符输出流

    四大家族的首领都是抽象类
    所有的流都实现了:
         java.io.Closeable接口,都是可关闭的,都有close()方法
        流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费很多            资源
    所有输出流都实现了:
        java.io.Flushable接口,都是可刷新的,都有flush()方法。输出流在最终输出以后
        一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余为输出的数据强行输出完
        刷新的作用就是清空管道
        注意:如果没有flush()可能会导致丢失数据

    注意:在java中只要“Stream”为结尾的都是字节流,以“Reader/Writer”为结尾的都是字符流

4、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

(1)java.io.FileInputStream         

        1、文件字节输入流,万能的,任何类型的文件都可以采用这个流来读

        2、字节的方式,完成输入的操作,完成读的操作(硬盘--->内存)

        3、read方法进行读取,返回它的编码值(这种方法效率低,频繁的内存和硬盘之间的交互)

        4、将数据读取到数组当中,返回一个读取多少数据

        5、int available() :返回流当中剩余的没有读到的字节数量

        6、long skip(long n):跳过几个字节不读       

        7、String 中有一个构造方法 传一个byte数组 String s = new String(bytes); 可以将获得的编                  码值转换为字符串

public class FileInputStreamTest04 {
    public static void main(String[] args) {
        //这个就相当于和硬盘中的文件有了一个桥梁 通道
        FileInputStream fis = null;

        try {
            fis= new FileInputStream("lianxi");
            //创建一个byte数组
            byte[] bytes = new byte[4];
            //进行读取
           /* while(true){
                int readCount = fis.read(bytes);
                if(readCount == -1){
                   break;
                }
                //将byte数组转换为String 用到String的构造方法
                //read几个数量 就输出几个
                System.out.print(new String(bytes,0,readCount));
            }*/

            //改良一下
            int readCount = 0;
            while ((readCount = fis.read(bytes)) != -1){
                System.out.print(new String(bytes,0,readCount));
            }

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

(2)java.io.FileOutputStream

        1、FileOutputStream:叫做输出流, 写

        2、如果不想要每次运行的时候将文件里面的内容清空,在创建对象的时候,后面添加一个              true        new FileOutputStream("lianxi2",true)

        3、写的时候不要忘记刷新 刷新的作用是防止通道里面有遗漏的数据

        

public class FileOutputStreamTest01 {
    public static void main(String[] args) {
        //创建输出流 写
        FileOutputStream fos = null;
        try {
            //lianxi2这个文件不存在,会自动创建的
            //这种方式谨慎使用,这种方式会将原文件清空,然后再写入
            //fos = new FileOutputStream("lianxi2");

            //这种方式以追加的方式在文件末尾写入。不会清空原文件内容,在后面添加一个true
            fos = new FileOutputStream("lianxi2",true);

            //创建一个数据
            byte[] bytes = {97,98,99,100,101,102,103};
            //进行读取
            //fos.write(bytes);
            //fos.write(bytes,0,3);
            //fos.write(bytes,0,3);

            //字符串
            String s = "我是一个中国人,我骄傲!!";
            //将字符串转换为byte数组
            byte[] bytes1 = s.getBytes();
            //将bytes1写入到文件当中
            fos.write(bytes1);





            //写的时候不要忘记刷新 刷新的作用是防止通道里面有遗漏的数据
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

(3)java.io.FileReader

        文件字符输入流,只能读取普通文本

        读取文本内容时,比较方便、便捷

        创建数组的时候 创建char类型的

public class FileReaderTest01 {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("lianxi");
            //创建数组
            char[] chars = new char[4];
            //进行循环读取数据
            int readCount = 0;
            while((readCount = fr.read(chars)) != -1){
                System.out.println(new String(chars,0,readCount));
            }

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

(4) java.io.FileWriter

        文件字符输出流。只能输出普通文本

        一定要注意:要是出现不报错,但是没有出现写的内容,有可能是没有刷新

public class FileWriterTest01 {
    public static void main(String[] args) {
        FileWriter fw = null;
        try {
            //后面加上true表示在文件后面添加东西,要是不加true的话,就会把文件清空在加
            fw = new FileWriter("lianxi2",true);
            //创建一个char数组 表示将要写的元素
            char[] chars = {'我','是','崔','铮','!'};
            //将这个char数组写进文件里面
            fw.write(chars);

            //记得要刷新
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fw == null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

        

(5)java.io.BufferedReader  

        1、带有缓冲区的字符输入流

        2、使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓冲

        3、当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流

        4、外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流

public class BufferedReaderTest01 {
    public static void main(String[] args) throws Exception{
        //当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
        //外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流
        //像当前这个程序来说:FileReader就是一个节点流,BufferedReader就是一个包装流/处理流
        FileReader reader = new FileReader("lianxi");
        BufferedReader br = new BufferedReader(reader);

        /*String s1 = br.readLine();
        System.out.println(s1);

        String s2 = br.readLine();
        System.out.println(s2);

        String s3 = br.readLine();
        System.out.println(s3);*/

        //使用while循环
        String s = null;
        while(( s = br.readLine()) != null){
            System.out.println(s);
        }



        //关闭流 只需要关闭最外面的流 里面的流不需要关闭 因为外面的流close会调用里面流的close
        br.close();
    }
}

(6)java.io.BufferedWriter

public class BufferedWriterTest01 {
    public static void main(String[] args) {
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter("BufferedWriterTest01",true));

            //进行写
            bw.write("你好啊");

            //刷新
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (bw == null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

(7) java.io.DataOutputStream

        这个流可以将数据连同数据的类型一并写入文件 

        注意:这个文件不是普通文件

public class DataOutputStreamTest01 {
    public static void main(String[] args) {
        DataOutputStream dataOutputStream = null;
        try {
            dataOutputStream = new DataOutputStream(new FileOutputStream("data",true));

            int i = 1000;
            boolean boo = true;
            double dou = 3.14;
            dataOutputStream.writeInt(i);
            dataOutputStream.writeBoolean(boo);
            dataOutputStream.writeDouble(dou);


            //刷新
            dataOutputStream.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dataOutputStream == null) {
                try {
                    dataOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

(8) java.io.DataInputStream

        1、DataOutputStream写的文件只能由DataInputStream去读,并且读的时候你需要提前知道写入的顺序

        2、读的顺序需要和写的顺序一致。才可以正常取出数据     

public class DataInputStreamTest01 {
    public static void main(String[] args) {
        DataInputStream dataInputStream = null;
        try {
            dataInputStream = new DataInputStream(new FileInputStream("data"));


            System.out.println(dataInputStream.readInt());
            System.out.println(dataInputStream.readBoolean());
            System.out.println(dataInputStream.readDouble());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dataInputStream == null) {
                try {
                    dataInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 (9)PrintStream

        1、日志工具

public class Logger {
    /*
    日志工具
     */
    public void log(String ms){
        //创建对象
        PrintStream ps = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("工作日志.txt",true);

            ps = new PrintStream(fos);

            //修改输出格式
            System.setOut(ps);
            //创建一个时间
            Date data = new Date();
            //修改日期格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
            String nowtime = sdf.format(data);
            System.out.println(nowtime + "发生了" +ms);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }
}

        2、System.setOut(ps); 更改规则,不在控制台输出,输出到log文件当中

public class PrintStreamTest01 {
    public static void main(String[] args) {
        PrintStream ps = null;
        try {
            ps = new PrintStream(new FileOutputStream("log"));

            //更改规则 不在控制台输出,输出到log文件当中
            System.setOut(ps);

            System.out.println("啊啊啊");
            System.out.println("你好啊");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

(10)java.io.ObjectOutputStream与java.io.ObjectInputStream

        1、NotSerializableException


                对象不支持序列化


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


                public interface Serializable{
                }


        这个接口当中什么代码都没有,那么它起到一个什么作用呢?
                起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个                    类进行特殊待遇


        Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类            自动生成一个序列化版本号


        3、序列化版本有什么用呢?


Student; local class incompatible: stream classdesc serialVersionUID = 8444881787634386424, local class serialVersionUID = -3527981529236755747

 java中采用什么机制来区分类的?
        第一:首先通过类名进行对比,如果类名不一样,肯定不是同一个类
        第二:如果类名一样,在怎么进行类的区别? 靠序列化版本号进行区分

对于java虚拟机来说,java虚拟机可以区分这两个类的,因为两个类都实现了Serializable接口
都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了(这是自动生成序列化版本的好处)

请思考?
自动生成序列化版本号有什么缺陷?
这种自动生成的序列化版本号一旦代码确定之后,不能进行后续的修改,因为只要修改,必定会重新
编译,此时生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(这样就不好了)

最终结论:
凡是一个类实现了Seriali接口,建议给该类提供一个固定不变的序列化

        4、代码展现

Student对象

public class Student implements Serializable {

    //java虚拟机看到Serializable接口之后,会主动生成一个序列化版本号
    //这里没有手动写出来,java虚拟机会默认提供这个序列化版本号
    //private transient int no;  transient表示游离的 不参加序列化

    //建议将序列化版本号手动的写出来。不建议自动生成
    private static final long serialVersionUID = 1L;
    private int no;
    private transient String name;
    private int age;

    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;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

进行序列化

public class ObjectOutputStreamTest02 {
    public static void main(String[] args) {
        //创建集合
        List<Student> list = new ArrayList<>();
        list.add(new Student(1,"lihua"));
        list.add(new Student(2,"zhangsan"));
        list.add(new Student(3,"lsii"));
        //序列化
        ObjectOutputStream objectOutputStream = null;
        try {
            objectOutputStream = new ObjectOutputStream(new FileOutputStream("ObjectOutputStreamTest02"));
            objectOutputStream.writeObject(list);

            //进行刷新
            objectOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

进行反序列化

public class ObjectInputStreamTest02 {
    public static void main(String[] args) {
        //反序列化
        ObjectInputStream objectInputStream = null;
        try {
            objectInputStream = new ObjectInputStream(new FileInputStream("ObjectOutputStreamTest02"));
            List<Student> list = (List<Student>) objectInputStream.readObject();
            for(Student l : list){
                System.out.println(l);
            }


        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

5、File类

        1、File类和四大家族没有什么关系,所以File类不能完成完成文                  件的读和写

        2、File对象代表什么?


                文件和目录路径的抽象表示形式
                一个File对象又可能对应的是目录,也可能是文件
                File只是一个路径名的抽象表达形式

        3、需要掌握File类中常用的方法:


                boolean createNewFile() 若是没有这个文件就创建这个文件
                boolean delete() 删除这个文件
                boolean exists() 检测文件是否存在
                String getAbsolutePath() 返回这个文件的绝对路径
                String getParent() 返回父路径的文件名字
                boolean isDirectory() 判断这个是不是目录
                boolean isFile() 判断这个是不是文件
                long lastModified() 返回此文件上次修改的时间
                File[] listFiles() 表示当前目录有哪些文件 存储到数组当中
                boolean mkdir() 创建目录
                boolean mkdirs() 创建所有目录

        4、创建目录

                

                

public class CopyDirectory {
    public static void main(String[] args) {
        //原路径
        File file1 = new File("F:\\租号");
        //目标路径
        File file2 = new File("D:\\");
        //调用方法
        copy(file1,file2);

    }

    static void copy(File file1,File file2){
        //先判断是否为文件
        if(file1.isFile()){
            copy2(file1,file2);
            return;
        }
        //将当前文件夹中的文件和文件夹放到数组当中
        File[] files = file1.listFiles();
        //遍历数组
        for(File f : files){

            if(f.isDirectory()){
                //原文件 文件夹路径
                String s = f.getAbsolutePath();
                //System.out.println(s);
                //目标文件 文件夹路径
                String m = (file2.getAbsolutePath().substring(0,3).endsWith("\\") ? file2.getAbsolutePath().substring(0,3) : file2.getAbsolutePath().substring(0,3) + "\\" ) + s.substring(3);
                //将目标文件转为File文件
                File mubiao = new File(m);

                if(!mubiao.exists()){
                    mubiao.mkdirs();
                }
            }

            //下面的file2不正确 传过去的参数不对 路径应该和f的一样只是变了前面
            String s = (file2.getAbsolutePath().substring(0,3).endsWith("\\") ? file2.getAbsolutePath().substring(0,3) : file2.getAbsolutePath().substring(0,3) + "\\" ) + f.getAbsolutePath().substring(3);
            file2 = new File(s);
            //进行递归
            copy(f, file2);
        }





    }

    static void copy2(File yuanlujing,File mubiaolujing){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        //创建数组
        byte[] bytes = new byte[1024 * 1024]; //1MB
        //读取的数量
        int count = 0 ;

        try {
            fis = new FileInputStream(yuanlujing);
            fos = new FileOutputStream(mubiaolujing);
            while((count = fis.read(bytes)) != -1){
                fos.write(bytes);
            }

            //进行刷新
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }


        }



    }





}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值