I/O输入输出流

I/O输入输出流

File类

java.io.file
表示电脑上的文件或者目录

  • 无论是文件还是目录都通过File类表示
  • 提供了对文件和目录的基本操作,如查看文件名,文件大小,新建或删除文件
  • File不能访问文件的内容,需要I/O输入输出流访问文件的内容

构造方法

指定文件路径
File file = new File(path);

创建File文件时只是创建一个指向某个路径的对象,并不表示在硬盘中创建文件,这个路径指向的文件可以存在,也可以不存在。

  • 绝对路径
    以根路径开始的路径
    关于路径分隔符(由于我的系统是Linux系统,故只说明Linux系统下的情况)
    例:/home/Desktop/a.txt
  • 相对路径
    不是以根开始的路径,相对于某个路径的路径,.表示当前目录,…表示上一级目录。相对路径的父目录默认的是项目根目录
指定父目录路径和文件名
File file = new File(father path, filepath);
指定父目录的对象和文件名
File file = new File(File fatherfile, filepath);
指定URI统一资源标识符

URL需要装换成URI使用

常用方法

访问文件属性方法
方法作用
getParent()获取指定了文件的父目录
length()文件大小,单位是字节
lastModified()返回long时间戳
canWrite()文件可读
isFile()是否是普通文件
isDirectory()是否是文件目录
操作方法
方法作用
createNewFile()成功创建文件返回true,失败返回false
renameTo()文件重命名
delete()返回是否删除成功
mkdir()若父目录不存在会创建失败
list()返回文件目录下所有文件和目录的名字,返回字符串数组
File.separator返回操作系统分隔符

文件过滤器

需要用到文件过滤的过滤器接口,重写过滤方法。例程如下:

public static void main(String[] args) {
        File file1 = new File("/data/JAVAProjects/Wangdao2022LF/File/src/com/file/test");
        //String[] list = file1.list();
        String[] list = file1.list(new FilenameFilter() {
            @Override
            public boolean accept(File file, String name) {
                return name.endsWith("png") || name.endsWith("gif") || name.endsWith("jpg");
            }
        });
        System.out.println(Arrays.toString(list));
    }

递归读取文件

在一般情况下,文件的路径有可能是一般文件,也有可能是文件目录,这时可以使用递归的思想,获取文件目录及其子目录下的所有文件。
代码实现如下:

public static void getFiles(File filePath){
        if(filePath.exists()){
            if(filePath.isFile()){
                System.out.println(filePath.getAbsolutePath());//如果目标文件是一
                //般文件,直接输出文件绝对路径
            }
            else{
                System.out.println(filePath.getAbsolutePath());//目标文件是目录
                //文件,直接输出目录绝对路径
                File[] files = filePath.listFiles();
                for(File f : files){
                    if(f.isFile())
                        System.out.println(f.getAbsolutePath());
                    else
                        getFiles(f);//此处使用递归,当子文件中含有目录的时候再次调
                        //用该函数,将子目录当成新的filePath参数,实现递归
                }
            }
        }
    }

清理空文件夹

public static void removeEmptyDirectory(File file){
        if(file.isDirectory()&& Objects.requireNonNull(file.listFiles()).length==0){
            file.delete();
            System.out.println("删除空目录" + file.getAbsolutePath());
            removeEmptyDirectory(file.getParentFile());
        }
        else if (file.isDirectory()){
            
            File[] files = file.listFiles();
            for(File f : files != null ? files : new File[0]){
                removeEmptyDirectory(f);
            }
        }
    }

IO流

可以理解成一组有顺序的、有起点有中点的动态数据集合
文件是数据在硬盘上的静态存储
流是传输时的动态形态

文件分类

  • 文本文件
    可以用记事本打开的文件,由字符组成
  • 二进制文件
    除文本以外的文件都是二进制文件

流的分类

  • 流的方向:
    输入流:由InputStream,Reader作为父类
    输出流:由OutputStream,Writer作为父类
  • 按流中数据的单位
    字节流byte:由InputStream,OutputStream作为父类
    字符流char:由Reader,Writer作为父类
  • 按数据的来源
    节点流:直接对数据进行操作
    包装流:对一个节点流进行操作

字节流

InputStream
方法作用
FileInputStream以字节为单位,读取数据,返回一个int类型的值
ByteArrayInputStream从字节中读取数据,将字节数组当做流输入的来源
ObjectInputStream反序列化,通过从磁盘或网络获取的二进制文件中读取对象
FileInputStream
//JDK7中提供了一种新语法,叫try-with-source,能够自动关闭外部资源,无需手动关闭
        //此处只能创建对象,而且只能创建那些有实现了AutoClosable接口的对象
        try(FileInputStream fis = new FileInputStream("test.txt")){
            byte[] buffer = new byte[3];//buffer数组储存在文件缓冲区,而且不是实际内存而是JVM
            int num = fis.read(buffer);//减少对硬盘的读取次数,返回值是实际读取的字节数,如果读取到末尾返回的是-1
        }
        catch (IOException e){
            e.printStackTrace();
        }
ByteArrayInputStream

字节数组输入输出流操作的是内存,不是外部设备因此无需关闭流

public static void byteArrayInputStream(){
        byte[] bytes = "hello world".getBytes();
        try{
            InputStream is = new ByteArrayInputStream(bytes);
            int i = -1;

            while((i = is.read())!= -1){
                System.out.println((char)i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
OutputStream
方法作用
FileOutputStream以字节为单位,将文件写出到文件中
ByteArrayOutputStream将数据写出到内置的字节数组中,即将字节数组当做输出的目的地
ObjectOutputStream用于序列化,将对象写入二进制文件,以便通过磁盘或者网络传输
FileOutputStream
public static void main(String[] args) {
        FileOutputStream fos = null;
        
        try{
        //如果文件不存在,会自动创建文件;如果文件存在,则会覆盖重写,但是在构造时,能够使用重载的构造方法追加写入数据
            //fos = new FileOutputStream("test.txt");
            fos = new FileOutputStream("test.txt", true);
            byte[] data = "hello world".getBytes();
            fos.write(data);//只是把数据写入到缓冲区中
            fos.flush();//将缓冲区的数据刷新,写入到文件中,但是当关闭输出流时,会自动调用该方法
        }catch (IOException e){
            e.printStackTrace();
        }
    }
ByteArrayOutputStream
public static void byteArrayOutputStream(){
        OutputStream os = new ByteArrayOutputStream();
        try {
            os.write("hello,world".getBytes());
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(os);
    }
文件复制
  • 每次复制一个字节,因为涉及到多次IO操作,传输时间更长
   int data = -1;
   while((data = is.read()) != -1){
   os.write(data);
            }
//488ms
//文件复制成功!

再看看多字节,我们一次复制1MB大小的字节数组

byte[] buffer = new byte[1024 * 1024];
int num = -1;
while((num = is.read(buffer))!=-1){
      os.write(buffer, 0, num);
}
//1ms
//文件复制成功!

时间就相差很大了。

序列化和反序列化

序列化:将Java对象写入到IO流中,实现将对象保存在磁盘上或者在网络中传递对象
反序列化:从IO流中读取Java对象,实现从磁盘和网络上恢复对象
要求:
对象必须实现Serializable接口才能序列化,转换成二进制流,通过网络进行传输

ObjectOutputStream 序列化操作

ObjectOutputStream和ObjectInputStream都属于包装流

  • 用于对节点流功能进行扩展
  • 创建包装流时,需要传入操作的节点流对象
  • 关闭流时,只需要关闭包装流,节点流也会被关闭
    ObjectOutputStream用于序列化,将对象写入二进制文件:
User user = new User(1001, "tom", 18);
        try {
            FileOutputStream fos = new FileOutputStream("user.data");//用来包装的流
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(user);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
ObjectInputStream 反序列化操作

注意事项:

  • 对象必须实现Serializable接口,才能被序列化转换成二进制流通过网络传输;如果成员变量有对象属性,则对象属性也要序列化
  • 通过serialVersionUID用来判断序列化版本的一致性(反序列化时将流中携带的UID和本地相应实体的UID是否一致,才能进行反序列化)
  • transient和static修饰的成员不会被序列化
缓冲字节流

字符流

Reader和Writer

文件输入输出流

/字符流不能操作二进制文件,只能操作文本文件

try (FileWriter writer = new FileWriter("noun.txt");
             FileReader reader = new FileReader("verb.txt")) {
            char[] buffer = new char[5];
            int num = -1;//每次读取一个字符,返回int类型的值,可以读汉字,不会出现乱码

            while ((num = reader.read(buffer)) != -1)
                writer.write(buffer, 0, num);
            System.out.println("文件复制成功");
            } catch (IOException e) {
            e.printStackTrace();
        }
缓冲输入输出流

缓冲输入输出流属于包装流,为字符添加缓冲的功能,当读取或者写出数据时,先放到缓冲区,再到缓冲区读取。

BufferedReader&BufferedWriter
try(
                BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
                BufferedWriter writer = new BufferedWriter(new FileWriter("verb.txt"))
        )
        {
            String data;//每次读一行,读不到数据返回null
            while((data = reader.readLine()) != null)
                writer.write(data);
            System.out.println("复制成功");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
out&in

System.out :标准输入输出流,默认为显示器
System.in :标准输入流,默认为键盘

转换流

只能由字节流转换成字符流,同时可以实现编码的转换

PrintWriter

打印流,可以直接打印输出;’可以只以文件名作为参数

随机读写流

随机读写流是一个字节流,可以定义到任意的文件位置进行读写操作,使用该流既能读取文件又能写入文件。

System.out.println(raf.getFilePointer());
            raf.seek(3);//使指针移动到指定位置
            raf.skipBytes(2);//指针往后跳过多少个字节,不能反着走
            System.out.println((char)raf.read());

文件加密

思想:将文件里的字节与int类型密码异或得到加密后的字符存到密码,然后对密码文件的每个字节和密码进行异或即可还原原来数据。

public static void secret(String filePath, int password) {
        String newFilePath, notice;
        if(filePath.endsWith(".sec"))
        {
            newFilePath = filePath.substring(0, filePath.length() - 4);
            notice = "解密完成!";
        }
        else {
            newFilePath = filePath + ".sec";
            notice = "加密完成!";
        }
        try(
                FileInputStream fis = new FileInputStream(filePath);
                FileOutputStream fos = new FileOutputStream(newFilePath);
                ) {
            int data = -1;
            while((data = fis.read()) != -1){
                fos.write(data ^ password);
            }

            System.out.println(notice);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        secret("test.txt.sec", 9527);
    }

加密后,由于生成的是二进制文件,显示出来是乱码:
߈�ф�ѯ�ߵ�߬�ߦ�ޏ�

解密后显示正常:
这波是肉蛋葱鸡

I/O部分到这里就结束了~😄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值