Java输入输出

Java输入输出(I/O)

Java I/O 系统很复杂。Java语言定义了许多类专门负责各种方式的输入或者输出,这些类主要被放在java.io包和java.nio包(意思是"new I/O")中,但这还不是全部,如可以用java.util.Scanner类来从控制台和文件中读取文本和数值,java.lang.System类提供了用于文本控制台程序(字符界面程序)的标准输入,标准输出和错误输出流。【关于java.util.Scanner类和java.lang.System类,参见 “Java基础类库(系统包)”https://blog.csdn.net/cnds123/article/details/111879459 一文有关部分。】

 

Java I/O 系统很复杂,原因在于对于一门编程语言,创建一套好的输入输出(I/O)系统,是一项难度极高的任务。这个问题难就难在它要面对的可能性太多了。不仅是因为有那么多I/O的源和目地(文件,控制台,网络连接等等),而且还有很多方法(顺序的[sequential],随机的[random-access],缓存的[buffered],二进制的[binary],字符方式的[character],行的[by lines],字的[by words],等等)。Java类库的设计者们用"创建很多类"的办法来解决这个问题。Java在1.0版之后又对其I/O类库作了重大的修改,原先是面向byte的,现在又补充了面向Unicode字符的类库。为了提高性能,完善功能,JDK 1.4又加了一个nio(意思是"new I/O")。这么以来,如果你想对Java的I/O类库有个全面了解,并且做到运用自如,你就得先学习了解大量的类,这一点确实比较难。本文将选择部分相关知识介绍。

 

本文将重点放在java.io包这一部分。

官网 https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-tree.html

中文 https://www.runoob.com/manual/jdk11api/java.base/java/io/package-tree.html

 

Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。其体系结构分为三个部分:

1)流式部分――IO的主体部分;

2)非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;

3)其他类——文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

Java输入输出(I/O)体系结构示意图:

 

IO流定义:

流的本质是一组单向有序,分起始和终止的数据传输过程。

一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。Java中的数据流分为两种,一种是字节流,另一种是字符流。这两种流主要由4个抽象类来表示,分别为InputStreamOutputStreamReaderWriter,输入输出各两种。其中InputStreamOutputStream表示字节流,ReaderWriter表示字符流,其他各种各样的流均是继承这4个抽象类而来的。

【输入流就是把数据(键盘输入、鼠标、扫描仪等等外设设备)读入到内存(程序)中,输出流就是把内存(程序)中的数据输出到外设或其他地方,从文件角度简单总结就是,输入流就是读数据,输出流就是写数据。在这个过程中,始终把内存作为参考点。】

 

IO流分类:

按数据类型分为:字节流和字符流

字节流:

按字节进行读取(可以处理任意类型数据)

字符流

字节流 + 编码表(处理纯文本数据优先考虑)

【字符流与字节流的区别

字节流操作的基本单元为字节;字符流操作的基本单元为Unicode编码。

字节流默认不使用缓冲区;字符流使用缓冲区。

字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode编码;字符流通常处理文本数据,它支持写入及读取Unicode编码。】

 

按数据流向分为:输入流和输出流

输入流

有Reader(字符输入流)、InputStream(字节输入流)

输出流

有Writer(字符输出流)、OutputStream(字节输出流)

 

File类主要用于命名文件、查询文件属性和处理文件目录。特别指出,java.io提供了一个File类,注意这个类很容易让人产生误会,它表示的是一个文件名或者目录名,而不是文件本身,所以通过这个类没法对文件里面的数据进行操作,File类提供了一序列对文件操作的功能:删除文件,创建目录,查询文件大小等等。

 

Java字节流类的层次结构和字符流类的层次结构:

InputStream字节输入流的基类(父类),OutputStream字节输出流的基类;Reader字符输入流的基类,Writer字符输出流的基类。其它Io流的40多个类都是从由这4个抽象类基类中派生出来的。

java输入/输出流体系中常用的流的分类表:

分类

字节输入流

字节输出流

字符输入流

字符输出流

抽象基类

InputStream

OutputStream

Reader

Writer

访问文件

FileInputStream

FileOutputStream

FileReader

FileWriter

访问数组

ByteArrayInputStream

ByteArrayOutputStream

CharArrayReader

CharArrayWriter

访问管道

PipedInputStream

PipedOutputStream

PipedReader

PipedWriter

访问字符串

 

 

StringReader

StringWriter

缓冲流

BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter

转换流

 

 

InputStreamReader

OutputStreamWriter

对象流

ObjectInputStream

ObjectOutputStream

 

 

抽象基类

FilterInputStream

FilterOutputStream

FilterReader

FilterWriter

打印流

 

PrintStream

 

PrintWriter

推回输入流

PushbackInputStream

 

PushbackReader

 

其中,表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:用下划线标出的类代表抽象基类,无法直接创建实例。

 

☆InputStream类中的常用方法:

1.  public abstract int read( ):读取一个byte的数据,返回值是高位补0的int类型值。

2.  public int read(byte b[ ]):读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的。

3.  public int read(byte b[ ], int off, int len):从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。

4.  public int available( ):返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用。

5.  public long skip(long n):忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取。

6.  public int close( ) :我们在使用完后,必须对我们打开的流进行关闭。

 

☆OutputStream类中的常用方法:

1. public void write(byte b[ ]):将参数b中的字节写到输出流。

2. public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流。

3. public abstract void write(int b) :先将int转换为byte类型,把低字节写入到输出流中。

4. public void flush( ) : 将数据缓冲区中数据全部输出,并清空缓冲区。

5. public void close( ) : 关闭输出流并释放与流相关的系统资源。

注意:

(1). 上述各方法都有可能引起异常。

(2). InputStream和OutputStream都是抽象类,不能创建这种类型的对象。

 

☆FileInputStream类

FileInputStream类是InputStream类的子类,用来处理以文件作为数据输入源的数据流。使用方法:

  方式1:

  File fin=new File("d:/abc.txt");

  FileInputStream in=new FileInputStream(fin);

  方式2:

  FileInputStream in=new

  FileInputStream("d: /abc.txt");

  方式3:

  构造函数将 FileDescriptor()对象作为其参数。

  FileDescriptor() fd=new FileDescriptor();

  FileInputStream f2=new FileInputStream(fd);

 

☆FileOutputStream类

FileOutputStream类用来处理以文件作为数据输出目的数据流;一个表示文件名的字符串,也可以是File或FileDescriptor对象。

  创建一个文件流对象方法:

  方式1:

  File f=new File("d:/abc.txt");

  FileOutputStream out=new FileOutputStream (f);

  方式2:

  FileOutputStream out=new

  FileOutputStream("d:/abc.txt");

  方式3:构造函数将 FileDescriptor()对象作为其参数。

  FileDescriptor() fd=new FileDescriptor();

  FileOutputStream f2=new FileOutputStream(fd);

      方式4:构造函数将文件名作为其第一参数,将布尔值作为第二参数。

  FileOutputStream f=new FileOutputStream("d:/abc.txt",true);

注意:

(1). 文件中写数据时,若文件已经存在,则覆盖存在的文件;

(2). 的读/写操作结束时,应调用close方法关闭流。

 

☆DataInputStream类和DataOutputStream类

DataInputStream类对象可以读取各种类型的数据。

DataOutputStream类对象可以写各种类型的数据。

创建这两类对象时,必须使新建立的对象指向构造函数中的参数对象。例如:

  FileInputStream in=new FileInputStream("d:/abc.txt");

  DataInputStream din=new DataInputStream(in);

 

☆BufferInputStream类和bufferOutputStream类

BufferInputstream定义了两种构造函数

(1). BufferInputStream b= new BufferInputstream(in);

(2).  BufferInputStream b=new BufferInputStream(in,size) //第二个参数表示指定缓冲器的大小。

同样BufferOutputStream也有两种构造函数。

 

☆printstream类

用于写入文本或基本类型。

两种构造函数方法:

  PrintStream ps=new PrintStream(out);

  PrintStream ps=new PrintStream(out, autoflush)

 

☆FileReader类

FileReader主要用来读取字符文件,使用缺省的字符编码,有三种构造函数:

  --将文件名作为字符串

  FileReader f=new FileReader(“c:/temp.txt”);

  --构造函数将File对象作为其参数。

  File f=new file(“c:/temp.txt”);

  FileReader f1=new FileReader(f);

  --构造函数将FileDescriptor对象作为参数

  FileDescriptor() fd=new FileDescriptor()

  FileReader f2=new FileReader(fd);

 

☆ charArrayReader

将字符数组作为输入流,构造函数为:

  public CharArrayReader(char[] ch);

 

☆StringReader

读取字符串,构造函数如下:

  public StringReader(String s);

 

☆ InputStreamReader

从输入流读取字节,在将它们转换成字符。

  Public inputstreamReader(inputstream is);

 

☆FilterReader

允许过滤字符流

  protected filterReader(Reader r);

 

☆BufferReader

接受Reader对象作为参数,并对其添加字符缓冲器,使用readline()方法可以读取一行。

  Public BufferReader(Reader r);

 

☆FileWrite

将字符类型数据写入文件,使用缺省字符编码和缓冲器大小。

  Public FileWrite(file f);

 

☆chararrayWrite()

将字符缓冲器用作输出。

  Public CharArrayWrite();

 

☆ PrintWrite

生成格式化输出

  public PrintWriter(outputstream os);

 

☆ filterWriter

用于写入过滤字符流

  protected FilterWriter(Writer w);

 

例1、下面的程序向文件scores.txt中写两行文本

public class WriteDataTest {

  public static void main(String[] args) throws java.io.IOException {

    java.io.File file = new java.io.File("d:/scores.txt");

    if (file.exists()) {

      System.out.println("文件已存在");

      System.exit(0);

    }

 

    // 创建一个文件

    java.io.PrintWriter output = new java.io.PrintWriter(file);

 

    // 写入文件

    output.print("李萌 ");

    output.println(90.5);

    output.print("赵一男 ");

    output.println(85);

 

    // 关闭文件

    output.close();

  }

}

 

运行完毕,在d盘文件scores.txt中的内容如下:

李萌 90.5

赵一男 85

 

使用blue演示如下图

 

 

附录、java.nio包

官网 https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/package-tree.html

中文 https://www.runoob.com/manual/jdk11api/java.base/java/nio/package-tree.html

java.nio与java.io之间有什么区别

IO                 NIO

面向流             面向缓冲

阻塞IO            非阻塞IO

无选择器           选择器

(1)面向流与面向缓冲

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

(2)阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

(3)选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。IO 没有选择器。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学习&实践爱好者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值