javaIO体系
主要分为两种,一种为读取字符的Reader/Writer,一种为读取字节的Input/OutputStream。下面对各个类进行详细介绍
FileInputStream/FileOutputStream
以文件作为数据输入源的数据流。或者说是打开文件,从文件读数据到内存的类。
FileInputStream可以使用read()方法一次读入一个字节,并以int类型返回,或者是使用read()方法时读入至一个byte数组,byte数组的元素有多少个,就读入多少个字节。在将整个文件读取完成或写入完毕的过程中,这么一个byte数组通常被当作缓冲区,因为这么一个byte数组通常扮演承接数据的中间角色。
public class FileInputStreamTest {
public static void main(String[] args) {
try{
File fin=new File("d:/root/FileInput.txt");
FileInputStream in=new FileInputStream(fin);
int n=512;
byte[] buffer=new byte[n];
while((in.read(buffer, 0, n)!=-1)&&(n>0)){
System.out.println(new String(buffer));
}
}catch(IOException IOe){
System.out.println(IOe.toString());
}
}
}
ByteArrayOutputStream/ByteArrayIntputStream
是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区,然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出byte型数据。在网络传输中我们往往要传输很多变量,我们可以利用ByteArrayOutputStream把所有的变量收集到一起,然后一次性把数据发送出去。具体用法如下:
ByteArrayOutputStream: 可以捕获内存缓冲区的数据,转换成字节数组。ByteArrayInputStream: 可以将字节数组转化为输入流
ByteArrayInputStream类有两个默认的构造函数:
ByteArrayInputStream(byte[] b): 使用一个字节数组当中所有的数据做为数据源,程序可以像输入流方式一样读取字节,可以看做一个虚拟的文件,用文件的方式去读取它里面的数据。
ByteArrayInputStream(byte[] b,int offset,int length): 从数组当中的 第offset开始,一直取出length个这个字节做为数据源。
ByteArrayOutputStream类也有两个默认的构造函数:
ByteArrayOutputStream(): 创建一个32个字节的缓冲区
ByteArrayOutputStream(int): 根据参数指定大小创建缓冲区
这两个构造函数创建的缓冲区大小在数据过多的时候都会自动增长,如果创建缓冲区以后,程序就可以把它像虚拟文件一样似的往它里面写入内容,当写完内容以后调用ByteArrayOutputStream()的方法就可以把其中的内容当作字节数组返回。
public class ByteArrayInputStreamTest {
public static void main(String[] args){
int a=0,b=1,c=2;
ByteArrayOutputStream out=new ByteArrayOutputStream();
out.write(a);
out.write(b);
out.write(c);
byte[] byteArray=out.toByteArray();
for(int i=0;i<byteArray.length;i++){
System.out.println(byteArray[i]);
}
int x;
ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
while((x=in.read())!=-1){
System.out.println(x);
}
}
}
计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样 可以减少访问硬盘的次数,提高传输效率。
BufferedInputStream:当向缓冲流写入数据时候,数据先写到缓冲区,待缓冲区写满后,系统一次性将数据发送给输出设备。
BufferedOutputStream :当从向缓冲流读取数据时候,系统先从缓冲区读出数据,待缓冲区为空时,系统再从输入设备读取数据到缓冲区。
public class BufferedInputStreamTest {
public static void main(String[] args){
try{
BufferedInputStream bin=new BufferedInputStream(
new FileInputStream("d:/root/bufferedInput.txt" ));
int n=512;
byte[] buffer=new byte[n];
while((bin.read(buffer, 0, n)!=-1)&&(n>0)){
System.out.println(new String(buffer));
}
}catch(IOException IOe){
System.out.println(IOe.toString());
}
}
}
DataOutputStream/DataInputStream
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后应用程序可以使用数据输入流将数据读入DataInputStream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。对于多线程访问不一定是安全的。 线程安全是可选的,它由此类方法的使用者负责。
包装类DataOutputStream、DataInputStream为我们提供了多种对文件的写入和读取方法,如writeBoolean(),writeUTF(),writeChar,writeByte(),writeDouble()等,和对应的read方法,这些方法极大的方便了我们的写入和读取操作。
public class DataInputStreamTest {
public static void main(String[] args){
try{
String path="d:/root/datasteam.txt";
DataOutputStream dos = new DataOutputStream(
new FileOutputStream(path));
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeInt(1000);
dos.writeBytes("世界");
/*按2字节写入,都是写入的低位 ,如程序中注释所说,对于出现汉字字符的情况不能用wri
* teBytes(),这会在写入文件时丢弃汉字字符的第一个字节从而在读取时出现错误。
*/
dos.writeChars("世界"); // 按照Unicode写入
dos.writeUTF("世界"); // 按照UTF-8写入(UTF8变长,开头2字节是由writeUTF函数写入的长度信息,方便readUTF函数读取)
dos.flush();
DataInputStream dis=new DataInputStream(
new FileInputStream(path));
double d=dis.readDouble();// 先写的先被读出来
System.out.println(d);
boolean bool=dis.readBoolean();
System.out.println(bool);
int x=dis.readInt();
System.out.println(x);
byte[] b=new byte[2];
dis.read(b);
System.out.println(new String(b, 0, 2));
char[] c = new char[2];
for (int i = 0; i < 2; i++){
c[i] = dis.readChar();
}
System.out.println(new String(c, 0, 2));
System.out.println(dis.readUTF());
dis.close();
}catch(IOException IOe){
System.out.println(IOe.toString());
}
}
}
ObjectOutputStream/ObjectInputStream
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。 ObjectOutputStream和ObjectInputStream处理的对象必须实现Serializable接口
public class Student implements Serializable{
private int id;
public String name;
private int age;
public Student(int id,String name,int age){
this.id=id;
this.name=name;
this.age=age;
}
public String toString() {
return "名字:" + name + ", id:" + id + ", age:" + age;
}
}
public class ObjectInputStreamTest {
/*
* ObjectOutputStream和ObjectInputStream处理的对象必须实现Serializable接口
*/
public static void main(String[] args){
Student stu1 = new Student(1, "Mankind", 20);
Student stu2 = new Student(2, "xxx", 33);
try{
String path="d:/root/student.txt";
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream(new File(path)));
os.writeObject(stu1);
os.writeObject(stu2);
os.close();
ObjectInputStream is = new ObjectInputStream(
new FileInputStream(new File(path)));
System.out.println((Student)is.readObject());
System.out.println((Student)is.readObject());
is.close();
}catch(Exception IOe){
System.out.println(IOe.toString());
}
}
}
SequenceInputStream
SequenceInputStream合并流,将与之相连接的流集组合成一个输入流并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 合并流的作用是将多个源合并合一个源。 可接收枚举类所封闭的多个字节流对象。
public class SequenceInputStreamTest {
public static void main(String[] args){
SequenceInputStream sis = null;
BufferedOutputStream bos = null;
try{
Vector<InputStream> vector = new Vector<InputStream>();
vector.addElement(new FileInputStream("d:/root/seqtest1.txt"));
vector.addElement(new FileInputStream("d:/root/seqtest2.txt"));
vector.addElement(new FileInputStream("d:/root/seqtest3.txt"));
Enumeration<InputStream> e = vector.elements();
sis = new SequenceInputStream(e);
bos = new BufferedOutputStream(
new FileOutputStream("d:/root/seqtest4.txt"));
byte[] buf = new byte[1024];
int len = 0;
while ((len = sis.read(buf)) != -1) {
bos.write(buf, 0, len);
bos.flush();
}
}catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if (sis != null)
sis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
PipedOutputStream/PipedInputStream
在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流.它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。 管道流内部在实现时还有大量的对同步数据的处理。管道输出流和管道输入流执行时不能互相阻塞,所以一般要开启独立线程分别执行
public class ReadThread implements Runnable{
private PipedInputStream pin;
ReadThread(PipedInputStream pin) //
{
this.pin=pin;
}
public void run() //由于必须要覆盖run方法,所以这里不能抛,只能try
{
try
{
sop("R:读取前没有数据,阻塞中...等待数据传过来再输出到控制台...");
byte[] buf = new byte[1024];
int len = pin.read(buf); //read阻塞
sop("R:读取数据成功,阻塞解除...");
String s= new String(buf,0,len);
sop(s); //将读取的数据流用字符串以字符串打印出来
pin.close();
}
catch(Exception e)
{
throw new RuntimeException("R:管道读取流失败!");
}
}
public static void sop(Object obj) //打印
{
System.out.println(obj);
}
}
public class WriteThread implements Runnable{
private PipedOutputStream pout;
WriteThread(PipedOutputStream pout) {
this.pout= pout;
}
public void run() {
try{
sop("W:开始将数据写入:但等个5秒让我们观察...");
Thread.sleep(5000); //释放cpu执行权5秒
pout.write("W:写入数据".getBytes()); //管道输出流
pout.close();
}
catch(Exception e) {
throw new RuntimeException("W:WriteThread写入失败...");
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
public class PipedInputStreamTest {
public static void main(String[] args) throws Exception{
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream();
pin.connect(pout); //输入流与输出流连接
ReadThread readTh = new ReadThread(pin);
WriteThread writeTh = new WriteThread(pout);
new Thread(readTh).start();
new Thread(writeTh).start();
}
}
BufferedWriter/BufferedReader
BufferedWriter 和 BufferedReader 为带有默认缓冲的字符输出输入流,因为有缓冲区所以很效率比没有缓冲区的很高。
public class BufferedWriterTest {
public static void main(String[] args) {
FileReader fr=null;
FileWriter fw=null;
BufferedReader bufr=null;
BufferedWriter bufw=null;
try{
fw=new FileWriter("d:/root/Buffered.txt");
bufw = new BufferedWriter(fw);
bufw.write("hello world !");
bufw.newLine();
bufw.write("hello world !");
System.out.println("写入成功");
}catch(IOException ex){
ex.printStackTrace();
}finally{
try{
if(bufw!=null){
bufw.flush();
bufw.close();
}
}catch(IOException ex){
ex.printStackTrace();
}
}
try{
fr=new FileReader("d:/root/Buffered.txt");
bufr=new BufferedReader(fr);
String line = null;
while((line = bufr.readLine()) != null){
System.out.println(line);
}
}catch(IOException IOe){
IOe.printStackTrace();
}finally{
try{
if(bufr!=null){
bufr.close();
}
}catch(IOException IOe){
IOe.printStackTrace();
}
}
}
}
CharArrayReader/CharArrayWriter
CharArrayReader和CharArrayWriter是字符数组流。它和ByteArrayInputStream、ByteArrayOutputStream类似,只不过ByteArrayXXputStream是字节数组流,而CharArrayXX是字符数组流。CharArrayWriter是用于读取字符数组,它继承于Writer类。以字符为单位进行数组的操作。
public class CharArrayWriterTest {
private static final char[] arrayLetters = new char[]
{'a','b','c','d','e','f','g','h','i','j','k','l',
'm','n','o','p','q','r','s','t','u','v',
'w','x','y','z'};
public static void main(String[] args){
CharArrayWriter caw;
try{
caw = new CharArrayWriter();
caw.write("A");
System.out.printf("caw=%s\n", caw);
caw.write("BC");
System.out.printf("caw=%s\n", caw);
caw.write(arrayLetters,3,5);
System.out.printf("caw=%s\n", caw);
caw.append('0').append("123456789").append(String.valueOf(arrayLetters), 8, 12);
System.out.printf("caw=%s\n", caw);
int size = caw.size();
System.out.printf("size=%s\n", size);
char[] buf = caw.toCharArray();
System.out.printf("buf=%s\n", String.valueOf(buf));
CharArrayWriter caw2 = new CharArrayWriter();
caw.writeTo(caw2);
System.out.printf("caw2=%s\n", caw2);
CharArrayReader car=new CharArrayReader(caw.toCharArray());
int ch;
while((ch=car.read())!=-1){
System.out.println((char)ch);
}
car.reset();
char[] dest=new char[10];
car.read(dest,0,10);
System.out.println(dest);
}catch(IOException IOe){
IOe.getMessage();
}
}
}
FileWriter/FileReader
创建一个可以往文件中写入字符数据的字符流输出流对象,创建时必须明确文件的目的地,如果文件不存在,这回自动创建。如果文件存在,则会覆盖。当路径错误时会抛异常,当在创建时加入true参数,会实现对文件的续写。
public class FileWriterTest {
public static void main(String[] aags) throws IOException{
FileWriter fw = new FileWriter("d:/root/filewriter.txt",false);
fw.write("Hello world!");
fw.write("\n");
fw.write("MM");
fw.flush();
fw.close();
FileReader fr=new FileReader("d:/root/filewriter.txt");
/*
* int read(); // 读取单个字符。返回作为整数读取的字符,如果已达到流末尾,则返回 -1。
* int read(char []cbuf);//将字符读入数组。返回读取的字符数。如果已经到达尾部,则返回-1。
*/
int ch = 0;
while((ch = fr.read()) != -1){
System.out.print((char)ch);
}
fr.close();
}
}
InputStreamReader/OutputStreamWriter
InputStreamReader是字节转字符的流(字节流通向字符流的桥梁),OutputStreamWriter是字符转字节的流(字符流通向字节流的桥梁)。
public class InputStreamWriterTest {
public static void main(String[] args) throws IOException{
FileInputStream fis=new FileInputStream("d:/root/a.txt");
InputStreamReader isr=new InputStreamReader(fis);
FileOutputStream fos=new FileOutputStream("d:/root/b.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos);
int len=0;
while((len=isr.read())!=-1){
osw.write(len);
System.out.print((char)len);
}
isr.close();
fis.close();
osw.close();
fos.close();
}
}
PrintWriter
PrintWriter是一种过滤流,也叫处理流。也就是能对字节流和字符流进行处理
print() 方法等同于 write() 方法
println() 方法是在 print() 的基础上多调用了一个 newLine() 方法(私有方法)
而 newLine() 方法会调用 flush()
public class PrintWriterTest {
public static void main(String[] args) {
PrintWriter pw = null;
String name = "Mankind";
int age = 20;
float score = 100.0f;
char sex = '男';
try{
pw = new PrintWriter(new FileWriter(new File("d:/root/last.txt")),true);
pw.printf("姓名:%s;年龄:%d;性别:%c;分数:%5.2f;", name,age,sex,score);
pw.println();
pw.println("多多指教");
pw.write(name.toCharArray());
}catch(IOException e){
e.printStackTrace();
}finally{
pw.close();
}
}
}
PipedWriter 是字符管道输出流,它继承于Writer。PipedReader 是字符管道输入流,它继承于Writer。
PipedWriter和PipedReader的作用是可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedWriter和PipedReader配套使用。
@SuppressWarnings("all")
/**
* 接收者线程
*/
public class Receiver extends Thread {
// 管道输入流对象。
// 它和“管道输出流(PipedWriter)”对象绑定,
// 从而可以接收“管道输出流”的数据,再让用户读取。
private PipedReader in = new PipedReader();
// 获得“管道输入流对象”
public PipedReader getReader(){
return in;
}
@Override
public void run(){
//readMessageOnce() ;
readMessageContinued() ;
}
// 从“管道输入流”中读取1次数据
public void readMessageOnce(){
// 虽然buf的大小是2048个字符,但最多只会从“管道输入流”中读取1024个字符。
// 因为,“管道输入流”的缓冲区大小默认只有1024个字符。
char[] buf = new char[2048];
try {
int len = in.read(buf);
System.out.println(new String(buf,0,len));
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 从“管道输入流”读取>1024个字符时,就停止读取
public void readMessageContinued(){
int total=0;
while(true) {
char[] buf = new char[1024];
try {
int len = in.read(buf);
total += len;
System.out.println(new String(buf,0,len));
// 若读取的字符总数>1024,则退出循环。
if (total > 1024)
break;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("all")
/**
* 发送者线程
*/
public class Sender extends Thread {
// 管道输出流对象。
// 它和“管道输入流(PipedReader)”对象绑定,
// 从而可以将数据发送给“管道输入流”的数据,然后用户可以从“管道输入流”读取数据。
private PipedWriter out = new PipedWriter();
// 获得“管道输出流”对象
public PipedWriter getWriter(){
return out;
}
@Override
public void run(){
//writeShortMessage();
writeLongMessage();
}
// 向“管道输出流”中写入一则较简短的消息:"this is a short message"
private void writeShortMessage() {
String strInfo = "this is a short message" ;
try {
out.write(strInfo.toCharArray());
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 向“管道输出流”中写入一则较长的消息
private void writeLongMessage() {
StringBuilder sb = new StringBuilder();
// 通过for循环写入1020个字符
for (int i=0; i<102; i++)
sb.append("0123456789");
// 再写入26个字符。
sb.append("abcdefghijklmnopqrstuvwxyz");
// str的总长度是1020+26=1046个字符
String str = sb.toString();
try {
// 将1046个字符写入到“管道输出流”中
out.write(str);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class PipedWriterTest {
public static void main(String[] args) {
Sender t1 = new Sender();
Receiver t2 = new Receiver();
PipedWriter out = t1.getWriter();
PipedReader in = t2.getReader();
try {
//管道连接。下面2句话的本质是一样。
//out.connect(in);
in.connect(out);
t1.start();
t2.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}