前言
"IO流"(Input/Output stream)指的是Java中用于处理输入输出(I/O)数据的机制。
在Java中,所有的输入和输出都被抽象为“流”对象,并通过输入流读取数据、输出流写入数据。
Java的I/O包提供了丰富的类和方法来支持不同类型的流,输入流和输出流之间可以自由地进行转换。它们分别主要包括字节流和字符流两种类型。其中,字节流是操作二进制数据的流,
可以处理任何类型的数据,常见的例如InputStream和OutputStream;字符流则是处理字符数据的流,其主要使用Reader和Writer接口。
Java中的IO流可以对文件、网络套接字等的数据进行读写操作,这些操作涉及到打开、关闭、查找、修改和删除文件、创建及维护网络连接等等一系列活动。因此,掌握Java中IO流的知识,有助于我们实现更加高效准确地操作文件、网络数据等各种实用应用。
一、IO流的分类
可以从三个不同的维度进行分类:
• 1、按照流的方向(输出输入都是站在程序所在内存的角度划分的)
• 输入流:只能从中读取数据【主要由InputStream和Reader作为基类】
• 输出流:只能向其写入数据【主要由outputStream和Writer作为基类】
• 2、按照流的操作颗粒度划分
• 字节流:以字节为单元,可操作任何数据【主要由InputStream和outPutStream作为基类】
• 字符流:以字符为单元,只能操作纯字符数据,比较方便【主要由Reader和Writer作为基类】
• 3、按照流的角色划分
• 节点流:可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,也叫【低级流,主要由】
• 处理流:用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能,也叫【高级流】
二、流的原理及流的数量
• 1、流的原理解析
流其实我们可以想象成一个“水管”,源端和目的端就是两个“水桶”,数据是通过这个“水管”进行流动传输的,以InputStream和Reader为例,水管的每个“水滴”就是具体的数据,如果是字节流,那么一个“水滴”就是一个字节,如果是字符流,那么一个“水滴”就是一个字符。
当创建一个流对象的时候,如fis=new FileInputStream(“…\xx\xx.txt”),记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。
2. java的流的图结构:
三、Java IO流对象
1. 输入字节流InputStream
定义和结构说明:
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。
意思是FileInputStream类可以通过一个String路径名创建一个对象,FileInputStream(String name)。而DataInputStream必须装饰一个类才能返回一个对象,DataInputStream(InputStream in)。
【案例 】读取文件内容
/**
* 字节流
* 读文件内容
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
InputStream in=new FileInputStream(f);
byte[] b=new byte[1024];
in.read(b);
in.close();
System.out.println(new String(b));
}
}
注意:
该示例中由于b字节数组长度为1024,如果文件较小,则会有大量填充空格。我们可以利用in.read(b);的返回值来设计程序,如下案例:
/**
* 字节流
* 读文件内容
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
InputStream in=new FileInputStream(f);
byte[] b=new byte[1024];
int len=in.read(b);
in.close();
System.out.println("读入长度为:"+len);
System.out.println(new String(b,0,len));
}
}
注意:
观察上面的例子可以看出,我们预先申请了一个指定大小的空间,但是有时候这个空间可能太小,有时候可能太大,我们需要准确的大小,这样节省空间,那么我们可以这样做:
/**
* 字节流
* 读文件内容,节省空间
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
InputStream in=new FileInputStream(f);
byte[] b=new byte[(int)f.length()];
in.read(b);
System.out.println("文件长度为:"+f.length());
in.close();
System.out.println(new String(b));
}
}
注意:
有时候我们不知道文件有多大,这种情况下,我们需要判断是否独到文件的末尾。
/**
* 字节流
*读文件
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
InputStream in=new FileInputStream(f);
byte[] b=new byte[1024];
int count =0;
int temp=0;
while((temp=in.read())!=(-1)){
b[count++]=(byte)temp;
}
in.close();
System.out.println(new String(b));
}
}
2. 输出字节流OutputStream
定义和结构说明:
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据,
ObjectOutputStream 和所有FilterOutputStream的子类都是装饰流。具体例子跟InputStream是对应的。
1.向文件中写入字符串
/**
* 字节流
* 向文件中写入字符串
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
OutputStream out =new FileOutputStream(f);
String str="Hello World";
byte[] b=str.getBytes();
out.write(b);
out.close();
}
}
2.向文件中追加新内容
/**
* 字节流
* 向文件中追加新内容:
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
OutputStream out =new FileOutputStream(f,true);//true表示追加模式,否则为覆盖
String str="Rollen";
//String str="\r\nRollen"; 可以换行
byte[] b=str.getBytes();
for (int i = 0; i < b.length; i++) {
out.write(b[i]);
}
out.close();
}
}
3. 复制文件
/**
* 文件的复制
* */
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
if(args.length!=2){
System.out.println("命令行参数输入有误,请检查");
System.exit(1);
}
File file1=new File(args[0]);
File file2=new File(args[1]);
if(!file1.exists()){
System.out.println("被复制的文件不存在");
System.exit(1);
}
InputStream input=new FileInputStream(file1);
OutputStream output=new FileOutputStream(file2);
if((input!=null)&&(output!=null)){
int temp=0;
while((temp=input.read())!=(-1)){
output.write(temp);
}
}
input.close();
output.close();
}
}
3.缓存流的使用(BufferedInputStream/BufferedOutputStream,BufferedReader/BufferedWriter)
import java.io.*;
public class TestBufferStream {
public static void main (String[] args) throws IOException{
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
try {
FileInputStream fis = new FileInputStream("C:\\Users\\41639\\Desktop\\java\\FileText\\src\\TestFileImportStream.java");
FileOutputStream fos = new FileOutputStream("C:\\Users\\41639\\Desktop\\java\\temp\\out2.java");
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024];
int off=0;
while ((off=bis.read(b))>0) {
bos.write(b,0,off);
}
bis.close();
bos.close();
}catch (IOException e) {
e.printStackTrace();
}finally {
bis.close();
bos.close();
}
}
}
他们最基本的其实也是FileInputStream和FileOutputStream,在这个“流”的基础上,又加了缓存的功能流BufferedInputStream和BufferedOutputStream。
4.转换流的使用(InputStreamReader/OutputStreamWriter)
字面意思理解,转化流就是用来转化的,那么到底是什么转什么呢?我们可以通过以下的例子来熟悉。读取键盘输入的每一行内容,并写入到文本中,直到遇到over行结束输入
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\41639\\Desktop\\java\\temp\\test1031.txt"));
String line =null;
while ((line=br.readLine())!=null) {
if ("over".contentEquals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
5.对象流的使用(FileInputStream/ObjectOutputStream)
import java.io.*;
public class ObjectStreamTest {
public static void main(String[] args) throws Exception{
try {
Person P=new Person("Jeccica",26);
FileOutputStream fos=new FileOutputStream("C:\\Users\\admin\\Desktop\\Java\\temp\\22.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(P);
oos.flush();
oos.close();
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
FileInputStream fis=new FileInputStream("C:\\Users\\admin\\Desktop\\Java\\temp\\22.txt");
ObjectInputStream ois=new ObjectInputStream(fis);
Person P2=(Person)ois.readObject();
System.out.println(P2.name+"的年龄为"+P2.age);
}
}
class Person implements Serializable{
String name=null;
int age=0;
Person(String _name,int _age){
name=_name;
age=_age;
}
}
字节数组流的使用(ByteArrayInputStream/ByteArrayOutputStream)【通常结合数据流DataInputStream/DataOutputStream】
public static void main(String[] args) {
ByteArrayOutputStream baos=new ByteArrayOutputStream();//创建字节数组流,同时会在内存里面创建数组
DataOutputStream dos=new DataOutputStream(baos);//对字节数组流外封装成数据处理流
try {
dos.writeDouble(Math.random());//利用数据流里面的写入方法,写一个Double类型的随机数据
dos.writeBoolean(true);
ByteArrayInputStream bias=new ByteArrayInputStream(baos.toByteArray());//toByteArray()方法是创建一个新分配的字节数组。数组的大小和当前输出流的大小。这里指的是baos这个字节数组
System.out.println(bias.available());
DataInputStream dis=new DataInputStream(bias);
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
dos.close();
dis.close();
}catch (IOException e) {
e.printStackTrace();
}
}