File类和IO流

简介: File类和IO流


File类的概述

  • File位于java.io包下,是对于文件和路径(文件夹)的抽象表现方式
  • Java把文件中的文件和文件夹,封装成了一个File类,我们可以使用File类的方法创建、获取、删除、判断、遍历文件和文件夹
  • File类是一个与系统无关的类,任何一个操作系统都可以使用这个类当中的方法
  • file文件,directory文件夹,path路径

File类当中的成员变量

  • File类当中共计四个File静态成员变量
static String pathSeparator 
与系统相关的路径分隔符字符,为方便起见,表示为字符串。  
static char pathSeparatorChar 
与系统相关的路径分隔符。  
static String separator 
与系统相关的默认名称 - 分隔符字符,以方便的方式表示为字符串。  
static char separatorChar 
与系统相关的默认名称分隔符。
/*
     * @Description:File文件中的四个静态成员变量
     * @Author: DaShu
     * @Date: 2021/5/31 21:31
     */
    @Test
    public void test01(){
        //文件分隔符:win是\,Linux是/
        System.out.println(File.separator);
        System.out.println(File.separatorChar);
        //路径分隔符:win是;Linux是:
        System.out.println(File.pathSeparator);
        System.out.println(File.pathSeparatorChar);
        //        \
        //        \
        //        ;
        //        ;
    }

绝对路径和相对路径

绝对路径是一个完整的路径,以盘符开始的路径,相对路径是一个简化的路径,相对指的是相对于当前项目的根目录

File类当中常用方法介绍

package com.pactera.io;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
 * @Auther: DaShu
 * @Date: 2021/6/1 16:13
 * @Description: 测试File类的常用方法
 *               获取:判断:创删
 */
public class FileMethodTest {
    public static void main(String[] args) throws Exception {
        getAbsolutPath();
        System.out.println("-----------------------getAbsolutPath();------------------------");
        getPath();
        System.out.println("-----------------------getPath();------------------------");
        getName();
        System.out.println("-----------------------getName();------------------------");
        length();
        System.out.println("-----------------------length();------------------------");
        exists();
        System.out.println("-----------------------exists();------------------------");
        isDirectory();
        System.out.println("--------------------------isDirectory();----------------------------");
        isFile();
        System.out.println("-----------------------isFile()--------------------------------------");
        createNewFile();
        System.out.println("----------------------------createNewFile();----------------------------------------");
        mkdirAndmkdirs();
        System.out.println("------------------------mkdirAndmkdirs()--------------------------------------------");
        delete();
        System.out.println("-----------------------delete()-----------------------------");
    }
    /*
     * @Target:研究File类getAbsolutePath()路径
     * @Author: DaShu
     * @Date: 2021/6/1 16:15
     * @Result:获取的是对象当中的路径,不论当时对象里封装的是相对路径还是绝对路径,返回的都是绝对路径
     */
    public static void getAbsolutPath(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        File absoluteFile = file1.getAbsoluteFile();
        System.out.println("absoluteFile="+absoluteFile);
        //absoluteFile=D:\DevelopPackage\codeshop\Spring5.x\a.txt
        File file2 = new File("a.txt");
        File absoluteFile1 = file2.getAbsoluteFile();
        System.out.println("absoluteFile1="+absoluteFile1);
        //absoluteFile1=D:\DevelopPackage\codeshop\Spring5.x\a.txt,所谓的这个项目的根路径指的是项目所在的文件夹。
    }
    /*
     * @Target:研究File类getPath();
     * @Author: DaShu
     * @Date: 2021/6/1 16:23
     * @Result: 对象当中封装的是绝对的,就是绝对的,相对的就是相对的。
     *          toString()方法调用的就是getPath()方法
     */
    public static void getPath(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        String path = file1.getPath();
        System.out.println("path="+path);
        //path=D:\DevelopPackage\codeshop\Spring5.x\a.txt
        File file2 = new File("a.txt");
        String path1 = file2.getPath();
        System.out.println("path1="+path1);
        //path1=a.txt
    }
    /*
     * @Target:研究File类getName();
     * @Author: DaShu
     * @Date: 2021/6/1 16:32
     * @Result:无论是什么路径获取的都是最后一段,对象当中的路径可以是绝对的,可以是相对的。
     */
    public static void getName(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        System.out.println(file1.getName());//a.txt
        File file2 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x");
        System.out.println(file2.getName());//Spring5.x
    }
    /*
     * @Target:研究File类length();
     * @Author: DaShu
     * @Date: 2021/6/1 16:35
     * @Result:文件夹是没有大小的,不能获取文件夹的大小,获取的也是文件夹所有文件的大小
     *         如果路径当中给出的路径不存在,那么length方法返回的值是0;
     */
    public static void length(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        long length = file1.length();
        System.out.println(file1.getAbsolutePath());
        System.out.println(file1.exists());
        System.out.println("length="+length);
        File file2 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x");
        long length2 = file2.length();
        System.out.println("length2="+length2);
        System.out.println(file2.hashCode() == file1.hashCode());
    }
    /*
     * @Target:判断File类当中的exists()方法是否存在
     * @Author: DaShu
     * @Date: 2021/6/1 17:04
     * @Result: 判断路径是否存在
     */
    public static void exists(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        System.out.println(file1.exists());//true
        File file2 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x");
        System.out.println(file2.exists());//true
        File file3 = new File("a.txt");
        System.out.println(file3.exists());//true
        File file4 = new File("b.txt");
        System.out.println(file4.exists());//false
    }
    /*
     * @Target:判断File类当中的isDirectory()方法是否存在
     * @Author: DaShu
     * @Date: 2021/6/1 17:04
     * @Result: 判断给定路径是否以为文件夹结尾
     *     注意:电脑中的文件要么是文件,要么是文件夹,这两个文件夹的路径必须是存在的否则就是默认返回false
     *
     */
    public static void isDirectory(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        System.out.println(file1.isDirectory());//false
        File file2 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x");
        System.out.println(file2.isDirectory());//true
        File file3 = new File("a.txt");
        System.out.println(file3.isDirectory());//false
        File file4 = new File("b");
        System.out.println(file4.isDirectory());//false
    }
    /*
     * @Target:判断File类当中的isFile()方法是否存在
     * @Author: DaShu
     * @Date: 2021/6/1 17:04
     * @Result: 判断给定路径是否以为文件夹结尾
     *     注意:电脑中的文件要么是文件,要么是文件夹,这两个文件夹的路径必须是存在的否则就是默认返回false
     *
     */
    public static void isFile(){
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\a.txt");
        System.out.println(file1.isFile());//false
        File file2 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x");
        System.out.println(file2.isFile());//true
        File file3 = new File("a.txt");
        System.out.println(file3.isFile());//false
        File file4 = new File("b");
        System.out.println(file4.isFile());//false
    }
    /*
     * @Target: File文件当中的createFile()方法
     * @Author: DaShu
     * @Date: 2021/6/1 17:19
     * @Result: createNewFile方法声明抛出了异常,我们调用也必须处理异常。
     */
    public static void createNewFile() throws Exception {
        File file1 = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\b.txt");
        boolean newFile = file1.createNewFile();
        System.out.println(newFile);//文件不存在的时候,才会创建文件,返回TRUE,此方法只能创建文件,不能创建文件夹,创建文件的路径必须存在,否则会抛出异常。
        //public boolean createNewFile() throws IOException {
        //    SecurityManager security = System.getSecurityManager();
        //    if (security != null) security.checkWrite(path);
        //    if (isInvalid()) {
        //        throw new IOException("Invalid file path");
        //    }
        //    return fs.createFileExclusively(path);
        //}
        File file2 = new File("c.bat");
        boolean newFile1 = file2.createNewFile();
        System.out.println(newFile1);//
        File file4 = new File("新建文件");
        boolean newFile2 = file4.createNewFile();
        System.out.println(newFile2);//没有文件后缀也能创建文件
    }
    /*
     * @Target: File文件当中的mk()方法
     * @Author: DaShu
     * @Date: 2021/6/1 17:30
     * @Result:
     */
    public static void mkdirAndmkdirs(){
        //mkdir创建单击文件夹,另一个创建单机多级都可以。
        //文件夹不存在创建文件夹,返回TRUE,存在返回false
        //构造方法中的文件路径不存在返回false
        File file = new File("com");
        boolean mkdir = file.mkdir();
        System.out.println(mkdir);
        File file01 = new File("com\\pactera\\aaa\\shit");
        boolean mkdirs = file01.mkdirs();
        System.out.println(mkdirs);
        File file02 = new File("com\\pactera\\aaa\\shit\\shit.txt");
        boolean mkdirss = file02.mkdirs();
        System.out.println(mkdirss);
    }
    /*
     * @Target: File文件当中的delete方法
     * @Author: DaShu
     * @Date: 2021/6/1 17:39
     * @Result: 删除文件和文件夹
     */
    public static void delete(){
        //删除成功返回TRUE,删除失败返回false,路径错误返回false,
        File file = new File("com");
        boolean mkdir = file.delete();
        System.out.println(mkdir);
        File file01 = new File("com\\pactera\\aaa\\shit");
        boolean mkdirs = file01.delete();
        System.out.println(mkdirs);
        File file02 = new File("com\\pactera\\aaa\\shit\\shit.txt");
        boolean mkdirss = file02.delete();
        System.out.println(mkdirss);
    }
}

File类的遍历方法和迭代

  • 文件搜索的原理也是递归
package com.pactera.io;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.io.File;
/**
 * @Auther: DaShu
 * @Date: 2021/6/2 17:31
 * @Description: 文件目录的遍历
 */
public class FileRoll {
    public static void main(String[] args) {
        list_and_listFile();
        File file = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\io");
        System.out.println("------------------------------");
        getAllFiles(file);
    }
    /*
     * @Target: list方法,listFile方法
     * @Author: DaShu
     * @Date: 2021/6/2 17:31
     * @Result:这两个方法遍历的的是File构造路径中的目录,如果路径是假的或者是文件
     *         就会抛出一个空指针异常,这个遍历的方法可以获取到隐藏的文件和文件夹,
     */
    public static void list_and_listFile(){
        File file = new File("D:\\DevelopPackage\\codeshop\\Spring5.x\\io");
        String[] list = file.list();
        for (String s : list) {
            System.out.println(s);
            //io.iml
            //pom.xml
            //src
            //target
        }
        File[] files = file.listFiles();
        for (File file1 : files) {
            System.out.println(file1.getAbsoluteFile());
            //D:\DevelopPackage\codeshop\Spring5.x\io\io.iml
            //D:\DevelopPackage\codeshop\Spring5.x\io\pom.xml
            //D:\DevelopPackage\codeshop\Spring5.x\io\src
            //D:\DevelopPackage\codeshop\Spring5.x\io\target
        }
        for (File file1 : files) {
            System.out.println(file1.getName());
            //io.iml
            //pom.xml
            //src
            //target
        }
    }
    //递归,递归的意思就是方法调用自己,
    //递归一定要有条件,要让方法能够停下来,防止栈内存溢出。
    //递归中虽然有条件限定,但是递归次数也不能太多,否则也会繁盛栈内存溢出。
    //构造方法禁止递归
    //为什么会发生栈内存溢出呢?
    public static void getAllFiles(File dir){
        File[] files = dir.listFiles();
        for (File file : files) {
            if(file.isFile()){
                System.out.println(file.getName());
            }else{
                System.out.println(file.getName());
                getAllFiles(file);
            }
            //io.iml
            //pom.xml
            //src
            //main
            //java
            //com
            //pactera
            //io
            //FileMethodTest.java
            //FileRoll.java
            //resources
            //test
            //java
            //target
            //classes
            //com
            //pactera
            //io
            //FileMethodTest.class
            //FileRoll.class
            //generated-sources
            //annotations
        }
    }
}

文件过滤器优化

文件过滤器是一个接口,刚才我们这个需求可以进行优化,我们可以使用过滤器来实现迭代文件和文件夹的接口,在File类当中由两个listFile()重载的方法,方法的参数就是过滤器,

在file类当中有两个和listFile()重载的方法,方法的参数传递的就是过滤器,是一个接口

  • 先说listFiles(FileFilter f)这个方法,作用用于过滤文件,过滤的内容都是File对象, 其中有一个抽象方法,boolean accept(File file) 测试指定抽象路径名是否应该包含在某个路径名列表中。
  • 方法的参数是就是file对象,
  • 另一个方法,参数也是一个接口,是一个文件名称过滤器,用于过滤文件名称,用于过滤抽象方法,叫做accept,参数有两个,File对象, String name 使用listFile方法比那里目录获取的每一个文件、文件夹的名称。
    注意事项:两个过滤器接口没有实现类,需要我们自己写实现类,重写过滤方法,在方法中自己定义过滤的规则。
//只要.java结尾的文件。
    public static void getAllFiles01(File dir){
        //方法一:
        FileFilter f1 = new FIleFilterImpl();
        f1 = new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                //如果pathname是一个文件夹,返回TRUE,不能遍历数组。
                return pathname.getName().toLowerCase().endsWith(".java") || pathname.isDirectory();
            }
        };
        File[] files = dir.listFiles(f1);
        //方法二:
        files = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return new File(dir,name).isDirectory()||name.toLowerCase().endsWith(".java");
            }
        });
        //方法三:使用lambda表达式
        files = dir.listFiles((fileType,name)->new File(fileType,name).isDirectory()||name.toLowerCase().endsWith(".java"));
        for (File file : files) {
            if(file.isFile()){
                System.out.println(file.getName());
            }else{
                System.out.println(file.getName());
                getAllFiles(file);
            }
        }
    }

io概述

这个是硬盘,硬盘在不停的转,硬盘在不停转的同时与磁头进行接触读取到硬盘上不同的文件。内存当中的数据存储是临时存储,电脑已关机,数据就没了,io,就是读取和输出。流:就是数据,数据共有两种:字节流和字符流,一个字符占两个字节,也就是16个二进制的位。一个字节=8个二进制的位,

数据分为字节和字符,所以流也分为:

这四个也是io流的最顶层的四个父类。

字节流

一切皆为字节

硬盘上的视频,图片,文本,TXT文件,。。都是以字节的方式存储的,存储的任意的字节都是字节,那么作为流传输的时候也是以二进制字节的方式进行传输的,**所以,这个字节流可以读取任意的文件。**所有的流都在。java.io包下

OutputStream流

字节输出流的最顶层的父类,是一个抽象类,一般超类当中定义的都是公共的所有类都能使用的方法,

第二个是往文件当中写内容的输出流。这个流也叫:文件字节输出流,作用就是把内存中的数据写入到硬盘的文件中。

流使用的时候会占用一定的jvm内存和操作系统资源,使用完毕之后一定要关闭流,减少资源的占用,提高程序的效率。

文件存储的原理

输出多个字节

public static void write02() throws IOException {
        OutputStream fos = new FileOutputStream("io\\src\\main\\java\\com\\pactera\\io\\b.txt");
        fos.write(49);//写出去一个字节
        fos.write(48);//写出去一个字节
        fos.write(-48);//写出去一个字节
        byte[] bytes = {-65,-66,67,-68,69};
        fos.write(bytes);
        byte[] bytes1 = {65,66,67,68,69};
        fos.write(bytes1,1,2);//10锌綜糆BC
        byte[] bytes2 = "你好".getBytes();
        System.out.println(Arrays.toString(bytes2));//[-28, -67, -96, -27, -91, -67]
        fos.write(bytes2);
        fos.close();
    }

数据追加(非覆盖重写)+换行

\r\n也是个字符串。

字节输入流InputStream

这是一个抽象类。这是所有的字节输入流的超类。定义类所有的字节输入流的共性的方法。

java.io.FileInputStream extends inputStream这个流叫做文件字节输入流。

可以把硬盘文件中的数据读取到内存中使用,这是他的一个作用,

构造方法

我们用第一个和第三个,他的参数是文件的数据源,Strring文件路径,File是文件对象。

常用方法进行数据读取

读取数据的原理:

public static void main(String[] args) throws Exception {
        InputStream fis = new FileInputStream("spring5.x\\a.txt");
        //一次读一个,读取文件中的一个字节,并进行返回,读取到文件的末尾,会返回-1
        //这个函数是一个特殊的函数,读一次会把他的指针往回挪一位(流对象当中的指针),所以,每次调用read方法得到的结果都不一样。
/*        int read = fis.read();
        System.out.println(read);
        read = fis.read();
        System.out.println(read);
        read = fis.read();
        System.out.println(read);
        read = fis.read();
        System.out.println(read);
        read = fis.read();
        System.out.println(read);*/
        //97
        //98
        //99
        //-1
        //-1
        //以上读取文件是一个重复的过程,重复的过程我们可以使用循环优化,不知道文件中由多少个字节,使用while循环
        int num ;
        while( (num = fis.read()) != -1){
            System.out.println((char)num);
        }
/*        97
        98
        99*/
        fis.close();
    }
  • 字节输入流读取文件的一个原理

结束标记是看不到的,是window系统的一个结束标记。

创建了FileInputStream之后就就创建了一这样的对象,这个对象指向的硬盘上的对应路径的那个文件,并且指向的是那个文件的第一个字节。使用read方法的时候,并不是直接read方法去读文件,而是read方法被虚拟机调用,虚拟机去找os,os去找对应的操作函数,函数去找文件,是这样的一个过程。数据回来的时候也是文件数据给函数,函数给操作系统,os给jvm,对应的指针向后移动一位,指针到了结束标记之后,就会把结束标记给操作系统,操作系统把结束标记给jvm,jvm把-1给我们。所以,Linux一样。

文件的复制

文件当中都是以字节存储的,我们使用字节流可以读取任意类型的文件,我们的目地就是将文件读进来之后在存进其他的盘当中,我们怎么进行文件的复制呀就是一读一写,读进jvm内存之后在写到磁盘上。

/**
 * @Auther: DaShu
 * @Date: 2021/6/7 19:17
 * @Description:
 */
public class Copy {
    public static void main(String[] args) throws Exception {
        File file;
        FileInputStream fis = new FileInputStream("D:\\love.shit");
        FileOutputStream fos = new FileOutputStream("d:\\lovefuct.shit");
        int len;
        while((len = fis.read()) != -1){
            System.out.println(len);
            fos.write(len);
        }
        //关闭流的话肯定是先关闭输出的,在关闭输入的,如果重复执行这个方法的话,会先把之前复制的文件给删除掉,在进行复制
        fos.close();
        fis.close();
    }
}
/**
 * @Auther: DaShu
 * @Date: 2021/6/7 19:17
 * @Description:
 */
public class Copy {
    public static void main(String[] args) throws Exception {
        long begin = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("D:\\love.shit");
        FileOutputStream fos = new FileOutputStream("d:\\mama.shit");
        int len;
        byte[] bytes = new byte[10240000];
//        while((len = fis.read(bytes)) != -1){
          System.out.println(len);
            fos.write(bytes,0,len);
        }
        len = fis.read(bytes);
        fos.write(bytes,0,len);
        int i = 0;
        FileInputStream fis1 = new FileInputStream("D:\\love.shit");
        FileOutputStream fos1 = new FileOutputStream("d:\\mama1.shit");
        while((len = fis1.read(bytes))!=-1){
            System.out.println(i);
            i++;
            fos1.write(bytes,0,len);
        }
        fos.close();
        fis.close();
        fos1.close();
        fis1.close();
        long end = System.currentTimeMillis();
        System.out.println(end - begin + "mm");
    }
}

字节流读取中文

/**
 * @Auther: DaShu
 * @Date: 2021/6/7 19:51
 * @Description: 使用字节流读取中文
 */
public class ChineseTest {
    public static void main(String[] args)throws IOException {
        FileInputStream fileInputStream = new FileInputStream("d:\\aaa.txt");
        int len = 0;
        /*
         * @Description:
         * @Author: DaShu
         * @Date: 2021/6/7 19:55
         * Gbk的中文占用两个字节,utf8的中文占用3个字节,
         */
        while((len = fileInputStream.read())!=-1){
            System.out.println((char)len);
        }
        //228       ä               
        //189       ½
        //160        
        //229       å
        //165       ¥
        //189       ½
        fileInputStream.close();
    }
}

使用字节流读取中文的时候会由于gbk和utf8的编码占用空间的不同,字节流读取的字符是一半或者三分之一,这样的话,使用字符可能出现所谓的乱码的问题,为了解决这个问题,java当中产生了字符流,使用字符流可以读取中文英文等任意类型的东西。

Reader字符输入流最顶层父类

Reader当中定义了公共的使用的方法,他是一个抽象类。

我们使用FileReader读取问价拿的字符输入流。

字符输入流的使用步骤:

/**
 * @Auther: DaShu
 * @Date: 2021/6/7 20:01
 * @Description:
 */
public class ReaderDemo {
    public static void main(String[] args) throws Exception {
        Reader reader = new FileReader("d:\\aaa.txt");
//        int len = 0;
//        while((len = reader.read()) != -1){
//            System.out.print((char)len);
//            //你好avxd##$$%%@@#
//        }
        char[] cs = new char[1024];
        int len = 0;//len记录的事读取到的有效字符的个数
        //new String(char[])
        //new String(char[],begin,lenth
        while ((len = reader.read(cs)) != -1){
            System.out.println(new String(cs,0,len));//你好avxd##$$%%@@#
        }
        reader.close();
    }
}

Writer字符输出流最顶层父类

这是一个抽象类,是字节输出流的最顶层的父类,定义了公共的方法,定义了共性的方法,这个流可以直接写一个字符,写一个数组,写数组一部分,直接写字符串,定义了共性成员方法,FileWriter继承了一个OutputStreamWriter,继承了writer,

FileWriter叫做文件字符字符流,将内存中的字符传输来写入到文件当中,

字符输出流的使用的过程,

创建Filewriter对象,构造方法中绑定要写入数据的目的地,

使用filewriter中的方法write将数据写入内存当中的缓存区,(这个是将字节转为字符的过程)

使用FIleWriter当中的方法flush将内存缓冲区的数据,刷新到文件中,

释放资源(close)会先把内存缓冲区当中的数据刷新到文件中,所以这个flush这个方法可以不用写,字符流根字节流最大的区别就在于,不是直接将数据写入到文件中而是先把数据写入到内存中。

这个FileWriter的方法并不是直接将数据写入到硬盘上,如果我们执行write方法之后,既不close,又不flush方法,内容还在缓冲区当中,是不会到磁盘上的,所以文件是空的。 他这个写入到字节缓存区是在内存当中,并没有进入磁盘。

/**
 * @Auther: DaShu
 * @Date: 2021/6/7 20:28
 * @Description:
 */
public class FileWriterTest {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\d.txt");
        //写单个字符
        fw.write(97);
        fw.flush();
        //执行close方法之前会先将缓冲区当中的流刷入磁盘当中。
        fw.close();
    }
}

close方法和flush方法的区别

flush:刷新缓冲区,流对象还可以继续使用

flush:刷新缓冲区,然后通知系统释放资源,流对象不可以在使用了。

写数据的其他方法

/**
 * @Auther: DaShu
 * @Date: 2021/6/7 20:50
 * @Description:
 */
public class FileWriterTest01 {
    public static void main(String[] args) throws Exception {
        FileWriter fw = new FileWriter("d:\\e.txt");
        //写字符数组
        char[] cs = {'a','b','c','d','e'};
        fw.write(cs);
        //写字符数组的一部分
        fw.write(cs,1,3);
        //写字符串
        fw.write("传智播客");
        fw.write("黑马程序员",2,3);
        //abcdebcd传智播客程序员
        fw.close();
    }
}

字符流的续写和换行

这个和字节流一模一样。

续写和追加写,使用两个参数的构造方法就可以了。

流异常的处理

jdk1.7之前可以使用try catch finally来进行异常处理。

变量的作用于只在变量的声明的大扩招当中有效。

/**
 * @Auther: DaShu
 * @Date: 2021/6/7 21:07
 * @Description:
 */
public class FileWriteTest02 {
    public static void main(String[] args) {
        //提高fw的作用域,这的变量是一个局部变量,变量使用的时候必须有值
        //创建对象有可能失败,这样的话,fw没有值,所以需要一个初始化的值,所以编译不过去
        FileWriter fw = null;
        try {
            fw  = new FileWriter("D:\\d.txt");
            fw.write(97);
        }catch (IOException e){
            System.out.println(e);
        }finally {
            //如果fw创建失败了,fw是null。
            if(fw != null){
                try {
                    //fw.close声明抛出异常,有可能报错,所以我们处理这个对象要么try ,
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

JDK7的新特性

在jdk7以后在try后边可以增加一个(),在括号中定义流对象,这个流对象的作用于是try当中有效,try当中的代码执行完毕就会自动把对象释放,不用写finally

try(定义一个或者多个流对象){

}catch(){

}

class StreamTest {
    public static void main(String[] args) {
        try(FileInputStream fis = new FileInputStream("D:\\love.shit");
            FileOutputStream fos = new FileOutputStream("d:\\mama.shit");){
            long begin = System.currentTimeMillis();
            int len;
            byte[] bytes = new byte[10240000];
            len = fis.read(bytes);
            fos.write(bytes,0,len);
            long end = System.currentTimeMillis();
            System.out.println(end - begin + "mm");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

jdk9的流的新特性

在jdk7的新特性上做了一点点修改。

两种方式try完成之后流已经关闭了,

属性集Properties

HashTable是最早期的一个双列集合,在jdk1.0的时候就存在了,但是因为是单线程被淘汰了,他的子类Properties依旧活跃,因为她是唯一一个和IO相结合的集合,Propertyes类表示的是一个持久的属性集,属性列表当中的键和值都是一个字符串,

java.util.Properties集合 extends hashtable implements Map

Properties是一个唯一一个和io流相结合的结合,可以使用store方法吧集合当中的临时数据写入到磁盘当中,可以使用properties集合当中的load方法把硬盘当中保存的文件(键值对),读取到集合中进行使用。这个属性列表当中的键和值都是一个字符串他是一个双列结构,并且他们的键和值都是一个字符串,所以不用使用泛型了,默认都是字符串

  • Properties集合当中操作字符串的方法
/**
 * @Auther: DaShu
 * @Date: 2021/6/7 21:39
 * @Description:
 */
public class PropertiesTest {
    public static void main(String[] args) {
        show();
    }
    public static void show(){
        Properties properties = new Properties();
        properties.setProperty("赵丽颖","168");
        properties.put("古力娜扎","shist");
        properties.setProperty("迪丽热巴","165");
        Set<String> strings = properties.stringPropertyNames();
        for (String string : strings) {
            String property = properties.getProperty(string);
            System.out.println(property);
            //168
            //shist
            //165
        }
    }
}
  • properties当中的store方法
public static void show02() throws IOException {
        Properties properties = new Properties();
        properties.setProperty("赵丽颖","168");
        properties.put("古力娜扎","shist");
        properties.setProperty("迪丽热巴","165");
        FileWriter fw = new FileWriter("d:\\prop.txt");
//        properties.store(fw,"save data");
        //匿名对象不用关,因为匿名对象使用完之后自己就关了
        //字符流可以写中文,字节流不可以写中文。
        properties.store(new FileOutputStream("d:\\prop11.txt"),"save data");
        fw.close();
        //#save data
        //#Mon Jun 07 21:57:16 CST 2021 这个时间是他自己家的。
        //赵丽颖=168
        //古力娜扎=shist
        //迪丽热巴=165
    }
  • load方法
    这个是今天的主要的方法,load方法可以把硬盘中保存的文件读取到内存当中,
public static void show03() throws Exception {
        Properties prop = new Properties();
        prop.load(new FileReader("d:\\prop11.txt"));
        Set<String> strings = prop.stringPropertyNames();
        for (String string : strings) {
            System.out.println(string + "=" + prop.getProperty(string));
            //一般我们使用这个load方法都用字符流。
            //迪丽热巴=165
            //古力娜扎=shist
            //赵丽颖=168
        }
    }

缓冲流

缓冲流的概念

缓冲流能够高效的读写,能够转换编码,能够持久化存储对象的序列化流,这些功能强大的缓冲流都是在基本的流对象基础之上创建来的,就像穿上铠甲的勇士一样,相当于是对基本流对象的增强。

缓冲流的原理

每个文件当中都有一个结束标记,现在使用一个流读取文件,如果使用基本的字节输入流的话,FileInputStream 中的read方法进行读取。读取的过程java程序要找到jvm,jvm要找到操作系统,操作系统要找到相应的函数来进行读取。字节的读取的时候,读取一个字符到什么然后进行一级一级的返回。这样的话就非常的影响效率,二使用字节缓冲输入流的话,就会给基本的字节输入流增加一个缓冲区,也就是一个数组,提高基本的字节输入流的效率,这是字节缓冲输入流,字节缓冲输出流也是这个流程,比如说bufferedInputStream它里边需要传入一个基本的字节输入流,FileInputStream,有了这个高效的字节缓冲输入流之后,有一个缓冲区数组,通过这个数组可以一次性把所有的数组读取进来,在把这个数组整体进行返回。

注意:不管是字节的还是字符的都是增加了一个这样的数组,提高了读取的效率。

BufferedOutputStream

继承了OutputStream所以,这是个字节流。字节缓冲输出流。集成共性的父类的构造方法

构造方法

一共有两个。

/**
 * @Auther: DaShu
 * @Date: 2021/6/8 19:00
 * @Description: 研究bufferdOutputStream
 */
public class BufferdOutputStreamTest {
    public static void main(String[] args) throws IOException{
        File file = new File("d:\\bufferedOutputStream.txt");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
        bos.write("我把数据写入到内存缓冲区".getBytes());
        bos.flush();//flush方法才会把数据刷入到文件当中。
        bos.close();
    }
}

BufferedInputStream

继承了inputStream也是一个字节流,叫字节缓冲输入流,他的集成父类的成员方法。字节输入流有这三个方法。

构造方法

两个:默认缓冲区大小,指定缓冲区大小。需要一个FileInputStream对象。size是指定缓冲区大小。

class BufferedInputStreamTest{
    public static void main(String[] args) throws IOException {
        File file = new File("d:\\BufferedOutputStream.txt");
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
//        int len;
//        while((len = bis.read()) != -1){
//            System.out.println(len);
//        }
        //这个效率最快。
        byte[] bytes = new byte[1024];
        int len = 0;
        while((len = bis.read(bytes)) != -1){
            System.out.println(new String(bytes,0,len));
        }
        bis.close();//资源释放关闭缓冲流就可以了,关闭缓冲流就可以关闭对应的普通流
    }
}

效率测试

class BufferedVSUniverse{
    public static void main(String[] args) throws IOException {
        try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("d:\\test.txt")));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("d:\\test01.txt")));
            FileInputStream fis = new FileInputStream(new File("d:\\test.txt"));
            FileOutputStream fos = new FileOutputStream(new File("d:\\test2.txt"));
        ){
            long start01 = System.currentTimeMillis();
            byte[] bytes = new byte[204800];
            int len ;
            while((len = bis.read(bytes)) != -1){
                bos.write(bytes,0,len);
            }
            bos.flush();
            long end01 = System.currentTimeMillis();
            System.out.println("buffered流的用时为:"+ (end01-start01)+"mm");
            bos.close();
            long start02 = System.currentTimeMillis();
            while((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,len);
            }
            fos.flush();
            long end02 = System.currentTimeMillis();
            System.out.println("非buffered流的用时为:"+ (end02-start02)+"mm");
        }catch (Exception e){
            System.out.println(e);
        }
    }
}
结论:

一个700多kb的文件,使用普通的输入输出流,单个字节读取需要6000多mm,使用数组需要10mm提升了600多倍,buffered这样的流的话进行读取,一个一个字节进行读取的话需要32mm,而使用数组的话大约需要5mm。

bufferedWriter

bufferedWriter extends writer 叫做字符缓冲输出流,集成自父类的成员方法。

println当中调用的换行符号就是newline(),他的作用就是写一个换行符号

在这里插入代码片

bufferedReader

字符缓冲输入流,extends Reader 继承了成员方法。

文本内容进行排序。

转换流

字符编码和字符流。

计算机当中只能识别10101,计算机当中存储的也是二进制数据,计算机当中的文件和视频多事二进制存储的,但是将图片记性打开的时候并不是1010,而是图或者视频,任何一个软件都有转换功能,经1010转换成字符,图片,

我们使用A编码进行编码,使用A编码进行解析就可以显示正确的文本,我们使用A规则存储使用B规则进行解析就会出现乱码的情况。

任何一个编码表都兼容了asill表,这是一个最基本的编码表。

双编码的意思就是任何一个字符都是采用两个字节进行存储,所以gbk一个字符使用两个字节

GB18030使用的是多字节编码,有可能是一个字节,有可能是两个字节,有可能是四个字节,支持的文字非常多。

万国码:Unicode,

gbk当中两个字节存储一个中文,utf-8是三个字节对应一个中文,什么叫编码表,就是生活中的文字与计算机的存储的一种对应规则。

编码引出的问题

/**
 * @Auther: DaShu
 * @Date: 2021/6/9 20:09
 * @Description:
 */
public class GbkTest {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader(new File("d:\\gbk编码.txt"));
        int len = 0;
        while((len = fr.read()) != -1){
            System.out.print((char)len);//����Һô�Һ�
        }
    }
}

编码和解码的方式不一样了。所以会造成乱码。

转换流

转换流的原理

FilerReader的原理是:底层使用fileInputStream这个字符流进行读取,然后fileReader查询utf8编码表进行转码把字节转换成我们能看懂的字符,

InputStreamReader是FileReader的父类,这个是字节流通向字符流的桥梁,他可以把字节流转换为字符流,他可以使用指定的字符集,

编码不一样占用的空间也不一样,字节的读写只能使用字节流,所以任何流的底层都是字节流。FileReader只能查询系统默认码表。

OutputStramWriter

以writer结尾肯定是一个字符流,extends Writer这是字符通过字节的桥梁,可以指定编码表,

class OutputStreamWriterTest{
    public static void main(String[] args) throws Exception{
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File("d:\\xxx.txt")),"GBK");
        osw.write("我爱你中国,亲爱的目前");
        osw.flush();
        osw.close();
        OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream(new File("d:\\xxxxxx.txt")));
        osw1.write("我爱你中国,亲爱的目前");
        osw1.flush();
        osw1.close();
    }
}

InputStreamReader

字节流通向字符流的桥梁,extends Reader是一个字节流,按照指定字符集进行解码。

文件转码

序列化流和反序列化流

  • 什么叫序列化,什么叫反序列化
    把对象写入到磁盘中去保存

序列化接口是一种标记性接口

反序列化

瞬态关键字 transient

静态的是不能被序列化的,

被transient修饰的成员变量也不能序列化,

被他修饰不能被序列化,就这么点东西。

每次反序列化的时候都会验证一个class文件的 序列号和 序列化文件的序列号是否相同

可以通过显示生命序列号的方法进行解决。

写这个值得目的在于不论我们如何更改我们的代码,我们的这个序列号都不会改变。这样的话,不论这个类怎么改,我都用的是这个序列号。

序列化集合

文件中保存多个对象的时候,我们可以吧多个对象存到一个集合中,对集合进行序列化和反序列化。目的:一次序列化多个对象

class ObjectOutputStreamTest{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\oos.txt")));
        List<Teacher> list = new ArrayList<>();
        list.add(new Teacher("小红","河北省石家庄市正定县"));
        list.add(new Teacher("aaa","河北省石家庄市正定县"));
        list.add(new Teacher("bbb","河北省石家庄市正定县"));
        list.add(new Teacher("ccc","河北省石家庄市正定县"));
        list.add(new Teacher("ddd","河北省石家庄市正定县"));
        oos.writeObject(list);
        oos.flush();
        oos.close();
        InputStream in;
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\oos.txt")));
        List<Teacher> listTeachers = (List<Teacher>)ois.readObject();
        ois.close();
        for (Teacher listTeacher : listTeachers) {
            System.out.println(listTeacher.getAddr());
            System.out.println(listTeacher.getName());
        }
        //河北省石家庄市正定县
        //小红
        //河北省石家庄市正定县
        //aaa
        //河北省石家庄市正定县
        //bbb
        //河北省石家庄市正定县
        //ccc
        //河北省石家庄市正定县
        //ddd
    }
}

打印流PrintStream

打印流我们们天都在用

System.out.println

java.in.printStream 打印流:extends

特点:只负责数据的输出,不负责读取

这个流永远不会抛出ioException;

里边有特有的方法,println,print两个方法。

构造方法是

注意事项:

如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97-a,如果使用他自己的特有的print或者println方法写数据,写的数据原样输出,比方说写97打印的就是97

相关文章
|
22天前
|
存储 缓存 安全
Java 中 IO 流、File文件
Java 中 IO 流、File文件
|
6天前
|
Java Unix Windows
|
4月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
53 0
|
3月前
|
运维 Java Unix
File类和IO流
File类和IO流
39 0
|
2天前
|
存储 Java Linux
【Java EE】 文件IO的使用以及流操作
【Java EE】 文件IO的使用以及流操作
|
2天前
|
存储 Java 测试技术
Java基础IO总结(下)
Java基础IO总结(下)
7 0
|
2天前
|
存储 Java
Java基础IO总结(上)
Java基础IO总结(上)
8 0
|
6天前
|
存储 Java 数据库
[Java 基础面试题] IO相关
[Java 基础面试题] IO相关
|
7天前
|
缓存 Java API
Java NIO和IO之间的区别
NIO(New IO),这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
12 1
|
10天前
|
Java
Java基础教程(12)-Java中的IO流
【4月更文挑战第12天】Java IO涉及输入输出,包括从外部读取数据到内存(如文件、网络)和从内存输出到外部。流是信息传输的抽象,分为字节流和字符流。字节流处理二进制数据,如InputStream和OutputStream,而字符流处理Unicode字符,如Reader和Writer。File对象用于文件和目录操作,Path对象简化了路径处理。ZipInputStream和ZipOutputStream则用于读写zip文件。