Java 输入输出流(下)
目录
1.Java 随机流
2.Java 数组流
1.字节数组流
2.字符数组流
3.Java 数据流
4.Java 对象流
5.Java 序列化与对象克隆
6.Java 使用Scanner解析文件
1.使用默认分隔标记解析文件
2.使用正则表达式作为分隔标记解析文件
7.Java 文件对话框
8.Java 带进度条的输入流
9.Java 文件锁
1.Java 随机流
通过前面的学习我们知道,如果准备读文件,需要建立指向该文件的输入流;如果准备写文件,需要建立指向该文件的输出流。那么,能否建立一个流,通过该流既能读文件也能写文件呢?这正是本节要介绍的随机流。
RandomAccessFile类创建的流称做随机流,与前面的输入、输出流不同的是,RandomAccessFile类既不是InputStream类的子类,也不是OutputStream类的子类。但是RandomAccessFile类创建的流的指向既可以作为流的源,也可以作为流的目的地,换句话说,当准备对一个文件进行读写操作时,创建一个指向该文件的随机流即可,这样既可以从这个流中读取文件中的数据,也可以通过这个流写入数据到文件。
以下是RandomAccessFile类的两个构造方法:
1)RandomAccessFile(String name,String mode)
参数name用来确定一个文件名,给出创建的流的源,也是流目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权利。
2)RandomAccessFile(File file,String mode)
参数file是一个File对象,给出创建的流的源,也是流目的地。参数mode取r(只读)或rw(可读写),决定创建的流对文件的访问权利。
注意:RandomAccessFile流指向文件时,不刷新文件。
RandomAccessFile类中有一个方法seek(long a)用来定位RandomAccessFile流的读写位置,其中参数a确定读写位置距离文件开头的字节个数。另外流还可以调用getFilePointer()方法获取流的当前读写位置。RandomAccessFile流对文件的读写比顺序读写更为灵活。
例如,把几个int型整数写入到一个名字为tom.dat文件中,然后按相反顺序读出这些数据:
import java.io.*;
public class Main {
public static void main(String args[]) {
RandomAccessFile inAndOut = null;
int data[] = {1,2,3,4,5,6,7,8,9,10};
try {
inAndOut = new RandomAccessFile("tom.dat","rw");
for(int i=0;i<data.length;i++) {
inAndOut.writeInt(data[i]);
}
for(long i = data.length-1;i>=0;i--) {
inAndOut.seek(i*4);
System.out.printf("\t%d",inAndOut.readInt());
/*
一个int型数据占4个字节,inAndOut从文件的第36个字节读取最后面的一个整数,每隔4个字节往前读取一个整数
*/
}
inAndOut.close();
}
catch(IOException e) {}
}
}
RandomAccessFile流的常用方法如下:
方法 | 说明 |
close() | 关闭文件 |
getFilePointer() | 获取当前读写的位置 |
length() | 获取文件的长度 |
read() | 从文件中读取一个字节的数据 |
readBoolean() | 从文件中读取一个布尔值,0代表false;其他值代表true |
readByte() | 从文件中读取一个字节 |
readChar() | 从文件中读取一个字符(2个字节) |
readDouble() | 从文件中读取一个双精度浮点值(8 个字节) |
readFloat() | 从文件中读取一个单精度浮点值(4个字节) |
readFully(byte b[]) | 读b.length字节放入数组b,完全填满该数组 |
readInt() | 从文件中读取一个int值(4个字节) |
readLine() | 从文件中读取一个文本行 |
readLong() | 从文件中读取一个长型值(8个字节) |
readShort() | 从文件中读取一个短型值(2个字节) |
readUnsignedByte() | 从文件中读取一个无符号字节(1个字节) |
readUnsignedShort() | 从文件中读取一个无符号短型值(2个字节) |
readUTF() | 从文件中读取一个UTF字符串 |
seek(long position) | 定位读写位置 |
setLength(long newlength) | 设置文件的长度 |
skipBytes(int n) | 在文件中跳过给定数量的字节 |
write(byte b[]) | 写b.length个字节到文件 |
writeBoolean(boolean v) | 把一个布尔值作为单字节值写入文件 |
writeByte(int v) | 向文件写入一个字节 |
writeBytes(String s) | 向文件写入一个字符串 |
writeChar(char c) | 向文件写入一个字符 |
writeChars(String s) | 向文件写入一个作为字符数据的字符串 |
writeDouble(double v) | 向文件写入一个双精度浮点值 |
writeFloat(float v) | 向文件写入一个单精度浮点值 |
writeInt(int v) | 向文件写入一个int值 |
writeLong(long v) | 向文件写入一个长型int值 |
writeShort(int v) | 向文件写入一个短型int值 |
writeUTF(String s) | 写入一个UTF字符串 |
注意:RandomAccessFile流的readLine()方法在读取含有非ASCⅡ字符的文件时,比如含有汉字的文件,会出现“乱码”现象。因此,需要把readLine()读取的字符串用“iso-8859-1”编码重新编码存放到byte数组中,然后再用当前机器的默认编码将该数组转化为字符串,操作如下:
1.读取
String str = in.readLine();
2.用“iso-8859-1”重新编码
byte b[] = str.getBytes("iso-8859-1");
3.使用当前机器的默认编码将字节数组转化为字符串
String content = new String(b);
如果机器的默认编码是“GB2312”,那么
String content = new String(b);
等同于
String content = new String(b,"GB2312");
例如:
import java.io.*;
public class Main {
public static void main(String args[]) {
RandomAccessFile in = null;
try {
in = new RandomAccessFile("Main.java","rw");
long length = in.length(); //获取文件的长度
long position = 0;
in.seek(position); //将读取位置定位到文件的起始
while(position<length) {
String str = in.readLine();
byte b[] = str.getBytes("iso-8859-1");
str = new String(b);
position = in.getFilePointer();
System.out.println(str);
}
}
catch(IOException e) {}
}
}
2.Java 数组流
我们要知道,流的源和目的地除了可以是文件以外,还可以是计算机内存。
1.字节数组流
字节数组输入流ByteArrayInputStream和字节数组输出流ByteArrayOutputStream分别使用字节数组作为流的源和目的地。
1)ByteArrayInputStream流的构造方法如下:
ByteArrayInputStream(byte[] buf);
ByteArrayInputStream(byte[] buf,int offset,int length);
第一个构造方法构造的字节数组流的源是参数buf指定的数组的全部字节单元;第二个构造方法构造的字节数组流的源是buf指定的数组从offset处按顺序取的length个字节单元。
字节数组输入流调用public int read();方法可以顺序地从源中读出一个字节,该方法返回读出的字节值;调用public int read(byte[] b,int off,int len);方法可以顺序地从源中读出参数len指定的字节数,并将读出的字节存放到参数b指定的数组中,参数off指定数组b存放读出字节的起始位置,该方法返回实际读出的字节个数,如果未读出字节read方法返回-1。
2)ByteArrayOutputStream流的构造方法如下:
ByteArrayOutputStream();
ByteArrayOutputStream(int size);
第一个构造方法构造的字节数组输出流指向一个默认大小为32字节的缓冲区,如果输出流向缓冲区写入的字节个数大于缓冲区时,缓冲区的容量会自动增加。
第二个构造方法构造的字节数组输出流指向的缓冲区的初始大小由参数size指定,如果输出流向缓冲区写入的字节个数大于缓冲区时,缓冲区的容量会自动增加。
字节数组输出流调用public void write(int b);方法可以顺序地向缓冲区写入一个字节;调用public void write(byte[] b,int off,int len);方法可以将参数b中指定的len个字节顺序地写入缓冲区,参数off指定从b中写出的字节的起始位置;调用public byte[] toByteArray();方法可以返回输出流写入到缓冲区的全部字节。
2.字符数组流
与字节数组流对应的是字符数组流CharArrayReader类和CharArrayWriter类,字符数组流分别使用字符数组作为流的源和目标。
例如,使用数组流向内存(输出流的缓冲区)写入“mid-autumn festival”和“中秋快乐”,然后再从内存读取曾写入的数据:
import java.io.*;
public class Main {
public static void main(String args[]) {
try {
ByteArrayOutputStream outByte = new ByteArrayOutputStream();
byte [] byteContent = "mid-autumn festival ".getBytes();
outByte.write(byteContent);
ByteArrayInputStream inByte = new ByteArrayInputStream(outByte.toByteArray());
byte backByte [] = new byte[outByte.toByteArray().length];
inByte.read(backByte);
System.out.println(new String(backByte));
CharArrayWriter outChar = new CharArrayWriter();
char [] charContent = "中秋快乐".toCharArray();
outChar.write(charContent);
CharArrayReader inChar = new CharArrayReader(outChar.toCharArray());
char backChar [] = new char [outChar.toCharArray().length];
inChar.read(backChar);
System.out.println(new String(backChar));
}
catch(IOException exp) {}
}
}
3.Java 数据流
DataInputStream和DataOutputStream类创建的对象称为数据输入流和数据输出流。这两个流是很有用的两个流,它们允许程序按着机器无关的风格读取Java原始数据。也就是说,当读取一个数值时,不必再关心这个数值应当是多少个字节。
DataInputStream和DataOutputStream的构造方法如下:
1)DataInputStream(InputStream in)
创建的数据输入流指向一个由参数in指定的底层输入流。
2)DataOutputStream(OutputStream out)
创建的数据输出流指向一个由参数out指定的底层输出流。
DataInputStream和DataOutputStream类的常用方法如下:
方法 | 说明 |
close() | 关闭流 |
readBoolean() | 读取一个布尔值 |
readByte() | 读取一个字节 |
readChar() | 读取一个字符 |
readDouble() | 读取一个双精度浮点值 |
readFloat() | 读取一个单精度浮点值 |
readInt() | 读取一个int值 |
readLong() | 读取一个长型值 |
readShort() | 读取一个短型值 |
readUnsignedByte() | 读取一个无符号字节 |
readUnsignedShort() | 读取一个无符号短型值 |
readUTF() | 读取一个UTF字符串 |
skipBytes(int n) | 跳过给定数量的字节 |
writeBoolean(boolean v) | 写入一个布尔值 |
writeBytes(String s) | 写入一个字符串 |
writeChars(String s) | 写入字符串 |
writeDouble(double v) | 写入一个双精度浮点值 |
writeFloat(float v) | 写入一个单精度浮点值 |
writeInt(int v) | 写入一个int值 |
writeLong(long v) | 写入一个长型int值 |
writeShort(int v) | 写入一个短型int值 |
writeUTF(String s) | 写入一个UTF字符串 |
例如,写几个Java类型的数据到一个文件,然后再读出来:
import java.io.*;
public class Main {
public static void main(String args[]) {
File file = new File("apple.txt");
try {
FileOutputStream fos = new FileOutputStream(file);
DataOutputStream outData = new DataOutputStream(fos);
outData.writeInt(100);
outData.writeLong(123456);
outData.writeFloat(3.1415926f);
outData.writeDouble(987654321.1234);
outData.writeBoolean(true);
outData.writeChars("How are you doing");
}
catch(IOException e) {}
try {
FileInputStream fis = new FileInputStream(file);
DataInputStream inData = new DataInputStream(fis);
System.out.println(inData.readInt()); //读取int数据
System.out.println(inData.readLong()); //读取long数据
System.out.println(+inData.readFloat()); //读取float数据
System.out.println(inData.readDouble()); //读取double数据
System.out.println(inData.readBoolean()); //读取boolean数据
char c = '\0';
while((c=inData.readChar()) !='\0') { //'\0'表示空字符
System.out.print(c);
}
}
catch(IOException e) {}
}
}
4.Java 对象流
ObjectInputStream和ObjectOutputStream类分别是InputStream和OutputStream类的子类。ObjectInputStream和ObjectOutputStream类创建的对象称为对象输入流和对象输出流。对象输出流使用writeObject(Object obj)方法将一个对象obj写入到一个文件,对象输入流使用readObject()读取一个对象到程序中。
ObjectInputStream和ObjectOutputStream类的构造方法如下:
1)ObjectInputStream(InputStream in)
2)ObjectOutputStream(OutputStream out)
ObjectOutputStream的指向应当是一个输出流对象,因此当准备将一个对象写入到文件时,首先用OutputStream的子类创建一个输出流。
例如,用FileOutputStream创建一个文件输出流,代码如下:
FileOutputStream fileOut = new FileOutputStream("tom.txt");
ObjectOutputStream objectout = new ObjectOutputStream(fileOut);
同样ObjectInputStream的指向应当是一个输入流对象,因此当准备从文件中读入一个对象到程序中时,首先用InputStream的子类创建一个输入流。
例如,用FileInputStream创建一个文件输入流,代码如下:
FileInputStream fileIn = new FileInputStream("tom.txt");
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
当使用对象流写入或读入对象时,要保证对象是序列化的,这是为了保证能把对象写入到文件,并能再把对象正确读回到程序中。
一个类如果实现了Serializable接口(java.io包中的接口),那么这个类创建的对象就是所谓序列化的对象。Java类库提供的绝大多数对象都是所谓序列化的。需要强调的是,Serializable接口中没有方法,因此实现该接口的类不需要实现额外的方法。另外需要注意的是,使用对象流把一个对象写入到文件时不仅要保证该对象是序列化的,而且该对象的成员对象也必须是序列化的。
Serializable接口中的方法对程序是不可见的,因此实现该接口的类不需要实现额外的方法,当把一个序列化的对象写入到对象输出流时,JVM就会实现Serializable接口中的方法,将一定格式的文本(对象的序列化信息)写入到目的地。当ObjectInputStream对象流从文件读取对象时,就会从文件中读回对象的序列化信息,并根据对象的序列化信息创建一个对象。
例如,使用对象流读写TV类创建的对象:
TV.java
import java.io.*;
public class TV implements Serializable {
String name;
int price;
public void setName(String s) {
name = s;
}
public void setPrice(int n) {
price = n;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
Main.java
import java.io.*;
public class Main {
public static void main(String args[]) {
TV changhong = new TV();
changhong.setName("长虹电视");
changhong.setPrice(5678);
File file = new File("television.txt");
try {
FileOutputStream fileOut = new FileOutputStream(file);
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(changhong);
objectOut.close();
FileInputStream fileIn = new FileInputStream(file);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
TV xinfei = (TV)objectIn.readObject();
objectIn.close();
xinfei.setName("新飞电视");
xinfei.setPrice(6666);
System.out.println("changhong的名字:"+changhong.getName());
System.out.println("changhong的价格:"+changhong.getPrice());
System.out.println("xinfei的名字:"+xinfei.getName());
System.out.println("xinfei的价格:"+xinfei.getPrice());
}
catch(ClassNotFoundException event) {
System.out.println("不能读出对象");
}
catch(IOException event) {
System.out.println(event);
}
}
}
5.Java 序列化与对象克隆
我们已经知道,一个类的两个对象如果具有相同的引用,那么它们就具有相同的实体和功能,比如:
A one = new A();
A two = one;
假设A类有名字为x的int型成员变量,那么,如果进行如下操作:
two.x = 100;
那么one.x的值也会是100。
再比如,某个方法的参数是People类型:
public void f(People p) {
p.x = 200;
}
如果调用该方法时,将People的某个对象的引用,比如zhang,传递给参数P,那么该方法执行后,zhang.x的值也将是200。
有时想得到对象的一个“复制品”,复制品实体的变化不会引起原对象实体发生变化,反之亦然。这样的复制品称为原对象的一个克隆对象或简称克隆。
使用对象流很容易获取一个序列化对象的克隆,只需将该对象写入对象输出流指向的目的地,然后将该目的地作为一个对象输入流的源,那么该对象输入流从源中读回的对象一定是原对象的一个克隆,即对象输入流通过对象的序列化信息来得到当前对象的一个克隆。例如,上节例子中的对象xinfei就是对象changhong的一个克隆。
当程序想以较快的速度得到一个对象的克隆时,可以用对象流将对象的序列化信息写入内存,而不是写入到磁盘的文件中。对象流将数组流作为底层流就可以将对象的序列化信息写入内存。例如,我们可以将上节例子中的
FileOutputStream fileOut = new FileOutputStream(file);
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
和
FileInputStream fileIn = new FileInputStream(file);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
分别更改为:
ByteArrayOutputStream outByte = new ByteArrayOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(outByte);
和
ByteArrayInputStream inByte = new ByteArrayInputStream(outByte.toByteArray());
ObjectInputStream objectIn = new ObjectInputStream(inByte);
java.awt包中的Component类是实现Serializable接口的类,组件是序列化对象,因此,程序可以把组件写入输出流,然后再用输入流读入该组件的克隆。
6.Java 使用Scanner解析文件
在上一章我们学习了怎样使用Scanner类的对象解析字符串中的数据,那么这一节我们将学习怎样使用Scanner类的对象解析文件中的数据。
应用程序可能需要解析文件中的特殊数据,此时,应用程序可以把文件的内容全部读入内存后,再解析所需要的内容,其优点是处理速度快,但如果读入的内容较大将消耗较多的内存,即以空间换取时间。
这一节主要介绍怎样借助Scanner类和正则表达式来解析文件,比如,要解析出文件中的特殊单词、数字等信息。使用Scanner类和正则表达式来解析文件的特点是以时间换取空间,即解析的速度相对较慢,但节省内存。
1.使用默认分隔标记解析文件
创建Scanner对象,并指向要解析的文件,例如:
File file = new File("hello.java");
Scanner sc = new Scanner(file);
那么sc将空格作为分隔标记,调用next()方法依次返回file中的单词,如果file最后一个单词已被next()方法返回,sc调用hasNext()将返回false,否则返回true。
另外,对于数字型的单词,比如108,167.92等可以用nextInt()或nextDouble()方法来代替next()方法,即sc可以调用nextInt()或nextDouble()方法将数字型单词转化为int或double数据返回,但需要特别注意的是,如果单词不是数字型单词,调用nextInt()或nextDouble()方法将发生InputMismatchException异常,在处理异常时可以调用next()方法返回该非数字化单词。
2.使用正则表达式作为分隔标记解析文件
创建Scanner对象,指向要解析的文件,并使用useDelimiter方法指定正则表达式作为分隔标记,例如:
File file = new File("hello.java");
Scanner sc = new Scanner(file);
sc.useDelimiter(正则表达式);
那么sc将正则表达式作为分隔标记,调用next()方法依次返回file中的单词,如果file最后一个单词已被next()方法返回,sc调用hasNext()将返回false,否则返回true。
另外,对于数字型的单词,比如1979,0.618等可以用nextInt()或nextDouble()方法来代替next()方法,即sc可以调用nextInt()或nextDouble()方法将数字型单词转化为int或double数据返回,但需要特别注意的是,如果单词不是数字型单词,调用nextInt()或nextDouble()方法将发生InputMismatchException异常,那么在处理异常时可以调用next()方法返回该非数字化单词。
例如,使用正则表达式(匹配所有非数字字符串)String regex="[^0123456789.]+"作为分隔标记解析student.txt文件中的学生成绩,并计算平均成绩。
student.txt
张三的成绩是70分,李四的成绩是80分,赵五的成绩是90分。
代码如下:
import java.io.*;
import java.util.*;
public class Main {
public static void main(String args[]) {
File file = new File("student.txt");
Scanner sc = null;
int count = 0;
double sum = 0;
try {
double score = 0;
sc = new Scanner(file);
sc.useDelimiter("[^0123456789.]+");
while(sc.hasNextDouble()) {
score = sc.nextDouble();
count++;
sum = sum+score;
System.out.println(score);
}
double aver = sum/count;
System.out.println("平均成绩:"+aver);
}
catch(Exception exp) {
System.out.println(exp);
}
}
}
7.Java 文件对话框
文件对话框是一个选择文件的界面。Javax.swing包中的JFileChooser类可以创建文件对话框,使用该类的构造方法JFileChooser()创建初始不可见的有模式文件对话框。然后文件对话框调用下述2个方法:
showSaveDialog(Component a);
showOpenDialog(Component a);
都可以使得对话框可见,只是呈现的外观有所不同,showSaveDialog方法提供保存文件的界面,showOpenDialog方法提供打开文件的界面。上述两个方法中的参数a指定对话框可见时的位置,当a是null时,文件对话框出现在屏幕的中央;如果组件a不空,文件对话框在组件a的正前面居中显示。
用户单击文件对话框上的“确定”、“取消”或“关闭”图标,文件对话框将消失,ShowSaveDialog()或showOpenDialog()方法返回下列常量之一:
JFileChooser.APPROVE OPTION
JFileChooser.CANCEL_OPTION
如果希望文件对话框的文件类型是用户需要的几种类型,比如,扩展名是.jpeg等图像类型的文件,可以使用FileNameExtensionFilter类事先创建一个对象,在JDK 1.6版本,FileNameExtensionFilter类在javax.swing.filechooser包中。
例如:
FileNameExtensionFilter filter = new FileNameExtensionFilter("图像文件","jpg","gif");
然后让文件对话框调用setFileFilter(FileNameExtensionFilter filter)方法设置对话框默认打开或显示的文件类型为参数指定的类型即可,例如:
chooser.setFileFilter(filter);
8.Java 带进度条的输入流
如果读取文件时希望看见文件的读取进度可以使用javax.swing包提供的输入流类ProgressMonitorInputStream,其构造方法是:
ProgressMonitor InputStream(Conmponent c,String s,InputStream);
该类创建的输入流在读取文件时会弹出一个显示读取速度的进度条,进度条在参数c指定的组件的正前方显示,若该参数取null,则在屏幕的正前方显示。
例如,使用带进度条的输入流读取文件的内容:
import javax.swing.*;
import java.io.*;
public class Main {
public static void main(String args[]) {
byte b[] = new byte[30];
try {
FileInputStream input = new FileInputStream("Main.java");
ProgressMonitorInputStream in = new ProgressMonitorInputStream(null,"读取java文件",input);
ProgressMonitor p = in.getProgressMonitor(); //获得进度条
while(in.read(b) !=-1) {
String s = new String(b);
System.out.print(s);
Thread.sleep(1000); //由于文件较小,为了看清进度条这里有意延缓1000毫秒
}
}
catch(Exception e) {}
}
}
9.Java 文件锁
经常出现几个程序处理同一个文件的情景,比如同时更新或读取文件。应对这样的问题做出处理,否则可能发生混乱。JDK 1.4版本后,Java提供了文件锁功能,可以帮助解决这样的问题。
FileLock和FileChannel类分别在java.nio和java.nio.channels包中。输入、输出流读写文件时可以使用文件锁,以下结合RandomAccessFile类来说明文件锁的使用方法。
RandomAccessFile创建的流在读写文件时可以使用文件锁,那么只要不解除该锁,其他程序无法操作被锁定的文件。
使用文件锁的步骤如下:
1)先使用RandomAccessFile流建立指向文件的流对象,该对象的读写属性必须是rw,例如:
RandomAccessFile input = new RandomAccessFile("Main.java","rw");
2)input流调用方法getChannel()获得一个连接到底层文件的FileChannel对象(信道),例如:
FileChannel channel = input.getChannel();
3)信道调用tryLock()或lock()方法获得一个FileLock(文件锁)对象,这一过程也称做对文件加锁,例如:
FileLock lock = channel.tryLock();
文件锁对象产生后,将禁止任何程序对文件进行操作或再进行加锁。对一个文件加锁之后,如果想读、写文件必须让FileLock对象调用release()释放文件锁,例如:
lock.release();
例如,Java程序通过每次单击按钮释放文件锁,并读取文件中的一行文本,然后马上进行加锁。当Java程序运行时,用户无法用其他程序来操作被当前Java程序加锁的文件,比如用户使用Windows操作系统提供的“记事本”程序(Notepad.exe)无法保存被当前Java程序加锁的文件。
Main.java
import java.io.*;
public class Main {
public static void main(String args[]) {
File file = new File("Main.java");
WindowFileLock win = new WindowFileLock(file);
win.setTitle("使用文件锁");
}
}
WindowFileLock.java
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class WindowFileLock extends Jframe implements ActionListener {
JTextArea text;
JButton button;
File file;
RandomAccessFile input;
FileChannel channel;
FileLock lock;
WindowFileLock(File f) {
file = f;
try {
input = new RandomAccessFile(file,"rw");
channel = input.getChannel();
lock = channel.tryLock();
}
catch(Exception exp) {}
text = new JTextArea();
button = new JButton("读取一行");
button.addActionListener(this);
add(new JScrollPane(text),BorderLayout.CENTER);
add(button,BorderLayout.SOUTH);
setSize(300,400);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
try {
lock.release();
String lineString = input.readLine();
text.append("\n"+lineString);
lock = channel.tryLock();
if(lineString==null)
input.close();
}
catch(Exception ee) {}
}
}