IO流 笔记

IO流

1、掌理解Java l/O系统中流的概念

2、掌握Java l/O系统中流的分类

3、掌握各类流的通用操作步骤

4、掌握主要流类的使用方法

5、掌握对象序列化和反序列化的方法

6、掌握文件类的使用方法

l/O概述

程序的运行都在内存中进行。在程序结束时,数据全部丢失。为了让操作后的数据永久保存下来, 我们需要使用外部存储器来完成。

那么,如何完成内存和外存之间的数据传输呢?我们可以使用输入输出(/o)机制来完成。

Java类库中提供了大量类,可以帮助我们从不同的设备读取数据,并保存或输出到不同的设备中。 这些类统一放在java io包和java.nio包中,统称Java l/O 系统。

其中,l 表示 input,意为输入,0 表示 Output,意为输出。

流(Stream)的基本概念

在java当中把 IO操作设计成一个统一的模型,也就是 “流模型 ”。

将IO操作,看做是两个端点(节点)之间进行数据的传输,流就是端点(节点)之间数据传输的通道,在流模型下,数据就好像流质一般从通道一端到另一端。

数据源 ------> 管道 --------> 目的地

流的分类

1. 按照数据传输方向来分

在java语言中,流的方向以内存作为参照物,当从数据源将数据读取到内存中时,称为输入流,也叫读取流。
数据源 --------> 内存 读取流

从内存将数据写入数据时,称为输出流(写入流)
数据源 <-------- 内存

2.在java语言中,按照流处理数据的最小单位分为

字节流 ————————> 以byte为最小单位传输
字符流 ————————>以char为最小单位传输

3.按照流的功能分

节点流 ------------> 可以直接从/向数据源读/写数据
处理流 ------------> 可以在数据传递过程中执行某个处理任务

处理流不能单独使用,可以在数据传输过程中加入处理操作。

字节流有两个抽象父类 :InputStream (字节输入流)和 OutputStream(字节输出流)
字符流有两个抽象父类 :Reader (字节输入流) 和 Writer(字节输出流)

提供四大抽象方法作用:因为在现实中,输入输出的外部设备(数据源)比如键盘等,不同的设备数据传输处理方式是不同的,统一提 供了四大抽象父类,各自不同的外部设备继承抽象父类,根据自身特点重写其中数据处理的操作,对于开发者来说,可以屏蔽不同设备之间进行数据传输的差异,只要是读就是read,只要是写就是write。

​ 输出流

​ OutputStream | Writer

字节流 <---------------------------------O----------------------------------> 字符流
InputStream | Reader
输入流

流的子类

不同的数据源读写数据的方式会有所不同,所以在字节流和字符流的父类
中,预留了读写数据的抽象方法,不同的子类根据自己数据源的特点分别
去实现。

BufferedInputStream                               数据源是缓冲区
FileInputStream                                   数据源是文件
ObjectInputStream                                 数据源是对象

流的选择

1.确定数据源 和目的地(数据传输的方向),输入、输出

2.确定管道粗细(以字节为单位传输还是以字符单位传输,字节/y字符)

输入流操作的步骤

第一步:建立流。建立内存和数据源的数据通道。

InputStream in = new FileInputStream(1.txt”);

第二步:操作流。读取输入流中的数据。

int n = 0;
while((n = in.read()) != -1 ){
  ……
}

第三步:关闭流,释放内存资源。

in.close();

输出流操作的步骤

第一步:建立流。建立内存和数据源的数据通道。

OutputStream out = new FileOutputStream(1.txt”);

第二步:操作流。向数据源写入数据。

out.write(Hello World.getByte());

第三步:关闭流,释放内存资源。

in.close();

输入流常用方法:read 、close

输出流常用方法:wirte、flush、close

InputStream方法

public class InputStreamTest {
    public static void main(String[] args) {
        // 数据源  管道   目的地

        try {
            // 1 建立数据源与目的地进行数据传输的管道
            InputStream in = new FileInputStream("1.txt");
            // 2.处理数据
            // read() 读取到流中的下一个字节数据,并返回该数据对应的Unicode,
            // 如果流中没有数据 则返回-1
//            int n = 0;
//            while ((n = in.read()) != -1){
//                System.out.print((char)n);
//            }
            // read(byte[] bytes)
            // 返回读取字节的个数,如果流中没有数据则返回-1
            byte[] bytes = new byte[1024];
            int len = 0;// 保存read方法一次读取返回的个数
            while ((len = in.read(bytes)) != -1){
//                System.out.println(len + "=============================");
                System.out.print(new String(bytes,0,len));
            }
            // 关闭流 释放资源
            in.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

}

OutPutStream方法

方法作用
write()向文件中写入指定字符
write(byte[] b)向文件中写入指定byte数组
write(byte[] b,int off, int )
out.flush()将缓存区数据强制写入数据源
close()关闭流
public class OutPutStreamTest {

    /**
     * 文件拷贝 将源文件内容拷贝至目标文件中
     * 1.将源文件数据读取到内存  数据源-->内存  输入流
     * 2.内存汇总源文件的数据写入到目标文件  内容 ---> 数据源 输出流
     * @param src 源文件
     * @param dest 目标文件
     */
    public static void copyFile(String src,String dest){

        try {
            // 得到字节输出流和输入流对象
            InputStream in = new FileInputStream(src);
            OutputStream out = new FileOutputStream(dest);
            // 利用输入流读取文件数据
            // 方式一
//            int n = 0;
//            while ((n = in.read()) != -1){
//                out.write(n);
//            }
            // 方式2
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = in.read(bytes)) != -1){
                out.write(bytes,0,len);
            }

            // 方式三
//            byte[] bytes = new byte[1024];
//            int n = 0;// 读取个数
//            StringBuffer sb = new StringBuffer();
//            while ((n = in.read(bytes)) != -1){
//                sb.append(new String(bytes,0,n));
//            }
//            out.write(sb.toString().getBytes());
            out.flush();
            out.close();
            in.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }




    public static void main(String[] args) {
        copyFile("4.jpg","5.jpg");
        // 建立数据源传输数据到目的地的通道
        // 内存 ---> 数据源 (FileOutputStream数据源是文件)
//        try {
//            OutputStream out = new FileOutputStream("2.txt");
//            // 2.对数据进行处理,写入流中
//            // write()本质上只是把数据写入到缓存区
//            out.write("hello world".getBytes());
//
//            // 将缓存区的数据 强制写入数据源
//            out.flush();
//            out.close();
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }


    }
}

Reader方法

方法作用
read()读取到流中下一个字节数据,并返回该数据对应的Unicode码 如果流中没有数据,则返回-1
read(byte[] b)返回读取的字节的个数 如果流中没有数据,返回-1
read(byte[] b,int off, int )
close()关闭流

字符读取流Reader类

FileReader ------------------> 读取文件中的字符信息

InputStreamReader ---------------> 将字节流转换为字符流

BufferedReader --------------------> 套接字符流,需要以另一个字符流作为基础。将数据读入缓冲区,然后从缓冲区读取

字符写入流Writer类

FileWriter ------------------> 向文件中写入字符信息

BufferedWriter -----------------> 套接字符流,需要以另一个字符流作为基础。将缓冲区数据,写入数据源。

public class ReaderTest {
    public static void main(String[] args) {
        // FileReader
        try {
            // 1、创建了一个文件字符读取流对象 通道
//            Reader reader = new FileReader("1.txt");
            // 操作数据
//            int n = 0;
//            while ((n = reader.read()) != -1){
//                System.out.print((char) n);
//            }
//            char[] chars = new char[1024];
//            int len = 0;
//            while ((len = reader.read(chars)) != -1){
//                System.out.println(new String(chars,0,len));
//            }

//            // 关闭流
//            reader.close();

//            节点流:可以直接向数据量写数据/从数据源读数据
//            InputStream in = new FileInputStream("1.txt");
//            // 操作流(处理流) 基于节点流或操作流,来创建,
//            // 可以在数据传输途中,对数据加以操作
//            Reader reader = new InputStreamReader(in);
//
            int n = 0;
            while ((n = reader.read()) != -1){
                System.out.print((char) n);
            }
//            char[] chars = new char[1024];
//            int len = 0;
//            while ((len = reader.read(chars)) != -1){
//                System.out.println(new String(chars,0,len));
//            }
//
//            // 关闭流 先开启的后关闭
//            reader.close();
//            in.close();
            Reader fileReader = new FileReader("1.txt");
            BufferedReader reader = new BufferedReader(fileReader);

            // readLine() 读取文档中一行数据,如果流中没有数据,返回null
            // 其他方法 如read() read(char[] chars) 同其他字符流
            String info = null;
            while ((info = reader.readLine()) != null){
                System.out.print(info);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

Writer方法

public class WriterTest {
    public static void main(String[] args) {
        try {
            // 字符输出流
//            Writer writer = new FileWriter("1.txt");
//            writer.write("好好学习,天天向上\n" +
//                    " Good Good Study, Day Day Up");
//
//            writer.flush();
//            writer.close();
            Writer writer = new FileWriter("1.txt");
            BufferedWriter bufferedWriter = new BufferedWriter(writer);

            bufferedWriter.write("小鸡炖蘑菇 宝塔镇河妖");

            bufferedWriter.flush();
            bufferedWriter.close();
            writer.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }



}

关闭流

写入流将数据写入数据源时,需要通过flush()刷新语句,才能将数
据真正写入数据源。在流关闭时,会自动执行flush()刷新语句。所以,
写入流在不关闭,也不刷新的情况下,有可能写不进数据。

为什么要关闭流:因为当我们完成流的操作后,流不会自动的进行关闭(除非程序结束),为了让无用的流不在占据消耗内存资源,需要主动调用close()方法完成流的关闭,释放内存资源。

ps:流关闭时,先开启的流,后关闭

并且为了保证流在除了程序结束(虚拟机关闭)的情况下,都会执行关闭操作,需要将关闭流的代码,写在就算异常发生都会被执行的位置(Finaly块),并且我们在调用流的close方法前,应当判断流是否被成功的建立(流的变量是否为null)

补充:异常的处理

事前处理:针对运行期异常,通过经验和代码特性提前做好预备工作,比如,对象进行调用前,判断是否为null,进行类型强转之前,instarceof判断类型,通过下标获取元素时,先判断下标是否越界。

事后处理:针对编译期异常,try-catch捕获或者throws抛出,try-catch捕获后再创建一个运行时运行时异常抛出。

//try-with-resource写法(JDk7后出现的) 会自动生成关闭流的代码
  //这种写法资源在括号里面声明,自动关闭资源,作用域只能try内部使用
    try ( Reader fileReader=null;
    BufferedReader bufferedReader=null;){
      //代码部分
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

操作文件流

写入流会在指定的目录下创建新文件。不过,指定的目录必须存在,
否则,也会抛出FileNotFoundException异常。

File类

File类构造方法:

public File(String pathname)                         通过文件路径或目录路径,创建File文件对象
String getAbsolutePath()                             得到当前文件对象的物理路径
boolean exists()                                     判断文件或目录是否存在
boolean isFile()                                     判断文件对象是否是文件
boolean isDirectory()                                判断文件对象是否是目录
boolean delete( )                                    删除当前文件对象表示的文件或目录,如果删除目录,那么目录必须为空
boolean mkdir( )                                     创建一个目录
        mkdirs( )                                    创建多个目录
File[] listFiles()                                   得到目录里所有的文件对象

File类提供了定位本地文件系统、描述文件和目录的功能。是 java.io 包中引用实际磁盘文件的对象。

对象序列化

在传输对象时,由于对象的数据庞大,无法直接传输。所以,在传输之前,
先将对象打散成字节序列,以利于传输。这个过程,称为序列化过程。

在字节序列到达目的地以后,又需要将字节序列还原成对象。这个过程,称为反序列化过程。

注意:能够被打散成为字节序列的对象必须是要实现序列化接口(Serializable),Serializable接口其实是一个空接口(没有任何抽象方法),一般称为空接口为标识接口。

实现对象序列化

public class UserBean implements java.io.Serializable 

java.io.Serializable序列化接口是标识性接口,该接口中没有定义方法。

如果对未实现序列化接口的对象进行传输,那么会抛出 java.io.NotSerializableException异常。

对象流

在java.io包中,提供了ObjectOutputStream,对对象信息进行保存操作。

public final void writeObject(Object obj)                    将对象序列化写入数据源
public final void writeObject(Object obj)                    将对象序列化写入数据源
public final Object readObject()                             将数据源的二进制数据,反序列化成对象。

transient

在传输对象时,默认情况下,对象的每个属性值都会被传输,如果想对对象的属性进行选择性传输,可以使用transient关键字。

transient是用于属性的修饰符,表示在对象序列化时,被transient修饰的属性值,不被传输。

private transient int money;

Properties类

Properties类可以将Map形式的键/值对数据存在文本文件中。能轻松的完成对属性文件数据的读取,以及将Properties对象中的数据保存在属性文件中。常用作软件的配置文件。 在后期一般使用属性文件做项目或框架的配置。

属性文件文件中,数据都已键/值对形式保存。

通过Properties类加载属性文件后,可以通过gitProperties(k)获取值,也可以通过setProperties(键、值)去给已存在的键修改或新增键值对。通过Store方法将内存中的Properties类数据保存到属性文件中。

Properties常用方法

load()                                   加载属性文件中的数据
store()Properties对象中现有的键值对保存进属性文件
getProperty()                            根据键对象,得到值对象。如果键对象不存在,返回null
setProperty()Properties对象中添加元素。如果键相同,则覆盖。

传统try-catch-finally的写法

    /**
     * 手动关闭
     */
    public static void test1() {
        final String FILE_PATH = "XXXXXXXXXXXX";
        FileOutputStream fileOutputStream = null;
        FileInputStream fileInputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(filePath));
            fileInputStream = new FileInputStream(new File(filePath));
            // 写
            fileOutputStream.write("ab1024".getBytes(Charset.defaultCharset()));
            // 读取
            int data = fileInputStream.read();
            while (data != -1) {
                System.out.print((char) data);
                data = fileInputStream.read();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭写流
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    // 关闭读流
                    if (fileInputStream != null) {
                        try {
                            fileInputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
 
        }
    }

try-with-resources写法

/**
 * 自动关闭 try-with-resource
 */
public static void test2() throws Exception {
    final String FILE_PATH = "XXXXXXXXXXXX";
    // java7中的try-with-resource,java9中有所改善
    try (FileOutputStream fileOutputStream = new FileOutputStream(new File(filePath));
         FileInputStream fileInputStream = new FileInputStream(new File(filePath))) {
        // 写
        fileOutputStream.write("ab1024".getBytes(Charset.defaultCharset()));
        // 读取
        int data = fileInputStream.read();
        while (data != -1) {
            System.out.print((char) data);
            data = fileInputStream.read();
        }
        // 模拟异常
      //  int a = 0 / 0;
    }
}
package onlytest;
 
import static java.lang.System.out;
 
public class testAutoClose {
 
    public static void main(String[] args) {
        try (MyClose close = new MyClose()) {
            close.println("liuqinhou");
            throw new Throwable("exist error");
        } catch (Exception e) {
            out.println("exception exists");
            e.printStackTrace();
        } catch (Throwable throwable) {
            out.println("throwable exists");
            throwable.printStackTrace();
        }
        out.println("main thread end");
    }
}
 
class MyClose implements AutoCloseable {
 
    public void println(String str) {
        out.println(str + " is working...");
    }
 
    @Override
    public void close() throws Exception {
        out.println("MyClose is closed now");
    }
}

File类

 File f = new File("./test");
        if (f.isDirectory()) {
            System.out.println(f.delete());
 }

Properties类方法案例

public class PropertiesTest {
    public static void main(String[] args) {
        Properties p = new Properties();
        try (Reader r = new FileReader("./configs/configs.properties")) {
            p.load(r);
            p.setProperty("pwd", "1234");
            System.out.println(p.getProperty("pwd"));
            System.out.println(p.getProperty("username"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        try (Writer w = new FileWriter("./configs/configs.properties")) {
            p.store(w,"Test");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
结果:
#Test
#Tue Apr 18 16:59:37 CST 2023
pwd=1234
username=123;

I/O流文件路径

Unix/Linux中,路径的分隔采用正斜"/",比如"cd /home/java"

而在Windows中,路径分隔采用反斜杠"\",比如"F:\yihong_\book"

开发是在Windows平台上,所以Java程序配置文件中文件路劲都是用的"\\",而项目是部署在Linux上的,所有文件路劲都是用的"/"

转义字符

Windows中,我在F盘复制地址“F:\yihong_\book”,粘贴至Java程序,会自动变成“F:\\yihong_\\book”。这个时候就发生了转义,这个操作是idea自动完成的。

String path = "F:\\yihong_\\book";` 对的

`String path = "F:\yihong_\book";` 错的

Java常见的系统路径与获取方法

// 分隔符
String fileSeperator = File.separator;
// 用户主目录
String userHome = System.getProperty("user.home");
// Java实时运行环境的安装目录
String javaPath = System.getProperty("java.home");
// 操作系统名称
String osName = System.getProperty("os.name");
// 当前用户程序所在目录
String userDir = System.getProperty("user.dir");
// JDK的安装目录
String jdkDir = System.getProperty("java.ext.dirs");

总结

1、java中使用java.io包中相关的api,完成输入输出数据操作。

2、I/O流按传输内容分为:字节流、字符流、对象流。按流的方向分为:输入流和输出流。

3、字节流的父类是InputStream和OutputStream;字符流的父类是Reader和Writer。

4、输入流常用的方法有:read(读取)、close(关闭)。输出流常用的方法有:write(写入)、flush(刷新)、close(关闭)。

5、File类提供定位本地文件系统、描述文件和目录的功能。

6、对象序列化是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象。

7、需要实现对象序列化的对象必须实现Serializable接口。

8、通过ObjectInputStream和ObjectOutputStream可以完成对象流操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值