JavaImprove--Lesson09--文件操作,UTF-8字符集,IO流-字节流

一.文件操作

在前面学习的内容中,存储数据的方式有很多,可以申请一个变量,一个数组,一个集合等方式,这些方式都可以记住数据,但是都是暂时的

因为这些方式的运行都是在内存中,内存运行快,对数据处理有很大的好处,但是缺点就是断电即失,所以不便于长期保存的场景

如果想长久的保存数据,就需要用到今天所讲的内容了:File类

File代表的是文件/文件目录,它存储在计算机的硬盘中,不易丢失,即使断电了,数据依旧还在,所以很适合长久的保存数据,但是效率低

File类是java.io包下的类,file类代表的是当前系统的文件:

  • 获取文件信息(时间,大小)
  • 判断文件类型
  • 创建/删除文件

但是注意File类代表的是文件本身,只能对文件进行操作,不能读写文件中的数据,操作数据使用IO流

IO流:读写文件数据(可以是本地数据,也可以是网络数据)

File:代表文件  IO流:读写数据

创建文件

创建文件需要使用到的方法:public  File(String pathname)

这是File类的构造器,使用此构造器,填写地址就可以拿到文件对象

//public File(String path)
File f1 = new File("D:\\java\\test.txt");
System.out.println(f1.getName());//test.txt
System.out.println(f1.length());//13
//路径写法二  / 单斜杠
File f2 = new File("D:/java/test.txt");
System.out.println(f2.length());//13
//路径写法三
File f3 = new File("D:" + File.separator + "java" + File.separator + "test.txt");
System.out.println(f3.length());//13

 

 

File类也可以指向一个不存在的路径,这并不会报错,因为后面会学创建文件的命令,既然是创建,原来的此路径肯定是没有文件的

 

//File可以指向一个不存在的路径
File f4 = new File("D:/java/test1.txt");
System.out.println(f4.length()); //0
//如上路径是不存在的,如果不确定路径是否有文件,Java提供了一个exists()方法
System.out.println(f4.exists());//false

 

文件路径的表达方式:绝对路径和相对路径

//路径表达的两种方式
//方式一:绝对路径:绝对路径是带盘符,也就是在找此文件时会从盘符开始找
File f5 = new File("D:\\java\\test.txt");
System.out.println(f5.exists());//true
//方式二:相对路径:相对路径是不带盘符的,且是从当前项目向下找文件的,灵活一些,也推荐使用
File f6 = new File("static/text/test.txt");
System.out.println(f6.exists());//true

 File类常用方法

public static void main(String[] args) throws IOException {
    //创建文件对象,指代某个文件
    File f1 = new File("D:/java/js.txt");
    //public boolean exists()判断当前文件对象是否存在,存在则返回ture
    System.out.println(f1.exists());//false
    //public boolean isFile()判断当前文件对象是否指代的文件,是文件则是true,反之
    System.out.println(f1.isFile());//false,应该是true,但是此文件不存在
    //public boolean isDirectory()判断当前文件对象是否为文件夹,是则true
    System.out.println(f1.isDirectory());//false
    //public String getName()获取文件名称(包含后缀)
    System.out.println(f1.getName());//js.txt
    //public long length()获取文件大小,字节个数
    System.out.println(f1.length());//0
    //public long lastModified() 获取文件最后的修改时间戳
    boolean CNF = f1.createNewFile();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
    String format = dateFormat.format(f1.lastModified());
    System.out.println(format);//2024/04/17 10:45:07
    //public String getPath()获取文件对象使用的路径
    System.out.println(f1.getPath());//D:\java\js.txt
    //public String getAbsolutePath()获取绝对路径
    System.out.println(f1.getAbsoluteFile());//D:\java\js.txt
}

 

 文件创建和删除方式

创建和删除需要选中一个文件对象

//文件创建和删除的方法
//public boolean createFile() 创建一个新的文件,创建成功则是true
File f2 = new File("D:\\java\\jk.txt");
System.out.println(f2.createNewFile());//true
//public boolean mkdir()用于创建文件夹,但是只能创建一级文件夹
File f3 = new File("D:/java/txt");
System.out.println(f3.mkdir());//true
//public boolean mkdirs()用于创建文件夹,而且是创建多级文件夹
File f4 = new File("D:/ts/ta/tb");
System.out.println(f4.mkdirs());//true
//public boolean delete()删除文件,或空文件夹,但是有文件的文件夹删除不了
System.out.println(f4.delete());//true

 

文件的遍历

public static void main(String[] args) {
    //遍历文件夹
    //public String[] list()获取当前文件夹下的所有一级文件名称,并且保存到一个字符串数组中
    File f1 = new File("D:/java");
    String[] list = f1.list();
    for (String s : list) {
        System.out.print(s+",");
    }
    //apache-maven-3.2.5,apache-maven-3.8.6,apache-tomcat-10.1.7,Dall,idea,JAVAWeb,javaweb-01-maven,
    // javaweb-01-maven02,javaweb-02-servlet,JavaWeb-QModel,javaweb-session-cookie,JAVAWebjsp,jk.txt,js.txt,
    // maven-webap,MySQL,OUR-test,spring-study,SpringBoot-Hellow,SpringMVC,ssmBuild,Study-mybatis,
    // Test,test.txt,Tomcat,Tomcat.rar,txt,
    
    
    //public File[] listFiles()重点方法,获取当前文件夹下的所有一级文件对象,保存到File数组中
    File[] f3 = f1.listFiles();
    System.out.println(Arrays.toString(f3));
    //[D:\java\apache-maven-3.2.5, D:\java\apache-maven-3.8.6, D:\java\apache-tomcat-10.1.7, 
    // D:\java\Dall, D:\java\idea, D:\java\JAVAWeb, D:\java\javaweb-01-maven, 
    // D:\java\javaweb-01-maven02, D:\java\javaweb-02-servlet, D:\java\JavaWeb-QModel, 
    // D:\java\javaweb-session-cookie, D:\java\JAVAWebjsp, D:\java\jk.txt, 
    // D:\java\js.txt, D:\java\maven-webap, D:\java\MySQL, D:\java\OUR-test, 
    // D:\java\spring-study, D:\java\SpringBoot-Hellow, D:\java\SpringMVC, 
    // D:\java\ssmBuild, D:\java\Study-mybatis, 
    // D:\java\Test, D:\java\test.txt, D:\java\Tomcat, 
    // D:\java\Tomcat.rar, D:\java\txt]
}

 

使用listFiles方法的注意事项:

  • 当主调是文件或路径不存在时,返回null
  • 当主调是一个空文件夹时,返回一个长度为0的数组
  • 当主调是一个有内容的文件夹时,将里面的一级文件和文件夹路径存放在File数组中返回
  • 当主调是一个文件夹时,有隐藏文件也会被包含返回
  • 当主调是文件夹,但是没有权限访问时,返回null 

 文件的递归查找

 文件的查找实际上是使用了递归的算法

当进入一个文件夹后,从第一个文件开始寻找,如果文件不是要找的文件,则判断此文件是文件夹还是文件

是文件夹则递归的进去找文件,不是文件夹则进行当前循环的下一次循环

public static void main(String[] args) {
    //D盘文件夹
    File file = new File("D:/Tencent");
    File[] files = file.listFiles();
    //查找QQ.exe
    LoopFile(files,"QQ.exe");
}
public static Boolean LoopFile(File[] files,String fileName){
    Boolean flag =false;
    //判断传入参数是否为空,换句话说就是判断文件是否存在
    if (files.length == 0 && files == null){
        return false;
    }
    //遍历文件夹
    for (File file : files) {
        String name = file.getName();
        //拿到文件名字,如果是找到的返回
        if (name.equals(fileName)) {
            System.out.println("找到了,路径在:" + file);
            Runtime runtime = Runtime.getRuntime();
            try {
                runtime.exec(file.getAbsolutePath());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //返回一个True,上层的函数接收到了以后,就会直接向上层返回,不在继续寻找
            return true;
        } else {
            //当此文件不是要找的时候,继续向下层寻找
            boolean f1 = file.isDirectory();//判断是否为文件夹
            System.out.println(file.getName());
            if (f1) {
                //是文件夹向下找
                File[] f2 = file.listFiles();
                if (f2 == null){
                    //判断文件是否为空,空则进行下一次循环
                    continue;
                }else {
                    //不为空,向下一个文件夹继续找
                    flag = LoopFile(f2,fileName);
                }
            } else {
                //不是文件夹又不是要找的文件,直接进行下一次循环
               continue;
            }
        }
        //下层函数返回的结果,如果为ture,则找到了,不需要继续找了,直接返回
        if (flag){
            return true;
        }
    }
    //一个文件夹刷完了都没找到,返回false
    return false;
}

 

其中有些时候文件夹内容可能很多,所以我们可以在找到文件后设置标志符,让函数直接返回,减小开销

如果文件不止一个,则不要设置标识符

删除文件夹

删除文件夹需要注意的是:Java不支持直接删除不为空的文件夹,所以删除文件夹时,先要删除文件夹中的内容

当文件夹还有内容时,继续进去删除,又是递归思想

public static void main(String[] args) {
    File file = new File("D:/86173/");
    removeDir(file);
}
public static void removeDir(File file){
    //文件本来就不存在,路径错误,删除不了
    if (file == null || !file.exists()){
        System.out.println(file.length());
        System.out.println(file.exists());
        return;
    }
    //单纯就是文件,直接删除
    if (file.isFile()){
        System.out.println("删除了:"+file.getName());
        file.delete();
    }else {
        if (file.listFiles() == null){
            //无权限,不能删除
            return;
        }
        File[] files = file.listFiles();
        //当是文件夹时,进入下一级继续删除
        for (File f1 : files) {
            if (f1.isFile()){
                //是文件直接删除
                f1.delete();
            }else {
                if (f1.listFiles() == null){
                    //有空文件夹直接删除
                    System.out.println("删除了:"+f1.getName());
                    f1.delete();
                    continue;
                }
                //又有文件且非空,继续进行删除
                removeDir(f1);
            }
        }
        //删除到最后注意要删除自己这个目录
        if (file.listFiles().length == 0){
            System.out.println("删除了:"+file.getName());
            file.delete();
        }
    }
}

 二.UTF-8字符集

 在计算机中,存储的从来就只有0或者1,但是在现实世界中,我们的世界却是千变万化的,并不只有0或1

所以计算机在存储这千变万化的世界要怎么存储呢?

在计算机发明之后,美国人采用编码的方式存储字符,一个字节是8位,所以最多可以表示256个字符

但是最著名的ASCII编码只用了128位,其中一些常用的字符0 - {48},a - {97} ,  A - {65}

对于字符少的一个字节完全够用,但是我们国家的汉字使用一个字节表示就完全不可能了

根据最新的《通用规范汉字表》(2013年发布),共收录了8105个汉字。但是,这并不意味着汉字的数量就只有这么多,因为除了通用的汉字外,还有很多生僻字、古汉字以及一些方言中使用的汉字没有被收录

所以汉字有自己的编码。即GBK,GBK编码兼容了ASCII编码,同时汉字使用两个字节表示可以表示32768个汉字

 如上图:当我们拿到一个字节之后,首先通过首位判断是中文还是ASCII码,

如果是0开始,则读取一个字节的数据,翻译ASCII

如果是1开始,则读取两个字节的数据,翻译成中文

 UTF-32编码

看了GBK编码之后,我们可以发现中国人使用计算机有自己的编码,那么,外国人使用计算机也会有自己的编码

但是世界上的国家太多了,如果任其自行编码,很难做到统一,也就是我编写的代码在中国能用,到外国去就要改,这样很不利于我们编码

所以,国际标准化组织就站出来了,它们的目标是设计一种编码,包含直接上的每一种语言,这样在编码时,就可以不用再关注编码标准,使用这一款就可以了

它们的想法是,使用4个字节来编码,并且不管你的字符占不占的了4个字节,这样编码后,在解码时每次固定截取4个字节解码就行,也很方便

4个字节可以包罗42亿个字符,所以肯定是可以包括完世界上所有的字符的

那为什么我们使用的却不是UTF-32字符集呢?

原因就是太奢侈了,如世界上使用最多的语言英语,它的字符集一个只占一个字节,使用UTF-32之后,白白多了三个字节来存,这三个字节是直接浪费掉了,就为了对其4字节

所以UTF-32我们是不用的,而使用的是UTF-8

UTF-8字符集

UTF-8是Unicode采用可变长字符编码方案,共分为4个长度区:1个字节,2个字节,3个字节,4个字节

ASCII字符依旧是占一个字节,汉字则占3个字节

UTF-8的边长方案则是根据UTF-8二进制表来确定的

 按照这个表,如果一个字符的编码满足一个字节,则它的首位一定是0

如果一个汉字,则它肯定是三个字节,则第一个字节为1110开始,第二个字节为10开始,第三个字节是从10开始

如一个汉字的编码值为:25105

则二进制为:110 001000 010001

 则最后25105的UTF-8编码为: 11100110 10001000 10010001

注意点:

  • 我们在程序开发时尽量使用UTF-8编码
  • 使用哪种字符集就一定要使用此种字符解码
  • 英文,数字一般不会乱码,因为绝大多数字符集都兼容了ASCII

当使用GBK编码 a 你 b 时,解码使用UTF-8则会被解码成 a ? ? b

原因:

 Java编码和解码

 Java对字符进行编码:

byte[ ] getBytes();使用平台默认的字符集,对字符串进行编码,并且把它存在字节数组中

byte[ ]  getBytes(String charsetName);使用指定的字符集 ,对字符串进行编码,并把它存在字节数组中

public static void main(String[] args) throws UnsupportedEncodingException {
    //byte[] getBytes()使用默认的字符集编码,默认是UTF-8
    String s = "ac你";
    byte[] b1 = s.getBytes();
    System.out.println(Arrays.toString(b1));
    //[97, 99, -28, -67, -96] 两个字符2字节,一个汉字三字节,一共五个字节
    //byte[] getBytes(String charsetName) 使用指定的字符集编码
    byte[] b2 = s.getBytes("GBK");
    System.out.println(Arrays.toString(b2));
    //[97, 99, -60, -29] 两个字符占2字节,一个汉字两个字节,一共四个字节
}

 

Java对字符进行解码:

String(byte[ ] bytes);使用平台的默认字符集解码,将字节数组转换为字符串

String (byte[ ] bytes,String charsetName); 使用指定的字符集解码,将字节数组转为String

//使用平台的默认字符集,UTF-8
//String(byte[ ] bytes)
String s1 = new String(b1);
System.out.println(s1);//ac你
//String(byte[ ] bytes,String charsetName)
String s2 = new String(b2, "GBK");
System.out.println(s2);//ac你

 

三.IO流 

 I指的是Input,即为输入流,负责把数据写到内存中去

O指的是Output,即为输出流,负责将数据写出去

IO流主体有四种流:分别是字节输入流,字节输出流,字符输入流,字符输出流

 

 其中IO流的体系分为字节流和字符流

它们这两个类下面的抽象类有:

字节流:InputStream,OutputStream

字符流:Reader,Writer

 FileInputStream文件字节输入流

作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读到内存中(文件字节输入流)

 构造器:

public FileInputStream(File file)//传入一个文件

//public FileInputStream(File file)
File file = new File("JavaSeImprove/static/test/test.txt");
InputStream is1 = new FileInputStream(file);

 

pulic FileInputStream(String path)//传入一个路径

//pulic FileInputStream(String path)
InputStream is2 = new FileInputStream("D:\\其它\\Markdown\\Project\\JavaSeImprove\\static\\text\\test.txt");

 

 方法:

public int read()每次读取流中的一个字节,并输出编码,当文件没有数据后输出-1

//pulic FileInputStream(String path)
InputStream is2 = new FileInputStream("D:\\其它\\Markdown\\Project\\JavaSeImprove\\static\\text\\test.txt");
//读取一个字节,没有字节输出-1
//文本为 :abc
int r1 = is2.read();
System.out.println((char) r1);//a
int r2 = is2.read();
System.out.println((char) r2);//b
int r3 = is2.read();
System.out.println((char) r3);//c
int r4 = is2.read();
System.out.println(r4);//-1

 

像这样一行一行的读取是一种效率低的表现,我们完全可以使用循环读取,直接读取完一个文件

public static void main(String[] args) throws IOException {

        //pulic FileInputStream(String path)
        InputStream is2 = new FileInputStream("D:\\其它\\Markdown\\Project\\JavaSeImprove\\static\\text\\test.txt");

        //读取一个字节,没有字节输出-1
        //文本为 :abc

        int a;
        //循环读取字节
        while ((a = is2.read()) != -1){
            //由于流读取一次就没了,所以在比较循环时,先存储到变量中
            System.out.print((char) a);
            //abc
        }
        is2.close();
    }

 

注意:

  • read()方法一个一个读字节性能很差 
  • 读取汉字会乱码,无法避免,(1/2字节原因)
  • 流使用完了必须关闭,释放系统资源(io流的实现就是调用操作系统的接口,使用完了之后就释放,将接口管理交给系统)

 一次读入多个字节

 public int read(byte[ ]  buffer)每次用一个字节数组去读取数据,返回字节数组读取了多少个字节,没有数据读时可返回-1

public static void main(String[] args) throws IOException {
    //拿取到操作文件的管道,并建立链接
    FileInputStream is = new FileInputStream("static/text/test.txt");
    //循环读取文件
    byte[] buffer = new byte[8];//创建一次读取的容器
    int len;//方法会返回字节大小,len用来存储
    while ((len = is.read(buffer)) != -1){
        String rs = new String(buffer,0,len);//将buffer中的字符进行解码,从字节数组的0开始,len结束
        System.out.print(rs);//abcdefghijklmnopqrstuvwxyz
    }
}

 

使用buffer字节容器来装可以快速的读取多个字节是一种快速方式,但是它也会有乱码的情况,毕竟中英文在UTF-8所占的字节不一致

而想要不乱码,可选的方式是一次将全部字节读取完,这样就可以避免乱码了

public static void main(String[] args) throws Exception{
    //建立链接管道
    FileInputStream is = new FileInputStream("static/text/test.txt");
    //拿取到文件实例
    File file = new File("static/text/test.txt");
    //通过文件拿取到文件的大小
    long length = file.length();
    byte[] buffer = new byte[(int)length];//文件大小为long ,而数组仅支持int,所以强转一下
    //由于buffer和文件等大,所以直接读就行,不需要循环读
    int len;
    len = is.read(buffer);
    String rs = new String(buffer, 0, len);
    System.out.println(rs);
    //关闭流,不要忘了!!!!
    is.close();
}

 

一次读取完里面有个注意点

在通过文件对象拿取文件大小的时候,返回的大小类型是long,这是因为拿取为文件一般在硬盘上,所以大小会很大,一个文件几十G都是有可能的,所以long类型才可以装下

而在申请buffer数组时,很显然是在内存中,我pc的内存是16G很显然int容纳不了long类型的数组,所以new数组只支持int,这就是我们在代码中要强转一下的原因

此外,Java官方也知道,我们在使用IO流拿取一个文件时,大概率要一次读完,所以也提供了一个方法专门用于一次读取完字节

public byte[ ] readAllBytes()  throw IOException 直接将当前字节输入流对应的文件对象的字节装到一个数组中返回

public static void main(String[] args) throws Exception{
    //建立链接管道
    FileInputStream is = new FileInputStream("static/text/test.txt");
    //使用readAllBytes()方法读取完一个文件
    byte[] buffer = is.readAllBytes();
    String s = new String(buffer);
    System.out.println(s);
    //关闭流,不要忘了!!!!
    is.close();
}

 

但是要注意:此方法是JDK9才支持的方法,注意更新自己的JDK,不要万年1.8了

在读写文本方面,还是字符流更擅长,字节流更适合写非文本的数据

FileOutputStream文件字节输出流

作用:以内存为基础,把内存中的数据以字节的形式写入到文件中去

 构造器:

public FileOutputStream(File file);创建字节输出流管道与文件对象接通

public FileOutputStream(String pathName);使用路径创建链接

public FileOutputStream(String pathNmae,boolean append);创建链接,是否在源文件后追加;默认false,即覆盖原文件

public static void main(String[] args) throws IOException {
    //使用文件输出流,填写的路径可以不存在,写出时自己创建
    //创建文件对象和输出流的链接
    FileOutputStream os = new FileOutputStream("static/text/testOut.txt");
    
    //以文件的方式创建,且追加的形式写入文件
    File file = new File("static/text/test.txt");
    FileOutputStream osf = new FileOutputStream(file, true);
    

}

 

文件写入的相关方法write():

 public void write(int a );写一个字节出去

public static void main(String[] args) throws IOException {
    //使用文件输出流,填写的路径可以不存在,写出时自己创建
    //创建文件对象和输出流的链接
    FileOutputStream os = new FileOutputStream("static/text/testOut.txt");
    
    os.write('a');//一个字符底层就是int
    os.write(99);
    
    //关闭流,好习惯要养成
    os.close();
}

 

public void write(byte[ ] buffer );写一个字节数组出去

public static void main(String[] args) throws IOException {
    //使用文件输出流,填写的路径可以不存在,写出时自己创建
    //创建文件对象和输出流的链接
    FileOutputStream os = new FileOutputStream("static/text/testOut.txt");
    byte[] buffer = "我爱中国,我爱世界!".getBytes();
    os.write(buffer);
    //关闭流,好习惯要养成
    os.close();
}

 

public void write(byte[] , int pos  ,  int len);写限定长度出去

public static void main(String[] args) throws IOException {
    //使用文件输出流,填写的路径可以不存在,写出时自己创建
    //创建文件对象和输出流的链接
    FileOutputStream os = new FileOutputStream("static/text/testOut.txt");
    byte[] buffer = "我爱中国,我爱世界!".getBytes();
    os.write(buffer,0,12);//一个汉字3个字节
    //关闭流,好习惯要养成
    os.close();
}

 

文件复制

文件复制的思想是将已有的一个文件读入到内存,形成一个文件输入流

在利用这个流写出到目的位置,使用字节流就可以完成

 代码实现:

public static void main(String[] args) throws Exception {
    //拿取到文件管道
    FileInputStream is = new FileInputStream("C:\\Users\\86173\\Pictures\\原神.photo\\fywy.jpeg");
    //拿取到字节输入流
    byte[] buffer = is.readAllBytes();
    //拿取到文件输出管道
    FileOutputStream os = new FileOutputStream("D:/fywy.png");//直接写出到D盘
    //开始写出
    os.write(buffer);
    //关闭流
    is.close();
    os.close();
}

 

总结:任何文件的底层都是字节,使用字节流来完成复制时肯定没问题的,当然最后解析时的文件类型必须一致

资源释放

在上面写的关闭输入输出流的方法中,是直接关闭的,这样直接将OS.close()和is.close()方法写在最后其实是不安全的

当资源被申请后,如果中间的代码出现意外,那么程序就直接终止了,而关闭流的方法根本来不及执行,所以流就没有被关闭

所以更高级的写法应该将关闭流也要纳入安全考虑

第一种写法:try -- catch -- finally

使用try - catch,都知道是用来捕捉代码异常的,最后这个finally的主要工作是,不管try{ }语句中的代码有没有报错,都会被执行

此外,当try{}语句块中,有终止程序时,如return,break等关键字,finally也会被执行一次,才会被终止

唯一不被执行的情况就是jvm宕机了,jvm都没了,是程序肯定都是执行不了的

所以,根据以上观点,我们可以将流的关闭放在finally代码块中

如下:

public static void main(String[] args) {
    FileInputStream is = null;
    FileOutputStream os = null;
    try {
        //拿取到文件管道
        is = new FileInputStream("C:\\Users\\86173\\Pictures\\原神.photo\\fywy.jpeg");
        //拿取到字节输入流
        byte[] buffer = is.readAllBytes();
        //拿取到文件输出管道
         os = new FileOutputStream("D:/fywy.png");//直接写出到D盘
        //开始写出
        os.write(buffer);
        //关闭流,从内往外关
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        //流为空就不要关闭了,只有流存在才需要关闭
        try {
            if (os != null)
                os.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            if (is != null)
                is.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

 

finally语句块可以用于资源释放(专业做法)

但是,写法虽然很安全,但是代码很臃肿,看起来一大片的try--catch,很不优雅

既然我们想到了流关闭的安全问题,官方肯定也想到了,所以接下来看看官方推荐怎么做

第二种写法:try -- catch -- resource

这是jdk7之后的新增的写法,专门用于流的释放

在释放资源时,不在需要自己释放,而是由系统调用,因为资源都实现了AutoCloseable的接口

 如上图:以后申请资源的时候,应当写道try()的括号里,在这个括号内的资源,当try语句块执行完后,会自动调用close()方法,就不再需要我们取手动关闭流

代码如下:

public static void main(String[] args) {
    try (    //拿取到文件管道
            FileInputStream is = new FileInputStream("C:\\Users\\86173\\Pictures\\原神.photo\\fywy.jpeg");
            //拿取到文件输出管道
            FileOutputStream os = new FileOutputStream("D:/fywy.png");//直接写出到D盘
            ){
        
        //拿取到字节输入流
        byte[] buffer = is.readAllBytes();
        
        //开始写出
        os.write(buffer);
        //关闭流,从内往外关
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

 

这样写法释放资源,代码就很简洁,推荐使用

注意点:

  • 括号中的只能时资源,对资源的定义是实现或简洁实现了AutoCloseable接口
  • 当写入括号的是普通变量时,就会报错
  • 括号中是资源,try语句块执行完成后,自动关闭这些资源 

 

posted @ 2024-04-21 17:54  回忆也交给时间  阅读(18)  评论(0编辑  收藏  举报