java进阶-第十二讲 IO流

java进阶-第十二讲 IO流

1 File类

在java中File类代表了什么意思?
	file:文件
	java中File类的描述:
		An abstract representation of file and directory pathnames. 
		File代表了文件和目录,不仅仅只是文件,一定要清楚


2 File的构造方法

File(File parent, String child) 
Creates a new File instance from a parent abstract pathname and a child pathname string.
    
File(String pathname) 
Creates a new File instance by converting the given pathname string into an abstract pathname.
    
File(String parent, String child) 
Creates a new File instance from a parent pathname string and a child pathname string. 
    
File(URI uri) 
Creates a new File instance by converting the given file: URI into an abstract pathname. 

3 成员方法

boolean createNewFile() // 创建一个文件
Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist. 

String getParent() // (文件与目录)的上一级目录
Returns the pathname string of this abstract pathname's parent, or null if this pathname does not name a parent directory. 

boolean isDirectory() //判断当前的File对象是否是一个目录
Tests whether the file denoted by this abstract pathname is a directory. 

boolean isFile() // 判断当前的File对象是否是一个文件
Tests whether the file denoted by this abstract pathname is a normal file. 


boolean mkdir() // 创建一个目录
Creates the directory named by this abstract pathname. 

boolean mkdirs() // 创建多级目录
Creates the directory named by this abstract pathname, including any necessary but nonexistent parent directories. 


String getAbsolutePath() 
Returns the absolute pathname string of this abstract pathname. 


File[] listFiles() // 返回一个File[]数组,
Returns an array of abstract pathnames denoting the files in the directory denoted by this abstract pathname. 

4 应用

// 扫描包
package com.tj.IO.fileTest;

import java.io.File;
import java.io.FileNotFoundException;

/**
 * @program: Study
 * @description: 遍历一个文件夹,打印其中所有的文件的路径
 * @author: tianjie
 * @create: 2021-02-18 09:39
 */
public class FileTest02 {
    public static void main(String[] args) {
        File file = new File("F:\\IdeaProjects\\Study\\src\\com\\tj\\demo01");
        try {
            scanPakage(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static void scanPakage(File file) throws FileNotFoundException {
        if (file == null) {
            throw new FileNotFoundException("文件不存在!");
        }
        // 递归
        // 1. 要出口 结束条件
        if (file.isFile()) {
            System.out.println(file.getAbsolutePath());
            return;
        }

        File[] files = file.listFiles();

        for (File childFile : files) {
//            System.out.println(childFile.getAbsolutePath());
            if (childFile.isDirectory()) {
                scanPakage(childFile);
            } else {
                System.out.println(childFile.getAbsolutePath());
            }
        }
    }
}
// 扫描包是一个很重要的应用,以后你们在学习框架的时候,就大量用到
// 使用反射的时候,会大量用到:扫描包+String字符串拼接+反射和注解+集合 ===> 创建对象

4.1 进一步的应用

需求:复制一个包从F:\\IdeaProjects\\Study\\src\\com\\tj 复制到
	 E:\\IdeaProjects\\Study\\src\\com\\tj
怎么做?
思路:
	1. 要用到哪些知识?递归 + IO + isfile,isdirectory,mkdirs
	2. 如果是文件,就要用到IO进行文件的拷贝
	3. 如果是目录,就要新建目录
复习:
	文件的拷贝
	1. 确定源(从哪个文件拷贝,拷贝到哪里去)
	2. 打开流(要使用到哪个流?Input Output)(字符流和字节流)
	3. 操作
	4. 关闭流

当成作业,拷贝一个文件夹。
package com.tj.IO.fileTest;

import java.io.*;
/**
 * @program: Study
 * @description:
 *      复制一个包从F:\\IdeaProjects\\Study\\src\\com\\tj 复制到
 * 	               E:\\IdeaProjects\\Study\\src\\com\\tj
 * @author: tianjie
 * @create: 2021-02-18 10:31
 */
public class CpyDir {
    public static void main(String[] args) {
        File srcFile = new File("F:/IdeaProjects/Study/src/com/tj");
        File destFile = new File("E:/");
        try {
            cpyDir(srcFile, destFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void cpyDir(File srcFile, File destFile) throws IOException {

        checkNull(srcFile);

        if (srcFile.isFile()) {
            // 拷贝
            copyFile(srcFile, destFile);
            return;
        }

        File[] files = srcFile.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                createNewDir(destFile, file);
            }
            cpyDir(file,destFile);
        }
    }

    private static void createNewDir(File destFile, File file) {
        String srcPath = file.getAbsolutePath();
        String destPath = destFile.getAbsolutePath();
        String newPath = (destPath + srcPath.substring(3));
        File newFile = new File(newPath);
        if (!newFile.exists()) {
            newFile.mkdirs();
        }
    }

    private static void copyFile(File srcFile, File destFile) throws IOException {
        InputStream fis = null;
        OutputStream fos = null;
        String srcPath = srcFile.getAbsolutePath();
        String destPath = destFile.getAbsolutePath();
        String newPath = (destPath + srcPath.substring(3));
        destFile = new File(newPath);
        try {
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            byte[] flush = new byte[1024*1024];

            while (fis.read(flush) != -1) {
                fos.write(flush);
                fos.flush();
            }
        } finally {
            if(fos != null) {
                fos.close();
            }
            if (fis != null) {
                fis.close();
            }
        }
    }

    private static void checkNull(File srcFile) throws FileNotFoundException {
        if (srcFile == null) {
            throw new FileNotFoundException("文件找不到!");
        }
    }
}

5 关于包装流

FileInputStream:
	如果我们按照Byte的大小去读,效率比较低。
	read() : 1个Byte 1个Byte的读
	应用中,我们都需要开辟一个缓冲区,也就是字节数组byte[] flush
	read(flush):往字节数组flush中读入数据,读满之后,一次性写出。
	这个缓存数组是我们可以自己控制大小的
	这样的话效率会好很多。
	
BufferedInputStream:已经有缓存的字节输入流
	Buffered:已缓存的
	猜:也就是说这个BufferedInputStream中,自带一个缓存字节数组。
	
1. 自带byte buf[]
    
2. 构造
public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
参数:InputStream in :抽象类;传递实参的时候,一定要传一个InputStream的子类,多态
    这里,我们可以传入一个FileInputStream fis
this(in, DEFAULT_BUFFER_SIZE);调用一个有两个参数的构造方法
    默认的缓冲区大小为8192

3. 成员方法:
    read()
    close()
    这个read方法调用的时候,还需要再开byte数组吗?不用了。
    这个close()方法被调用的时候,还需要关闭FileInputStream吗?不需要
    
 4. BufferedInputStream叫做包装流,它把InputStream包装了一下,包装成带有缓冲区的
    字节输入流。在这个包装流中,InputStream的子类,都叫做结点流。装饰模式。
package com.tj.IO;

import java.io.*;

/**
 * @program: Study
 * @description:
 * @author: tianjie
 * @create: 2021-02-18 11:16
 */
public class IOTest03 {

    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream("F:\\IdeaProjects\\Study\\src\\com\\tj\\IO\\小人书"));
            bos = new BufferedOutputStream(new FileOutputStream("F:\\IdeaProjects\\Study\\src\\com\\tj\\IO\\小人书02"));
            int len = 0;
            while ((len = bis.read()) != -1) {
                bos.write(len);
                bos.flush();
            }

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

6 ObjectInput/OutputStream

应用最广泛的,也是最重要的一个流:对象输入输出流
ObjectInput/OutputStream:这个流的核心要输入输出的东西:对象
java中最重要的是:对象
作用:序列化与反序列化
什么是序列化:
	将内存中的对象写到文件中
什么是反序列化:
	将对象从文件中读入到内存中

对象创建了之后在哪里存放:堆内存空间
内存:一旦断电了之后,内存是不是就要释放。程序运行结束之后,内存也要释放。
内存释放之后,对象还存在吗?不存在了。
有的对象我们希望不要反复的创建。把对象存入到硬盘上。每次要用的时候加载一下。
对象到底是什么东西?是不是一个二进制序列。一串二进制数据。数据是不是可以放到硬盘上。
User usr = new User(...);
将内存中的对象放入到硬盘文件的过程叫做序列化(OutputStream)
将硬盘文件中的对象写入到内存中的过程叫做反序列化(InputStream)
序列化与反序列有一个专用的流:ObjectInput/OutputStream

7 序列化与反序列化

定义:
	将内存中的对象写入到硬盘文件中---序列化
	将硬盘文件中的对象数据读入到内存中---反序列化
	
前提:
1. 要有对象
2. 类一定要实现Serializable接口,
	只有实现了Serializable接口的类所产生的对象才能被序列化与反序列化
3. ObjectOutputStream:序列化 ObjectInputStream:反序列化

刨根问底:
	Serializable接口到底是个啥?
	如果一个类不实现该接口,在序列化的时候,会报:NotSerializableException
	所以说,对于可以被序列化与反序列化的对象而言,它所属的类一定要实现Serializable
	接口。为什么非得实现?
应用场景:
	我序列化好了一个user对象,过了一段时间,项目的需求发生一些改变,需要给Uer类增加一个属性。
	于是乎,我改了代码。
	但是,其他同事想要反序列化原来的user对象。这时候,会出现以下错误:
	java.io.InvalidClassException: com.tj.IO.serializeTest.User; 
	local class incompatible: 
	stream classdesc serialVersionUID = -3484567756084152433, 
	local class serialVersionUID = 1411792367602759118
	序列化版本号不一致。
	非法类异常
	推断:这个版本号是谁给的?JVM给我们的,JVM凭什么给我们定义的类一个版本号?
	因为我们的User类实现了Serializable接口
	这个接口为什么没有内容呢?因为它是一个标识接口,这个接口对于我们程序员来说
	没有太大的使用意义,它是给JVM看的。JVM看到类实现了改接口,就会给它分配一个
	序列化版本号(serialVersionUID),这个版本号就是用来:序列化好的对象可以通过
	该版本号找到它所对应的类。如果,序列化好的对象中的版本号,与类所拥有的版本号
	不一致,那么无法反序列化,因为JVM认为这不是同一个类的。
	这是JVM分配的,那么它是存在缺陷的。怎么办呢?
	一般情况下,现实开发中,我们都需要自定义序列化版本号。自定义的序列化版本号
	要求全球唯一。
	
	如果改变了类的属性之后,反序列化所得结果有值的部分仍是以前的序列化好的,
	新加入的属性值会给一个默认值。
	开发过程中,要和团队的人商议好,这个序列化版本号不能冲突。
	serialVersionUID是一个常量,一旦定义不要修改。
	
	readObject()这个方法只会按照当前的类结构来读。也就是说:
	以前序列化的对象,放在硬盘文件中。现在,已经改变了代码。
	反序列化的时候,通过序列化版本号,来找到当前的类,按照当前的类结构来反序列化
	少的就舍弃,多的就补上默认值。
  • 实例代码
package com.tj.IO.serializeTest;

import java.io.Serializable;

/**
 * @program: Study
 * @description:
 * @author: tianjie
 * @create: 2021-02-18 11:59
 */
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;


    public User() {
    }

    public User(int id) {
        this.id = id;
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                '}';
    }
}




package com.tj.IO.serializeTest;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @program: Study
 * @description:
 * @author: tianjie
 * @create: 2021-02-18 12:00
 */
public class SerializeTest {

    public static void main(String[] args) {
        User zangs = new User(1);
        ObjectOutputStream  oos = null;

        try {
            oos = new ObjectOutputStream(new FileOutputStream("user2"));
            // 序列化,把内存中的对象写入到硬盘文件中user

            oos.writeObject(zangs);
            oos.flush();

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


}


package com.tj.IO.serializeTest;

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

/**
 * @program: Study
 * @description:
 * @author: tianjie
 * @create: 2021-02-18 12:10
 */
public class DeSerializeTest {

    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("F:\\IdeaProjects\\Study\\user2"));
            Object object =  ois.readObject();
            System.out.println(object);
            // deep clone
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

8 接口的补充

package com.tj.IO.lambdaTest;


@FunctionalInterface //注解
public interface IUser {

    void test();

    default void t1() {
        System.out.println("hello world");
    }
    default void t3() {
        System.out.println("hello jsu");
    }
    static void t2() {
        System.out.println("static func");
    }
    static void t4() {
        System.out.println("hello csj");
    }
}


package com.tj.IO.lambdaTest;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: Study
 * @description:
 * @author: tianjie
 * @create: 2021-02-18 13:04
 */
public class LTest {
    public static void main(String[] args) {

        IUser user = new IUser() {
            @Override
            public void test() {
                System.out.println("12345");
            }
        };
        user.test();
        IUser iUser = ()-> System.out.println("hi!");
        iUser.test();
        iUser.t1();
        IUser.t2();
        iUser.t3();
        IUser.t4();


        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.forEach(System.out::println);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值