浅谈Java的输入输出流
副标题#e#
Java语言的输入输出成果是十分强大而机动的,美中不敷的是看上去输入输出的代码并不是很简捷,因为你往往需要包装很多差异的工具。在Java类库中,IO部门的内容是很复杂的,因为它涉及的规模很遍及:尺度输入输出,文件的操纵,网络上的数据流,字符串流,工具流,zip文件流….本文的目标是为各人做一个扼要的先容。
流是一个很形象的观念,当措施需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络毗连。雷同的,当措施需要写入数据的时候,就会开启一个通向目标地的流。这时候你就可以想象数据仿佛在这个中“流”动一样,如下图:
Java中的流分为两种,一种是字节约,另一种是字符流,别离由四个抽象类来暗示 (每种流包罗输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变革的流均是由它们派生出来的:
#p#副标题#e#
在这个中InputStream和OutputStream在早期的Java版本中就已经存在了,它们是基于字节约的,而基于字符流的Reader和Writer是厥后插手作为增补的。以上的条理图是Java类库中的一个根基的条理体系。
在这四个抽象类中,InputStream和Reader界说了完全沟通的接口:
int read ()
int read (char cbuf[])
int read (char cbuf[], int offset, int length)
而OutputStream和Writer也是如此:
int write (int c)
int write (char cbuf[])
int write (char cbuf[], int offset, int length)
这六个要领都是最根基的,read ()和write ()通过要领的重载来读写一个字节,可能一个字节数组。
更多机动多变的成果是由它们的子类来扩充完成的。知道了Java输入输出的根基条理布局今后,本文在这里想给各人一些今后可以重复应用例子,对付所有子类的细节及其成果并不具体接头。
import java.io.*;
public class IOStreamDemo {
public void samples () throws IOException {
//1. 这是从键盘读入一行数据,返回的是一个字符串
BufferedReader stdin =new BufferedReader (new InputStreamReader (System.in));
System.out.print ("Enter a line:");
System.out.println (stdin.readLine ());
//2. 这是从文件中逐行读入数据
BufferedReader in = new BufferedReader (new FileReader ("IOStreamDemo.java"));
String s, s2 = new String ();
while ( (s = in.readLine ())!= null)
s2 += s + "\n";
in.close ();
//3. 这是从一个字符串中逐个读入字节
StringReader in1 = new StringReader (s2);
int c;
while ( (c = in1.read ()) != -1)
System.out.print ( (char)c);
//4. 这是将一个字符串写入文件
try {
BufferedReader in2 = new BufferedReader (new StringReader (s2));
PrintWriter out1 = new PrintWriter (new BufferedWriter (new FileWriter ("IODemo.out")));
int lineCount = 1;
while ( (s = in2.readLine ()) != null )
out1.println (lineCount++ + ": " + s);
out1.close ();
} catch (EOFException e) {
System.err.println ("End of stream");
}
}
}
对付上面的例子,需要说明的有以下几点:
1. BufferedReader是Reader的一个子类,它具有缓冲的浸染,制止了频繁的从物理设备中读取信息。它有以下两个结构函数:
BufferedReader (Reader in)
BufferedReader (Reader in, int sz)
这里的sz是指定缓冲区的巨细。
它的根基要领:
void close () //封锁流
void mark (int readAheadLimit) //标志当前位置
boolean markSupported () //是否支持标志
int read () //担任自Reader的根基要领
int read (char[] cbuf, int off, int len) //担任自Reader的根基要领
String readLine () //读取一行内容并以字符串形式返回
boolean ready () //判定流是否已经做好读入的筹备
void reset () //重设到最近的一个标志
long skip (long n) //跳过指定个数的字符读取
2. InputStreamReader是InputStream和Reader之间的桥梁,由于System.in是字节约,需要用它来包装之后变为字符流供应BufferedReader利用。
3. PrintWriter out1 = new PrintWriter (new BufferedWriter (new FileWriter ("IODemo.out")));
#p#分页标题#e#
这句话浮现了Java输入输出系统的一个特点,为了到达某个目标,需要包装好几层。首先,输出目标地是文件IODemo.out,所以最内层包装的是FileWriter,成立一个输出文件流,接下来,我们但愿这个流是缓冲的,所以用BufferedWriter来包装它以到达目标,最后,我们需要名目化输出功效,于是将PrintWriter包在最外层。
Java提供了这样一个成果,将尺度的输入输出流转向,也就是说,我们可以将某个其他的流设为尺度输入或输出流,看下面这个例子:
import java.io.*;
public class Redirecting {
public static void main (String[] args) throws IOException {
PrintStream console = System.out;
BufferedInputStream in = new BufferedInputStream ( new FileInputStream ( "Redirecting.java"));
PrintStream out = new PrintStream ( new BufferedOutputStream ( new FileOutputStream ("test.out")));
System.setIn (in);
System.setOut (out);
BufferedReader br = new BufferedReader ( new InputStreamReader (System.in));
String s;
while ( (s = br.readLine ()) != null)
System.out.println (s);
out.close ();
System.setOut (console);
}
}
在这里java.lang.System的静态要领
static void setIn (InputStream in)
static void setOut (PrintStream out)
提供了从头界说尺度输入输出流的要领,这样做是很利便的,好比一个措施的功效有许多,有时候甚至要翻页显示,这样未便于观当作果,这是你就可以将尺度输出流界说为一个文件流,措施运行完之后打开相应的文件观当作果,就直观了很多。
Java流有着另一个重要的用途,那就是操作工具流对工具举办序列化。下面将开始先容这方面的问题。
在一个措施运行的时候,个中的变量数据是生存在内存中的,一旦措施竣事这些数据将不会被生存,一种办理的步伐是将数据写入文件,而Java中提供了一种机制,它可以将措施中的工具写入文件,之后再从文件中把工具读出来从头成立。这就是所谓的工具序列化Java中引入它主要是为了RMI (Remote Method Invocation)和Java Bean所用,不外在平时应用中,它也是很有用的一种技能。
所有需要实现工具序列化的工具必需首先实现Serializable接口。下面看一个例子:
import java.io.*;
import java.util.*;
public class Logon implements Serializable {
private Date date = new Date ();
private String username;
private transient String password;
Logon (String name, String pwd) {
username = name;
password = pwd;
}
public String toString () {
String pwd = (password == null) ? " (n/a)" : password;
return "logon info: \n " + "username: " + username + "\n date: " + date + "\n password: " + pwd;
}
public static void main (String[] args) throws IOException, ClassNotFoundException {
Logon a = new Logon ("Morgan", "morgan83");
System.out.println ( "logon a = " + a);
ObjectOutputStream o = new ObjectOutputStream ( new FileOutputStream ("Logon.out"));
o.writeObject (a);
o.close ();
int seconds = 5;
long t = System.currentTimeMillis ()+ seconds * 1000;
while (System.currentTimeMillis () < t) ;
ObjectInputStream in = new ObjectInputStream ( new FileInputStream ("Logon.out"));
System.out.println ( "Recovering object at " + new Date ());
a = (Logon)in.readObject ();
System.out.println ("logon a = " + a);
}
}
#p#分页标题#e#
类Logon是一个记录登录信息的类,包罗用户名和暗码。首先它实现了接口Serializable,这就符号着它可以被序列化。之后再main要领里ObjectOutputStream o = new ObjectOutputStream ( new FileOutputStream ("Logon.out"));新建一个工具输出流包装一个文件流,暗示工具序列化的目标地是文件Logon.out。然后用要领writeObject开始写入。想要还原的时候也很简朴ObjectInputStream in = new ObjectInputStream ( new FileInputStream ("Logon.out"));新建一个工具输入流以文件流Logon.out为参数,之后挪用readObject要领就可以了。
需要说明一点,工具序列化有一个神奇之处就是,它成立了一张工具网,将当前要序列化的工具中所持有的引用指向的工具都包括起来一起写入到文件,更为奇妙的是,假如你一序次列化了好几个工具,它们中沟通的内容将会被共享写入。这简直是一个很是好的机制。它可以用来实现深层拷贝。
要害字transient在这里暗示当前内容将不被序列化,好比例子中的暗码,需要保密,所以没有被写入文件。
对Java的输入输出成果,就浅浅的先容到这里,本文的目标只是开一个好头,但愿能让各人对Java输入输出流有个根基的认识。