1. **数据源**data source,提供数据的原始媒介。常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO设备。数据源分为:源设备、目标设备。<br /> **流**是一个抽象、动态的概念,是一连串连续动态的数据集合。

IO可以理解成bio

  • BIO:同步阻塞IO。一个线程触发io操作后必须等待这个io操作执行完成,期间不能去做其它的事
  • NIO:同步非阻塞。一个线程触发io操作后它可以立即返回,但是需要不断地去获取执行结果
  • AIO:异步非阻塞。一个线程触发io操作后它可以立即返回去做其它工作,内核系统将io操作执行完成后会通知该线程

流的概念细分

按流的方向分类:
1. 输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
2. 输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。
按处理的数据单元分类:
1. 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。
2. 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。
按处理对象不同分类:
1. 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
2. 处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过它们进行; 处理流可以对节点流进行包装,提高性能或提高程序的灵活性。

四大抽象类

抽象类 说明 常用方法
InputStream 字节输入流的父类,单位字节
- int read();
- void close();
OutputStream 字节输出流的父类,单位字节
- void write(int);
- void flush();
- void close();
Reader 字符输入流的父类,单位字符
- int read();
- void close();
Writer 字符输出流的父类,单位字符
- void write(string);
- void flush();
- void close();

FileInputStream

通过字节的方式读取文件,适合读取所有类型的文件

  1. ////写入字节流 按字段读取
  2. public class ReadTxt {
  3. public static void main(String[] args) {
  4. File file2 = new File("a.txt");
  5. FileInputStream in2 = null;
  6. try {
  7. in2 = new FileInputStream(file2);
  8. //缓冲容器 接收字节数组
  9. byte[] temp = new byte[5];
  10. int len=-1; //接收长度
  11. //read( byte[]): 返回读入缓冲区的字节总数,到末尾返回-1
  12. while((len = in2.read(temp)) != -1) { //in2把字节读入到temp中
  13. //字节数组到字符(使用默认字符集解码指定的 byte 子数组)
  14. String s1 = new String(temp,0,len);
  15. System.out.println(s1);
  16. }
  17. } catch (FileNotFoundException e) {
  18. e.printStackTrace();
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }finally {
  22. try {
  23. if(in2 != null) {
  24. in2.close();
  25. }
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }
  31. }

FileOutputStream

通过字节的方式写数据到文件中,适合所有类型的文件

  1. //写出字节流
  2. public class OutStream {
  3. public static void main(String[] args) {
  4. File f1 = new File("t3.txt");
  5. OutputStream os1 = null;
  6. try {
  7. os1 = new FileOutputStream(f1);
  8. String s1 = "IO stream";
  9. byte[] dates = s1.getBytes();//字符串到字节数组(编码)
  10. os1.write(dates, 0, dates.length);
  11. os1.flush();
  12. } catch (FileNotFoundException e) {
  13. e.printStackTrace();
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }

文件的拷贝

  1. //文件的拷贝
  2. public static void copy(String srcpath,String destpath) {
  3. File src = new File(srcpath); //源头
  4. File dest = new File(destpath); //目的地
  5. InputStream in1 = null;
  6. OutputStream os1 = null;
  7. try {
  8. in1 = new FileInputStream(src);
  9. os1 = new FileOutputStream(dest);
  10. byte[] temp = new byte[10]; //缓冲容器
  11. int len =-1;
  12. while((len=in1.read(temp))!= -1) { //read方法已将它转为字节
  13. os1.write(temp,0,len); //write方法直接写出字节
  14. os1.flush();
  15. }
  16. } catch (FileNotFoundException e) {
  17. e.printStackTrace();
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }finally {
  21. //释放资源 分别关闭 先打开的后关闭
  22. try {
  23. os1.close();
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. try {
  28. in1.close();
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }

FileReader

通过字符的方式读取文件

文件字符输入流

  1. //文件字符流(可输出文字)
  2. public class CharStream {
  3. public static void main(String[] args) {
  4. File f1 = new File("a.txt");
  5. Reader read= null;
  6. try {
  7. read = new FileReader(f1);
  8. char[] temp = new char[1024]; //缓冲容器
  9. int len =-1;
  10. while((len=read.read(temp))!= -1) {
  11. //字符数组到字符串
  12. //String str = new String(temp,0,len);
  13. System.out.println(str);
  14. }
  15. } catch (FileNotFoundException e) {
  16. e.printStackTrace();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }finally {
  20. try {
  21. if(read!=null) {
  22. read.close();
  23. }
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }
  29. }

FileWriter

通过字节的方式或追加数据到文件中,仅适合字符文件

文件字符输出流

  1. //FileWriter
  2. public class CharOutStream {
  3. public static void main(String[] args) {
  4. File f1 = new File("a.txt");
  5. FileWriter fw1 = null;
  6. try {
  7. fw1 = new FileWriter(f1);
  8. /*法1
  9. String str1 = "开心每一天";
  10. char[] date = str1.toCharArray();//字符串转换成字符数组
  11. fw1.write(date,0,date.length);
  12. */
  13. /*法2 直接写入字符串
  14. String str1 = "开心每一天";
  15. fw1.write(str1);
  16. */
  17. //法3
  18. fw1.append("一天").append("快乐");
  19. //字符读取不够整个数组长度时 不会写入文件中 要刷新一下
  20. fw1.flush();
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }finally {
  24. try {
  25. if(fw1!=null) {
  26. fw1.close();
  27. }
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. }

ByteArrayInputStream和ByteArrayOutputStream

经常用在需要流和数组之间转化的情况,字节数组是二进制,转换后方便网络传输

注:在内存里运行,适用于量小的字节数据

字节数组输入流

  1. //字节数组输入流
  2. public class ByteArrayStream {
  3. public static void main(String[] args) throws IOException {
  4. byte[] src = "abcde".getBytes();//字符串转换成字节数组
  5. InputStream in1 = null;
  6. in1 = new ByteArrayInputStream(src); //字节数组
  7. byte[] temp =new byte[5];
  8. int len = -1;
  9. while((len=in1.read(temp))!=-1) {
  10. //转回字符串输出
  11. String str1 = new String(temp,0,len);
  12. System.out.println(str1);
  13. }
  14. }
  15. }

字节数组输出流

  1. //字节数组输出流
  2. public class ByteoutStream {
  3. public static void main(String[] args) throws IOException {
  4. byte[] dest = null;
  5. ByteArrayOutputStream b1 = null;
  6. b1 = new ByteArrayOutputStream();//不用指定目的地 内存会自动分配
  7. //写出操作
  8. String str1 = "abcdefg";
  9. byte[] dates = str1.getBytes(); //字符串写入字节数组
  10. b1.write(dates, 0, dates.length);
  11. b1.flush();
  12. //使用toByteArray() 或 tostring() 检索数据
  13. dest = b1.toByteArray();
  14. System.out.println(dest.length+"--"+new String(dest,0,b1.size()));
  15. }
  16. }

练习:图片的写入写出

  1. public class ByteStream2 {
  2. public static void main(String[] args) {
  3. byte[] data = filetoByte("1.jpg");
  4. System.out.println(data.length);
  5. Bytetofile(data,"3.jpg");
  6. }
  7. //图片到字节数组中
  8. public static byte[] filetoByte(String filepath) {
  9. File f1 = new File(filepath);
  10. byte[] dest = null;
  11. InputStream in1 = null;
  12. ByteArrayOutputStream b1 = null;
  13. try {
  14. in1 = new FileInputStream(f1);
  15. b1 = new ByteArrayOutputStream();
  16. byte[] temp = new byte[1024];
  17. int len =-1; //接收长度
  18. while((len=in1.read(temp))!=-1) {
  19. b1.write(temp,0,len); //写出到字节数组中
  20. }
  21. b1.flush();
  22. return b1.toByteArray();
  23. } catch (FileNotFoundException e) {
  24. e.printStackTrace();
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. return null;
  29. }
  30. //字节数组写出到图片
  31. public static void Bytetofile(byte[] src,String filepath) {
  32. File dest = new File(filepath);
  33. InputStream in1 = null;
  34. OutputStream o1 = null;
  35. try {
  36. in1 = new ByteArrayInputStream(src);
  37. o1 = new FileOutputStream(dest);
  38. byte[] temp = new byte[5];
  39. int len = -1;
  40. while((len = in1.read(temp))!=-1) {
  41. o1.write(temp,0,len);
  42. }
  43. o1.flush();
  44. } catch (FileNotFoundException e) {
  45. e.printStackTrace();
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. }finally {
  49. try {
  50. if(o1!=null) {
  51. o1.close();
  52. }
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. }
  58. }

注:某类型数据转为字节数组 除了字符串 其它的都是流的对接

装饰器模式

将一个对象嵌入到另一个对象中去,实际上相当于这个对象被另一个对象包装起来

  1. public class Decorate2 {
  2. public static void main(String[] args) {
  3. coffee c1 = new coffee();
  4. System.out.println(c1.info()+c1.cost());
  5. suger s1 = new suger(c1);
  6. System.out.println(s1.info()+s1.cost());
  7. milk m1 = new milk(s1);
  8. System.out.println(m1.info()+m1.cost());
  9. }
  10. }
  11. //抽象组件:需要装饰的抽象对象(接口或抽象父类)
  12. interface drink{
  13. double cost(); //费用
  14. String info(); //说明
  15. }
  16. //具体组件:需要装饰的对象
  17. class coffee implements drink{
  18. private String name = " 原味咖啡 ";
  19. @Override
  20. public double cost() {
  21. return 10;
  22. }
  23. @Override
  24. public String info() {
  25. return name;
  26. }
  27. }
  28. //抽象装饰类:包含了对抽象组件的引用以及装饰者共有的方法
  29. abstract class decorate implements drink{
  30. private drink d1;
  31. public decorate(drink d) {
  32. this.d1 = d;
  33. }
  34. @Override
  35. public double cost() {
  36. return this.d1.cost();
  37. }
  38. @Override
  39. public String info() {
  40. return this.d1.info();
  41. }
  42. }
  43. //具体装饰类:被装饰的对象
  44. class milk extends decorate{
  45. public milk(drink drink) {
  46. super(drink);
  47. }
  48. @Override
  49. public double cost() {
  50. return super.cost()*2;
  51. }
  52. @Override
  53. public String info() {
  54. return super.info()+" 加入牛奶 ";
  55. }
  56. }
  57. class suger extends decorate{
  58. public suger(drink drink) {
  59. super(drink);
  60. }
  61. @Override
  62. public double cost() {
  63. return super.cost()*2;
  64. }
  65. @Override
  66. public String info() {
  67. return super.info()+"加入糖";
  68. }
  69. }

装饰流部分

  1. **BufferedInputStream**和**BufferedOutputStream**这两个流是**缓冲字节流**,通过内部缓存数组来提高操作流的效率。 当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果**使用缓冲流就能够更高效的读写信息 **

BufferedInputStream/BufferedOutputStream

  1. public class BufferedOutStream {
  2. public static void main(String[] args) {
  3. File f1 = new File("a.txt");
  4. FileOutputStream os1 = null;
  5. BufferedOutputStream bs1 = null;
  6. try {
  7. os1 = new FileOutputStream(f1);
  8. bs1 = new BufferedOutputStream(os1);
  9. String msg = "java2缓存流";
  10. byte[] temp = msg.getBytes();
  11. bs1.write(temp);
  12. bs1.flush();
  13. } catch (FileNotFoundException e) {
  14. e.printStackTrace();
  15. } catch (IOException e) {
  16. e.printStackTrace();
  17. }finally {
  18. try {
  19. if(bs1!=null) { //释放外部流 内部字节流会自动释放
  20. bs1.close();
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }

BufferedReader/BufferedWriter

BufferedReader/BufferedWriter字符缓存流增加了缓存机制,大大提高了读写文本文件的效率
readLine()方法是BufferedReader特有的方法,可以对文本文件进行更加方便的读取操作。
写入一行后使用newLine()方法换行

  1. public class BufferedT3 {
  2. public static void main(String[] args) {
  3. File f1 = new File("a.txt");
  4. Reader r1 = null;
  5. BufferedReader br1 = null;
  6. try {
  7. r1 = new FileReader(f1);
  8. br1 = new BufferedReader(r1);
  9. String str1 = null;
  10. while((str1=br1.readLine())!=null) { //readLine()方法实现一次读取一行操作
  11. System.out.println(str1);
  12. }
  13. } catch (FileNotFoundException e) {
  14. e.printStackTrace();
  15. } catch (IOException e) {
  16. e.printStackTrace();
  17. }finally {
  18. try {
  19. if(br1!=null) {
  20. br1.close();
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }

InputStreamReader/OutputStreamWriter 转换流

用来实现将字节流转化成字符流,并且能指定字符集(防止乱码)

  1. //以字符流的形式操作字节流(纯文本)
  2. public class Convert1 {
  3. public static void main(String[] args) throws IOException {
  4. //用buffered建立缓冲 字节流转为字符流处理
  5. BufferedReader in1 = new BufferedReader(new InputStreamReader(System.in));
  6. BufferedWriter os1 = new BufferedWriter(new OutputStreamWriter(System.out));
  7. String msg = "";
  8. while(!msg.equals("exit")) {
  9. msg = in1.readLine();
  10. os1.write(msg);
  11. os1.newLine();
  12. os1.flush();
  13. }
  14. }
  15. }
  1. //读写网络源码
  2. public class Prac {
  3. public static void main(String[] args) throws IOException{
  4. InputStream in1 = new URL("https://www.baidu.com/").openStream();//打开一个网络流
  5. BufferedReader br1 = new BufferedReader(new InputStreamReader(in1));
  6. FileOutputStream fo1 = new FileOutputStream("b1.html");
  7. BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(fo1));
  8. String msg;
  9. while((msg=br1.readLine())!=null) {
  10. bw1.write(msg);
  11. bw1.newLine();
  12. }
  13. bw1.flush();
  14. }
  15. }

DataInputStream和DataOutputStream 数据流

提供了可以存取与机器无关的所有Java基础类型数据 方便后期不用强转数据类型

  1. public class DataStream {
  2. public static void main(String[] args) throws IOException {
  3. //写出
  4. ByteArrayOutputStream b1 = new ByteArrayOutputStream();
  5. DataOutputStream d1 = new DataOutputStream(b1);
  6. //操作数据类型加数据
  7. d1.writeUTF("java学习");
  8. d1.writeInt(18);
  9. d1.writeBoolean(false);
  10. d1.writeChar('a');
  11. d1.flush();
  12. byte[] datas = b1.toByteArray();
  13. //读取
  14. ByteArrayInputStream b2 = new ByteArrayInputStream(datas);
  15. DataInputStream d2 = new DataInputStream(b2);
  16. //写入的顺序与写出要一致
  17. String msg = d2.readUTF();
  18. int a1 = d2.readInt();
  19. boolean b3 = d2.readBoolean();
  20. System.out.println(msg);
  21. }
  22. }

ObjectInputStream/ObjectOutputStream 对象流

以 “对象” 为数据源,但是必须将传输的对象进行序列化与反序列化操作

:不是所有的对象都可以序列化 必须要实现一个Serializable接口 起标识作用

序列化

无论是何种类型的数据,都会以二进制序列的形式在网络上传送;
处理对象流的一种机制,即可以很方便的保存内存中java对象的状态,同时也为了方便传输

序列化:将java对象转化为字节序列的过程
反序列化:将字节序列转化为java对象的过程

对象序列化的作用有如下两种:
1. 持久化: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,比如:休眠的实现。以后服务器session管理,hibernate将对象持久化实现
2. 网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递

注:
1. static属性不参与序列化。
2. 对象中的某些属性如果不想被序列化,不能使用static,而是使用transient修饰
3. 为了防止读和写的序列化ID不一致,一般指定一个固定的序列化ID

ObjectOutputStream 对Java对象进行序列化处理,处理后的数据,不是文本数据,该数据保存到文件中(给系统看的),用文本编辑器打开是乱码。
image.png

  1. public class ObjectStream1 {
  2. public static void main(String[] args) throws IOException, ClassNotFoundException {
  3. //序列化
  4. ByteArrayOutputStream b1 = new ByteArrayOutputStream();
  5. ObjectOutputStream d1 = new ObjectOutputStream(new BufferedOutputStream(b1));
  6. //操作数据类型加数据
  7. employee emp1 = new employee("h",12312);
  8. d1.writeObject(emp1);
  9. d1.flush(); //每次写出都要强制刷新 避免后面找不到数据
  10. byte[] datas = b1.toByteArray();
  11. //对象数据还原 反序列化
  12. ByteArrayInputStream b2 = new ByteArrayInputStream(datas);
  13. ObjectInputStream d2 = new ObjectInputStream(new BufferedInputStream(b2));
  14. //写出的顺序与写入要一致
  15. Object o2 = d2.readObject();
  16. //添加类型转换避免类型转换错误 还原数据
  17. if(o2 instanceof employee) { //判断是否为其实例
  18. employee empobj =(employee)o2; //转为employee类型
  19. System.out.println(empobj.getName()+" "+empobj.getSalary());
  20. }
  21. }
  22. }
  23. //封装employee javabean
  24. class employee implements Serializable{
  25. private String name; //属性前加关键字transient,这个属性就不会被序列化
  26. private double salary;
  27. ....
  28. }

PrintStream打印流

将OutputStream进行包装,可以方便地输出字符,更加灵活

  1. //法一 PrintStream
  2. public class printstream {
  3. public static void main(String[] args) throws FileNotFoundException {
  4. PrintStream ps= System.out;
  5. ps.print("??");
  6. ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")),true); //true表示自动刷新
  7. ps.println("abc");
  8. //ps.flush();
  9. //重定向输出端
  10. System.setOut(ps);
  11. System.out.println("学习");
  12. //重定向会控制台 通过系统定义好的FileDescriptor描述符
  13. System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)),true));
  14. ps.close();
  15. }
  16. }
  17. //法2 PrintWrite
  18. public class PrintStream2 {
  19. public static void main(String[] args) throws FileNotFoundException {
  20. PrintWriter pw=new PrintWriter(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
  21. pw.println("pw");
  22. pw.close();
  23. }
  24. }

Commons IO

Apache-commons工具包中提供了IOUtils/FileUtils,可以方便的对文件和目录进行操作

  1. public class test1 {
  2. public static void main(String[] args) throws IOException {
  3. //文件大小
  4. long len = FileUtils.sizeOf(new File("src/RwArrayList/RwArrayList2.java"));
  5. //目录大小
  6. //len =FileUtils.sizeOf(new File("C:/C语言/eclipse"));
  7. System.out.println(len);
  8. //子孙级目录
  9. Collection<File> list3=FileUtils.listFiles(new File("C:/C语言/eclipse"),
  10. EmptyFileFilter.NOT_EMPTY, null);
  11. for(File temp:list3) {
  12. System.out.println(temp.getAbsolutePath());
  13. }
  14. Collection<File> list2=FileUtils.listFiles(new File("C:/C语言/eclipse"),
  15. EmptyFileFilter.NOT_EMPTY, DirectoryFileFilter.INSTANCE);
  16. for(File temp:list2) {
  17. System.out.println(temp.getAbsolutePath());
  18. }
  19. //读取文件
  20. String msg = FileUtils.readFileToString(new File("a.txt"),"UTF-8");
  21. System.out.println(msg);
  22. //读到字节数组中
  23. byte[] datas = FileUtils.readFileToByteArray(new File("a.txt"));
  24. System.out.println(datas.length);
  25. //逐行读取
  26. List<String> msgs=FileUtils.readLines(new File("a.txt"),"UTF-8");
  27. for(String temp:msgs) {
  28. System.out.println(temp);
  29. }
  30. //读取迭代器
  31. LineIterator lt = FileUtils.lineIterator(new File("a.txt"),"UTF-8");
  32. while(lt.hasNext()) {
  33. System.out.println(lt.nextLine());
  34. }
  35. //写出文件
  36. FileUtils.write(new File("t3.txt"), "沁园春","UTF-8",true); //true作为是否追加
  37. FileUtils.writeStringToFile(new File("t3.txt"), "沁园春","UTF-8",true);
  38. FileUtils.writeByteArrayToFile(new File("t3.txt"), "沁园春".getBytes("UTF-8"),true);
  39. //写出列表
  40. List<String> list4 = new ArrayList<String>();
  41. list4.add("H");
  42. list4.add("M");
  43. list4.add("N");
  44. FileUtils.writeLines(new File("t3.txt"), list4,true);
  45. }
  46. }