JAVA IO全面总结

JAVA IO全面总结

简单描述:对于初学者来学,Java IO相对来说较为复杂,因为里面使用的父类与子类较多,所以我们要坚定一个学习规则:抽象类中定义的抽象方法会根据实例化子类的不同,也会完成不同的方法

如果要进行所有的文件以及文件内容的开发操作,应该使用java.io包完成,而且整个java.io包实际上就是五个类和一个接口:
(插上一嘴多线程中是几个接口一个类)
接下来的知识,我们应该熟读加背诵给安排上🐶🐱 🐤 🐮

  • 五个类:File ,InputStream,OutStream,Reader,Writer
  • 一个接口:Serializable

我们再次加深多态性的概念以及特点,而对象多态类中最为核心的概念是:如果抽象类或者接口中的抽象方法被子类覆写了,那么实例化该对象,所调用的方法一定是被覆写的方法,即方法的名称以父类声明为标准,而具体的实现需要子类完成

1.0文件操作类:File

在这java.io包中,File类是唯一个的与文件本身操作有关的类,所谓的文件本身是指,文件的创建,删除,重命名,取得文件大小,和修改日期.

我们先研究File类的构造方法有哪些

方法类型描述
public File(File parent, String child)构造给出要操作文件的父路径和子文件名称
public File(String pathname)构造给定一个要操作文件的完整路径

注意:第二种方法常用与安卓开发中,因为安卓常将许多文件临时存储到手机SD卡中

1.1使用File操作文件的方法

几个简单文件操作方法,创建文件,删除文件,判断文件路径是否存在

1.1.1 createNewFile()
public boolean createNewFile()
                      throws IOException    

这里我们需要注意的是什么时候会抛出异常

  • 如果目录不能访问
  • 如果文件重名或者,文件名称错误
public class FileDemo {
    public static void main(String[] args) throws IOException {
//        我们需要在F盘操作文件,资源管理器下访问文件可以直接使用
//        ‪F:\demo.txt,对就是":\"单横进行访问
//        但是在File对象参数里面需要对"\"进行转义
        File file = new File("F:\\demo1.txt");
        if (!file.exists()){
            file.createNewFile();
            System.out.println("创建该文件"+file.getName());
        }
    }
}
1.1.1.2 关于路径分隔符

我们知道在windos里路径分隔符常用\,但是在linux下面使用/,所有在File里面直接给出一个字段``

我们可以观察File里面有四个字段,它都表示啥意思那?

 File file = new File("F:"+File.separatorChar+"demo1.txt");
字段类型描述
public static final char separatorCharchar在 UNIX 系统上,此字段的值为 ‘/’;在 Microsoft Windows 系统上,它为 ‘\’。
public static final String separatorString同上
public static final char pathSeparatorCharchar在 UNIX 系统上,此字段为 ‘:’;在 Microsoft Windows 系统上,它为 ‘;’。
public static final String pathSeparatorString同上

aI2eQH.png

1.1.1.3创建多级和单级目录
  1. 对于文件的路径操作,File的方法里提供了getParentFile,它判断路径是否存在,而且它返回File对象,可以直接再调用exits()方法判断路径是否存在
  2. 对于创建文件的路径,FIle的方法提供了mkdirmkdirs两个方法,什么区别哪?一个只能创建一级目录,一个可以把你提供的所有都不存在的路径都给安排了

说了这么多,不如凑一凑

方法类型描述
public String getParent()String返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
public File getParentFile()File返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。
public boolean mkdir()boolean创建此抽象路径名指定的目录。
public boolean mkdirs()boolean创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。注意,此操作失败时也可能已经成功地创建了一部分必需的父目录。
public class FileDemo2 {
    public static void main(String[] args){
        File file = new File("F:"+File.separator+"Test1"+File.separator+"test2"+File.separator+"test3"+File.separator+"test");
        if (!file.getParentFile().exists()){
            file.mkdirs();
            System.out.println("创建:"+file.getParent()+"下的文件"+file.getName());
        }else {
            System.out.println("文件以及存在");
        }
    }
}
1.1.14 File文件常用类

我们常用的文件操作方法有哪些?很好回答的文件,我们需要查看文件名称,我们要分辨是文件还是文件夹,我们要看看文件夹有没有隐藏,看文件修改日期,我们有时候看文件字节大小,给文件重命名,取得文件夹下所有文件

方法类型描述
public String getName()普通返回由此抽象路径名表示的文件或目录的名称
public boolean isDirectory()普通测试此抽象路径名表示的文件是否是一个目录。
public boolean isFile()普通测试此抽象路径名表示的文件是否是一个标准文件。
public boolean isHidden()普通测试此抽象路径名指定的文件是否是一个隐藏文件
public long lastModified()普通返回此抽象路径名表示的文件最后一次被修改的时间。
public long length()普通返回由此抽象路径名表示的文件的长度。如果此路径名表示一个目录,则返回值是不确定的。
public boolean renameTo(File dest)普通重新命名此抽象路径名表示的文件。
public File[] listFiles()普通返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。

1.2字节流和字符流

上面我们复习File类,File里虽然可以操作文件,但是并不是操作文件内容,如果要进行内容的操作,只能通过两种途径完成字节流和字符流

​ 假如我们要对一个文件进行输入,输出操作,一般会按照如下的步骤进行

  1. 通过File类定义一个要操作文件的路径(如果不是对文件进行操作则可以忽略)
  2. 通过字节流或者字符流的子对象对父类对象进行实例化
  3. 进行数据的读(输入),写(输出)操作
  4. 数据流属于资源资源操作,资源操作必须关闭
  • 字节流(JDK1.0):InputStream, OutputStream
  • 字符流(JDK1.1):Reader ,Writer

1.2.1字节输出流OutputStream(重点)

OutputStream是一个专门进行字节数据输出的一个类,这个类定义如下

public abstract class OutputStream extends Object implements Closeable, Flushable
{
    //整个IO里面就是类和抽象类
}

All Implemented Interfaces:
CloseableFlushableAutoCloseable

首先我们发现OutoutStream类实现了两个接口:CloseableFlushable两个接口

我们先看Closeable 接口

Closeable

public interface Closeable extends AutoCloseableA Closeable是可以关闭的数据的源或目的地。 调用close方法来释放对象持有的资源(如打开的文件)。
从以下版本开始:
1.5

这里出现了一个很厉害的地方,接口继承接口

public interface Closeable extends AutoCloseable
{
  	public void close() throws IOException;
}

我们打开Closeable继承的接口AutoCloseable的定义,jdk1.7出现的自动关闭机制

Interface AutoCloseable

public interface AutoCloseable{
	void close() throws 异常
}

还有一个Flushable接口

public interface Flushable

A Flushable是可以刷新的数据的目的地。 调用flush方法将任何缓冲输出写入底层流。

  • 从以下版本开始:

    1.5

Interface Flushable
    public interface Flushable
{  void flush() throws IOException
}

清空处理

1.2.1.1 OutputStream方法

OutputStream类里面一共提供提供三个输出方法

输出单个字节

public abstract void write(int b) throws IOException

将指定的字节写入此输出流。 write的一般合同是将一个字节写入输出流。 要写入的字节是参数b的八个低位。 b的24个高位被忽略。 bit一个字节,int八个字节

OutputStream的OutputStream必须为此方法提供一个实现。

输出全部字节数组

public void write(byte[] b) throws IOException

将b.length字节从指定的字节数组写入此输出流。 write(b)的一般合约是应该具有与电话write(b, 0, b.length)完全相同的效果。

输出部分字节数组

public void write(byte[]b,int off,int len) throws IOException

write的方法OutputStream调用写出在每个字节中的一个参数的写入方法。 鼓励子类覆盖此方法并提供更有效的实现。

OutputStream是一个抽象类,按照抽象类的使用规则,需要定义抽象类的子类,而如果是执行文件操作OutputStream下有一个子类叫FileOutputStream子类,子类要为抽象类进行对象的实例化,我们再调用的方法以父类中定义的方法为主,而具体的实现是实例化这个父类的子类完成,所有我么对应子类最关注的也就是子类的构造方法了

构造方法是

第一个:

public FileOutputStream(File file) throws FileNotFoundException
//   创建文件输出流以写入由指定的File对象表示的文件。 创建一个新的FileDescriptor对象来表示此文件连接
//参数 file - 要打开的文件进行写入。 

第二个:

public FileOutputStream(File file, boolean append) throws FileNotFoundException
/*创建文件输出流以写入由指定的File对象表示的文件。 如果第二个参数是true ,则字节将被写入文件的末尾而不是开头。 创建一个新的FileDescriptor对象来表示此文件连接。
参数 
file - 要打开的文件写入。 
append - 如果 true ,则字节将被写入文件的末尾而不是开头 
*/ 
    

我们利用FileOutputStream对一个文件进行写操作

public class FileOutputStreamDemo {
    public static void main(String[] args) throws Exception{
//             1.定义输出文件的路径
        File file = new File("F:"+File.separator+"demo.txt");
        if (!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
//        2.通过子对象对父类进行实例化
        OutputStream output = new FileOutputStream(file,true);
//        3.进行文件内容的输入
        String str="\n唐诗三百首";
        for (int i = 0; i <1000 ; i++) {
            byte [] data=str.getBytes(); //将字符串变成字节数组
            output.write(data);
        }


        System.out.println("写入成功");
//        4.资源操作一定要关闭
        output.close();
    }
}

1.2.2 字节输入流:InputStream

如果需要对数据进行读取操作,可以利用InputStream类实现功能,此类定义为

这个抽象类是表示输入字节流的所有类的超类。
需要定义InputStream子类的应用InputStream必须始终提供一种返回输入的下一个字节的方法。

从以下版本开始:
JDK1.0

public abstract class InputStream extends Object implements Closeable
{
    
}

虽然继承了Closeable接口但几乎不用,自用自己定义的方法

1.2.2.1 InputStream主要方法

在InputStream类里面,定义了3个读取数据的操作

方法类型描述
public abstract int read() throws IOExceptionabstract int从输入流读取数据的下一个字节。 值字节被返回作为int范围0255
public int read(byte[] b) throws IOExceptionint从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。结果 读取到缓冲区的总字节数,或者如果没有更多的数据,因为已经到达流的末尾,则是 -1 。
public int read(byte[] b, int off, int len) throws IOException从输入流读取len字节的数据到一个字节数组。 尝试读取多达len个字节,但可以读取较小的数字。 实际读取的字节数作为整数返回。
  1. public abstract int read() throws IOException

从输入流读取数据的下一个字节。 值字节被返回作为int范围0255

**返回值:**返回读取的数据长度,如果读取到了结尾返回-1

  1. public int read(byte[] b) throws IOException

从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。结果 读取到缓冲区的总字节数,或者如果没有更多的数据,因为已经到达流的末尾,则是 -1 。

**返回值:**返回读取的数据长度,如果读取到了结尾返回-1

  1. public int read(byte[] b, int off, int len) throws IOException

从输入流读取len字节的数据到一个字节数组。 尝试读取多达len个字节,但可以读取较小的数字。 实际读取的字节数作为整数返回。

aI2EWD.png aI2ZSe.png
package com.rango.InputStream;

import java.io.*;

/**
 * @program: java_IO复习
 * @description: 测试InputStream中的一些方法, 进行文件输入操作
 * @author: JiaYadong
 **/
public class InputStreamDemo {
    public static void main(String[] args) throws Exception {
//        定义读取的文件路径
        File file = new File("F:"+File.separator+"demo.txt");
//        2.使用InputStream进行读取
        InputStream stream = new FileInputStream(file);
//        3.将数据保存到一个数组中
        int foot=0;
        byte[] bytes = new byte[1000010];
        int temp=0;
        while((temp=stream.read())!=-1){
            bytes[foot++]=(byte)temp;   //保存返回的单个字节
        }
//        4.关闭输入流
        stream.close();
//        输出数据
        System.out.println("该文本一共有: "+foot+" 个字");
        System.out.println(new String(bytes,0,foot));

    }
}

1.2.3 字符输出流Writer

OutputStream类作为字节数据输出,其实字符数据也可以使用Writer类完成

public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable

用于写入字符流的抽象类。 子类必须实现的唯一方法是write(char [],int,int),flush()和close()。 然而,大多数子类将覆盖这里定义的一些方法,以便提供更高的效率,附加的功能或两者。
从以下版本开始:
JDK1.1

Closeable和Flushable我们已经不陌生的,我们需要看一看Appendable的定义

Appendable

追加功能

public interface Appendable
{
   Appendable append(CharSequence csq) throws IOException;
}

三个方法:

Appendable append(CharSequence csq) throws IOException;
/*将指定的字符序列追加到此Appendable 。 
根据哪个类实现字符序列csq ,可能不会附加整个序列。 例如,如果csq是CharBuffer,则附加的子序列由缓冲区的位置和限制来定义。 
参数 
csq - 要追加的字符序列。 如果csq是null ,则四个字符"null"附加到该附录*/
Appendable append(CharSequence csq,  int start, int end)    throws IOException
/*
将指定的字符序列的子序列附加到此Appendable 。 
形式的这种方法的调用时out.append(csq, start, end) csq不是null,行为以完全相同的方式调用 
 out.append(csq.subSequence(start, end)) 
*/
Appendable append(char c) throws IOException
/*
将指定的字符附加到此 Appendable 。 
参数 
c - 要追加的角色 
*/

1.2.3.1 字节输出流中的主要几个方法

public void write(int c) throws IOException

写一个字符 要写入的字符包含在给定整数值的16个低位中; 16个高位被忽略。
旨在支持高效单字符输出的子类应该覆盖此方法。

public void write(char[] cbuf) throws IOException

写入一个字符数组。 参数 :cbuf - 要写入的字符数组

public abstract void write(char[] cbuf, int off, int len) throws IOException

写入字符数组的一部分。 参数
cbuf - cbuf数组
off - 从中开始编写字符的偏移量
len - 要写入的 len数

public void write(String str) throws IOException

写一个字符串的一部分。
参数
str - 字符串
off - 开始编写字符的偏移量
len - 要写入的 len数

我们需要注意的是Writer只是一个抽象类,我们需要使用它只能用他的子类,现在我们是对文件进行操作,所有可以使用FileWriter进行使用

public class FileWriterTest {
    public static void main(String[] args) throws IOException {
        Writer writer=null;
        File file = new File("F:"+File.separator+"demo.txt");
        try {
             writer = new FileWriter(file,true);
            String data="\r\n不要被打败,坚持做自己";
            writer.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            System.out.println("写入成功!");
            if (writer!=null){
                writer.close();
            }
        }
    }
}

1.2.4字符输入流Reader

public abstract class Reader extends Object implements Readable, Closeable

/*
用于读取字符流的抽象类。 子类必须实现的唯一方法是read(char [],int,int)和close()。 然而,大多数子类将覆盖这里定义的一些方法,以便提供更高的效率,附加的功能或两者。 
从以下版本开始: 
JDK1.1 
*/

我们再字符输入流Reader中发现一个新的接口Readable,我们查看其定义

public interface ReadableA Readable是一个字符源。 Readable的字符通过CharBuffer提供给读取方法的呼叫者 。
从以下版本开始:
1.5

不怎么考虑

1.2.4.1字节输出流中的主要几个方法

在Reader里面也提供了一系列的read()方法,

  • 读取内容到字符数组public int read(CharBuffer target) throws IOException

表示正在读取的数据长度,如果已经读取到结尾返回-1;

为Reader类实例化的可以使用FileReader子类完成,

public class FileReaderTest {

    public static void main(String[] args){
        Reader reader=null;
        File file = new File("F:"+File.separator+"demo.txt");
        try{
          reader= new FileReader(file);
//          申请数组
            char[] chars = new char[1024];
            int len=reader.read(chars);
            System.out.println("输入字符数组中的数据"+new String(chars,0,len));
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.3字节流与字符流的区别

给出两个流我们应该用哪个?有什么区别

字节流和字符流最大的区别:字节流直接与终端进行数据交换,而字符流需要将数据经过缓冲区(理解为内存)处理之后,再由缓存区操作终端(如文件),这属于间接操作,属于间接操作,按照这种方式,如果字节流最后不关闭,也可以将内容直接输出,但是字符流最后不关闭,则缓冲区的内容不会流出来,当然,可以有用户自己使用Flush函数进行操作

aIchuR.png

1.4转换流

以上我们知道字符流会有一个缓冲区,在不用关闭流的情况下,需要手动关闭流文件,但是有一个不能忽略的问题,字符输出流可以直接操作字符,所有有时候我们会使用字符流和字节流的使用

在java.io里面提供了两个子类,我们观察两个类

public class InputStreamReader extends Reader

InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
每个调用InputStreamReader的read()方法之一可能会导致从底层字节输入流读取一个或多个字节。 为了使字节有效地转换为字符,可以从底层流读取比满足当前读取操作所需的更多字节。

//构造方法,就是字节流对象,返回字符流对象
public InputStreamReader(InputStream in)

由于这部分内容使用很少,所有我们直接观察类的继承结构

aIcTUK.png

我们知道文件保存在磁盘上,磁盘上能够保存文件形式都是以字节的方式保存,而我们在字符操作的时候也是对字节数据读取的,不过是这个过程被电脑在缓冲区操作了

总结:

  1. 如果要进行转换 唯一可能出现的形式就是处理中文
  2. 两个转换类都是字符流的子类,属于字符流字节流共同桥梁

2.0文件拷贝小实验

模拟Dos系统的文件拷贝命令.有初始化参数输入源文件,和拷贝文件的路径, 接着执行文件拷贝

**思路:**对文件复制要使用字节流,因为文件有可能是图片.对应这样的操作有两种实现方式

  • 一次性读取,再一次性输出
  • 边读边输,一点一点进行复制

要知道我们的内存空间没有这么大,一下子读取这么多文件会时间会很慢

所有使用边读边输出的方式还是比较好的

设置main(String args[])

aIcRgJ.png
package com.rango.Test;

import java.io.*;

/**
 * @program: java_IO复习
 * @description: 模拟Dos文件对进行拷贝
 * @author: JiaYadong
 * @create: 2020-08-08 12:16
 **/
public class TestCopy {
    public static void main(String[] args) throws IOException {
            if (args.length!=2){
                System.out.println(args.length);
//                长度不是二.命令有误
                System.out.println("执行命令出错!");
                System.exit(1);
            }
//            为了测试程序运行时间
        long start = System.currentTimeMillis();
//            1.要读取的文件路口,已经在类中配置完毕
        File infile = new File(args[0]);//复制文件
        File outfile = new File(args[1]);//输出路径
        InputStream inputStream = new FileInputStream(infile);
        OutputStream outputStream = new FileOutputStream(outfile);
        int temp=0;
        byte[] bytes = new byte[1024];
//        将输入流输入到字节数组中
        while ((temp=inputStream.read(bytes))!=-1){ //有内容
            outputStream.write(bytes,0,temp);
        }
        long end = System.currentTimeMillis();
        System.out.println("拷贝完成花费时间: "+(end-start)+" 毫秒");
        inputStream.close();
        outputStream.close();
    }
}

3.0字符编码

aIcoE6.png

4.0 内存流

当我们学习了AJAX+XML(JSON)应用才会牵扯到此部分

可以使用内存流操作IO操作

什么时候会出现内存流的使用情况?

在每个操作中必须使用IO操作,但用不希望有一些临时文件产生,那我们不能使用文件操作流了,应该使用内存操作流了

针对io操作内存里提供了两种操作

  • 字节内存流:内存输入流(ByteArrayInputStream),内存输出流(ByteArrayOutputStream)
  • 字符内存流:内存输入流(CharArrayReader),内存输出流(CharArrayWriter)
public class Demo1 {
    public static void main(String[] args) throws IOException {
            String str="Hello world.";
//            将数据输出到内存中去
        InputStream bIn = new ByteArrayInputStream(str.getBytes());
//        从内存中读取数据
        OutputStream bOut = new ByteArrayOutputStream();
        int temp=0;
        while ((temp=bIn.read())!=-1){
            bOut.write((char)Character.toUpperCase(temp));
//            从内存中输出

        }
//        可以直接读数据
        String s = bOut.toString();
        bOut.close();
        bIn.close();
        System.out.println(s);
    }
}

我们没有用到文件操作,但可以直接把数据读取出来

aIc4D1.png aIc5Hx.png

4.1一个电影合并代码

package edu.youzg.about_io.about_file.core.Test;

import java.io.*;
import java.util.ArrayList;

public class Test {

    public static void main(String[] args) throws IOException {
        FileInputStream in1 = new FileInputStream("妇联 上.mp4");
        FileInputStream in2 = new FileInputStream("妇联 下.mp4");
        FileOutputStream out = new FileOutputStream("妇联 玩整版.mp4");

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        //创建一个集合
        ArrayList<FileInputStream> list = new ArrayList<>();
        list.add(in1);
        list.add(in2);
        int len=0;
        byte[] bytes = new byte[1024 * 8];
        for (FileInputStream in : list) {
            while ((len=in.read(bytes))!=-1){
                byteOut.write(bytes,0,len);
                byteOut.flush();
            }
            in.close();
        }

        //取出两部电影的字节数据
        byte[] allBytes = byteOut.toByteArray();
        
        //将两部电影的字节数据,写到硬盘上
        ByteArrayInputStream byteIn = new ByteArrayInputStream(allBytes);
        int len2 = 0;
        byte[] bytes2 = new byte[1024 * 8];

        while ((len2 = byteIn.read(bytes)) != -1) {
            out.write(bytes, 0, len2);
            out.flush();
        }

		//释放资源
        out.close();
    }

}

5.0 打印流

我们会学习打印流的实现原理

打印流操作类的使用

打印流属于整个java开发中,非常重要的概念

image 20200808185106190

5.1 PrintStream字节打印流和PrintWriter字符打印流

image 20200808185218155 aIcLgH.png

5.2代理模式(未完成)

未完成

5.3装饰设计模式(未完成)

未完成

将一个功能不足的操作类,通过包装,形成更好用的工具类

6.0 System类

在PrintStream类中有许多的println方法,下面是System类的一些定义,有3个与IO有关的常量

System类的3个常量,有两个都是PrintStream类的实例对象

System类常用三个变量

常量类型描述
public static final PrintStream err常量“标准”错误输出流。 此流已经打开并准备好接受输出数据。
public static final PrintStream out常量“标准”输出流。
public static final InputStream in“标准”输入流。

6.1 System.in的输入

系统的输出是将所有信息输出到指定的输出设备(显示器上).System.out本身属于PrintStream对象,而PrintStream是OutputStream的子类

public class Demo1 {
    public static void main(String[] args) throws IOException {
        InputStream in = System.in;
        byte[] bytes = new byte[1024];
        System.out.println("请输入数据:");
        int len=in.read(bytes);
//        等待用户输入,程序进入阻塞状态
        System.out.println("您一共输入:"+len+"个字符");
        System.out.println("您输入的数据是"+new String(bytes,0,len));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

以码平川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值