JavaSe之IO流

Java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在Java中把对于输入/输入操作是以流的方式进行操作的。java.io 包下提供了大量的供我们使用的操作【流】的方法和接口,用于进行各类数据的处理和传输。

计算机的输入和输出都是通过二进制来完成的。在网络中我们要传递数据就要将数据【流化】,换句话说就是将文件、复杂的对象转化成能够在网络上传输的一个个的0和1,我在这里先画几幅图帮助大家理解一下。

文件在磁盘的输入输出:

image-20210812143541971

文件在网络中的输入输出:

image-20210812143720659

内存中的对象的输入输出:

image-20210812144810455

文件的操作

1、文件路径

正斜杠,又称左斜杠,符号是"/“;反斜杠,也称右斜杠,符号是”" 。

在Unix/Linux中,路径的分隔采用正斜杠"/“,比如”/home/hutaow";而在Windows中,路径分隔采用反斜杠"“,比如"C:\Windows\System”

image-20210910104203903

在Java当中反斜杠代表的是转义:

比如:

制表符(也叫制表位)的功能是在不使用表格的情况下在垂直方向按列对齐文本,就是咱们的Tab键。

  • " 将双引号转义为真正的双引号
  • ‘\r’ (回车):即将光标回到当前行的行首(而不会换到下一行),之后的输出会把之前的输出覆盖
  • ‘\n’ 换行,换到当前位置的下一位置,而不会回到行首

2、File类简介

在 Java 中,File 类是 java.io 包中唯一代表磁盘文件本身的对象。File 类定义了一些与平台无关的方法来操作文件,File类主要用来获取或处理与磁盘文件相关的信息,像文件名、 文件路径、访问权限和修改日期等,还可以浏览子目录层次结构。   File 类表示处理文件和文件系统的相关信息。也就是说,File 类不具有从文件读取信息和向文件写入信息的功能,它仅描述文件本身的属性。

3、构造方法

构造器描述
File(String pathname)通过将给定路径名字符串来创建一个新 File 实例
File(String parent,String child)根据指定的父路径和文件路径创建一个新File对象实例
File(File parent,String child)根据指定的父路径对象和文件路径创建一个新的File对象实例

其实很简单的,其实这个意思:

File file = new File("D:\\code\\a.txt");
File file = new File("D:\\code\\","a.txt");
File file = new File("D:\\code");
File child = new File(file,"a.txt");

4、File类创建和删除功能

booleancreateNewFile()指定路径不存在该文件时创建文件,返回true 否则false
booleanmkdir()当指定的单击文件夹不存在时创建文件夹并返回true 否则false
booleanmkdirs()当指定的多级文件夹在某一级文件夹不存在时,创建多级文件夹并返回true 否则false
booleandelete()删除文件或者删除单级文件夹

5、File类的判断功能

booleanexists()判断指定路径的文件或文件夹是否为空
booleanisAbsolute()判断当前路径是否是绝对路径
booleanisDirectory()判断当前的目录是否存在
booleanisFile()判断当前的目录是否是一个文件
booleanisHidden()判断当前路径是否是一隐藏文件

6、File类的获取功能和修改名字功能

FilegetAbsoluteFile()获取文件的绝对路径,返回File对象
StringgetAbsolutePath()获取文件的绝对路径,返回路径的字符串
StringgetParent()获取当前路径的父级路径,以字符串形式返回该父级路径
StringgetName()获取文件或文件夹的名称
StringgetPath()获取File对象中封装的路径
longlastModified()以毫秒值返回最后修改时间
longlength()返回文件的字节数
booleanrenameTo(File dest)将当前File对象所指向的路径修改为指定File所指向的路径

7、文件夹列表操作

返回值方法描述
Stringlist()得到这个文件夹下的所有文件,返回路径数组
String[]list(FilenameFilter filter)通过过滤器过滤文件,过滤通过文件名过滤,返回路径数组
File[]listFiles()得到这个文件夹下的所有文件,返回文件数组
File[]listFiles(FileFilter filter)通过过滤器过滤文件,过滤通过文件过滤,返回文件数组
File[]listFiles(FilenameFilter filter)通过过滤器过滤文件,过滤通过文件名过滤,返回文件数组

8、作业

列出D:\code\image文件夹下的所有的图片:

public class ListAllPng {

    public static void main(String[] args) throws IOException {
        listAll(new File("D:\\code\\image"));
    }
    
    // 单独列出方法获取目录下的图片
    public static void listAll(File parent)  {
        MyFilter myFilter = new MyFilter();
        File[] children = parent.listFiles(myFilter);

        for (int i = 0; i < children.length; i++) {
            // 如果子文件是个文件夹,则递归调用
            if(!children[i].isFile()){
                listAll(children[i]);
            } else {
                System.out.println(children[i].getName());
            }
        }
    }

    //定义文件过滤器
    static class MyFilter implements FilenameFilter{
        @Override
        public boolean accept(File dir, String name) {
            return name.contains(".png") || dir.isDirectory();
        }
    }
}

IO流的分类:

Java中一切皆对象,流也是对象,在学习之前我们不妨先看分类和概念,至于是哪个类其实没那么重要。

其实说到流,我们能想到流水,其实这已经很形象了,水从汪洋大海流入湖泊就是要通过河流。如果你还不知道,接着往下看。

其实到目前为止,我们对流已经有了基本的概念,接下来我们就要深入学习流了。按照不同的分类方式,可以把流分为不同的类型。常用的分类有三种:

按照流向分

  • 输入流: 只能从中读取数据,而不能向其写入数据。
  • 输出流:只能向其写入数据,而不能向其读取数据。

image-20210812160823989

其实计算机在读取文件的时候是很麻烦的:

image-20210812161248102

当然系统级别的方法调用我们可以暂时不用考虑。但是我们确确实实看到一个文件在传输过程中经历了很多次的拷贝,IO的性能本来就不是很高,所以后来又有了零拷贝、Nio等技术,这些知识点我们计划在附加课讲解。

2 、按照操作单元划分

  • 字节流:是一个字节一个字节的读取或写入
  • 字符流:是一个字符一个字符的读取或写入,一个字符就是两个字节,主要用来处理字符。

3、 按照角色划分

  • 节点流:直接从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,称为节点流。
  • 处理流:“连接”在已存在的流(节点流或处理流)之上通过对数据的处理为程序提供更为强大的读写功能的流。

image-20210812162235651

4、Java输入/输出流体系中常用的流的分类表

| 分类 | 字节输入流 |字节输出流|字符输入流|字符输出流|

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问字符串StringReaderStringWriter
缓冲流(处理)BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
操作对象ObjectInputStreamObjectOutputStream

流的案例

1、继承结构

InputStream和OutputStream

image-20210812164824415

Reader和Writer

image-20210812164846302

2、流到底怎么用

(1)将一个流对象插在一个节点上:

其实通过名字我们就可以很好的理解了:FileInputStream就是怼在文件上的输入流啊!

public abstract class InputStream implements Closeable

InputStream本身是抽象类,我们需要使用它的子类去构造对象:

InputStream inputStream = new FileInputStream(file);

既然是输入流就要一点一点的往内存里读数据啊!

image-20210812165322884

其实inputStream的方法并不多,关键在于几个read方法,管子已经插上了,接下来就是读了。

// 读一个字节
int read = inputStream.read();

// 一次性读1024个字节到那个内存数组
int read = inputStream.read(new byte[1024]);

// 从第0个字节开始读,读120个字节
int read = inputStream.read(new byte[1024],0,120);
(2)使用read()方法读取

它的读取流程大概是这个样子的,inputStream内部有一个游标,它会记录目前读到哪里了,看下图:

image-20210812173337468

我们不妨尝试一下:

我的D盘的code目录下新建一个文本:

image-20210812170548644

我知道:read返回-1时就代表文件读完了,所以我写了如下代码:

public static void main(String[] args) throws IOException {
    InputStream inputStream = new FileInputStream("D:/code/a.txt");
    int read;
    while ((read =inputStream.read())  != -1){
        System.out.print(read+" ");
    }
}
72 101 108 108 111 32 87 111 114 108 100 33 
H  e   l   l   0      W  o   r   l   d   !

read就是每次读出的字节,直到-1就停止。

小tips:一个流我读完了一次还能读第二次吗?

 public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("D:/code/a.txt");
        int read;

        while ((read =inputStream.read())  != -1){
            System.out.print(read+" ");
        }
        System.out.println("再读一次---------------");
        while ((read =inputStream.read())  != -1){
            System.out.print(read+" ");
        }
    }
    
    72 101 108 108 111 32 87 111 114 108 100 33 
再读一次---------------

我们发现一个流读完了就没有了,就不能在读了。当然文档里有mark和reset方法,我们在系统中测试是不可用的。

System.out.println(inputStream.markSupported());
(3)使用read(byte[] byte)读取
public static void main(String[] args) throws IOException {
    InputStream inputStream = new FileInputStream("D:/code/a.txt");
    int read;

    byte[] buf = new byte[3];
    while ((read =inputStream.read(buf))  != -1){
        System.out.print(read+" ");
    }
}

image-20210812173613721

我们想向深入走一步,看看源码:

但是发现,源码目前位置看不了了,这些方法都带有native,这更加说明了读文件一定是JVM调用系统方法读取的。

image-20210812171830458

(4)输出流的使用

我们要学会举一反三,其实他们的区别就是一个读,一个写嘛,我写一个例子就好了。

有一个小知识点:

在定义文件输出流时,有两个参数,第二个如果是true代表追加文件,如果false代表覆盖文件,意思就是如果人家这个文件原来有内容,就覆盖的没了,这一点要注意。

OutputStream outputStream = new FileOutputStream("D:/code/a.txt",true);

1

OutputStream outputStream = new FileOutputStream("D:/code/a.txt",true);
// 一个字节一个字节的写
outputStream.write(97);

// 97是一个字节啊

我们发现文件中被写入的是一个a

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jfJUeSPt-1676010747405)(data:image/png;base64,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)]

OutputStream outputStream = new FileOutputStream("D:/code/b.txt",true);
// 直接将一个字节数组写出
outputStream.write("Hello World".getBytes());

我们不妨升级一下,一个文件的拷贝程序就写好了。

public static void main(String[] args) throws IOException {
    InputStream inputStream = new FileInputStream("D:/code/a.txt");
    OutputStream outputStream = new FileOutputStream("D:/code/b.txt",true);

    byte[] buf = new byte[3];
    int len;
    while ((len =inputStream.read(buf))  != -1){
        outputStream.write(buf,0,len);
    }
}
(5)资源的释放

一个IO流的标准写法是什么呢?

我们发现IO有以下几点需要我们处理:

1、绝大部分的对IO的操作都需要处理可能出现的IO异常。

image-20210812175913456

2、我们发现不管是inputStream还是outputStream都有一个close方法,IO是需要消耗系统资源的,每一个stream都需要系统分配资源,是弥足珍贵的,所以没有流一旦使用完成就一定要关闭资源。

经过反复修改我们写出了如下代码:

 public static void main(String[] args) {
        // 定义资源
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream("D:/code/a.txt");
            outputStream = new FileOutputStream("D:/code/b.txt",true);

            byte[] buf = new byte[3];
            int len;
            while ((len =inputStream.read(buf))  != -1){
                outputStream.write(buf,0,len);
            }
        }  catch (IOException e) {
            e.printStackTrace();
         // 最终无论如何,都要释放资源
        } finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

【AutoCloseable接口的好处】

以上代码如此繁杂,jdk1.7之后,很多资源类的类都实现了AutoCloseable接口

实现了这个接口的类可以在try中定义资源,并会主动释放资源:

这样就极大的简化了代码的编写,但是你这么写了可能会有人看不懂呦!

public static void main(String[] args) {
    try(InputStream inputStream = new FileInputStream("D:/code/a.txt");
        OutputStream outputStream= new FileOutputStream("D:/code/b.txt",true)) {
        byte[] buf = new byte[3];
        int len;
        while ((len =inputStream.read(buf))  != -1){
            outputStream.write(buf,0,len);
        }
    }  catch (IOException e) {
        e.printStackTrace();
    }
}

3、案例(作业)

(1)字符流读文件
@Test
public void testReader() throws Exception{
    //怼了一个输入流到文件上
    Reader reader = new FileReader("E:\\test\\a\\word.txt");
    BufferedReader br = new BufferedReader(reader);
    String str;
    while ((str = br.readLine()) != null){
        System.out.println(str);
    }
    reader.close();
    br.close();
}
(2)向文件里写内容
//这个用main方法测吧
public void testWriter() throws Exception{
    //怼了一个输入流到文件上
    Writer writer = new FileWriter("E:\\test\\a\\writer.txt");
    BufferedWriter bw = new BufferedWriter(writer);
    Scanner scanner = new Scanner(System.in);

    while (true){
        System.out.print("请输入:");
        String words = scanner.next();
        bw.write(words);
        bw.flush();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值