JAVA IO

发布时间:2024年01月18日
java io 可以让我们用标准的读写操作来完成对不同设备的读写数据工作 .
java IO 按照方向划分为输入与输出 , 参照点是我们写的程序 .
输入 : 用来读取数据的 , 是从外界到程序的方向 , 用于获取数据 .
输出 : 用来写出数据的 , 是从程序到外界的方向 , 用于发送数据 .
java IO 比喻为 " ", :stream. 就像生活中的 " 电流 "," 水流 " 一样 , 它是以同一个方向顺序移动的过程 . 只不
过这里流动的是字节 (2 进制数据 ). 所以在 IO 中有输入流和输出流之分 , 我们理解他们是连接程序与另一端
" 管道 ", 用于获取或发送数据到另一端

1.Java定义了两个超类(抽象类):

java.io.InputStream: 所有字节输入流的超类 , 其中定义了读取数据的方法 . 因此将来不管读取的是什
么设备 ( 连接该设备的流 ) 都有这些读取的方法 , 因此我们可以用相同的方法读取不同设备中的数据
java.io.OutputStream: 所有字节输出流的超类 , 其中定义了写出数据的方法 .

2.java将流分为两类:节点流与处理流:

2.1节点流:

也称为低级流 . 节点流的另一端是明确的 , 是实际读写数据的流 , 读写一定是建立在节点流基础
上进行的 .

2.2处理流:

也称为高级流 . 处理流不能独立存在 , 必须连接在其他流上 , 目的是当数据流经当前流时对数据
进行加工处理来简化我们对数据的该操作 .
实际应用中 , 我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读
, 这个过程也成为流的连接 , 这也是 IO 的精髓所在 .

3.文件流

文件流是一对低级流 , 用于读写文件数据的流 . 用于连接程序与文件 ( 硬盘 ) " 管道 ". 负责读写文件数据 .

3.1文件输出流:java.io.FileOutputStream

?

package io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* JAVA IO
* IO:Input,Output 即:输入与输出
*
* JAVA IO用于我们程序可以和外界交换数据。用于与外界的数据进行读写操作的。
*
* java中将输入与输出比喻为"流":stream
* 如何理解流:讲流想象为一个连接我们程序和另一端的"管道",在其中按照同一方向顺序移动的数据。
* 有点像"水管"中向着统一方向流动的水。
* 输入流:从外界向我们的程序中移动的方向,因此是用来获取数据的流,作用就是:读取操作
* 输出流:写出操作
* 注意:流是单向的,输入永远用来读,输出永远用来写。将来我们在实际开发中希望与程序交互的另一端
* 互相发送数据时,我们只需要创建一个可以连接另一端的"流",进行读写操作完成。
*
* java定义了两个超类,来规范所有的字节流
* java.io.InputStream:所有字节输入流的超类(抽象类),里面定义了读取字节的相关方法。
* 所有字节输入流都继承自它
* java.io.OutputStream:所有字节输出流的超类(抽象类),里面定义了写出字节的相关方法。
* 所有的字节输出流都继承自它
*
*
* 文件流
* java.io.FileInputStream和java.io.FileOutputStream
* 作用是真实连接我们程序和文件之间的"管道"。其中文件输入流用于从文件中读取字节。而文件输出流则
* 用于向文件中写入字节。
*
* 文件流是节点流
* JAVA IO将流划分为两类:节点流和处理流
* 节点流:俗称"低级流",特点:真实连接我们程序和另一端的"管道",负责实际读写数据的流
* 文件流就是典型的节点流,真实连接我们程序与文件的"管道",可以读写文件数据了。
* 处理流:俗称"高级流"
* 特点:
* 1:不能独立存在(单独实例化进行读写操作不可以)
* 2:必须连接在其他流上,目的是当数据"流经"当前流时,可以对其做某种加工操作,简化我们的
工作、
* 流的连接:实际开发中经常会串联一组高级流最终到某个低级流上,对数据进行流水线式的加工读写。
*
*/
public class FOSDemo {
public static void main(String[] args) throws IOException {
//需求:向当前目录的文件fos.dat中写入数据
/*
在创建文件输出流时,文件输出流常见的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
上述两种构造器都会在创建时将该文件创建出来(如果该文件不存在才会这样做),自动创建
该文件的前提是该文件所在的目录必须存在,否则会抛出异常。
*/
// File file = new File("./fos.dat");
// FileOutputStream fos = new FileOutputStream(file);
/*
一个小技巧:在指定相对路径时,如果是从"当前目录"(./)开始的,那么"./"是可以忽略不写
的
因为在相对路径中,默认就是从"./"开始
*/
// FileOutputStream fos = new FileOutputStream("./fos.dat");
FileOutputStream fos = new FileOutputStream("fos.dat");//与上面一句位置相同
/*
OutputStream(所有字节输出流的超类)中定义了写出字节的方法:
其中:
void write(int d)
写出一个字节,将给定的参数int值对应的2进制的"低八位"写出。
文件输出流继承OutputStream后就重写了该方法,作用是将该字节写入到文件中。
*/
/*
向文件中写入1个字节
fow.write(1)
将int值的1对应的2进制的"低八位"写如到文件第一个字节位置上
1个int值占4个字节,每个字节是一个8为2进制
int 1的2进制样子:
00000000 00000000 00000000 00000001
^^^^^^^^
写出的字节
write方法调用后,fos.dat文件中就有了1个字节,内容为:
00000001
再次调用:
fos.write(5)
int 5的2进制样子:
00000000 00000000 00000000 00000101
^^^^^^^^
写出的字节
write方法调用后,fos.dat文件中就有了2个字节,内容为:
00000001 00000101
上次写的 本次写的
*/
fos.write(1);
fos.write(5);
System.out.println("写出完毕!");
//注意!流使用完毕后要关闭,来释放底层资源
fos.close();
}
}

3.2文件输入流:java.io.FileInputStream

package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 使用文件输入流读取文件中的数据
*/
public class FISDemo {
public static void main(String[] args) throws IOException {
//将fos.dat文件中的字节读取回来
/*
fos.dat文件中的数据:
00000001 00000010
*/
FileInputStream fis = new FileInputStream("fos.dat");
/*
java.io.InputStream(所有字节输入流的超类)定义着读取字节的相关方法
int read()
读取1个字节并以int型整数返回读取到的字节内容,返回的int值中对应的2进制的"低八位"
就是读取到的数据。如果返回的int值为整数-1(这是一个特殊值,32位2进制全都是1)表达
的
是流读取到了末尾了。
int read(byte[] data)
文件输入流重写了上述两个方法用来从文件中读取对应的字节。
*/
/*
fos.dat文件中的数据:
00000001 00000010
^^^^^^^^
第一次读取的字节
当我们第一次调用:
int d = fis.read();//读取的是文件中第一个字节
该int值d对应的2进制:
00000000 00000000 00000000 00000001
|------自动补充24个0-------| ^^^^^^^^
读取到的数据
而该2进制对应的整数就是1.
*/
int d = fis.read();//读取到的就是整数1
System.out.println(d);
/*
fos.dat文件中的数据:
00000001 00000010
^^^^^^^^
第二次读取的字节
当我们第二次调用:
d = fis.read();//读取的是文件中第二个字节
该int值d对应的2进制:
00000000 00000000 00000000 00000010
|------自动补充24个0-------| ^^^^^^^^
读取到的数据
而该2进制对应的整数就是2.
*/
d = fis.read();//2
System.out.println(d);
/*
fos.dat文件中的数据:
00000001 00000010 文件末尾
^^^^^^^^
没有第三个字节
当我们第三次调用:
d = fis.read();//读取到文件末尾了!
该int值d对应的2进制:
11111111 11111111 11111111 11111111
该数字是正常读取1个字节永远表达不了的值。并且-1的2进制格式好记。因此用它表达读取
到了末尾。
*/
d = fis.read();//-1
System.out.println(d);
fis.close();
}
}

3.3文件复制

package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 利用文件输入流与输出流实现文件的复制操作
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
//用文件输入流读取待复制的文件
// FileInputStream fis = new FileInputStream("image.jpg");
FileInputStream fis = new FileInputStream("01.rmvb");
//用文件输出流向复制文件中写入复制的数据
// FileOutputStream fos = new FileOutputStream("image_cp.jpg");
FileOutputStream fos = new FileOutputStream("01_cp.rmvb");
/*
原文件image.jpg中的数据
10100011 00111100 00001111 11110000....
^^^^^^^^
读取该字节
第一次调用:
int d = fis.read();
d的2进制:00000000 00000000 00000000 10100011
读到的字节
fos向复制的文件image_cp.jpg中写入字节
第一次调用:
fos.write(d);
作用:将给定的int值d的2进制的"低八位"写入到文件中
d的2进制:00000000 00000000 00000000 10100011
写出字节
调用后image_cp.jpg文件数据:
10100011
*/
/*
循环条件是只要文件没有读到末尾就应该复制
如何直到读取到末尾了呢?
前提是:要先尝试读取一个字节,如果返回值是-1就说明读到末尾了
如果返回值不是-1,则说明读取到的是一个字节的内容,就要将他写入到复制文件中
*/
int d;//先定义一个变量,用于记录每次读取到的数据
long start = System.currentTimeMillis();//获取当前系统时间
while ((d = fis.read()) != -1) {
fos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:" + (end - start) + "ms");
fis.close();
fos.close();
}
}

3.4块读写的文件复制操作

int read(byte[] data) 一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。 返回
值为实际读取到的字节量。若返回值为 -1 则表示读取到了文件末尾。
块写操作 void write(byte[] data) 一次性将给定的字节数组所有字节写入到文件中
void write(byte[] data,int offset,int len) 一次性将给定的字节数组从下标 offset 处开始的连续 len 个字节 写入文件
package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 通过提高每次读写的数据,减少读写次数可以提高读写效率。
*/
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("01.rmvb");
FileOutputStream fos = new FileOutputStream("01_cp.rmvb");
/*
块读:一次性读取一组字节
块写:一次性将写出一组字节
java.io.InputStream上定义了块读字节的方法:
int read(byte[] data)
一次性读取给定字节数组length个字节并从头开始装入到数组中。返回值为实际读取到的字
节量
如果返回值为-1则表示流读取到了末尾。
文件流重写了该方法,作用是块读文件里的数据。
java.io.OutputStream上定义了块写字节的方法:
void write(byte[] data)
一次性将给定的字节数组中所有的字节写出。
void write(byte[] data,int offset,int len)
一次性将给定的字节数组data中从下标offset处开始的连续len个字节写出。
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010
byte[] buf = new byte[4];//创建一个长度为4的字节数组
buf默认的样子(每个元素若以2进制表现):{00000000,00000000,00000000,00000000}
int len;//记录每次实际读取的字节数
当第一次调用:
len = fis.read(buf);
由于字节数组buf的长度为4.因此可以一次性最多从文件中读取4个字节并装入到buf数组中
返回值len表示的整数是这次实际读取到了几个字节。
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
第一次读取的4个字节
buf:{11110000,00001111,01010101,11111111}
len:4 表示本次读取到了4个字节
第二次调用:
len = fis.read(buf);
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010 文件末尾了
^^^^^^^^ ^^^^^^^^ ^^^^^^^^
^^^^^^^^
本次实际只能读取到2个字节
buf:{00000000,10101010,01010101,11111111}
|本次新读的2字节数据| |---上次的旧数据---|
len:2表示本次实际只读取到了2个字节。它的意义就是告诉你buf数组中前几个字节是本次真
实
读取到的数据
第三次调用:
len = fis.read(buf);
原文件数据(假设文件共6个字节):
11110000 00001111 01010101 11111111 00000000 10101010 文件末尾了
^^^^^^^^
^^^^^^^^ ^^^^^^^^ ^^^^^^^^
buf:{00000000,10101010,01010101,11111111} 没有任何变化!
len:-1 表示本次读取时已经是文件末尾了!!
*/
/*
00000000 8位2进制 1byte 1字节
1024byte = 1kb
1024kb = 1mb
1024mb = 1gb
1024gb = 1tb
*/
/*
编译完该句代码:byte[] buf = new byte[10240];
在实际开发中,有时候用一个计算表达式更能表现这个值的含义时,我们不妨使用计算表达式
long t = 864000000;
long t = 60 * 60 * 24 * 1000;
*/
byte[] buf = new byte[1024 * 10];//10kb
int len;//记录每次实际读取到的字节数
long start = System.currentTimeMillis();
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕,耗时:" + (end - start) + "ms");
fis.close();
fos.close();
}
}

3.5写文本数据

String 提供方法 : byte[] getBytes(String charsetName) 将当前字符串转换为一组字节
参数为字符集的名字,常用的是 UTF-8 。 其中中文字 3 字节表示 1 个,英文 1 字节表示 1 个。
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 使用文件输出流向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输出流
2:将写出的文字先转换为2进制(一组字节)
3:关闭流
文件流有两种创建方式:
1:覆盖模式,对应的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后
在陆续将通过该流写出的内容保存到文件中。
*/
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String line = "让我再看你一遍,从南到北。";
/*
String提供了将内容转换为一组字节的方法:getBytes()
java.nio.charset.StandardCharsets
*/
byte[] data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
line = "像是北五环路蒙住的双眼。";
data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}

3.6文件输出流-追加模式

重载的构造方法可以将文件输出流创建为追加模式
FileOutputStream(String path,boolean append)
FileOutputStream(File file,boolean append)
当第二个参数传入 true 时,文件流为追加模式,即 : 指定的文件若存在,则原有数据保留,新写入的数据
会被顺序的追加到文件中
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 使用文件输出流向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输出流
2:将写出的文字先转换为2进制(一组字节)
3:关闭流
文件流有两种创建方式:
1:覆盖模式,对应的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后
在陆续将通过该流写出的内容保存到文件中。
2:追加模式,对应的构造器
FileOutputStream(String filename,boolean append)
FileOutputStream(File file,boolean append)
当第二个参数为true时,那么就是追加模式。
所谓追加模式:文件流在创建时若发现该文件已存在,则原内容都保留。通过当前流陆续写
出
的内容都会被陆续追加到文件末尾。
*/
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String line = "斯国一!";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
line = "奥里给!";
data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}

3.7读取文本数据

package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 从文件中读取文本数据
*/
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输入流
2:从文件中将字节都读取回来
3:将读取到的字节转换回字符串
*/
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[1024];//1kb
int len = fis.read(data);//块读操作,返回值表达实际读取到了多少字节
System.out.println("实际读取了:"+len+"个字节");
/*
String提供了构造方法可以将一个字节数组还原为字符串
String(byte[] data,Charset charset)
将给定的字节数组data中所有字节按照给定的字符集转换为字符串。
String(byte[] data,int offset,int len,Charset charset)
将给定的字节数组data从下标offset处开始的连续len个字节按照指定的字符集转换为字符
串
*/
String line = new String(data,0,len,StandardCharsets.UTF_8);
System.out.println(line.length());//输出字符串长度
System.out.println(line);
fis.close();
}
}

4.高级流

流连接示意图

4.1缓冲流

java.io.BufferedOutputStream BufferedInputStream.
缓冲流是一对高级流 , 作用是提高读写数据的效率 .
缓冲流内部有一个字节数组 , 默认长度是 8K. 缓冲流读写数据时一定是将数据的读写方式转换为块读写来
保证读写效率 .
使用缓冲流完成文件复制操作
package io;
import java.io.*;
/**
* java将流分为节点流与处理流两类
* 节点流:也称为低级流,是真实连接程序与另一端的"管道",负责实际读写数据的流。
* 读写一定是建立在节点流的基础上进行的。
* 节点流好比家里的"自来水管"。连接我们的家庭与自来水厂,负责搬运水。
* 处理流:也称为高级流,不能独立存在,必须连接在其他流上,目的是当数据经过当前流时
* 对其进行某种加工处理,简化我们对数据的同等操作。
* 高级流好比家里常见的对水做加工的设备,比如"净水器","热水器"。
* 有了它们我们就不必再自己对水进行加工了。
* 实际开发中我们经常会串联一组高级流最终连接到低级流上,在读写操作时以流水线式的加工
* 完成复杂IO操作。这个过程也称为"流的连接"。
*
* 缓冲流,是一对高级流,作用是加快读写效率。
* java.io.BufferedInputStream和java.io.BufferedOutputStream
*
*/
public class CopyDemo3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("ppt.pptx");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("ppt_cp.pptx");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d;
long start = System.currentTimeMillis();
while((d = bis.read())!=-1){//使用缓冲流读取字节
bos.write(d);//使用缓冲流写出字节
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+"ms");
bis.close();//关闭流时只需要关闭高级流即可,它会自动关闭它连接的流
bos.close();
}
}

?

4.1.1缓冲输出流写出数据时的缓冲区问题
通过缓冲流写出的数据会被临时存入缓冲流内部的字节数组 , 直到数组存满数据才会真实写出一次
package io;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 缓冲输出流写出数据的缓冲区问题
*/
public class BOS_FlushDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String line = "奥里给!";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
bos.write(data);
System.out.println("写出完毕!");
/*
缓冲流的flush方法用于强制将缓冲区中已经缓存的数据一次性写出。
注:该方法实际上实在字节输出流的超类OutputStream上定义的,并非只有缓冲
输出流有这个方法。但是实际上只有缓冲输出流的该方法有实际意义,其他的流实现
该方法的目的仅仅是为了在流连接过程中传递flush动作给缓冲输出流。
*/
bos.flush();//冲
bos.close();
}
}

4.2对象流

java.io.ObjectOutputStream ObjectInputSteam
对象流是一对高级流,在流连接中的作用是进行对象的序列化与反序列化。
对象序列化 : 将一个 java 对象按照其结构转换为一组字节的过程
对象反序列化 : 将一组字节还原为 java 对象 ( 前提是这组字节是一个对象序列化得到的字节 )
对象序列化的流连接操作原理图

?

package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* 对象流(是一对高级流)
* java.io.ObjectInputStream和ObjectOutputStream
* 对象流在流连接中的作用是进行对象的序列化与反序列化
* 其中对象输出流负责对象序列化。对象输入流负责对象反序列化
*
* 所谓对象序列化:
* 将写出的对象按照其结构转换为一组字节的过程。
*/
public class OOSDemo {
public static void main(String[] args) throws IOException {
String name = "苍老师";
int age = 40;
String gender = "女";
String[] otherInfo = {"摄影技术一流","喜欢拍片儿","是一名技术老师"};
//将该Person对象写入文件person.obj中
Person p = new Person(name,age,gender,otherInfo);
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
/*
对象输出流提供了一个直接写出对象的方法(进行对象序列化的操作)
void writeObject(Object obj)
序列化时可能出现异常:
java.io.NotSerializableException: io.Person
注:冒号后面的io.Person是指序列化的就是这个类的实例出现的错误
原因:
对象输出流在进行序列化对象时,要求该对象所属的类必须实现接
口:java.io.Serializable接口
并且该类中所有引用类型属性也必须实现该接口,否则会抛出上述异常。
*/
oos.writeObject(p);
System.out.println("写出完毕!");
oos.close();
}
}
4.2.1对象反序列化
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* 使用对象输入流完成对象的反序列化
*/
public class OISDemo {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
//从person.obj文件中将对象反序列化回来
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
/*
Object readObject()
该方法会进行对象的反序列化,如果对象流通过其连接的流读取的字节分析并非
是一个java对象时,会抛出异常:ClassNotFoundException
*/
Person p = (Person)ois.readObject();
System.out.println(p);
}
}
需要进行序列化的类必须实现接口 :java.io.Serializable 实现序列化接口后最好主动定义序列化版本号这
个常量。 这样一来对象序列化时就不会根据类的结构生成一个版本号 , 而是使用该固定值。 那么反序列
化时,只要还原的对象和当前类的版本号一致就可以进行还原。
transient 关键字可以修饰属性,用于在进行对象序列化时忽略不必要的属性,达到对象瘦身的目的
package io;
import java.io.Serializable;
import java.util.Arrays;
/**
* 使用当前类实例测试对象流的读写操作
*/
public class Person implements Serializable {
public static final long serialVersionUID = 1L;
private String name;//姓名
private int age;//年龄
private String gender;//性别
private String[] otherInfo;//其他信息
public Person(String name, int age, String gender, String[] otherInfo) {
this.name = name;
this.age = age;
this.gender = gender;
this.otherInfo = otherInfo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getOtherInfo() {
return otherInfo;
}
public void setOtherInfo(String[] otherInfo) {
this.otherInfo = otherInfo;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", otherInfo=" + Arrays.toString(otherInfo) +
'}';
}
}

?

5.字符流

java 将流按照读写单位划分为字节流与字符流 .
java.io.InputStream OutputStream 是所有字节流的超类
java.io.Reader Writer 则是所有字符流的超类 , 它们和字节流的超类是平级关系 .
Reader Writer 是两个抽象类 , 里面规定了所有字符流都必须具备的读写字符的相关方法 .
字符流最小读写单位为字符 (char), 但是底层实际还是读写字节 , 只是字符与字节的转换工作由字符流
完成 .
转换流
java.io.InputStreamReader OutputStreamWriter
它们是字符流非常常用的一对实现类同时也是一对高级流 , 实际开发中我们不直接操作它们 , 但是它们在流
连接中是非常重要的一环 .
使用转换输出流向文件中写入文本数据
package io;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* JAVA IO将流按照读写数据的单位将流分为了两类:字节流与字符流
* java.io.InputStream和OutputStream这两个超类是所有【字节流】的超类
* java.io.Reader和Writer这两个是所有【字符流】的超类
* 这两对超类之间是没有继承关系的,属于平级的。
*
* 字符流是以字符为最小单位(char)读写数据的。
* 注:底层实际还是读写字节,只不过字符与字节的转换由字符流自动完成了。
* 由于字符流最小读写单位为字符,因此字符流【只适合读写文本数据】
*
* 转换流(是一对高级流,同时是一对字符流)
* 作用:
* 1:衔接字节流与其他字符流
* 2:将字符与字节相互转换
* 实际开发中我们不会直接使用这一对流,但是在流连接中它是重要的一环。
*/
public class OSWDemo {
public static void main(String[] args) throws IOException {
/*
使用这一对流演示转换流的读写字符方法
java.io.Writer所有字符输出流的超类上,定义了写出字符的相关方法
void write(int d)写出一个字符,实际传入的应当是一个char。
void write(char[] data)
void write(char[] data,int offset,int len)
void write(String str) 直接写出一个字符串
*/
FileOutputStream fos = new FileOutputStream("osw.txt");
OutputStreamWriter osw = new
OutputStreamWriter(fos,StandardCharsets.UTF_8);
String line = "如果你突然打了个喷嚏,那一定就是我在想你。";
osw.write(line);//转换流的write(String str)会将写出的字符串转换为字节然后写出
osw.write("如果半夜被手机吵醒,那一定就是我关心。");
System.out.println("写出完毕!");
osw.close();
}
}

5.1?使用转换输入流读取文本文件

package io;
import java.io.*;
/**
* 转换字符输入流
* 可以将读取的字节按照指定的字符集转换为字符
*/
public class ISRDemo {
public static void main(String[] args) throws IOException {
//将osw.txt文件中的所有文字读取回来.
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
/*
字符流读一个字符的read方法定义:
int read()
读取一个字符,返回的int值实际上表示的是一个char(低16位有效).如果返回的
int值表示的是-1则说明EOF
*/
//测试读取文件中第一个字
// int d = isr.read();
// char c = (char)d;
// System.out.println(c);
//循环将文件所有字符读取回来
int d;
while((d = isr.read()) != -1){
System.out.print((char)d);
}
isr.close();
}
}

5.2转换流的意义:

实际开发中我们还有功能更好用的字符高级流 . 但是其他的字符高级流都有一个共通点 : 不能直接连接在字
节流上 . 而实际操作设备的流都是低级流同时也都是字节流 . 因此不能直接在流连接中串联起来 . 转换流是
一对可以连接在字节流上的字符流 , 其他的高级字符流可以连接在转换流上 . 在流连接中起到 " 转换器 " 的作
( 负责字符与字节的实际转换 )

6.缓冲字符流

缓冲字符输出流 :java.io.PrintWriter
java.io.BufferedWriter BufferedReader
缓冲字符流内部也有一个缓冲区 , 读写文本数据以块读写形式加快效率 . 并且缓冲流有一个特别的功能 :
以按行读写文本数据 .
java.io.PrintWriter 具有自动行刷新的缓冲字符输出流 , 实际开发中更常用 . 它内部总是会自动连接
BufferedWriter 作为块写加速使用 .
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
/**
* 缓冲字符流(是一对高级流)
* java.io.BufferedWriter和BufferedReader
* 缓冲流内部维护一个char数组,默认长度8k.以块读写方式读写字符数据保证效率
*
* java.io.PrintWriter则是具有自动行刷新的换成字符输出流(实际缓冲功能是靠BufferedWriter
* 实现的,它内部总是连接着这个流。)
*
* 使用缓冲字符流后就可以实现按行读写字符串,并且读写效率高。
*/
public class PWDemo1 {
public static void main(String[] args) throws FileNotFoundException,
UnsupportedEncodingException {
//按行向文件pw.txt中写入字符串
/*
PrintWriter继承自Writer.
它提供很多构造方法,其中就有可以直接对文件进行写操作的构造器
PrintWriter(File file)
PrintWriter(String filename)
*/
// PrintWriter pw = new PrintWriter("pw.txt");
/*
这里可以按照指定的字符集写出字符串到文本文件中。但是字符集只能以字符串形式
表达。因此注意拼写。字符集不区分大小写。
但是如果字符集名字拼写错误,会抛出异常:
UnsupportedEncodingException
不支持的 字符集 异常
*/
PrintWriter pw = new PrintWriter("pw.txt","UTF-8");
/*
println()方法是输出字符出后带上换行符
print()方法输出字符串后不带换行符
*/
pw.println("夜空中最亮的星,能否听清。");
pw.println("那仰望的人,心底的孤独和叹息。");
System.out.println("写出完毕!");
pw.close();
}
}

?6.1在流链接中使用PW

package io;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* 练习PrintWriter的流连接操作
*/
public class PWDemo2 {
public static void main(String[] args) throws FileNotFoundException {
//文件输出流(低级流,字节流) 作用:向文件中写出字节
FileOutputStream fos = new FileOutputStream("pw2.txt");
//转换输出流(高级流,字符流) 作用:1衔接字符与字节流的 2:将写出的字符转换为字节
OutputStreamWriter osw = new OutputStreamWriter(fos,
StandardCharsets.UTF_8);
//缓冲字符输出流(高级流,字符流) 作用:块写文本数据加速的(内部有一个8k的char数组)
BufferedWriter bw = new BufferedWriter(osw);
//具有自动行刷新功能(高级流,字符流) 作用:1按行写出字符串(println) 2:自动行刷新
PrintWriter pw = new PrintWriter(bw);
/*
完成一个简易记事本工具
将控制台上输入的每一行字符串按行写入到该文件中
如果单独输入exit,则程序退出。
思路:
用一个死循环,重复做下面的工作
1:在控制台上输入一行字符串
2:判断输入的字符串是否为"exit"
若是:则break掉循环退出程序
若不是:则将输入的字符串通过println方法写入文件
*/
Scanner scanner = new Scanner(System.in);
while(true) {
String line = scanner.nextLine();
if("exit".equals(line)){
break;
}
pw.println(line);
}
System.out.println("写出完毕!");
pw.close();
}
}

6.2PrintWriter的自动行刷新功能

如果实例化 PW 时第一个参数传入的是一个流,则此时可以再传入一个 boolean 型的参数,此值为 true
就打开了自动行刷新功能。 即 : 每当我们用 PW println 方法写出一行字符串后会自动 flush.
package io;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* 练习PrintWriter的流连接操作
*/
public class PWDemo2 {
public static void main(String[] args) throws FileNotFoundException {
//文件输出流(低级流,字节流) 作用:向文件中写出字节
FileOutputStream fos = new FileOutputStream("pw2.txt");
//转换输出流(高级流,字符流) 作用:1衔接字符与字节流的 2:将写出的字符转换为字节
OutputStreamWriter osw = new OutputStreamWriter(fos,
StandardCharsets.UTF_8);
//缓冲字符输出流(高级流,字符流) 作用:块写文本数据加速的(内部有一个8k的char数组)
BufferedWriter bw = new BufferedWriter(osw);
//具有自动行刷新功能(高级流,字符流) 作用:1按行写出字符串(println) 2:自动行刷新
/*
当我们创建PrintWriter时,构造方法里第一个参数为一个流,那么就支持再传入
一个boolean值参数表示是否打开自动行刷新功能,传入true则打开。
此时每当我们调用它的println方法写出一行字符串后就会自动flush()一次。
注意:print方法和write方法写出字符串时并不会自动flush()!!!!
*/
PrintWriter pw = new PrintWriter(bw,true);
/*
完成一个简易记事本工具
将控制台上输入的每一行字符串按行写入到该文件中
如果单独输入exit,则程序退出。
思路:
用一个死循环,重复做下面的工作
1:在控制台上输入一行字符串
2:判断输入的字符串是否为"exit"
若是:则break掉循环退出程序
若不是:则将输入的字符串通过println方法写入文件
*/
Scanner scanner = new Scanner(System.in);
while(true) {
String line = scanner.nextLine();
if("exit".equals(line)){
break;
}
pw.println(line);
// pw.print("");//不会自动flush
// pw.write("");//不会自动flush
}
System.out.println("写出完毕!");
pw.close();
}
}

6.3缓冲字符输入流:java.io.BufferedReader

是一个高级的字符流,特点是块读文本数据,并且可以按行读取字符串。
package io;
import java.io.*;
/**
* 使用缓冲字符输入流按行读取字符串
* 该高级流的主要作用:
* 1:块读文本数据加速(内部有一个默认8k的char数组)
* 2:可以按行读取字符串
*/
public class BRDemo {
public static void main(String[] args) throws IOException {
//将当前源代码输出到控制台上
/*
思路:
读取当前源代码文件,按行读取,并且将读取到的每一行字符串都输出到控制台上即可
*/
//文件输入流(低级流,字节流) 作用:从文件中读取字节
FileInputStream fis = new FileInputStream("./src/io/BRDemo.java");
//转换输入流(字符流,高级流) 作用:1衔接字节与字符流 2将读取的字节转换为字符
InputStreamReader isr = new InputStreamReader(fis);
//缓冲字符输入流(字符流,高级流) 作用:1块读字符数据加速 2按行读取字符串
BufferedReader br = new BufferedReader(isr);
/*
BufferedReader缓冲字符输入流
提供了一个独有的方法:readLine()
作用:读取一行字符串。连续读取若干字符直到遇到了换行符位置,并将换行符之前的
内容返回。注意:返回的字符串里不包含最后的换行符。
特殊情况:
如果这一行只有一个换行符,那么返回值为空字符串:""
如果读取到了流的末尾,那么返回值为null。
实际运行时:
当我们第一次调用readLine()方法时,缓冲字符输入流实际会一次性读取8k的char
回来并存入内部的char数组中(块读文本操作)。readLine方法只将char数组中从头
开始一直到第一个换行符位置的内容以一个字符串形式返回。
*/
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
}

7.总结

java 将流分为两类 : 节点流与处理流 :
节点流 : 也称为 低级流 .
节点流的另一端是明确的 , 是实际读写数据的流 , 读写一定是建立在节点流基础上进行的 .
处理流 : 也称为 高级流 .
处理流不能独立存在 , 必须连接在其他流上 , 目的是当数据流经当前流时对数据进行加工处理来简化
我们对数据的该操作 .
实际应用中 , 我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读
, 这个过程也成为流的连接 , 这也是 IO 的精髓所在 .
缓冲流
缓冲流是一对高级流,在流链接中链接它的 目的是加快读写效率 。缓冲流内部 默认缓冲区为 8kb ,缓冲
总是块读写数据来提高读写效率
java.io.BufferedOutputStream 缓冲字节输出流,继承自
java.io.OutputStream
常用构造器
BufferedOutputStream(OutputStream out) :创建一个默认 8kb 大小缓冲区的缓冲字节输出流,
并连接到参数指定的字节输出流上。
BufferedOutputStream(OutputStream out,int size) :创建一个 size 指定大小 ( 单位是字节 ) 缓冲区
的缓冲字节输出流,并连接到参数指定的字节输出流上。
常用方法
flush() :强制将缓冲区中已经缓存的数据一次性写出
缓冲流的写出方法功能与 OutputStream 上一致,需要知道的时 write 方法调用后并非实际写出,而是先将数
据存入缓冲区 ( 内部的字节数组中 ) ,当缓冲区满了时会自动写出一次。 java.io.BufferedInputStream 缓冲字节输出流,继承自 java.io.InputStream
常用构造器
BufferedInputStream(InputStream in) :创建一个默认 8kb 大小缓冲区的缓冲字节输入流,并连接
到参数指定的字节输入流上。
BufferedInputStream(InputStream in,int size) :创建一个 size 指定大小 ( 单位是字节 ) 缓冲区的缓冲
字节输入流,并连接到参数指定的字节输入流上。
常用方法
对象流
对象流是一对高级流,在流链接中的作用是完成对象的 序列化 反序列化
序列化:是对象输出流的工作,将一个对象按照其结构转换为一组字节的过程。
反序列化:是对象输入流的工作,将一组字节还原为对象的过程。
java.io.ObjectInputStream 对象输入流,继承自 java.io.InputStream
常用构造器
ObjectInputStream(InputStream in) :创建一个对象输入流并连接到参数 in 这个输入流上。
常用方法
Object readObject() :进行对象反序列化,将读取的字节转换为一个对象并以 Object 形式返回 ( 多态 )
如果读取的字节表示的不是一个 java 对象会抛出异常 :java.io.ClassNotFoundException
java.io.ObjectOutputStream 对象输出流,继承自 java.io.OutputStream
常用构造器
ObjectOutputStream(OutputStream out) :创建一个对象输出流并连接到参数 out 这个输出流上
常用方法
void writeObject(Object obj) :进行对象的序列化,将一个 java 对象序列化成一组字节后再通过连接的
输出流将这组字节写出。
如果序列化的对象没有实现可序列化接口 :java.io.Serializable 就会抛出异
:java.io.NotSerializableException
序列化接口 java.io.Serrializable
该接口没有任何抽象方法,但是只有实现了该接口的类的实例才能进行序列化与反序列化。
实现了序列化接口的类建议显示的定义常量 :static final long serialVersionUID = 1L;
可以为属性添加关键字 transient ,被该关键字修饰的属性在序列化是会被忽略,达到对象 序列化瘦身
的目的。
缓冲流的读取方法功能与 InputStream 上一致,需要知道的时 read 方法调用后缓冲流会一次性读取缓冲区大
小的字节数据并存入缓冲区,然后再根据我们调用 read 方法读取的字节数进行返回,直到缓冲区所有数据都已
经通过 read 方法返回后会再次读取一组数据进缓冲区。即 : 块读取操作 字符流
java 将流按照读写单位划分为字节与字符流。字节流以字节为单位读写,字符流以字符为单位读写。
转换流 java.io.InputStreamReader OutputStreamWriter
功能无需掌握,了解其核心意义 :
1: 衔接其它字节与字符流
2: 将字符与字节进行转换
相当于是现实中的 " 转换器 "
缓冲字符输出流
缓冲字符输出流需要记住的是 PrintWriter BufferedReader
作用 :
1: 块写或块读文本数据加速
2: 可以按行写或读字符串
java.io.PrintWriter 具有自动行刷新的缓冲字符输出流
常用构造器
PrintWriter(String filename) : 可以直接对给定路径的文件进行写操作
PrintWriter(File file): 可以直接对 File 表示的文件进行写操作
上述两种构造器内部会自动完成流连接操作。
PrintWriter(OutputStream out): PW 链接在给定的字节流上 ( 构造方法内部会自行完成转换流等流连
)
PrintWriter(Writer writer): PW 链接在其它字符流上
PrintWriter(OutputStream out,boolean autoflush)
PrintWriter(Writer writer,boolean autoflush)
上述两个构造器可以在链接到流上的同时传入第二个参数,如果该值为 true 则开启了自动行刷新功能。
常用方法
void println(String line) :按行写出一行字符串
特点
自动行刷新,当打开了该功能后,每当使用 println 方法写出一行字符串后就会自动 flush 一次

?

JAVA.IO的自我总结就结束了,有不足错误的请大家评论区留言,谢谢

文章来源:https://blog.csdn.net/2201_75413624/article/details/135557579
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。