File文件类和IO流

File文件类

1.File文件类的作用

在Java中,File就代表一个文件类,这里的文件既包含了普通文件,又包含了文件目录。

File文件类可实现对文件的基本操作(创建文件、删除文件、获取文件长度、更改文件的名字等等)。

2.File文件类常用的方法

3. 递归删除层级目录

public class Exercise2 {
    public static void main(String[] args) {
        File dir = new File("D:/aaa/javase");
        forceDeleteDir(dir);
    }
    public static void forceDeleteDir(File dir) {
        if (dir != null && dir.isDirectory()) {
            File[] listFiles = dir.listFiles();
            if(listFiles!=null){
                for (File sub : listFiles) {
                    forceDeleteDir(sub);
                }
            }
        }
        dir.delete();
    }
}

 4.File文件类总结

 Java中的流

1.流的概念

在Java中,利用程序完成对数据的(存储)操作所使用的工具就是流。

2.字节输出流

 /* 需求:
 * 采用字节流,指定路径下的文件。写入数据
 * 1.实例化字节流对象(输出流(写入操作)),指定文件路径
 * 2.调用方法,写入数据
 */
public class Test01 {
    public static void main(String args[]) throws Exception {
        //实例化字节流对象,指定文件路径
        FileOutputStream fs = new FileOutputStream(new File("D://a.txt"), true);
        /**
         * 调用方法,写入数据
         * 是以最基本的单位字节 进行数据写入的
         *
         * 指定字符数据
         */
        String value = "热烈庆祝中华人民共和国成立70周年";
        //将指定的字符数据转换成字节数据
        byte[] b = value.getBytes();
        //将得到的字节数据,作为参数,写入到指定文件中
        fs.write(b);
        //释放流资源
        fs.close();
    }
}

 3.字节输入流

public class ByteInputputStreamDemo2 {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileInputStream fis = new FileInputStream("D:\\a.txt");
        //2.读取数据
        int b1 = fis.read();
        System.out.println((char)b1);

        //3.释放资源
        fis.close();
    }
}

4.一次读一个字节数组时的覆盖问题

5.字符集问题

utf-8:一个中文3个字节

GBK中一个中文2个字节

6.乱码问题解决

 7.字符流

  • 以字符为单位去读取数据

 

 8.字符输入流

 9.字符输出流

书写细节

8.字符输入流原理

 

 

 9.缓冲区的细节

public class CharReaderStreamDemo4 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:\\b.txt");
        fr.read();//会把文件中的数据放到缓冲区当中
        //清空文件
        FileWriter fw = new FileWriter("D:\\b.txt");
        

        fw.close();
        fr.close();
    }
}

如果我再次使用fr进行读取,会读取到数据吗?如果能读取到,请问读取的数据是什么?

public class CharReaderStreamDemo4 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:\\b.txt");
        fr.read();//会把文件中的数据放到缓冲区当中
        //清空文件
        FileWriter fw = new FileWriter("D:\\b.txt");
        //请问,如果我再次使用fr进行读取,会读取到数据吗?

        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.println((char) ch);
        }//再次读取

        fw.close();
        fr.close();
    }
}

正确答案:但是只能读取缓冲区中的数据,文件中剩余的数据无法再次读取

10.字符输出流的原理

 11.flush和close的区别

 12.字节流与字符流的区别

相同点

都可以完成对数据的读取和写入操作

本质上都是在以字节为单位在读取

不同点

组成不同

1字节流的组成:字节流是由字节组成的。
2、
字符流的组成:字符流是由字符组成的。

缓冲区的不同

操作对象的不同

1字节流 :字节读写,  字节流(ASCII)处理二进制文件。

       可以传输音频,视频,图片,文本等,传输数据的基本单位为字节。

         InputStream OutputStream 

2字符流:快读写 ,字符流(Unicode)处理文本文件。

     只能传输纯文本, 传输数据的基本单位为字符 。

      FileWriter FileReader 一个字符等于2个字节

两者的处理不同

字节流与字符流主要的区别是他们的的处理方式

字节流是最基本的,采用ASCII编码,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的

但实际中很多的数据是文本,又提出了字符流的概念,采用Unicode编码.它是按虚拟机的encode来处理,也就是要进行字符集的转化

这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联

 字节流与字符流的应用场景

13.应用场景

13.1文件的拷贝

public class Test01 {
    public static void main(String[] args) throws IOException {
        //拷贝一个文件夹,考虑子文件夹

        //1.创建对象表示数据源
        File src = new File("D:\\aaa\\src");
        //2.创建对象表示目的地
        File dest = new File("D:\\aaa\\dest");

        //3.调用方法开始拷贝
        copydir(src,dest);
    }

    /*
    * 作用:拷贝文件夹
    * 参数一:数据源
    * 参数二:目的地
    *
    * */
    private static void copydir(File src, File dest) throws IOException {
        dest.mkdirs();
        //递归
        //1.进入数据源
        File[] files = src.listFiles();
        //2.遍历数组
        for (File file : files) {
            if(file.isFile()){
                //3.判断文件,拷贝
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));
                byte[] bytes = new byte[1024];
                int len;
                while((len = fis.read(bytes)) != -1){
                    fos.write(bytes,0,len);
                }
                fos.close();
                fis.close();
            }else {
                //4.判断文件夹,递归
                copydir(file, new File(dest,file.getName()));
            }
        }
    }
}

13.2文件加密

public class Test02 {
    public static void main(String[] args) throws IOException {
        /**
         为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
         加密原理:
         对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中。
         解密原理:
         读取加密之后的文件,按照加密的规则反向操作,变成原始文件。
         */

        /*
         *  ^ : 异或
         *        两边表达式相同:false
         *             System.out.println(true ^ true);//false
         *             System.out.println(false ^ false);//false
         *        两边表达式不同:true
         *             System.out.println(false ^ true);//true
         *             System.out.println(true ^ false);//true
         */
        System.out.println(true ^ true);//false
        System.out.println(false ^ false);//false

        System.out.println(false ^ true);//true
        System.out.println(true ^ false);//true
        /**
         *
         *  表达式还可以是数字:System.out.println(100 ^ 10);//110-二进制对位计算后得到的二进制转换成十进制就是110
         *      100的二进制:1100100
         *      10的二进制: 1010
         *                  0:false
         *                  1:true
         *      01100100
         *      00001010
         *      -------
         *      01101110---十进制--->110
         *-------------------------------------------
         *      100的二进制:1100100
         *      10的二进制: 1010
         *                  0:false
         *                  1:true
         *      01101110
         *      00001010
         *      -------
         *      01100100---十进制--->100
         *
         *  结论:可以利用^完成加密、解密
         *      如把100当做原始内容,那么经过 ^ 10后得到的110就是加密后的内容;
         *      反之把110当做解密内容,那么经过 ^ 10后得到的100就是原始的内容;
         */
        System.out.println(100 ^ 10);//110
        System.out.println(110 ^ 10);//100

        //加密处理
        /*encryptionAndReduction("D://我想吃鱼了.png","D://ency.png");*/
        //解密处理
        encryptionAndReduction("D://ency.png","D://我想吃鱼了.png");
    }

    public static void encryptionAndReduction(String src, String dest) throws IOException {
        //1.创建对象关联的原始文件
        FileInputStream fis = new FileInputStream(src);
        //2.创建对象关联的加密文件
        FileOutputStream fos = new FileOutputStream(dest);
        //3.加密处理
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b ^ 2);
        }
        //4.释放资源
        fos.close();
        fis.close();
    }
}

 13.3 修改文件中的数据

13.3.1 常规实现 
public class Test03 {
    public static void main(String[] args) throws IOException {
        /**
            文本文件中有以下的数据:
                2-1-9-4-7-8
            将文件中的数据进行排序,变成以下的数据:
                1-2-4-7-8-9
        */
        //1.读取数据
        FileReader fr = new FileReader("D:\\a.txt");
        StringBuilder sb = new StringBuilder();
        int ch;
        while((ch = fr.read()) != -1){
            sb.append((char)ch);
        }
        fr.close();
        System.out.println(sb);

        //2.排序
        String str = sb.toString();
        String[] arrStr = str.split("-");//2-1-9-4-7-8

        ArrayList<Integer> list = new ArrayList<>();
        for (String s : arrStr) {
            int i = Integer.parseInt(s);
            list.add(i);
        }
        Collections.sort(list);
        System.out.println(list);

        //3.写出
        FileWriter fw = new FileWriter("D:\\a.txt");
        for (int i = 0; i < list.size(); i++) {
            if(i == list.size() - 1){
                fw.write(list.get(i) + "");
            }else{
                fw.write(list.get(i) + "-");
            }
        }
        fw.close();
    }
}
 13.3.2stream流实现
public class Test04 {
    public static void main(String[] args) throws IOException {
        /**
            文本文件中有以下的数据:
                2-1-9-4-7-8
            将文件中的数据进行排序,变成以下的数据:
                1-2-4-7-8-9
           细节1:
                文件中的数据不要换行
           细节2:
                bom头
        */
        //1.读取数据
        FileReader fr = new FileReader("D:\\a.txt");
        StringBuilder sb = new StringBuilder();
        int ch;
        while((ch = fr.read()) != -1){
            sb.append((char)ch);
        }
        fr.close();
        System.out.println(sb);
        //2.排序
        Integer[] arr = Arrays.stream(sb.toString()
                .split("-"))
                .map(Integer::parseInt)
                .sorted()
                .toArray(Integer[]::new);
        //3.写出
        FileWriter fw = new FileWriter("D:\\a.txt");
        String s = Arrays.toString(arr).replace(", ","-");
        String result = s.substring(1, s.length() - 1);
        fw.write(result);
        fw.close();
    }
}

高级流

1.缓冲流

1.1字符缓冲流

  • 字符缓冲输入流:BufferedReader
  • 字符缓冲输出流:BufferedWriter
  • 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
  • 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
  • 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,

BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法

flush()可以强制将缓冲区的内容全部写入输出流。

1.2字节缓冲流

  • 字节缓冲输入流:BufferedInputStream
  • 字节缓冲输出流:和 BufferedOutputStream

为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区

  • 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
  • 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
  • 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,

BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法

flush()可以强制将缓冲区的内容全部写入输出流。

1.3缓冲流原理

2.转换流 

 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\a.txt")));

 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\a.txt"),"GBK");

 3.数据流

作用:

    可以让(写入的)输入的数据以该数据对应的数据类型字节大小保存在文件中,读取数据的时候再以对应的数据类型进行读取。

3.1数据输出流

/**
 *
 * 需求:
 *   以字节的形式写入数据,要求,数据是以字节的大小进行保存的。用DataOutputStream(数据输出流)完成
 *   123  file.txt  123
 *   123  file.txt  int所占字节大小保存的
 */
public class Test01 {
    public static void main(String args[]) throws Exception{
        //构建数据输入流,并指定路径
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("D://file.txt")));
        //调用writeInt()方法,写入Int类型数据
        dos.writeInt(123);

        //调用writeInt()方法,写入Int类型数据
        dos.writeInt(123);

        //调用writeLong方法,写入Long类型数据
        dos.writeLong(123);

        //用writeFloat方法,写入Float类型数据
        dos.writeFloat(2.5f);

        //调用writeDouble方法,写入Double类型数据
        dos.writeDouble(1.9999999999999);

        //调用writeUTF方法,写入字符串类型数据
        dos.writeUTF("你好世界");

    }
}

3.2 数据输入流 

/**
 * 需求:
 * 采用数据输入流按照数据类型去读取数据(DataInputStream)
 */
public class Test02 {
    public static void main(String arg[]) throws Exception {
        //构建数据输入流对象,并指定路径
        DataInputStream dis = new DataInputStream(new FileInputStream(new File("D://file.txt")));
        //调用readInt()方法,读取Int类型数据
        int value1 = dis.readInt();

        //调用readInt()方法,读取Int类型数据
        int value2 = dis.readInt();

        //调用readLong()方法,读取Long类型数据
        long value3 = dis.readLong();

        //调用readFloat()方法,读取Float类型数据
        float value4 = dis.readFloat();

        //调用readDouble()方法,读取Double类型数据
        double value5 = dis.readDouble();

        //调用readUTF()方法,读取字符串类型数据
        String value6 = dis.readUTF();

        System.out.println("value1:" + value1);
        System.out.println("value2:" + value2);
        System.out.println("value3:" + value3);
        System.out.println("value4:" + value4);
        System.out.println("value5:" + value5);
        //System.out.println("value6:"+value6);
        dis.close();
        /**
         * 注意:
         *     采用数据输入流去读取数据时,读取数据的顺序一定要和,输入的顺序一致,否则产生异常
         */
    }
}

4.序列化流(对象操作流)

4.1序列化流(对象操作输出流)

  • ObjectOutputStream
  • writeObject()
* 需求:
 *     采用对象流,完成对象数据的读取,(对象输入流)
 *  1.构建对象字节输入流对象,并且指定路径
 *  2.调用方法,读取对象数据
 *
 弊端:
 *如果不知道要写入多少个对象数据该怎么呢?(也不知道要读取多少个数据) 写入和读取不一致了
 *  优化此程序:
 *     优化的背景:
 *        读取数据的顺序必须和写入数据的顺序一致
 *     解决方案:
 *     1.化零为整,以整体的形式去写对象数据
 *     2.把要写的对象数据,存储到集合中,再把整体把集合中的数据通过对象流写出去
 *     3.读取的时候也是集合的形式进行读取
 *
 */
public class Test03 {
    public static void main(String args[]) throws Exception, IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D://file.txt")));
        List<Person> list = new ArrayList<Person>();
        list.add(new Person("刘备", 48, new Date()));
        list.add(new Person("关羽", 49, new Date()));
        list.add(new Person("张飞", 50, new Date()));

        oos.writeObject(list);
        oos.close();
    }
}

 4.2 反序列化流(对象操作输入流)

需求:
 *     采用对象流,完成对象数据的读取,(对象输入流)
 *  1.构建对象字节输入流对象,并且指定路径
 *  2.调用方法,读取对象数据
 *
 *     优化此程序:
 *        优化的背景:
 *           读取数据的顺序必须和写入数据的顺序一致
 *        解决方案:
 *          3.读取的时候也是集合的形式进行读取
 *
 */
public class Test04 {
    public static void main(String args[]) throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D://file.txt")));

        List<Person> list = (List<Person>)ois.readObject();
        for(Person p: list){
            System.out.println(p);
        }
        /**
         * 1.某个属性不想要被序列化呢?该怎么办呢?比如说age
         * 2.再自定义一个类类型,Pet 作为Person内部的属性,问此类Person是否需要序列化
         */
    }
}
  • Serializable

序列化标识接口,无须重写任何方法。只有实现此标识接口,对象数据才能被读取,这也是大多数常用类,实现此接口的原因。

  • Transient

修饰不需要被序列化的属性

4.3 序列化和反序列化的应用 

* 单独声明一个类,用于封装序列化代码(通过流的方式实现对象的克隆)
 * 核心:通过流将对象写入到缓冲区,然后再利用流对象读取出来。
 */
public class DeepCopy {
    //Object obj 代表克隆时所须要的对象
    public static Object copyDeep(Object obj) {
        //声明所需要的局部对象
        Object o = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            //完成对象写的操作
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            //完成对象读的操作
            byte[] b = baos.toByteArray();
            ois = new ObjectInputStream(new ByteArrayInputStream(b));
            o = ois.readObject();
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }
}
public class Test07 {
    public static void main(String args[]) throws CloneNotSupportedException {
        //实例化对象,得到一个宠物,
        Pet pet = new Pet("雪豹");
        //实例化对象,得到一个人
        Person p_1 = new Person("李达康", 55, pet);

        System.out.println("对象p_1情况");
        System.out.println("p_1:" + p_1);

        System.out.println("对象p_1情况");
        //调用序列化的方法,实现克隆
        Person p_2 = (Person) DeepCopy.copyDeep(p_1);
        //通过p_2对p_1这个人的宠物的名字进行修改,检测p_1是否会受到影响
        p_2.getPet().setName("花斑虎");
        System.out.println("p_1:" + p_1);
    }
}
/**
 * 程序运行结果分析:
 * <p>
 * 对象p_1情况
 * p_1:Person [name=李达康, age=55, pet=Pet [name=雪豹]]
 * 对象p_1情况
 * p_1:Person [name=李达康, age=55, pet=Pet [name=雪豹]]
 * <p>
 * <p>
 * 通过程序的执行结果大家可以发现,p_1宠物的名字原来是雪豹,在克隆出来的p_2基础之上把宠物的名字修改成花斑虎,发现最终p_1的宠物的名字依然是雪豹,所以说我们最终就可以得出一个结论,现在通过序列化的方式可以直接克隆含有引用类型的类,是可以实现克隆的。
 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值