Java总结系列之输入输出I/O

本文深入讲解Java中的IO流概念及应用,包括字符流与字节流的区别、缓冲区的使用方法、装饰设计模式的应用,以及如何选择合适的流来进行高效的数据读写操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java对数据的操作是通过流的方式进行的

IO流用来处理设备之间的数据传输,Java用语数据流的对象都在IO包里面,流按照操作数据分为两种:

字节流InPutStream、OutPutStream和字符流Reader、Writer

字符流与字节流的区别是什么?

字符流在读取时,在读取完字节数据之后会继续查表,而文字有特定的编码格式,

所以读取文本文件要使用字符流,而要读取媒体文件要使用字节流

Java的垃圾回收机制只针对堆内存中的对象,不回收物理内存,所以我们需要在finally里面对流进行关闭


记住;如果要操作文字数据,建议优先考虑字符流。

而且要将数据从内存写到硬盘上,要使用字符流中的输出流。Writer

硬盘的数据基本体现是文件。 希望找到一个可以操作文件的Writer.

 

File:文件和目录路径名的抽象表示形式

 

//创建一个可以往文件中写入字符数据的字符输出流对象。
/*
* 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)。

* 如果文件不存在,则会自动创建。
* 如果文件存在,则会被覆盖。

* 如果构造函数中加入true,可以实现对文件进行续写!
*/
FileWriter fw = new FileWriter("demo.txt",true);

/*
* 调用Writer对象中的write(string)方法,写入数据。 

* 其实数据写入到临时存储缓冲区中。

*/
fw.write("abcde"+LINE_SEPARATOR+"hahaha");
// fw.write("xixi");

/*
* 进行刷新,将数据直接写到目的地中。
*/

// fw.flush();

/*
* 关闭流,关闭资源。在关闭前会先调用flush刷新缓冲中的数据到目的地。
*/
fw.close();

}

FileWriter:能在已有的目录(文件夹)下创建文件,但是不能创建目录也就是文件夹

 

如何处理这段程序的异常异常。

FileWriter fw = new FileWriter("demo.txt");创建失败

根据给定的 File 对象构造一个 FileWriter 对象。如果第二个参数为 true

则将字节写入文件末尾处,而不是写入文件开始处。

为什么需要抛出异常?或者try()catch()异常?

IOException - 如果该文件存在,但它是一个目录,而不是一个常规文件;或者该

文件不存在,但无法创建它;抑或因为其他某些原因而无法打开它,都会导致异常

fw.write("abcde" + LINE_SEPARATOR + "hahaha");输入失败

fw.close();关闭失败

FileWriter fw = null;
try {

fw = new FileWriter("k:\\demo.txt");
fw.write("abcde" + LINE_SEPARATOR + "hahaha");

} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fw != null)
try {
fw.close();
} catch (IOException e) {
// code....
throw new RuntimeException("关闭失败");
}
}

字符流:FileReader 建立一个流对象将一个已存在的文件加载进流

Reader注意有Reader都是读取方法所以其必须有已存在的读取对象。

 

需求:读取一个文本文件。将读取到的字符打印到控制台

1,创建读取字符数据的流对象。FileReader

FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream。

在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。

可以理解为:

用一个读取流关联一个已存在文件。

read() 读取单个字符,一次读一个

使用 read(char[])读取文本文件数据。

public int read(char[] cbuf,int offset,int length)

throws IOException

将字符读入数组中的某一部分。

Offset起始位置。  Length 读取长度 ,读取时包含不包含尾。

返回: 

注意:读取的字符数,返回每次读取的字符数,如有一个长度为5的源文件,

定义一个长度为3的数组,则第一次返回的是3,第二次返回2,第三次返回-1 

抛出: 

IOException - 如果发生 I/O 错误

 

FileReader fr = new FileReader("demo.txt");

如何使用两种方法读取文本文件数据,以及每一种读取方法如何实现,以及其优点

/*
 * 使用read(char[])读取文本文件数据。
 *  
* 先创建字符数组。
*/
char[] buf = new char[1024];

int len = 0;

while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}

/*
int num = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num+""):

需求:将c盘的一个文本文件复制到d盘。

思路:

 * 1,需要读取源,

 * 2,将读到的源数据写入到目的地。

 * 3,既然是操作文本数据,使用字符流。

第一种方法:

//1,读取一个已有的文本文件,使用字符读取流和文件相关联。

FileReader fr = new FileReader("IO_2.txt");

//2,创建一个目的,用于存储读到数据。

FileWriter fw = new FileWriter("copytext_1.txt");

//3,频繁的读写操作。

int ch = 0;

while((ch=fr.read())!=-1){每读取一次

fw.write(ch);就存储一次

//4,关闭流资源。

fw.close();

fr.close();


---------------------------------------------------------------------

字符流缓冲区:
BufferedWriter BufferedReader:



缓冲区的例子就像之前读取时创建的数组char[] buf

FileWriter fw = new FileWriter("buf.txt");

新建一个缓冲区,并且接受一个文件注意缓冲区必须接收流文件对象

否则缓冲区没有意义

public class BufferedWriter  extends Writer

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的

高效写入。

可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了

缓冲区的作用:

为了提高写入的效率。使用了字符流的缓冲区。

创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联


private static final String LINE_SEPARATOR = System.getProperty("line.separator");

创建一个方法,定义一个常量,用于获取所在系统的换行符这种方法在哪里都通用。

BufferedReader:

方法:

readLine()  读取一个文本行。 回车换行符为一行

新建一个读取缓冲区,接收一个已有文件

BufferedReader bufr = new BufferedReader(fr);

bufr:readLine();字符流缓冲区对象的方法,是从字符流中读取的数据

 

FileReader fr = new FileReader("buf.txt");

//新建一个流对象,并且接受一个已有的文件buf.txt文件。

BufferedReader bufr = new BufferedReader(fr);

//新见一个缓冲区对象,并且接收流对象fr的内容。

FileWriter fw = new FileWriter("buf_copy.txt");

//创建流对象,并且建立数据存储文件buf_copy.txt

BufferedWriter bufw = new BufferedWriter(fw);

String line = null;//定义一个string变量,赋值为空。

while((line=bufr.readLine())!=null){//当变量不为空时,继续执行

bufw.write(line);//这里的读和写都是针对的缓冲区对象。

bufw.newLine();

bufw.flush();



------------------------------------------------------------------------------------


装饰设计模式:
对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。 



装饰和继承都能实现一样的特点:进行功能的扩展增强。 


有什么区别呢?


首先有一个继承体系。
Writer
|--TextWriter:用于操作文本
|--MediaWriter:用于操作媒体。

想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。 
效率提高需要加入缓冲技术。

Writer
|--TextWriter:用于操作文本
|--BufferTextWriter:加入了缓冲技术的操作文本的对象。
|--MediaWriter:用于操作媒体。
|--BufferMediaWriter:


到这里就哦了。但是这样做好像并不理想。
如果这个体系进行功能扩展,有多了流对象。
那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现只为提高功能,进行的继承,
导致继承体系越来越臃肿。不够灵活。 


重新思考这个问题?
既然加入的都是同一种技术--缓冲。
前一种是让缓冲和具体的对象相结合。 
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。


class Buffer{
Buffer(TextWriter w)
{}

Buffer(MediaWirter w)
{

}
}
class BufferWriter extends Writer{
BufferWriter(Writer w)
{
}
}
Writer
|--TextWriter:用于操作文本
|--MediaWriter:用于操作媒体。
|--BufferWriter:用于提高效率。

装饰比继承灵活。


特点:装饰类和被装饰类都必须所属同一个接口或者父类。 

----------------------------------------------------------------

转换流:
InputStreamReader :字节到字符的桥梁。解码。
OutputStreamWriter:字符到字节的桥梁。编码。

字符流中有字符装饰流类,而字节流没有,

所以如果将字节流转换成字符流,就可以使用字符流的装饰设计模式。

字节流加上编码表就是字符流。

将字节转成字符的桥梁。转换流。 

InputStreamReader isr = new InputStreamReader(in);

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的

 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,

或者可以接受平台默认的字符集。

InputStreamReader(InputStream in) 将字节流转换成字符流。
          创建一个使用默认字符集的 InputStreamReader。

字符与字节流的区别就是:

比如:一个中文“你”,字节流会度两次,返回两个对应值,例:197、 230、

而字符流只会对一次,其实字符流调用的还是字节流的方法,但是当他读取到两个值之后,

会拿着这两个值去查表,然后返回对应的一个值例:20325.

 

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 

将要写入流中的字符编码成字节。

例子:传递情报时,将文字变成电报的码,就叫编码,对方接受到了之后将电报码

变成文字就叫解码。



流的操作规律:
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。


想要知道开发时用到哪些对象。只要通过四个明确即可。


1,明确源和目的(汇)
源:InputStream  Reader
目的:OutputStream  Writer


2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream

到这里,就可以明确需求中具体要使用哪个体系。

3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流

目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流


4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。





需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream  Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer

3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File

FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");

4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));

================================================


需求2:读取键盘录入信息,并写入到一个文件中。

1,明确源和目的。
源:InputStream Reader
目的:OutputStream  Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File

InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");

还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));



===================================================

需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream  Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out

FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。 
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

================================================================


需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream  Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out

InputStream in = System.in;
OutputStream out = System.out;

4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);

为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));


============================================================


5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.

1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File 
FileWriter fw = new FileWriter("a.txt");
fw.write("你好"); 

注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);

需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));


什么时候使用转换流呢?


1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。

要注意字符流需要刷新,字节流不需要刷新。


 

字符流字节流的相互转换的原理


第一步:从控制台输入的字节流开始,先转换成字符流,在进行高效处理

  从而实现读取字符数据。

第二步:将字符数据写入缓冲区,再转成字节流,最后输出到控制台

这就是从控制台输入到控制台输出的整个过程。

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

键盘录入方法的最佳书写

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in))

键盘输出方法的最佳书写

BufferedWriter bufw =new BufferedWriter(new OutputStreamWriter(System.out))

----------------------------------------------------------------------------------------------

File :提供了将文件所在目录或者路径封装成对象的方法。文件和目录路径名的

抽象表示形式。

File类的常用方法:

boolean b = dir.mkdir();//make directory创建单级目录。

System.out.println("b="+b);

dir.mkdirs();//创建多级目录

创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。

注意,此操作失败时也可能已经成功地创建了一部分必需的父目录。

Char pathSeparatorChar

与系统有关的路径分隔符。此字段被初始为包含系统属性 path.separator 值

的第一个字符。此字符用于分隔以路径列表 形式给定的文件序列中文件名。

File(String parent, String child)

File f1 = new File("c:\\a.txt");

File f2 = new File("c:\\","a.txt")

 *根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。File f4 = new File("c:"+File.separator+"abc"+File.separator+"a.txt")

Separator():与系统有关的默认名称分隔符,为了方便,它被表示为一个

字符串。这里的名称分隔符就相当于之前的“\\”,但是这个方法可以获取

任意所在系统的分隔符

详细方法介绍见:

E:\Java\java\day22e\src\cn\itcast\io\p2\file\demo FileDemo文件

File对象的常见方法。

1,获取。

1.1 获取文件名称1.2 获取文件路径1.3 获取文件大小1.4 获取文件修改时间。

2,创建与删除。

3,判断。

4, 重命名



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值