Lesson12 Java Io

1 Java输入输出(基础)

Java 的 IO 流主要包括输入、输出两种 IO 流,每种输入、输出流又可分为字节流字符流两大类

  • 字节流以字节为单位来处理输入、输出操作
  • 字符流以字符为单位来处理输入、输出操作

命令行输入

Pasted image 20241202163917.png

标准输入输出

System.out 输出
System.err 输出标准错误
System.in 输入

Pasted image 20241202164517.png
Pasted image 20241202164521.png

格式化输出:String.format
out.printf("name count\n");
String s = String.format(%s %5d%n”, user, total);

Pasted image 20241202164703.png
Pasted image 20241202164723.png

scanner
  • Scanner的作用:通过分隔符模式将输入分解为标记,默认情况下该分隔符模式与空白匹配。
  • 通过Scanner 类的 next()nextLine() 方法获取输入的字符串,在读取前我们一般需要使用 hasNexthasNextLine 判断是否还有输入的数据

next
Pasted image 20241202164838.pngPasted image 20241202164845.png
nextLine
Pasted image 20241202164854.pngPasted image 20241202164901.png
nextInt
Pasted image 20241202164936.pngPasted image 20241202164949.png

2 IO 流

IO分为字节流和字符流

  • 字节流可以操作所有类型的文件;
  • 字符流只能操作纯文本文件

Java IO、NIO、AIO

  • IO流(同步、阻塞)
  • NIO(同步、非阻塞):NIO(NEW IO)
  • NIO2(异步、非阻塞):AIO(Asynchronous IO)
同步与异步
  • 同步:是一种可靠的有序运行机制,进行同步操作时,后续的任务须等待当前调用返回,才会进行下一步;
  • 异步:后续任务不需要等待当前调用返回,通常依靠事件、回调等机制来实现任务间次序关系;
阻塞与非阻塞
  • 阻塞:在进行读写操作时,当前线程会处于阻塞状态,无法从事其他任务。只有当条件就绪才能继续;
  • 非阻塞:不管IO操作是否结束,直接返回,相应操作在后台继续处理
IO与NIO

NIO和IO作用和目的相同,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。
Pasted image 20241202170053.png

NIO的三个核心基础组件
  • Channels(通道):流是单向的,Channel是双向的,既可以读又可以写,Channel可以进行异步的读写,对Channel的读写必须通过buffer对象
  • Buffer(缓冲区)
  • Selector(选择器):Selector是一个对象,它可以注册到很多个Channel上,监听各个Channel上发生的事件,并且能够根据事件情况决定Channel读写。这样,通过一个线程管理多个Channel,就可以处理大量网络连接了

3 Java文件管理(File类)

Pasted image 20241202170724.png

  • 输入流将数据从文件、标准输入或其它外部设备输入加载到内存;
  • 输出流的作用正好相反,将内存中的数据保存到文件中,或传输给输出设备。
什么是输入输出流
  • 输入数据流:输入数据流只能读,不能写
    • 字节流:Java中的输入数据流(字节流)都是抽象类InputStream的子类;
    • 字符流:Java中的输入数据流(字符流)都是抽象类Reader的子类;
  • 输出数据流:输出数据流只能写,不能读
    • 字节流:java中的输出数据流(字节流)都是抽象类OutputStream的子类;
    • 字符流:java中的输出数据流(字符流)都是抽象类Writer的子类;
File 类
  • File 类代表与平台无关的文件和目录。

  • File 能新建、删除、重命名文件和目录。

  • 类File一般不涉及文件内部的具体内容,而是从整体上对文件进行处理,因此类File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用文件输入/输出流(FileInputStream/FileOutputStream)。
    Pasted image 20241202171013.pngPasted image 20241202171020.pngPasted image 20241202171034.png

  • File类有四个构造方法

    • File(File parent, String child);
    • File(String pathname);
    • File(String parent, String child);
    • File(URI uri);
  • 在Java文件体系中,文件和目录是统一抽象成文件来处理的;

4 输入/输出——字节流

InputStream

Pasted image 20241202171820.png

简介

  • InputStream是抽象类,所以不能通过“new InputStream()”的方法构造InputStream的实例。但它声明了输入流的基本操作,包括读取数据(read)、关闭输入流(close)、获取流中可读的字节数(available)、移动读取指针(skip)、标记流中的位置(mark)和重置读取指针(reset) 等,它的子类一般都重写这些方法。
  • 通过构造InputStream子类的实例方式可以获得InputStream类型的实例。

相关函数介绍

  • 方法read()提供了三种从流中读数据的方法.
    • int read():一次只能读一个字节,抽象方法。
    • int read(byte b[]):一次读多个字节到数组中
    • int read(byte[],int off,int len);
  • 一般available和read()混合使用,这样在读操作前可以知道有多少字符需要读入。
  • mark通常与reset()方法配合使用,可重复读取输入流所指定的字节数据。

分类

  • FileInputStream:用于从本地文件中读出数据。
  • ObjectInputStream:用来读取对象
  • PipedIntputStream:用于管道输入/输出时从管道中读取数据
  • SequencedInputStream:用来把两个或更多的InputStream输入流对象转换为单个inputStream输入流对象使用。
  • FilterInputStream:其子类PushbackInputStream和BufferedInputStream读取数据时可以对数据进行缓冲,这样可以提高效率增加特殊功能
  • DataInputStream实现了java.io包中的DataInput接口,读取数据的同时,可以对数据进行格式处理。因此,能用来读取java中的基本数据类型
  • ByteArrayInputStream:包含一个内存缓冲区,用于从内存中读取数据。
  • AudioInputStream:Audio的输入输出
OutputStream

Pasted image 20241202172144.png

简介
OutputStream是抽象类,所以不能通过“newOutputStream()”的方法构造OutputStream的实例。但它声明了输出流的基本操作,包括输出数据(write)、关闭输出流(close)、清空缓冲区(flush)等

分类

  • FileOutputStream:用于向本地文件中写入数据。
  • PipedOutputStream:用于管道输入/输出时把数据向管道输出
  • DataOutputStream:提供了对java的基本数据类型的支持
  • PrintStream:提供了向屏幕输出有格式数据的很多方法
FileInputStream和FileOutputStream
FileInputStream

FileInputStream是InputStream的子类,用于从本地文件中读取数据,他有三个构造方法。

  • 对文件内容进行操作的基本步骤如下:
    • 创建该文件所对应的实例,以获得相关的资源,如存放该文件信息的内存空间和对该文件的控制权限
    • 对该文件进行读(输入)/写(输出)操作。
    • 最后关闭该文件,以释放所占用的资源。
  • FileInputStream不支持缓冲和做标记。
    Pasted image 20241209162055.png
FileOutputStream
  • FileOutputStream是OutputStream的子类,他有五个构造方法;其中,FileOutputStream(String name);和FileOutputStream(String name, boolean append);这两个构造方法最常用。
  • 应用FileOutputStream将数据写入文件的基本步骤如下:
    • 创建FileOutputStream的实例,以获得相关的资源;
    • 将数据写入文件中;
    • 最后关闭文件,以释放所获得的资源。
  • FileInputStream(String name) throws FileNotFoundException
    例如:读取C盘javatemp目录下的文件test.txt
    FileInputStream fis=new FileInputStream(“c:\javatemp\test.txt”)
  • FileInputStream(File f) throws FileNotFoundException
    例如:
    File file=new File(“./temp/test.txt ”);
    FileInputStream fis=new FileInputStream(file);
FilterInputStream和FilterOutputStream

FilterInputStream和FilterOutputStream是两个抽象类,都属于过滤流类。

  • 过滤流类的主要功能是为输入/输出流提供一个通用的接口,提供将流连接在一起的能力,即将一个流连接到另一个流的尾部,这样可以得到满足应用程序要求的很长的输入和输出过滤器。
  • 为了防止多线程同时对一个I/O流进行操作所带来的严重后果,过滤流类实现了对象的同步机制,使得在某一时刻只能一个线程访问它。
FilterInputStream

FilterInputStream:过滤器输入流是其它带过滤器的输入流的超类

  • FilterInputStream的方法与InputStream的方法完全一样,也就是说前者只是简单地重写了后者的所有方法
    • 构造方法: FilterInputStream(InputStream in);
      过滤器输入流以一个InputStream为自己的数据
    • 成员变量:protected InputStream in在构造方法里会对这个变量赋值,即:this.in = in;
FilterOutputStream

FilterOutputStream:过滤器输出流是其它带过滤器的输出流的超类

  • FilterOutputStream的方法与OutputStream的方法完全一样,也就是说前者只是简单地重写了后者的所有方法;
    • 构造方法: FilterOutputStream(OutputStream out);
    • 过滤器输出流位于一个OutputStream之上;
    • 成员变量:protected OutputStream out
      在构造方法里会对这个变量赋值,即:this.out = out;
BufferedInputStream和BufferedOutputStream

带缓存的输入流和输出流对应的类是:BufferedInputStream和BufferedOutputStream:

  • 当创建BufferedInputStream或BufferedOutputStream的实例时,均会在内存中开辟一个字节数组的存储单元(一般称为缓存),默认为8192字节,用来存放数据流中的数据。
  • 他们用来连接别的输入/输出流,以获得读写性能上的提高
    为什么?
    借助于字节数组缓存,在读取或存储数据时可以将一个较大数据块读如内存中,或将内存中较大数据块一次性写入指定的文件中,从而达到提高读/写效率的目的。
  • 这两个类通过缓存机制进一步增强了输入流(InputStream)和输出流(OutputStream)读取和存储数据的效率。
  • 方法flush():当通过BufferedOutputStream写数据时,数据不直接写入数据流,而是先写入缓冲区,当缓冲区满时,数据才会写入所连接的数据流。当缓冲区未满时,可以用该类的方法flush(),将缓冲区的数据强制全部写入输出流。
  • 第一次读取数据按块读入缓冲区,此后如果有数据读取操作则直接访问缓冲区。缓冲区的作用除了可以提高性能外,还提供了标记读取位置(mark方法)和重置标记(reset方法)的支持
    Pasted image 20241209163125.png
    Pasted image 20241209163142.png
DataInputStream和DataOutputStream
  • 类DataInputStream与DataOutputStream:主要用来读取于存储基本数据类型的数据。而且数据流的存储格式采用统一的形式(故应当二者同时用),即与具体的计算机无关。
  • 一般来说,DataInputStream是用来读取DataOutputStream存储的数据。
  • DataInputStream与DataOutputStream的构造方法分别要求以输入流与输出流的实例引用作为参数。
例1

Pasted image 20241209163325.png

例2

Pasted image 20241209164210.png
Pasted image 20241209164220.png
Pasted image 20241209164228.png
Pasted image 20241209164235.png
Pasted image 20241209164252.png

I/O Stream Chaining

Pasted image 20241209164321.png

PushbackInputStream(回压输入流)
  • 必要性:在一些情况下,往往需要先读入一个字节或几个字节以判断输入流的一些属性,然后在正式读取时,再将全部内容读出。这时,直接使用FileInputSteam类就很不方便
  • 类PushbackInputStream提供了几个unread()方法,把读过的一个或几个字节数据退回到输入流中,当然也可以回压别的字节数据到输入流中
  • 创建回压输入流对象的构造函数如下:
    • Public PushbackInputStream(InputStream in);
    • Public PushbackInputStream(InputStream in,int size);
  • Unread方法:
    • Public void unread(int b);
    • Public void unread(byte[] b);
    • Public void unread(byte[]b,int off, int len);
PrintStream(打印流)

PrintStream是非常重要的输出流,在标准输出中经常用到的System.out就是指向PrintStream实例的一个引用。

  • PrintStream的实例是建构在其它输出流实例的基础之上,即PrintStream的构造方法要求将其它OutputStream子类的实例作为它的参数。
  • PrintStream提供的数据输出方法比较多,主要可以分成三类:write、print和println
    Pasted image 20241209164822.png

为什么PrintStream适合做打印流?
与其它OutputStream子类相比,PrintStream在使用上显得更为方便一些。

  • 它提供了更多的输出成员方法,输出的数据不必先转换成字符串类型或其它类型
  • PrintStream的成员方法一般不会抛出异常
  • PrintStream具有自动强制输出(flush)功能,即当输出回车换行时,在缓存中的数据会全部自动写入指定的文件或在标准输出窗口中显示
ObjectInputStream和ObjectOutputStream
  • 必要性:有时我们希望把程序中创建的一些类对象保存到文件中,以供其他的程序使用。

  • Java.io包中提供了对象流ObjectInputStream类和ObjectOutputStream类,用来创建对象输入流和对象输出流,读取文件中的对象或将对象写入文件中

  • 创建对象流:

    • Public ObjectInputStream(InputStream in);
    • Public ObjectOutputStream(OutputStream in);
  • 读写对象流:

    • Public final void writeObject(Object obj) throws IOException
    • Public final void readObject() Throws OptionalDataException, ClassNotFoundException, IOException
  • 这两个类中除基本的读写处理方法之外,还提供了大量的读/写java基本数据类型、读/写Unicode编码的字符串的方法。

需要注意的是,使用对象流读取和写入对象时,要保证对象是串行化(Serializable)的;

串行化

串行化的概念:指对象通过把自己转化为一系列字节,记录字节的状态数据,以便再次利用的这个过程

  • 串行化(Serializable)是java.io包中定义的一个接口。
    • Serializable接口中没有定义任何方法,只是一个特殊的标记,用来告诉编译器,这个对象参加了串行化的协议,可以把它串行化
    • 有些对象不能进行串行化,例如Thread对象,FileInputStream对象、FileOutputSteam对象等,因为其状态是暂时的。对不希望串行化的对象要用关键字transient修饰
  • 串行化和对象流的关系?
    • 将串行化的对象通过对象输入输出流写入文件或传送到其它地方
  • 对象串行化方法的一个典型应用是编写网络应用程序,通过对象串行化方法可以在另一台主机中重构指定的对象。
  • 一个类要具有可串行化的特性就必须实现接口java.io.Serializable。对于可以串行化的对象可以用 java.io.ObjectOutputStream来输出该对象,用java.io.ObjectInputStream来读入该对象。
    Pasted image 20241209165711.png
    Pasted image 20241209165716.png
    Pasted image 20241209165800.png
    Pasted image 20241209165807.png
    Pasted image 20241209165942.png
PipeInputStream和PipeOutputStream

管道数据流的两个类一定是成对的,同时使用并相互连接的,这样才形成一个数据通信管道。管道数据流主要用于线程间的通信

  • 创建管道输入流(PipeInputStream)对象
    • Public PipeInputStream();
    • Public PipeInputStream(PipeOutputStream src);
  • 创建管道输出流(PipeOutputStream)对象
    • Public PipeOutputStream();
    • Public PipeOutputStream(PipeInputStream src);
  • 管道连接方法(对没有连接的管道流):
    • Public void connect(PipeInputStream src)
    • Public void connect(PipeOutputStream src)
  • 管道输入流中还增加了一个方法:
    Protected void receive(int b)throws IOException
    用来接收一个字节数据b,如果管道中没有数据,该方法将阻塞
使用管道流

PipedInputStream pis=new PipedInputStream();
PipedOutputStream pos=new PipedOutputStream(pis);
或:
PipedOutputStream pos=new PipedOutputStream();
PipedInputStream pis=new PipedInputStream(pos);

PipedInputStream pis=new PipedInputStream();
PipedOutputStream pos=new PipedOutputStream();
Pis.connect(pos);

注意事项
  • 被连接的管道流必须没有与任何别的管道流连接,否则会抛出IOException异常,管道输出流输出数据到管道,管道输入流在管道中读取数据。
  • 因为管道流是InputStream和OutputStream类的子类,因此它能作为别的输入/输出流的参数,即连接到别的流,从而增加其输入/输出的功能。
    Pasted image 20241209170348.png

5 输入/输出——字符流

Pasted image 20241202172444.png
Pasted image 20241202172450.png

  • Reader类和Writer类中的大部分方法与InputStream类和OutputStream类中的对应方法名称相同,只是读取或写入的数据是字符、字符数组和字符串等。
  • 输入输出流中多个方法都可能抛出IOException异常,因此调用它们时,应放在 try…catch 块中,以便捕获和处理异常。
  • 如果程序读到的数据是不同国家的语言,其编码不同,那么程序应使用Reader和Writer流。
  • 采用读写器构造出来的文件一般是文本文件。文本文件可以用文本编辑器进行浏览,阅读比较方便,但往往需要更大的存储空间。
    Pasted image 20241209170659.pngPasted image 20241209170723.png
InputStreamReader类和OutputStreamWriter类
  • InputStreamReader类继承自Reader类,通过其read方法从字节输入流中读取一个或多个字节数据转换为字符数据,它不是一个缓冲流,因此其转换的效率并不高
  • OutputStreamWriter类继承自Writer类,其作用是转变字符输出流为字节流输出
  • InputStreamReader类和OutputStreamWriter类都可以接一个缓冲流来提高效率
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
    Pasted image 20241209171036.pngPasted image 20241209171156.png

Pasted image 20241209171105.png
Pasted image 20241209171120.pngPasted image 20241209171137.png

java中的unicode

如果字符流不是来自本地,有可能编码不一样,直接读取会读出不正确字符
处理方法:ir=new InputStreamReader(is,“utf-8”);

FileReader和FileWriter

FileReader和FileWriter类分别是Reader和Writer子类,他们分别用来从字符文件读取字符向字符文件输出字符数据

总结

Pasted image 20241209171453.png
Pasted image 20241209171507.png
Pasted image 20241209171546.png
Pasted image 20241209171714.png

Built with MDFriday ❤️