java实验十三io_Java语言基础13—IO

I/O(输入/输出)

参考资料:《Java从入门到精通》/明日科技编著. 4版. 北京:清华大学出版社,2016

一、流概述

流是一种有序的数据序列,根据操作的类型,可分为输入流和输出流。

I/O(Input/Output,输入/输出)流提供了一条通道程序,可以用这条通道把源中的字节序列送到目的地。

虽然I/O流通常与磁盘有关,但是程序的源和目的地也可以是键盘、鼠标、内存或显示器窗口等。

Java由数据流处理输入/输出模式,程序从指向源的输入流中读取数据,数据源可以是文件、网络、压缩包或其他数据源,这个过程也称为数据的读取,如下图所示:

caec5aec812127de5e30857e9782309d.png

输出流是指向数据要到达的目的地,程序通过向输出流中写入数据,把信息传递到目的地,输出流的目的地可以是文件、网络、压缩包或其他输出目标,该过程也称为数据的写入,如下图所示:

affd1fc89640e0a768651ba7c00bc655.png

总的来说,在Java程序中,要想从文件中读取数据或者将数据写入文件中,都需要在程序和文件之间建立一条数据的传输通道,这个通道就是输入/输出流。

当程序创建输入流对象时,Java会自动创建输入流通道;

当程序创建输出流对象时,Java会自动创建输出流通道。

二、输入/输出流

Java语言在java.io包中定义了很多处理各种输入/输出的类。

字节流

抽象类InputStream(字节输入流)是所有输入字节流类的父类;

抽象类OutputStream(字节输出流)是所有输出字节流类的父类;

字符流

抽象类Reader(字符输入流)是所有输入字符流类的父类;

抽象类Writer(字符输出流)是所有输出字符流类的父类;

字节流与字符流的区别:

dd8a55fc403e64e4c70f173dcf7496f3.png

简而言之,字节流在操作文件时不会用到缓存(内存),而是直接操作文件本身。而字符流在操作的时候使用了缓存区,而不是直接操作文件。例如,使用字符流执行写入数据操作,如果使用close()方法没有关闭输出流,则文件中看不到任何写入的内容,除非使用flush()方法强行将缓存数据写入目标文件。

本质上来说,一切都是字节流,其实没有字符流这个东西。字符只是根据编码集对字节进行解码的产物。

字节流:01010100010010100101

字符流:aah65Khannskm3bhbasv //本质上还是字节流

【技巧】:字节流可以处理所有数据文件,若处理的是纯文本数据,建议使用字符流。

1、输入流

(1)字节输入流

InputStream类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如下所示:

6957ea697a5f1c167e0c421604299bab.png

该类中所有方法遇到错误时都会引发IOException异常,下面是该类的一些常用方法:

read ():从输入流中读取数据的下一个字节。

read (byte[] b):从输入流中读入一定长度的字节,并以整数形式返回字节数。

mark (int readlimit):在输入流的当前位置放置一个标记,readlimit参数告知此输入流在标记位置失效之前允许读取的字节数。

reset ():将输入指针返回到当前所做的标记处。

skip (long n):跳过输入流上的n个字符并返回实际跳过的字节数。

close() :关闭此输入流,并释放与该流相关的所有系统资源。

说明:并不是所有的InputStream类的子类都支持InputStream中定义的所有方法,如skip()、mark()、reset()等方法只对某些子类有用。

(2)字符输入流

Java中的字符是Unicode编码,是双字节的,即一个字符占用两个字节。InputStream是用来处理字节的,并不适合处理字符文本。Java为字符文本的输入专门提供了一套单独的类Reader,但是Reader类并不是InputStream类的替换者,只是在处理字符串时简化了编程。

Reader类是字符输入流的抽象类,是所有字符输入流的父类。

2、输出流

(1)字节输出流

OutputStream类是字节输出流的抽象类,该类是所有表示字节输出流类的父类,其层次结构如下所示:

258e4f0bd0c8b1a3b17887d4ae520c66.png

OutputStream类中的所有方法均返回void,在遇到错误时会引发IOException异常,下面对OutputStream类中的方法做简单的介绍:

write (int b):将指定的字节写入此输出流。

write (byte[] b):将b个字节从指定的byte数组写入此输出流。

write (byte[] b, int off, int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流。

flush():彻底完成输出并清空缓存区。

close():关闭输出流。

(2)字符输出流

Writer类是字符输出流的抽象类,此类为所有字符输出流类的父类,其层次结构如下所示:

8352df639ac58b394eaee261add5619e.png

三、File类

File类是java.io包中唯一代表磁盘文件本身的对象。

可以通过调用File类中的方法,实现创建、删除、重命名文件等操作。

File类的对象主要用来获取文件本身的一些信息,如文件所在的目录、文件的长度、文件的读写权限等,但是不能对文件执行读取或写入数据的操作。

数据流可以将数据写入到文件中,文件也是数据流最常用的数据媒介。

1、文件的创建与删除

可以使用File类创建一个文件对象。通常使用以下3种构造方法来创建文件对象:

File (String pathname)

该构造方法通过将给定的路径名字符串,并转换为抽象路径名来创建一个新的File实例。语法格式如下:

new File(String pathname)

其中,pathname指路径名称(包含文件名),例如:

File file = new File("C:\Users\XULIANG\abc.txt")

File (String parent, String child)

该构造方法根据定义的父路径和子路径字符串(包含文件名)创建一个新的File对象。语法格式如下:

new File (String parent, String child)

parent:父路径字符串。例如,"C:\Users"。

child:子路径字符串。例如,"letter.txt"。

【问】那何为父路径与子路径呢?

这里举个例子,关于"C:\Users\XULIANG\Documents"这个目录,"C:"是"C:\Users"的父目录,"C:\Users"是"C:\Users\XULIANG"的父目录。简而言之,父目录就是当前目录的上级目录。

File (File f, String child)

该构造方法根据parent抽象路径名和子路径字符串创建一个新的File对象。语法格式如下:

new File (File f, String child)

f:父路径对象。例如,"D:/doc/"。

child:子路径字符串。例如,"letter.txt"。

import java.io.File;

public class FileTest {

public static void main(String[] args) {

//创建文件对象,并指定文件名

File file = new File("test.txt");

//判断该文件是否存在

if (file.exists()){

//若存在,则删除文件

file.delete();

System.out.println("文件已删除");

}else {

//捕捉可能出现的异常

try {

//若不存在,则创建该文件

file.createNewFile();

System.out.println("文件已创建");

}catch (Exception e){

e.printStackTrace();

}

}

}

}

运行结果:

文件已创建

上述代码执行后,会在该项目所在的路径下创建一个名为"test.txt"的文本文件。

3、获取文件信息

File类提供了很多方法用于获取文件的一些信息,常用的方法如下所示:

方法

返回值

说明

getName()

String

获取文件的名称

canRead()

boolean

判断文件是否可读

canWrite()

boolean

判断文件是否可写

exits()

boolean

判断文件是否存在

length()

long

获取文件的长度(以字节为单位)

下面通过实例来介绍使用方法来获取文件的信息:

import java.io.File;

public class FileTest2 {

public static void main(String[] args) {

File file = new File("test.txt");

if(file.exists()){

String name = file.getName();

long length = file.length();

Boolean hidden = file.isHidden();

System.out.println("文件的名字:" + name);

System.out.println("文件的长度:" + length);

System.out.println("是否为隐藏文件:" + hidden);

}

}

}

运行结果:

文件的名字:test.txt

文件的长度:0

是否为隐藏文件:false

四、文件输入/输出流

在程序的运行期间,大部分数据都保存在内存中进行操作,当程序结束后或关闭时,这些数据将消失。如果需要将数据永久保存,可使用文件输入/输出流与指定的文件建立连接,将需要的数据永久保存到文件中。

1、FileInputStream和FileOutputStream类

FileInputStream和FileOutputStream类都是用来操作磁盘文件。

(1)FileInputStream类

FileInputStream类继承于InputStream类,满足比较简单的文件读取要求。FileInputStream类常用的构造方法有两种:

FileInputStream (String name)

FileInputStream (File file)

第一种构造方法使用给定文件名name,创建一个FileInputStream对象。第二种构造方法使用File对象创建FileInputStream对象,该构造方法允许在把文件连接输入流之前对文件作进一步分析。

(2)FileOutputStream类

FileOutputStream类与FileInputStream类具有相同的构造方法,创建一个FileOutputStream

对象时,可以指定不存在的文件名,但此文件不能是一个被其他程序打开的文件。

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

public class FileTest3 {

public static void main(String[] args) {

File file = new File("word.txt");

System.out.println("---------------写入操作----------------");

try{

FileOutputStream fileOutputStream = new FileOutputStream(file);

//字符串转化成字节码形式,采用Java平台默认字符集进行编码

byte[] bytes = "Java基础班学习".getBytes();

System.out.println("\"Java基础班学习\"字符串的字节码形式:");

//遍历数组

for (int i = 0; i < bytes.length; i++) {

System.out.print(bytes[i] + " ");

}

System.out.println("\n总共 " + bytes.length + " 个字节");

//执行写入文件操作

fileOutputStream.write(bytes);

//关闭输出流

fileOutputStream.close();

}catch (IOException e){

e.printStackTrace();

}

System.out.println("---------------读取操作----------------");

try{

FileInputStream inputStream = new FileInputStream(file);

byte bytes[] = new byte[100];

//读取文件的数据内容,然后保存到bytes数组中,返回字节的个数

int str = inputStream.read(bytes);

System.out.println("该文件内容所占用的字节个数为:" + str);

/*

* 将数组中的字节进行解码,并转化为字符串

* 采用Java平台默认字符集进行解码

* 形参0表示起始索引位置,str表示截取长度

*/

String content = new String(bytes,0, str);

System.out.println("文件中的内容为:" + content);

//关闭输入流

inputStream.close();

}catch (IOException e){

e.printStackTrace();

}

}

}

运行结果:

---------------写入操作----------------

"Java基础班学习"字符串的字节码形式:

74 97 118 97 -27 -97 -70 -25 -95 -128 -25 -113 -83 -27 -83 -90 -28 -71 -96

总共 19 个字节

---------------读取操作----------------

该文件内容所占用的字节个数为:19

文件中的内容为:Java基础班学习

【注意】虽说Java在程序结束时自动关闭所有打开的流,但是当使用完流后,应该显示地关闭所有打开地流仍是一个良好的习惯,一个被打开的流有可能耗尽系统资源。

2、FileReader和FileWriter类

使用FileOutputStream类向文件中写入数据与FileInputStream类从文件中读取内容,都存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。

FileReader和FileWriter字符流对应了FileInputStream和FileOutputStream类。FileReader流顺序地读取文件,只要不关闭流,每次调用read()方法就会顺序地读取源中地内容,直到源的末尾或流被关闭。

五、带缓存的输入/输出流

缓存是I/O的一种性能优化。

缓存流为了I/O流增加了内存缓存区,有了缓存区,使得在流上执行skip()\mark()和reset()方法都成为可能。

1、BufferedInputStream和BufferedOutputStream类

BufferedInputStream类可以对所有InputStream类进行缓存区的包装以达到性能优化的目的。BufferedInputStream类有两个构造方法:

BufferedInputStream (InputStream in)

BufferedInputStream (InputStream in, int size)

第一种形式的构造方法创建了一个带有32个字节的缓存流,第二种形式的构造方法按指定的大小来创建缓存区。BufferedInputStream读取文件的过程如下所示:

数据流 数据流 数据流

文件 -------> InputStream -------> BufferedInputStream -------> 目的地

使用BufferedOutputStream输出信息和OutputStream输出信息完全一样,只不过BufferedOutputStream有一个flush()方法将缓存区的数据强制写入文件中。BufferedOutputStream也有两个构造方法:

BufferedOutputStream (InputStream in)

BufferedOutputStream (InputStream in, int size)

第一种形式的构造方法创建了一个带有32个字节的缓存流,第二种形式的构造方法按指定的大小来创建缓存区。

【注意】flush()方法用于即使在缓存区没有满的情况下,也将缓存区的内容强制写入到外设,习惯上将这个过程称为刷新。

2、BufferedReader与BufferedWriter类

BufferedReader与BufferedWriter类分别继承于Reader和Writer类,这两个类同样具有内部缓存机制,并可以以行为单位进行输入/输出。

BufferedReader类常用的方法:

read() 方法:读取单个字符。

readLine()方法:读取一个文本行,并将其返回为字符串。若无数据可读则返回null。

BufferedWriter类的方法都返回void,常用的方法:

write(String s, int off, int len)方法:写入字符串的某一个部分。

flush()方法:刷新该流的缓存。

newLine()方法:写入一个行分隔符。

在使用BufferedWriter类的write()方法时,数据并没有立刻被写入输出流,而是首先进入缓存区中。如果想立即将缓存区中的数据写入输出流时,必须调用flush()方法。

import java.io.*;

public class Student {

public static void main(String[] args) {

String content[] = {"你好吗","好久不见","记得常联系"};

File file = new File("word.txt");

try{

FileWriter fw = new FileWriter(file);

BufferedWriter bw = new BufferedWriter(fw);

for (int i = 0; i < content.length; i++) {

bw.write(content[i]); //将字符数组的元素写入磁盘文件

bw.newLine(); //写入行分隔符

}

bw.close(); //关闭BufferedWriter流

fw.close(); //关闭FileWriter流

}catch (IOException e){

e.printStackTrace();

}

try {

FileReader fr = new FileReader(file);

BufferedReader bufr = new BufferedReader(fr);

String str = null;

int i = 0;

//读取文件的一行,若不为null,则进入循环

while ((str = bufr.readLine()) != null){

i++;

System.out.println("第" + i + "行:" + str);

}

bufr.close(); //关闭BufferedReader流

fr.close(); //关闭FileReader流

}catch (IOException e){

e.printStackTrace();

}

}

}

运行结果:

第1行:你好吗

第2行:好久不见

第3行:记得常联系

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值