流是用来读写数据的,java有一个类叫File,它封装的是文件的文件名,只是内存里面的一个对象,真正的文件是在硬盘上的一块空间,在这个文件里面存放着各类各样的数据,咱们想读文件里面的数据怎么办呢?是经过一个流的方式来读,我们要想从程序读数据,对于计算机来讲,不管读什么类型的数据都是以010101101010这样的形式读取的。怎么把文件里面的数据读出来呢?你能够把文件想象成一个小桶,文件就是一个桶,文件里面的数据就至关因而这个桶里面的水,那么咱们怎么从这个桶里面取水呢,也就是怎么从这个文件读取数据呢。java
常见的取水的办法是咱们用一根管道插到桶上面,而后在管道的另外一边打开水龙头,桶里面的水就开始哗啦哗啦地从水龙头里流出来了,桶里面的水是经过这根管道流出来的,所以这根管道就叫流,JAVA里面的流式输入/输出跟水流的原理如出一辙,当你要从文件读取数据的时候,一根管道插到文件里面去,而后文件里面的数据就顺着管道流出来,这时你在管道的另外一头就能够读取到从文件流出来的各类各样的数据了。当你要往文件写入数据时,也是经过一根管道,让要写入的数据经过这根管道哗啦哗啦地流进文件里面去。除了从文件去取数据之外,还能够经过网络,好比用一根管道把我和你的机子链接起来,我说一句话,经过这个管道流进你的机子里面,你立刻就能够看获得,而你说一句话,经过这根管道流到个人机子里面,我也立刻就能够看到。有的时候,一根管道不够用,比方说这根管道流过来的水有一些杂质,咱们就能够在这个根管道的外面再包一层管道,把杂质给过滤掉。从程序的角度来说,从计算机读取到的原始数据确定都是010101这种形式的,一个字节一个字节地往外读,当你这样读的时候你以为这样的方法不合适,不要紧,你再在这根管道的外面再包一层比较强大的管道,这个管道能够把010101帮你转换成字符串。这样你使用程序读取数据时读到的就再也不是010101这种形式的数据了,而是一些能够看得懂的字符串了。c++
io包里面定义了全部的流,因此一说流指的就是io包里面的小程序
什么叫输入流?什么叫输出流?用一根管道一端插进文件里程序里面,而后开始读数据,那么这是输入仍是输出呢?若是站在文件的角度上,这叫输出,若是站在程序的角度上,这叫输入。数组
记住,之后说输入流和输出流都是站在程序的角度上来讲。网络
你要是对原始的流不满意,你能够在这根管道外面再套其它的管道,套在其它管道之上的流叫处理流。为何须要处理流呢?这就跟水流里面有杂质,你要过滤它,你能够再套一层管道过滤这些杂质同样。dom
节点流就是一根管道直接插到数据源上面,直接读数据源里面的数据,或者是直接往数据源里面写入数据。典型的节点流是文件流:文件的字节输入流(FileInputStream),文件的字节输出流(FileOutputStream),文件的字符输入流(FileReader),文件的字符输出流(FileWriter)。测试
处理流是包在别的流上面的流,至关因而包到别的管道上面的管道。编码
咱们看到的具体的某一些管道,凡是以InputStream结尾的管道,都是以字节的形式向咱们的程序输入数据。spa
read()方法是一个字节一个字节地往外读,每读取一个字节,就处理一个字节。read(byte[] buffer)方法读取数据时,先把读取到的数据填满这个byte[]类型的数组buffer(buffer是内存里面的一块缓冲区),而后再处理数组里面的数据。这就跟咱们取水同样,先用一个桶去接,等桶接满水后再处理桶里面的水。若是是每读取一个字节就处理一个字节,这样子读取也太累了。命令行
以File(文件)这个类型做为讲解节点流的典型表明
范例:使用FileInputStream流来读取FileInputStream.java文件的内容
package cn.galc.test; import java.io.*; public class TestFileInputStream { public static void main(String args[]) { int b = 0;// 使用变量b来装调用read()方法时返回的整数 FileInputStream in = null; // 使用FileInputStream流来读取有中文的内容时,读出来的是乱码,由于使用InputStream流里面的read()方法读取内容时是一个字节一个字节地读取的,而一个汉字是占用两个字节的,因此读取出来的汉字没法正确显示。 // FileReader in = null;//使用FileReader流来读取内容时,中英文均可以正确显示,由于Reader流里面的read()方法是一个字符一个字符地读取的,这样每次读取出来的都是一个完整的汉字,这样就能够正确显示了。 try { in = new FileInputStream("D:\\Java\\MyEclipse 10\\Workspaces\\AnnotationTest\\src\\cn\\galc\\test\\FileInputStream.java"); // in = new FileReader("D:/java/io/TestFileInputStream.java"); } catch (FileNotFoundException e) { System.out.println("系统找不到指定文件!"); System.exit(-1);// 系统非正常退出 } long num = 0;// 使用变量num来记录读取到的字符数 try {// 调用read()方法时会抛异常,因此须要捕获异常 while ((b = in.read()) != -1) { // 调用int read() throws Exception方法时,返回的是一个int类型的整数 // 循环结束的条件就是返回一个值-1,表示此时已经读取到文件的末尾了。 // System.out.print(b+"\t");//若是没有使用“(char)b”进行转换,那么直接打印出来的b就是数字,而不是英文和中文了 System.out.print((char) b); // “char(b)”把使用数字表示的汉字和英文字母转换成字符输入 num++; } in.close();// 关闭输入流 System.out.println(); System.out.println("总共读取了" + num + "个字节的文件"); } catch (IOException e1) { System.out.println("文件读取错误!"); } } }
范例:使用FileOutputStream流往一个文件里面写入数据
package cn.galc.test; import java.io.*; public class TestFileOutputStream { public static void main(String args[]) { int b = 0; FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("D:\\Java\\MyEclipse 10\\Workspaces\\AnnotationTest\\src\\cn\\galc\\test\\MyMouseAdapter.java"); out = new FileOutputStream("D:/java/TestFileOutputStream1.java"); // 指明要写入数据的文件,若是指定的路径中不存在TestFileOutputStream1.java这样的文件,则系统会自动建立一个 while ((b = in.read()) != -1) { out.write(b); // 调用write(int c)方法把读取到的字符所有写入到指定文件中去 } in.close(); out.close(); } catch (FileNotFoundException e) { System.out.println("文件读取失败"); System.exit(-1);// 非正常退出 } catch (IOException e1) { System.out.println("文件复制失败!"); System.exit(-1); } System.out .println("TestFileInputStream.java文件里面的内容已经成功复制到文件TestFileOutStream1.java里面"); } }
FileInputStream和FileOutputStream这两个流都是字节流,都是以一个字节为单位进行输入和输出的。因此对于占用2个字节存储空间的字符来讲读取出来时就会显示成乱码。
范例:使用FileWriter(字符流)向指定文件中写入数据
package cn.galc.test; /*使用FileWriter(字符流)向指定文件中写入数据 写入数据时以1个字符为单位进行写入*/ import java.io.*; public class TestFileWriter{ public static void main(String args[]){ /*使用FileWriter输出流从程序把数据写入到Uicode.dat文件中 使用FileWriter流向文件写入数据时是一个字符一个字符写入的*/ FileWriter fw = null; try{ fw = new FileWriter("D:/java/Uicode.dat"); //字符的本质是一个无符号的16位整数 //字符在计算机内部占用2个字节 //这里使用for循环把0~60000里面的全部整数都输出 //这里至关因而把全世界各个国家的文字都0~60000内的整数的形式来表示 for(int c=0;c<=60000;c++){ fw.write(c); //使用write(int c)把0~60000内的整数写入到指定文件内 //调用write()方法时,我认为在执行的过程当中应该使用了“(char)c”进行强制转换,即把整数转换成字符来显示 //由于打开写入数据的文件能够看到,里面显示的数据并非0~60000内的整数,而是不一样国家的文字的表示方式 } /*使用FileReader(字符流)读取指定文件里面的内容 读取内容时是以一个字符为单位进行读取的*/ int b = 0; long num = 0; FileReader fr = null; fr = new FileReader("D:/java/Uicode.dat"); while((b = fr.read())!= -1){ System.out.print((char)b + "\t"); num++; } System.out.println(); System.out.println("总共读取了"+num+"个字符"); }catch(Exception e){ e.printStackTrace(); } } }
FileReader和FileWriter这两个流都是字符流,都是以一个字符为单位进行输入和输出的。因此读取和写入占用2个字节的字符时均可以正常地显示出来,以上是以File(文件)这个类型为例对节点流进行了讲解,所谓的节点流指定就是直接把输入流或输出插入到数据源上,直接往数据源里面写入数据或读取数据。
带有缓冲区的,缓冲区(Buffer)就是内存里面的一小块区域,读写数据时都是先把数据放到这块缓冲区域里面,减小io对硬盘的访问次数,保护咱们的硬盘。能够把缓冲区想象成一个小桶,把要读写的数据想象成水,每次读取数据或者是写入数据以前,都是先把数据装到这个桶里面,装满了之后再作处理。这就是所谓的缓冲。先把数据放置到缓冲区上,等到缓冲区满了之后,再一次把缓冲区里面的数据写入到硬盘上或者读取出来,这样能够有效地减小对硬盘的访问次数,有利于保护咱们的硬盘。
package cn.gacl.test; import java.io.*; public class TestBufferStream { public static void main(String args[]) { FileInputStream fis = null; try { fis = new FileInputStream("D:/java/TestFileInputStream.java"); // 在FileInputStream节点流的外面套接一层处理流BufferedInputStream BufferedInputStream bis = new BufferedInputStream(fis); int c = 0; System.out.println((char) bis.read()); System.out.println((char) bis.read()); bis.mark(100);// 在第100个字符处作一个标记 for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) { System.out.print((char) c); } System.out.println(); bis.reset();// 从新回到原来标记的地方 for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) { System.out.print((char) c); } bis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e1) { e1.printStackTrace(); } } }
package cn.gacl.test; import java.io.*; public class TestBufferStream1{ public static void main(String args[]){ try{ BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\java\\dat.txt")); //在节点流FileWriter的外面再套一层处理流BufferedWriter String s = null; for(int i=0;i<100;i++){ s = String.valueOf(Math.random());//“Math.random()”将会生成一系列介于0~1之间的随机数。 // static String valueOf(double d)这个valueOf()方法的做用就是把一个double类型的数转换成字符串 //valueOf()是一个静态方法,因此可使用“类型.静态方法名”的形式来调用 bw.write(s);//把随机数字符串写入到指定文件中 bw.newLine();//调用newLine()方法使得每写入一个随机数就换行显示 } bw.flush();//调用flush()方法清空缓冲区 BufferedReader br = new BufferedReader(new FileReader("D:/java/dat.txt")); //在节点流FileReader的外面再套一层处理流BufferedReader while((s = br.readLine())!=null){ //使用BufferedReader处理流里面提供String readLine()方法读取文件中的数据时是一行一行读取的 //循环结束的条件就是使用readLine()方法读取数据返回的字符串为空值后则表示已经读取到文件的末尾了。 System.out.println(s); } bw.close(); br.close(); }catch(Exception e){ e.printStackTrace(); } } }
程序的输入指的是把从文件读取到的内容存储到为程序分配的内存区域里面去。流,什么是流,流无非就是两根管道,一根向里,一根向外,向里向外都是对于咱们本身写的程序来讲,流分为各类各样的类型,不一样的分类方式又能够分为不一样的类型,根据方向来分,分为输入流和输出流,根据读取数据的单位的不一样,又能够分为字符流和字节流,除此以外,还能够分为节点流和处理流,节点流就是直接和数据源链接的流,处理流就是包在其它流上面的流,处理流不是直接和数据源链接,而是从数据源读取到数据之后再经过处理流处理一遍。缓冲流也包含了四个类:BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter。流都是成对的,没有流是是不成对的,确定是一个in,一个out。
转换流很是的有用,它能够把一个字节流转换成一个字符流,转换流有两种,一种叫InputStreamReader,另外一种叫OutputStreamWriter。InputStream是字节流,Reader是字符流,InputStreamReader就是把InputStream转换成Reader。OutputStream是字节流,Writer是字符流,OutputStreamWriter就是把OutputStream转换成Writer。把OutputStream转换成Writer以后就能够一个字符一个字符地经过管道写入数据了,并且还能够写入字符串。咱们若是用一个FileOutputStream流往文件里面写东西,得要一个字节一个字节地写进去,可是若是咱们在FileOutputStream流上面套上一个字符转换流,那咱们就能够一个字符串一个字符串地写进去。
转换流测试代码:
package cn.gacl.test; import java.io.*; public class TestTransform1 { public static void main(String args[]) { try { OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("D:/java/char.txt")); osw.write("MircosoftsunIBMOracleApplet");// 把字符串写入到指定的文件中去 System.out.println(osw.getEncoding());// 使用getEncoding()方法取得当前系统的默认字符编码 osw.close(); osw = new OutputStreamWriter(new FileOutputStream( "D:\\java\\char.txt", true), "ISO8859_1"); // 若是在调用FileOutputStream的构造方法时没有加入true,那么新加入的字符串就会替换掉原来写入的字符串,在调用构造方法时指定了字符的编码 osw.write("MircosoftsunIBMOracleApplet");// 再次向指定的文件写入字符串,新写入的字符串加入到原来字符串的后面 System.out.println(osw.getEncoding()); osw.close(); } catch (Exception e) { e.printStackTrace(); } } }
package cn.gacl.test; import java.io.*; public class TestTransform2{ public static void main(String args[]){ try{ InputStreamReader isr = new InputStreamReader(System.in); //System.in这里的in是一个标准的输入流,用来接收从键盘输入的数据 BufferedReader br = new BufferedReader(isr); String s = null; s = br.readLine();//使用readLine()方法把读取到的一行字符串保存到字符串变量s中去 while(s != null){ System.out.println(s.toUpperCase());//把保存在内存s中的字符串打印出来 s = br.readLine();//在循环体内继续接收从键盘的输入 if(s.equalsIgnoreCase("exit")){ //只要输入exit循环就结束,就会退出 break; } } }catch(Exception e){ e.printStackTrace(); } } }
数据流测试代码:
package cn.gacl.test; import java.io.*; public class TestDataStream{ public static void main(String args[]){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); //在调用构造方法时,首先会在内存里面建立一个ByteArray字节数组 DataOutputStream dos = new DataOutputStream(baos); //在输出流的外面套上一层数据流,用来处理int,double类型的数 try{ dos.writeDouble(Math.random());//把产生的随机数直接写入到字节数组ByteArray中 dos.writeBoolean(true);//布尔类型的数据在内存中就只占一个字节 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); System.out.println(bais.available()); DataInputStream dis = new DataInputStream(bais); System.out.println(dis.readDouble());//先写进去的就先读出来,调用readDouble()方法读取出写入的随机数 System.out.println(dis.readBoolean());//后写进去的就后读出来,这里面的读取顺序不能更改位置,不然会打印出不正确的结果 dos.close(); bais.close(); }catch(Exception e){ e.printStackTrace(); } } }
经过bais这个流往外读取数据的时候,是一个字节一个字节地往外读取的,所以读出来的数据没法判断是字符串仍是bool类型的值,所以要在它的外面再套一个流,经过dataInputStream把读出来的数据转换就能够判断了。注意了:读取数据的时候是先写进去的就先读出来,所以读ByteArray字节数组数据的顺序应该是先把占8个字节的double类型的数读出来,而后再读那个只占一个字节的boolean类型的数,由于double类型的数是先写进数组里面的,读的时候也要先读它。这就是所谓的先写的要先读。若是先读Boolean类型的那个数,那么读出来的状况可能就是把double类型数的8个字节里面的一个字节读了出来。
测试代码:
package cn.gacl.test; /*这个小程序是从新设置打印输出的窗口, * 把默认在命令行窗口输出打印内容设置成其余指定的打印显示窗口 */ import java.io.*; public class TestPrintStream{ public static void main(String args[]){ PrintStream ps = null; try{ FileOutputStream fos = new FileOutputStream("D:/java/log.txt"); ps = new PrintStream(fos);//在输出流的外面套接一层打印流,用来控制打印输出 if(ps != null){ System.setOut(ps);//这里调用setOut()方法改变了输出窗口,之前写System.out.print()默认的输出窗口就是命令行窗口. //但如今使用System.setOut(ps)将打印输出窗口改为了由ps指定的文件里面,经过这样设置之后,打印输出时都会在指定的文件内打印输出 //在这里将打印输出窗口设置到了log.txt这个文件里面,因此打印出来的内容会在log.txt这个文件里面看到 } for(char c=0;c<=60000;c++){ System.out.print(c+"\t");//把世界各国的文字打印到log.txt这个文件中去 } }catch(Exception e){ e.printStackTrace(); } } }
测试代码:
package cn.gacl.test; import java.io.*; public class TestObjectIo { public static void main(String args[]) { T t = new T(); t.k = 8;// 把k的值修改成8 try { FileOutputStream fos = new FileOutputStream( "D:/java/TestObjectIo.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); // ObjectOutputStream流专门用来处理Object的,在fos流的外面套接ObjectOutputStream流就能够直接把一个Object写进去 oos.writeObject(t);// 直接把一个t对象写入到指定的文件里面 oos.flush(); oos.close(); FileInputStream fis = new FileInputStream( "D:/java/TestObjectIo.txt"); ObjectInputStream ois = new ObjectInputStream(fis); // ObjectInputStream专门用来读一个Object的 T tRead = (T) ois.readObject(); // 直接把文件里面的内容所有读取出来而后分解成一个Object对象,并使用强制转换成指定类型T System.out.print(tRead.i + "\t" + tRead.j + "\t" + tRead.d + "\t" + tRead.k); ois.close(); } catch (Exception e) { e.printStackTrace(); } } } /* * 凡是要将一个类的对象序列化成一个字节流就必须实现Serializable接口 * Serializable接口中没有定义方法,Serializable接口是一个标记性接口,用来给类做标记,只是起到一个标记做用。 * 这个标记是给编译器看的,编译器看到这个标记以后就能够知道这个类能够被序列化 若是想把某个类的对象序列化,就必须得实现Serializable接口 */ class T implements Serializable { // Serializable的意思是能够被序列化的 int i = 10; int j = 9; double d = 2.3; int k = 15; // transient int k = 15; // 在声明变量时若是加上transient关键字,那么这个变量就会被看成是透明的,即不存在。 }
直接实现Serializable接口的类是JDK自动把这个类的对象序列化,而若是实现public interface Externalizable extends Serializable的类则能够本身控制对象的序列化,建议能让JDK本身控制序列化的就不要让本身去控制。