一、Java io流 的概念
流存在的意义:
1. 数据的传输量很大
2. 内存有限
3. 带宽有限
而Stream可以1点1点地逐步传输所有数据, 这就是Stream存在的根本意义。想想我们是怎样下载1个大文件的, 下载软件(例如x雷)并不会占用你内存很大的空间, 而只是在内存划分1个缓冲区, 一点一点地下载到自己的内存(缓冲区满了再写到硬盘), 这也是流的1个例子。
1、java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。
2、Stream是java的1个类, 这个类专门用于程序和外部设备的输入输出(IO). 基本上所有流都在 java.io这个包中.
实际上Stream就是数据在程序和外部设备的单向管道, 流的各种方法相当于管道上的各种按钮.
所谓的外部设备可以包括硬盘文件, 网络设备, 另个程序等. 也就是当前程序之外的数据设备
Java将所有传统的流类型都放在Java.io包下,用于实现输入和输出功能。
二、io 流的分类
按照流的不同的分类方式,可以把流分为不同的类型,流常见的分类有3种:
1、按照流的流向分:输入流和输出流
- 输入流: 只能从中读取数据,而不能向其写入数据。
- 输出流:只能向其写入数据,而不能向其读取数据。
此处的输入,输出涉及一个方向的问题,对于如图15.1所示的数据流向,数据从内存到硬盘,通常称为输出流——也就是说,这里的输入,输出都是从程序运行所在的内存的角度来划分的。
注:java的输入流主要是InputStream和Reader作为基类,而输出流则是主要由outputStream和Writer作为基类。它们都是一些抽象基类,无法直接创建实例。
2、按照操作单元划分,可以划分为字节流和字符流。
字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。
字节流主要是由InputStream和outPutStream作为基类,而字符流则主要有Reader和Writer作为基类。
3、按照流的角色划分为节点流和处理流。
可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,称为节点流。节点流也被称为低级流。图15.3显示了节点流的示意图。
从图15.3中可以看出,当使用节点流进行输入和输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。
处理流则用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能。处理流也被称为高级流。
节点流:
节点流的类型:
1)File 文件流。对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream。、
(2)Memory
1)从/向内存数组读写数据: CharArrayReader与 CharArrayWriter、ByteArrayInputStream与ByteArrayOutputStream。
2)从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream。
(3)Pipe管道流。 实现管道的输入和输出(进程间通信): PipedReader与PipedWriter、PipedInputStream与PipedOutputStream
节点流执行图示:
处理流:
处理流的类型:
(1)Buffering缓冲流:在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader与BufferedWriter、BufferedInputStream与BufferedOutputStream。
- (2)Filtering 滤流:在数据进行读或写时进行过滤:FilterReader与FilterWriter、FilterInputStream与FilterOutputStream。
- (3)Converting between Bytes and Characters 转换流:按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader):InputStreamReader、OutputStreamWriter。
- (4)Object Serialization 对象流 :ObjectInputStream、ObjectOutputStream。
- (5)DataConversion数据流: 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream 。
- (6)Counting计数流: 在读入数据时对行记数 :LineNumberReader、LineNumberInputStream。
- (7)Peeking Ahead预读流: 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream。
- (8)Printing打印流: 包含方便的打印方法 :PrintWriter、PrintStream。
处理流的执行图示:
缓冲流(buffering)是处理流的一种,对I/O进行缓冲是一种常见的性能优化,缓冲流为I/O流增加了内存缓冲区,增加缓冲区的两个目的:
(1)允许Java的I/O一次不只操作一个字符,这样提高䇖整个系统的性能;
(2)由于有缓冲区,使得在流上执行skip、mark和reset方法都成为可能。
【2】缓冲流:它是要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,
提高了读写的效率,同时增加了一些新的方法。例如:BufferedReader中的readLine方法,
BufferedWriter中的newLine方法。
j2sdk提供了4种缓冲流,重用的构造方法如下:
//字符输入流
BufferedReader(Reader in)//创建一个32字节的缓冲区
BufferedReader(Reader in, int size)//size为自定义缓存区的大小
//字符输出流
BufferedWriter(Writer out)
BufferedWriter(Writer out, int size)
//字节输入流
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)
//字节输出流
BufferedOutputStream(OutputStream in)
BufferedOutputStream(OutputStream in, int size)
备注:
(1)缓冲输入流BufferedInputSTream除了支持read和skip方法意外,还支持其父类的mark和reset方法;
(2)BufferedReader提供了一种新的ReadLine方法用于读取一行字符串(以\r或\n分隔);
(3)BufferedWriter提供了一种新的newLine方法用于写入一个行分隔符;
(4)对于输出的缓冲流,BufferedWriter和BufferedOutputStream,写出的数据会先在内存中缓存,
使用flush方法将会使内存的数据立刻写出。
分类2:
1、java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java Io流的40多个类都是从如下4个抽象类基类中派生出来的。
2、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 | ||
特殊流 | DataInputStream | DataOutputStream |
注:表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:斜体字标出的类代表抽象基类,无法直接创建实例。
CODE:
/**2019.6.14 * cpu将数据传入设备叫输出流-写入数据write,设备将数据传到cpu输入流-读数据 *java.io 包 * 定规范就是为了让别人写出想要的方法 * InputStream int read() * OutputStream void write(,int); * File:对文件进行的操作:FileInputStream 是从文件里面读东西的 ,调用read * FileOutStream :从文件里面输出信息 ,调用write()方法 */
package flow; import org.junit.Test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.*; import java.util.Date; import test.Student; public class TestIo { /*测试文件输出流*/ @Test public void test1() throws Exception { //与众多的设备打交道的流叫基础流(低级流)文件流是低级流 FileOutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\a.txt"); int a = 20;//00000000 00000000 00000000 00010100 一个字节代表8位 byte[] b = {0,0,0,20}; fout.write(b[0]); fout.write(b[1]); fout.write(b[2]); fout.write(b[3]); fout.close(); //0号字符,20号字符显示不出来 FileInputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\a.txt"); byte[] c = new byte[4]; c[0] = (byte) fin.read(); c[1] = (byte) fin.read(); c[2] = (byte) fin.read(); c[3] = (byte) fin.read(); System.out.println(b[3]); fin.close(); } @Test public void test2() throws Exception { FileOutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt"); String mesg = "Hello!Laurence。"; // 00000000 1010111 Java采用Unicod码 byte[] ss = mesg.getBytes(); for (byte s : ss) {//for循环遍历的另一种形式 fout.write(s);//对象流 } fout.close(); } //读 @Test public void test3() throws Exception { FileInputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt"); byte[] b = new byte[fin.available()];//测量文件占用的字节长度 int t; byte i = 0; while ((t = fin.read()) != -1) { b[i] = (byte) t; i++; } String c = new String(b); System.out.println(c); fin.close();//释放流的资源 } /* 高级流一般不读写文件 */ @Test public void test4() throws Exception { OutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt"); //OutputStream :写 ObjectOutputStream objout = new ObjectOutputStream(fout);//实际上是fout在写字节 // ObjectOutputStream :类型转换:将其他类型转换为字节类型 //write()是写字节的方法 objout.writeInt(222);//对象流完成其他类型到字节类型的转换 objout.writeDouble(22.0); objout.writeBoolean(true ); objout.writeChar('l'); objout.writeChars("liujiaheng is a good man!"); objout.flush();//刷新 objout.close(); fout.close();//释放流的资源 } //装饰模式:两个流类合作完成一个功能 @Test public void test5() throws Exception { InputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\b.txt"); ObjectInputStream objout = new ObjectInputStream(fin);//实际上是fout在写字节 int r=objout.readInt();//对象流完成其他类型到字节类型的转换 System.out.println(r); double t1=objout.readDouble(); System.out.println(t1); boolean t2=objout.readBoolean(); System.out.println(t2); char e=objout.readChar(); System.out.println(e); // String t3=objout.readUTF();//此方法读不出字符串 //// System.out.println(t3); objout.close(); fin.close();//释放流的资源 } @Test public void test6() throws Exception { OutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt"); //OutputStream :写 ObjectOutputStream objout =new ObjectOutputStream(fout); Date d=new Date(); objout.writeObject(d); Student s=new Student(2000,"Laurence"); objout.writeObject(s); objout.flush(); objout.close(); } @Test public void test7() throws Exception { InputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt"); //OutputStream :实现写的操作 ObjectInputStream obin = new ObjectInputStream(fin);//实际上是fout在写字节 // ObjectOutputStream :类型转换 //write()是写字节的方法; Date d=(Date)obin.readObject(); Student s=(Student)obin.readObject(); System.out.println(d); System.out.println(s.getName()); System.out.println(s.getId()); obin.close(); } //高级流实现读写操作 public void test8() throws Exception { OutputStream fout = new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt"); //OutputStream :实现写的操作 //下面也是高级流 BufferedOutputStream bout = new BufferedOutputStream(fout); ObjectOutputStream obin = new ObjectOutputStream(bout);//实际上是fout在写字节 fout.write(22); bout.write(222); bout.flush();//此时将数据写入文件里了 ObjectOutputStream objout = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\d.txt") ) ); Date d=new Date(); objout.writeObject(d); Student s=new Student(2000,"Laurence"); objout.writeObject(s); objout.flush(); objout.close(); } public void test9() throws Exception { InputStream fin = new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt"); //OutputStream :实现写的操作 ObjectInputStream obin = new ObjectInputStream( new BufferedInputStream( new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt") ) );//实际上是fout在写字节 // ObjectOutputStream :类型转换 //write()是写字节的方法; Date d=(Date)obin.readObject(); Student s=(Student)obin.readObject(); System.out.println(d); System.out.println(s.getName()); System.out.println(s.getId()); obin.close(); } }
package flow;
import org.junit.Test;
import java.io.*;
import java.util.*;
public class testIo2 {
@Test
public void test1() throws Exception{
FileInputStream fin=new FileInputStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
int a;
while ((a=fin.read())!=-1){
System.out.println((char)a);
}
fin.close();//因为字节流是一个字节一个字节读的,但是一个字符是占用两个字节。所以读出来的数据是乱码的
//Reader、 Writer字符流
}
@Test
public void test2() throws Exception{
FileWriter w=new FileWriter("D:\\新建文件夹\\练习\\javacode\\src\\test\\c.txt");
w.write('A');
w.write("刘家亨真棒!");
w.close();
}
//字符流
@Test
public void test3() throws Exception{
BufferedWriter w=new BufferedWriter(
new FileWriter("D:\\新建文件夹\\练习\\javacode\\src\\test\\e.txt")
);
w.write("sucessful\n");
w.flush();//
w.close();
}
@Test
public void test4() throws Exception{
FileReader w=new FileReader("D:\\新建文件夹\\练习\\javacode\\src\\test\\e.txt");
int a;
while((a=w.read())!=-1){
System.out.print((char)a);
}
w.close();
}
@Test
public void test5() throws Exception{
BufferedReader w=new BufferedReader(new FileReader("D:\\新建文件夹\\练习\\javacode\\src\\test\\e.txt")
);
String mesg = w.readLine();
System.out.println(mesg);
w.close();
}
//输入流 //输入流不需要flush//输入流包装字符流
@Test
public void tes6() throws Exception {
InputStreamReader ir=new InputStreamReader(System.in) ;
BufferedReader br=new BufferedReader(ir);
String mesg=br.readLine();
System.out.println(mesg);
}
@Test
public void test7() throws Exception{
OutputStreamWriter w=new OutputStreamWriter(System.out);
BufferedWriter bw=new BufferedWriter(w);
bw.write("中国你好!");
bw.flush();
bw.close();
}
@Test//PrintStream主要用于包装其他字符流,还可以用来写字符串
public void test8() throws Exception{
PrintStream c=new PrintStream("D:\\新建文件夹\\练习\\javacode\\src\\test\\rr.txt");
c.print("hello"+"my name is Laurence");//
c.flush();//刷新后才能写进去
}
//既可以包装字符流,又可以包装字节流,还可以给文件直接写字符,它可以和其他的流搭配着用
@Test
public void test9()throws Exception {
PrintWriter pw = new PrintWriter("D:\\新建文件夹\\练习\\javacode\\src\\test\\rr.txt");
pw.println("hello"+"bjdhsvcjhdbvcjhdsc");//给文件中写入这样一段字符
pw.flush();
pw.close();
}
@Test//文件对象 用来标识文件的,判断文件或目录是否存在
public void test10() throws Exception{
File f=new File("D:\\liujiaheng\\pp");
if(f.exists()){//判断文件目录是否存在
System.out.println("存在");
}else{
System.out.println("不存在");
System.out.println( f.mkdirs());//如果不存在就创建一个目录树
}
}
@Test
public void tes11() throws Exception {
File f = new File( "D:\\新建文件夹\\练习\\aa.txt" );
if(f.exists()){//判断文件是否存在
System.out.println("存在");
}else{
System.out.println("不存在");
f.createNewFile();//必须在已将创建的目录下才可以创建一个空文件
}
}
@Test
public void tes12() throws Exception {
File f = new File("D:\\新建文件夹\\练习\\javacode\\src\\test");//这是一个目录
File e=new File("D:\\新建文件夹\\练习\\javacode\\src\\test\\rr.txt");
System.out.println(f.isDirectory());//判断目录是否存在
System.out.println(f.isFile());//判断是否为文件返回值为true或false
System.out.println(e.isDirectory());
System.out.println(e.isFile());
}
@Test
public void tes13() throws Exception {
File f = new File("D:\\新建文件夹");
//目录名两端加一个[]代表它是一个目录。
if(f.isDirectory()){
System.out.println("[" + f.getName() + "]");
}else{
System.out.println(f.getName());
}
File[] fs = f.listFiles();
if(fs != null && fs.length > 0){
listFile(fs,1);
}
}
private void writeBlank(int level){
for(int i = 0 ; i < level ; i++)
System.out.print(" ");//一级打两个空格
}
private void listFile(File[] fs,int level){//file数组,level表示是第几层
for(File f : fs){//循环
writeBlank(level);//打空格
if(f.isDirectory()){//判断这个文件是不是目录
System.out.println("[" + f.getName() + "]");
File[] ff = f.listFiles();
if(ff != null && ff.length > 0){//判断目录是否为空
listFile(ff,++level);//不为空就迭代
}
}else{
System.out.println(f.getName());//不是目录打印出名字
}
}
}
}
流的重要特性:
1、流是Java 的1个类,但类不是流。
2、数据并不会在流里自动流动,因此需要我们调用流的方法,一次传输一定量的数据。
3、一个流对象只有一个传输方向,也就是说流是单向的, 数据要么从程序到设备(OutputStream), 要么从设备到程序(InputStream)。