你需要知道的JavaI/O流(一)

135 阅读11分钟

一、文件

文件是保存数据的地方

1.File类

文件在程序中是以流的形式来操作的

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

判断输入还是输出是以java程序为参考点的,流向java程序(内存)的是输入流,从java内存流向其他位置的是输出流。

2.常用的文件操作

2.1创建文件

创建文件对象相关构造器和方法

new File(String pathname)//根据路径构建一个File对象

new File(File parent,String child)//根据父目录文件+子路径构建

new File(String parent,String child)//根据父目录+子路径构建

public class File implements Serializable, Comparable<File>{
        /* -- Constructors -- */
    //两个私有构造器
    /**
     * 已经规范化的路径名字符串的内部构造函数
     */
    private File(String pathname, int prefixLength) {
        this.path = pathname;
        this.prefixLength = prefixLength;
    }

    /**
     * 已经规范化的路径名字符串的内部构造函数。
     *参数顺序用于消除此方法与 public(File, String) 构造函数的歧义
     */
    private File(String child, File parent) {
        assert parent.path != null;
        assert (!parent.path.equals(""));
        this.path = fs.resolve(parent.path, child);
        this.prefixLength = parent.prefixLength;
    }
}

public class CreatFile {
    @Test
    public void creatNewFile1(){
        String path = "e:\new1.txt";
        //这一句代码只是在内存创建了一个File对象,并没有在硬盘上创建文件
        File file = new File(path);  
        try {
            //在硬盘上创建文件,会抛出异常
            file.createNewFile();  
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void creatNewFile2(){
        File parentFile = new File("e:\\");
        String childPath = "new2.txt";
        File child = new File(parentFile, childPath);
        try {
            child.createNewFile();
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void creatNewFile3(){
        String parentPath = "e:\\";
        String childPath = "new3.txt";
        File child = new File(parentPath, childPath);
        try {
            child.createNewFile();
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 获取文件相关信息

public void creatNewFile1(){
        String path = "e:\new1.txt";
        File file = new File(path);
        System.out.println(file.getName());
    }

//源码
public class File implements Serializable, Comparable<File>{
    //检查文件是否有无效路径。目前对文件路径的检查非常有限,只包括Nul字符检查。
    //返回 true 意味着路径肯定是无效垃圾。但是返回 false 并不能保证路径是有效的
    final boolean isInvalid() {
        if (status == null) {
            status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
                                                       : PathStatus.INVALID;
        }
        return status == PathStatus.INVALID;
    }
    
    //返回文件的名字
    //返回此抽象路径名表示的文件或目录的名称。这只是路径名的名称序列中的最后一个名称。
    //如果路径名的名称序列为空,则返回空字符串。
    public String getName() {
        int index = path.lastIndexOf(separatorChar);
        if (index < prefixLength) return path.substring(prefixLength);
        return path.substring(index + 1);
    }
}

常用的方法有:

函数名返回类型解释
getName()String获取文件的名字
getParent()String返回此抽象路径名的父目录的路径名字符串,如果此路径名未命名父目录,则返回null 。
getParentFile()File在上一方法的基础上,返回父级文件对象
getPath()String返回此抽象路径名的绝对路径名字符串
isAbsolute()boolean测试此抽象路径名是否是绝对的。绝对路径名的定义取决于系统。在 UNIX 系统上,如果前缀是"" ,则路径名是绝对的。在 Microsoft Windows 系统上,如果路径名的前缀是驱动器说明符后跟"\" ,或者它的前缀是"\\" ,则路径名是绝对路径名
getAbsolutePath()String返回此抽象路径名的绝对路径名字符串,如果这个抽象路径名已经是绝对的,那么路径名字符串就像getPath方法一样简单地返回。如果此抽象路径名是空的抽象路径名,则返回由系统属性user.dir命名的当前用户目录的路径名字符串。否则,此路径名将以系统相关的方式解析。在 UNIX 系统上,通过根据当前用户目录解析相对路径名,使其成为绝对路径名。在 Microsoft Windows 系统上,通过将相对路径名解析为由路径名命名的驱动器的当前目录(如果有),从而使相对路径名成为绝对路径;如果不是,则针对当前用户目录进行解析。
exists()boolean测试此抽象路径名表示的文件或目录是否存在。
isDirectory()boolean测试此抽象路径名表示的文件是否为目录。
isFile()boolean测试此抽象路径名表示的文件是否为文件。
length()long返回此抽象路径名表示的文件的长度。如果此路径名表示目录,则返回值未指定。
createNewFile()void创建文件,不能创建目录,即使路径写的是目录的路径也会创建一个文件(该文件好像没有后缀),像下面这样

2.3目录操作

函数名返回类型解释
delete()boolean删除此抽象路径名表示的文件或目录。如果此路径名表示一个目录,则该目录必须为空才能被删除,也就是说只能删除文件或者空目录
mkdir()boolean创建由此抽象路径名命名的目录,当且仅当目录已创建返回真,否则为假,只能创建一级目录,创建多级目录会失败
mkdirs()boolean创建由此抽象路径名命名的目录,包括任何必要但不存在的父目录。请注意,如果此操作失败,它可能已成功创建一些必要的父目录。 回报: 当且仅当创建了目录以及所有必要的父目录时才为true ;否则false?
@Test
    public void testMakeDir(){
        String pth = "D:\demo\e";
        File file = new File(pth);
        file.mkdirs();

        File file1 = new File("D:\demo");
        if (file1.exists()){
            if (file1.delete()){
                System.out.println(file1.getName()+"删除成功");
            }else {
                System.out.println("删除失败");
            }
        }
        if (file.exists()){
            if (file.delete()){
                System.out.println(file.getName()+"删除成功");
            }else {
                System.out.println("删除失败");
            }
        }else {
            System.out.println("wenjianbucunzai");
        }
    }

二、IO流

1.Java IO流原理

1.I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。

2.Java程序中,对于数据的输入/输出操作以”流(stream)”的方式进行。

3.java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据

4.输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。

5.输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

PS:输入跟输出是相对当前程序而言的,从当前程序输出到其他地方为输出流,从其他地方输入到当前程序是输入流。

2.IO流的分类

按操作数据单位不同分为:字节流(8 bit),字符流(按字符)。字节流可以无损操作二进制文件,比如说音频和视频,但是效率较低。字符流按字符操作文件,效率高,一个字符等于多少个字节要按照文件的编码格式来判断。

按数据流的流向不同分为:输入流,输出流

按流的角色的不同分为:节点流,处理流/包装流

抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类(不能实例化)派生的,并且由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

文件和流是不一样的,可以理解为流是文件和程序之间传输的介质。

3.常用的类

InputStream抽象类是所有字节输入流类的超类,InputStream常用的子类有FilelnputStream(文件输入流)、

BufferedlnputStream(缓冲字节输入流)、ObjectInputStream(对象字节输入流)。

3.1 字节输入流(FileInputStream)

3.1.1 构造方法

3.1.2 常用方法

@Test
    public void testFileInputStream(){
        String path = "D:\\demo\\FileInputStream.txt";
        FileInputStream fileInputStream = null;
        try {
            //构造器,用路径名来创建对象
            fileInputStream = new FileInputStream(path);
            //定义读取的数据的字节码
            int readData = 0;
            //read方法的返回值是int类型,返回的是这一个字节的字节码,
            //效率比较低,而且不适合用来处理
            while((readData = fileInputStream.read()) != -1){
                System.out.print((char) readData);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //一定要关闭流,回收资源
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
@Test
public void testFileInputStream2(){
    String path = "D:\new1.txt";
    FileInputStream fileInputStream = null;
    try {
        fileInputStream = new FileInputStream(path);
        byte[] readData = new byte[6];
        int i = 0;
        //一次读取五个字节,字节码存在byte数组里面,这个方法返回的是这一次读取出的字节数量
        while((i = fileInputStream.read(readData)) != -1){
            System.out.print(new String(readData,0, i));
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.2 字节输出流(FileOutPutStream)

3.2.1 构造方法

注意:针对上图前两个和后两个构造函数,带有append参数的,如果该参数为true,则创建对象时不会覆盖原文件,而且写入文件的字符,会追加到文件的末尾;如果该参数为false,则创建对象是和不带该参数的构造函数的一样,会覆盖已有的文件内容。该类创建文件的前提是,父级目录存在,而且该构造函数不能创建目录,否则会抛出异常(文件无法打开也会抛异常)。

另外,这个覆盖只发生在创建对象的时候,如果对象已经创建(只经历一次构造函数),进行写入操作不会覆盖文件已有的内容。

3.2.2 常用方法

@Test
public void testFileOutputStrea(){
    String path = "D:\new1.txt";
    //File file = new File(path);
    FileOutputStream fileOutputStream = null;
    try {
        //创建对象时,如果文件不存在则直接创建,如果文件已存在,则会创建新的文件来覆盖
        fileOutputStream = new FileOutputStream(path,true);
        //将指定的字节写入此文件输出流。
        //fileOutputStream.write('1');
        String str = "123456";
        //将所有字节从指定的字节数组写入此文件输出流。
        //fileOutputStream.write(str.getBytes());
        //从位于偏移量 off的指定字节数组写入 len字节到该文件输出流。
        fileOutputStream.write(str.getBytes(),0,3);
        fileOutputStream.write(str.getBytes(),0,3);
        testfile(fileOutputStream);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void testfile(FileOutputStream fileOutputStream){
    String str = "1111";
    try {
        fileOutputStream.write(str.getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
 @Test
    public void copyFile() {
        //String srcFile = "C:\Users\Administrator\Pictures\image.png";
        String srcMusic = "C:\Users\Administrator\Music\The xx - Intro.mp3";
        String destMusic = "e:\The xx - Intro.mp3";
        String destFile = "e:\image.png";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream = new FileInputStream(srcMusic);
            fileOutputStream = new FileOutputStream(destMusic);
            byte[] bytes = new byte[1024];
            int num = 0;
            while ((num = fileInputStream.read(bytes)) != -1) {
                //用这个方法进行写入操作,只写入需要的数据,也就是读取的行数据
                //如果不用这个方法,最后一次写入可能会将旧数据再写入一次
                fileOutputStream.write(bytes, 0, num);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3.3 字符输入流(FileReader)

3.3.1 构造方法

FileReader源码里面的结构只有这几个构造函数,调用了父类的构造方法,InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。

3.3.2 常用方法

FileReader的常用方法直接继承了父类的方法,并没有重写

返回类型方法以及描述
voidclose()关闭流并释放与之相关联的任何系统资源。
StringgetEncoding()返回此流使用的字符编码的名称。
intread()读一个字符,如果已经达到流的结尾,则为-1
intread(char[] cbuf, int offset, int length)将字符读入数组的一部分。cbuf - 目的缓冲区offset - 开始存储字符的偏移量length - 要读取的最大字符数读取的字符数,如果已经达到流的结尾,则为-1
booleanready()告诉这个流是否准备好被读取。如果下一个read()保证不阻止输入,则为True,否则为false。 请注意,返回false并不能保证下一次读取将被阻止。
public void testFileReader(){
        String path = "C:\Users\demo\新建文件.txt";
        FileReader fileReader = null;
        try {
            fileReader = new FileReader(path);
            int readLen = 0;
//            while ((readData = fileReader.read()) != -1){
//                System.out.print((char) readData);
//            }
            char[] readData = new char[8];
            while ((readLen = fileReader.read(readData)) != -1){
                System.out.print(new String(readData, 0 ,readLen));
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                if (fileReader != null){
                    //一定要关闭
                    fileReader.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

3.4 字符输出流

3.4.1 构造函数

名称描述
FileWriter(File file)给一个File对象构造一个FileWriter对象。
FileWriter(File file, boolean append)给一个File对象构造一个FileWriter对象。
FileWriter(FileDescriptor fd)构造与文件描述符关联的FileWriter对象。
FileWriter(String fileName)构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append)构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。

3.4.2 常用方法

FileWriter的源码也是只有重写的几个构造函数,FileWriter的常用方法也就是Writer的常用方法主要是写操作的方法(输出内容到文件)

返回类型方法描述
voidwrite (char[] cbuf)写入一个字符数组。
abstract voidwrite (char[] cbuf, int off, int len)写入字符数组的一部分。
voidwrite (int c)写一个字符
voidwrite(String str)写一个字符串
voidwrite(String str, int off, int len)写一个字符串的一部分。
@Test
public void testFilrWriter(){
    String path = "C:\Users\邱野\新建文件.txt";
    FileWriter fileWriter = null;
    try {
        fileWriter = new FileWriter(path);
        fileWriter.write(12);
        fileWriter.write("==测试FileWriter ");
        char[] chars = {'1','2','3'};
        fileWriter.write(chars);
        fileWriter.write(chars,0,2);
        String s = "敲代码";
        fileWriter.write(s,1,2);
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        try {
            if (fileWriter != null){
                //一定要进行关闭或者刷新,否则内容将不会被写入到文件里,只会在内存里
                fileWriter.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}