|
真爱的事业和真正的爱情一生只有一次,都值得我们温柔地相待,因为那种感觉是永远都无法复制的, 这世界真正属于你的东西其实并不多,你不好好珍惜,它便会离你而去,包括机遇,包括爱情,包括生命。 不要找任何理由, 当幸福在你身边的时候就抓住它,你就一定会很幸福! |
时 间 记 忆 |
« | September 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | | | | | |
|
blog名称:玻璃杯中的花生壳 日志总数:162 评论数量:249 留言数量:1 访问次数:828982 建立时间:2004年11月4日 |
 | | |
|
|
Java语
言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为你往往需要包装许多不同的对象。在Java类库中,IO部分的
内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流....本文的目的是为大家做一个
简要的介绍。
流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样,如下图:
500)this.width=500'>
500)this.width=500'>
Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变化的流均是由它们派生出来的:
500)this.width=500'>500)this.width=500'>
500)this.width=500'>
500)this.width=500'>
在这其中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")));
这句话体现了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); }}
类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输入输出流有个基本的认识。 |
|
|
回复:浅谈Java的输入输出流 |
[ 2007/7/31 19:16:18 | By: 想入非非(游客) ] |
非常感谢,你的bolgger里的文章非常好!!!受益菲浅!!! |
|
|
回复:浅谈Java的输入输出流 |
[ 2007/3/30 19:58:05 | By: 探讨中(游客) ] |
呵呵,非常感谢你这篇文章,使我弄明白了不少,再次谢谢! |
|
JAVA学习笔记之输入输出
这次来看看关于java的I/O
首先java的输入输出给我的第一个感觉就是比较庞杂,不像c/c++那么精简,在java里,输入输出有很多类,一不小心就会陷入迷魂阵,呵呵。
静下来看一看,其实这么多I/O类,其实都归结于两大类:字符型I/O和字节型I/O
那些以stream结尾的,比如OutputStream系列拉,InputStream系列拉,都是属于字节型的,都是处理byte数据。所以他们的缓冲区都是由byte型的数组构建的。输入输出一般均为int型数据。字节型输入输出一般用来处理非文本数据,比如图像等等。
而那些以reader或writer结尾的,比如FileReader,FileWriter等等,则属于字符型,处理的是char型数据。他们的缓冲区由char型数组构建。输入输出为char或String型数据。字符型输入输出一般用来处理文本数据。
再仔细分析一下。
这两大类输入输出都是需要配套使用的。所谓配套就是输入和输出必须均属于同一类,而不能输入为字节型,输出为字符型。如果要不配套使用,则必须进行转换。比如对于System.in这样的字节型输入流,如果要将其转换为字符型,则必须使用InputStreamReader类进行转换。
BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in));
这里就将System.in转换成字节型输入,并且用BufferedReader将其包装起来,使其拥有缓冲区。
说到了封装,就来看看JAVA I/O中的包装。
我们时常会看到一些很长的转换,好像比较恐怖。其实,在JAVA I/O里面,为了达到某一个目的而进行一层甚至数层的包装都是很常见的。比如:
PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("IO.out")));
首先,输出目的地是文件IO.out,所以最内层包装的是FileWriter,建立一个输出文件流,接下来,我们希望这个流是缓冲的,所以用BufferedWriter来包装它以达到目的,最后,我们需要格式化输出结果,于是将PrintWriter包在最外层。
说形象点就像管道之间的连接,大口径的管道要连接到小口径的管道上,就必须使用转换,而如果要使管道拥有一些特性的话,那就必须在管道外再包上一层附属物,从而使其拥有某种所期望的特性。
至于java I/O的输入输出流的重定向,就是用了
System.setIn(InputStream in)和System.setOut(PrintStream out)这两个函数。
从而实现将自己定义的某个字节型输入/输出流代替标准输入/输出流。
我这里说的只是自己的学习心得,关于更多的内容,请参考sun公司的doc文档,那才是王道啊,呵呵 |
| » 1 »
| | |
|