欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

举例讲解Java中的Stream流概念

程序员文章站 2024-03-06 16:30:08
1、基本的输入流和输出流 流是 java 中最重要的基本概念之一。文件读写、网络收发、进程通信,几乎所有需要输入输出的地方,都要用到流。 流是做什么用的呢?就是做输入输...

1、基本的输入流和输出流
流是 java 中最重要的基本概念之一。文件读写、网络收发、进程通信,几乎所有需要输入输出的地方,都要用到流。

流是做什么用的呢?就是做输入输出用的。为什么输入输出要用“流”这种方式呢?因为程序输入输出的基本单位是字节,输入就是获取一串字节,输出就是发送一串字节。但是很多情况下,程序不可能接收所有的字节之后再进行处理,而是接收一点处理一点。比方你下载魔兽世界,不可能全部下载到内存里再保存到硬盘上,而是下载一点就保存一点。这时,流这种方式就非常适合。

在 java 中,每个流都是一个对象。流分为两种:输入流(inputstream)和输出流(outputstream)。对于输入流,你只要从流当中不停地把字节取出来就是了;而对于输出流,你只要把准备好的字节串传给它就行。
举例讲解Java中的Stream流概念
流对象是怎么获得的呢?不同的外部系统,获取流的方式也不同。例如,文件读写就要创建 fileinputstream/fileoutputstream 对象,而网络通信是通过 socket 对象来获取输入输出流的。一般来说,如果一个类有 getinputstream() 或 getoutputstream() 这样的方法,就表明它是通过流对象来进行输入输出的。
 
inputstream 是输入流,下面是一个通过 inputstream 读取文件的例子:

import java.io.file; 
import java.io.fileinputstream; 
import java.io.ioexception; 
import java.io.inputstream; 
import java.io.filenotfoundexception; 
import java.util.arrays; 
 
/** 
 * 通过流读取文件 
 */ 
public class readfiledemo { 
 
 // 程序入口 
 public static void main(string[] args) { 
  string path = "c:/boot.ini"; 
  file file = new file(path); 
 
  // 创建输入流 
  inputstream is; 
  try { 
   is = new fileinputstream(file); 
  } catch (filenotfoundexception e) { 
   system.err.println("文件 " + path + " 不存在。"); 
   return; 
  } 
 
  // 开始读取 
  byte[] content = new byte[0];  // 保存读取出来的文件内容 
  byte[] buffer = new byte[10240]; // 定义缓存 
 
  try { 
   int eachtime = is.read(buffer); // 第一次读取。如果返回值为 -1 就表示没有内容可读了。 
   while (eachtime != -1) { 
    // 读取出来的内容放在 buffer 中,现在将其合并到 content。 
    content = concatbytearrays(content, buffer, eachtime); 
    eachtime = is.read(buffer); // 继续读取 
   } 
  } catch (ioexception e) { 
   system.err.println("读取文件内容失败。"); 
   e.printstacktrace(); 
  } finally { 
   try { 
    is.close(); 
   } catch (ioexception e) { 
    // 这里的异常可以忽略不处理 
   } 
  } 
 
  // 输出文件内容 
  string contentstr = new string(content); 
  system.out.println(contentstr); 
 } 
 
 /** 
  * 合并两个字节串 
  * 
  * @param bytes1  字节串1 
  * @param bytes2  字节串2 
  * @param sizeofbytes2 需要从 bytes2 中取出的长度 
  * 
  * @return bytes1 和 bytes2 中的前 sizeofbytes2 个字节合并后的结果 
  */ 
 private static byte[] concatbytearrays(byte[] bytes1, byte[] bytes2, int sizeofbytes2) { 
  byte[] result = arrays.copyof(bytes1, (bytes1.length + sizeofbytes2)); 
  system.arraycopy(bytes2, 0, result, bytes1.length, sizeofbytes2); 
  return result; 
 } 
} 

虽然写得很啰嗦,但这确实是 inputstream 的基本用法。注意,这只是一个例子,说明如何从输入流中读取字节串。实际上,java 提供更简单的方式来读取文本文件。以后将会介绍。

相比从流中读取,使用 outputstream 输出则是非常简单的事情。下面是一个例子:

import java.io.outputstream; 
import java.io.fileoutputstream; 
import java.io.file; 
import java.io.ioexception; 
import java.util.date; 
 
/** 
 * 将当前日期保存到文件 
 */ 
public class savefiledemo { 
 
 public static void main(string[] args) throws ioexception { 
  string path = "c:/now.txt"; 
 
  file file = new file(path); 
  if (!file.exists() && !file.createnewfile()) { 
   system.err.println("无法创建文件。"); 
   return; 
  } 
 
  outputstream os = new fileoutputstream(file); // 创建输出流(前提是文件存在) 
  os.write(new date().tostring().getbytes());  // 将当前时间写入文件 
  os.close();          // 必须关闭流,内容才会写入文件。 
  system.out.println("文件写入完成。"); 
 } 
} 

 
java 还提供其它的流操作方式,但它们都是对 inputstream 和 outputstream 进行扩展或包装。所以这两个类是基础,必须要理解它们的使用。

2、reader 和 writer
介绍了 inputstream 和 outputstream,接下来介绍 reader 和 writer。这两个类其实就是将 inputstream 和 outputstream 包装了一下。不过它们处理的不是字节(byte),而是字符(char)。如果一个流当中的内容都是文本,那么用 reader/writer 处理起来会简单些。下面是一个用 reader 读取文本文件的例子:

import java.io.filereader; 
import java.io.ioexception; 
import java.io.reader; 
 
/** 
 * 读取文本文件 
 */ 
public class readtextfiledemo { 
 
 // 程序入口 
 public static void main(string[] args) { 
  string path = "c:/boot.ini"; 
  string content = ""; 
 
  try { 
   reader reader = new filereader(path); 
   char[] buffer = new char[10240]; 
   int count; 
 
   while ((count = reader.read(buffer)) != -1) { 
    content += new string(buffer, 0, count); 
   } 
  } catch (ioexception e) { 
   system.err.println("读取文件失败。"); 
   e.printstacktrace(); 
  } 
 
  system.out.println(content); 
 } 
 
} 

至于如何用 writer 将文本内容写入文件,这里就不给出例子了,看官自己试着写一下吧。

上面这个例子,仍然不是读取文本文件最常用的方式。java 提供 bufferedreader,我们通常用它来读取文本文件。下面是一个例子:

import java.io.bufferedreader; 
import java.io.filereader; 
import java.io.ioexception; 
 
/** 
 * 用 bufferedreader 读取文本文件 
 */ 
public class readtextdemo2 { 
 
 public static void main(string[] args) { 
  string path = "c:/boot.ini"; 
  string content = ""; 
 
  try { 
   bufferedreader reader = new bufferedreader(new filereader(path)); 
   string line; 
   while ((line = reader.readline()) != null) { 
    content += line + "/n"; 
   } 
  } catch (ioexception e) { 
   system.err.println("读取文件失败。"); 
   e.printstacktrace(); 
  } 
 
  system.out.println(content); 
 } 
} 

3、对象序列化
对象序列化也是流应用的一个重要方面。序列化就是把一个对象转换成一串字节,既可以保存起来,也可以传给另外的 java 程序使用。objectoutputstream 和 objectinputstream 就是专门用来进行序列化和反序列化的。下面就是一个简单的例子:

import java.io.objectoutputstream; 
import java.io.fileoutputstream; 
import java.io.file; 
import java.io.ioexception; 
import java.io.serializable; 
import java.io.objectinputstream; 
import java.io.fileinputstream; 
import java.io.eofexception; 
  
/** 
 * objectoutputstream/objectinputstream 示例。 
 * 这两个类分别用于序列化和反序列化。 
 */ 
public class serializationdemo { 
  
  public static void main(string[] args) throws exception { 
    string path = "c:/persons.data"; 
    file f = new file(path); 
    if (!f.exists()) { 
      f.createnewfile(); 
    } 
  
    writepersons(path); 
    readpersons(path); 
  } 
  
  // 从指定的文件中读取 person 对象 
  private static void readpersons(string path) throws ioexception, classnotfoundexception { 
    objectinputstream ois = new objectinputstream(new fileinputstream(path)); 
  
    person p; 
    while (true) { 
      try { 
        p = (person) ois.readobject(); 
        system.out.println(p); 
      } catch (eofexception e) { 
        break; 
      } 
    } 
  } 
  
  // 将 person 对象保存到指定的文件中 
  private static void writepersons(string path) throws ioexception { 
    person[] persons = new person[]{ 
        new person("张三", 23), 
        new person("李四", 24) 
    }; 
  
    objectoutputstream oos = new objectoutputstream(new fileoutputstream(path)); 
    for (person person : persons) { 
      oos.writeobject(person); 
    } 
    oos.close(); 
  } 
  
  /////////////////////////////////////////////////////////// 
  
  private static class person implements serializable { 
  
    private string name; 
  
    private int age; 
  
    public person() { 
    } 
  
    public person(string name, int age) { 
      this.name = name; 
      this.age = age; 
    } 
  
    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; 
    } 
  
    @override 
    public string tostring() { 
      return "person{" + 
          "name='" + name + '/'' + 
          ", age=" + age + 
          '}'; 
    } 
  } 
} 

这个例子里面看不到字节和字符的读写,因为这两个类都包装好了。上面只是一个简单的例子,序列化要写好的话还是有不少讲究的。想深入了解序列化,可以看看这里。本文只关注跟流有关的部分。其实序列化用的很少,因为序列化降低了灵活性,所以可以不用的话一般都不会用。