Java 编程思想(六)Java I/O

转载:java基础学习总结——流

转载:《Java编程思想》学习笔记11——Java I/O

转载:

1. Java I/O和流

Java中使用流来处理程序的输入和输出操作,流是一个抽象的概念,封装了程序数据于输入输出设备交换的底层细节。Java I/O中又将流分为字节流和字符流,字节流主要用于处理诸如图像,音频视频等二进制格式数据,而字符流主要用于处理文本字符等类型的输入输出。



2. 节点流和处理流

节点流类型


节点流就是一根管道直接插到数据源上面,直接读数据源里面的数据,或者是直接往数据源里面写入数据。典型的节点流是文件流:文件的字节输入流(FileInputStream),文件的字节输出流(FileOutputStream),文件的字符输入流(FileReader),文件的字符输出流(FileWriter)。

处理流类型




3.字节输入流InputStream

输入流InputStream负责从各种数据/文件源产生输入,输入源包括:数组,字符串,文件,管道,一系列其他类型的流,以及网络连接产生的流等等。


常用字节输入流的主要类型:
(1).ByteArrayInputStream字节数组输入流
主要功能:允许内存缓存作为输入流。
ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read()方法要提供的下一个字节。
注意:关闭ByteArrayInputStream无效,该类中的方法在关闭此流之后仍可被调用,而不会产生任何的IOException。
(2).FileInputStream文件输入流
主要功能:从文件系统中的某个文件获得输入字节,用于读取诸如图像数据子类的原始字节流。若要读取字符流,请使用FileReader。
(3).PipedInputStream管道输入流
主要功能:和管道输出流一起构成一个输入输出的管道,是管道的数据输入端。
管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。通常,这些数据有某个线程从PipedInputStream对象中读取,并有其他线程将其写入到相应的PipedOutputStream对象中。
注意:不建议PipedInputStream和PipedOutputStream对象使用单线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可以在缓冲区限定范围内将读操作和写操作分离开,如果先连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
(4).SequenceInputStream顺序输入流
重要功能:将两个或多个输入流对象转换为一个单个输入流对象。
SequenceInputStream表示其他输入流的逻辑串联关系,它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止。
(5).FilterInputStream过滤输入流
主要功能:包含其他一些输入流,将这些被包含的输入流用作其基本数据源,它可以直接传输数据或者提供一些额外的功能。
常用的FilterInputStream是DataInputStream数据输入流,主要用于允许程序以与机器无关的方式从底层输入流中读取java基本数据类型。其常用的方法有readInt(),readBoolean(),readChar()等等。

2.字节输出流OutputStream


和字节输入流相对应,字节输出流负责字节类型数据想目标文件或设备的输出。常见的字节输出流如下:
(1). ByteArrayOutputStream字节数组输出流
主要功能:在内存中创建一个缓冲区,将接收到的数据放入该内存缓冲区中。
ByteArrayOutputStream实现了一个输出流,其中的数据被写入一个byte数组中。缓冲区会随着数据的不断写入而自动增长,可使用toByteArray()和toString()方法获取数据。
注意:和ByteArrayInputStream类似,关闭ByteArrayOutputStream也是无效的,此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
(2). FileOutputStream文件输出流
主要功能:将数据写入到指定文件中。
文件输出流是用于将数据写入File或FIleDescriptor的输出流,用于写入诸如图像数据之类的原始字节流,若要写入字符流,请使用FileWriter。
(3). PipedOutputStream管道输出流
主要功能:连接管道输入流用来创建通信管道,管道输出流是管道数据输出端。
(4). FilterOutputStream过滤输出流
主要功能:用于将已存在的输出流作为其基本数据接收器,可以直接传输数据或提供一些额外的处理。
常用的FIlterOutputStream是DataOutputStream数据输出流,它允许程序以适当的方式将java基本数据类型写入输出流中。其常用方法有writeInt(intV),writeChar(int v),writeByte(String s)等等。

3.字符流

Java中得字节流只能针对字节类型数据,即支持处理8位的数据类型,由于java中的是Unicode码,即两个字节代表一个字符,于是在JDK1.1之后提供了字符流Reader和Writer。

字符流相关常用类如下:
(1).Reader
用于读取字符串流的抽象类,子类必须实现的方法只有reader(char[],int, int)和close()。


(2).InputStreamReader
是将字节输入流转换为字符输入流的转换器,它使用指定的字符集读取字节并将其解码为字符。即:字节——>字符。
它使用的字符集可以有名称指定或显式给定,也可以使用平台默认的字符集。


(3).Writer
用于写入字符流的抽象类,子类必须实现的方法只有write(char[],int, int)和close()。


(4).OutputStreamWriter
是将字符输出流转换为字节输出流的转换器,它使用指定的字符集将要写入流的字符编码成字节。即:字符——>字节。

4.综合使用java IO各种流

Java IO中的各种流,很少单独使用,经常结合起来综合使用,既可以满足特定需求,又高效。例子如下:
(1).使用缓冲流读取文件:

import java.io.*;

public class BufferedInputFile{
	public static String read(String filename) throws IOException{
		//缓冲字符输入流
		BufferedReader in = new BufferedReader(new FileReader(filename));
		String s;
		StringBuilder sb = new StringBuilder();
		//每次读取文件中的一行
		While((s = in.readLine()) != null){
			sb.append(s + “\n”);
		}
		in.close();
		return sb.toString();
	}
	public static void main(String[] args) throws IOException{
		System.out.println(read(“BufferedInputFile.java”));
	}
}
(2).读取内存中的字符串:
import java.io.*;

public class MemoryInput{
	public static void main(String[] args) throws IOException{
		//将字符串包装为字符输入流
		StringReader in = new StringReader(
		BufferedInputFile.read(“BufferedInputFile.java”));
	    int c;
		//读取字符输入流中的字符
		while((c == in.read()) != -1){
			System.out.println((char)c);
		}
	}
}
(3).数据输入/输出流:
import java.io.*;

public class DataInputOutput{
	public static void main(String[] args) thows IOException{
		DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(“Data.txt”)));
		out.writeDouble(3.14159);
		out.writeUTF(“That was pi”);
		out.writeDouble(1.41413);
		out.writeUTF(“Square root of 2”);
		out.close();
		DataInputStream in = new DataInputStream(new BufferedInputStream(new FileOutputStream(“Data.txt”)));
		System.out.println(in.readDouble());
		System.out.println(in.readUTF());
		System.out.println(in.readDouble());
		System.out.println(in.readUTF());
	}
}

(4).文本文件输出流:
import java.io.*;  
  
public class TextFileOutput{  
    //输出文件名  
    static String file = “BasicFileOutput.out”;  
    public static void main(String[] args) throws IOException{  
        //将字符串先包装成字符串输入流,然后将字符串输入流再包装成缓冲字符输入流  
	    BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read(“TextFileOutput.java”)));  
        //字符输出流  
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));  
        int lineCount = 1;  
        String s;  
        While((s = in.readLine()) != null){  
	    	out.println(lineCount++ + “: ” + s);  
		}  
		out.close();  
	}  
}  

5. 处理流的使用

缓冲流


带有缓冲区的,缓冲区(Buffer)就是内存里面的一小块区域,读写数据时都是先把数据放到这块缓冲区域里面,减少io对硬盘的访问次数,保护我们的硬盘。可以把缓冲区想象成一个小桶,把要读写的数据想象成水,每次读取数据或者是写入数据之前,都是先把数据装到这个桶里面,装满了以后再做处理。这就是所谓的缓冲。先把数据放置到缓冲区上,等到缓冲区满了以后,再一次把缓冲区里面的数据写入到硬盘上或者读取出来,这样可以有效地减少对硬盘的访问次数,有利于保护我们的硬盘。


转换流


转换流非常的有用,它可以把一个字节流转换成一个字符流,转换流有两种,一种叫InputStreamReader,另一种叫OutputStreamWriter。InputStream是字节流,Reader是字符流,InputStreamReader就是把InputStream转换成Reader。OutputStream是字节流,Writer是字符流,OutputStreamWriter就是把OutputStream转换成Writer。把OutputStream转换成Writer之后就可以一个字符一个字符地通过管道写入数据了,而且还可以写入字符串。我们如果用一个FileOutputStream流往文件里面写东西,得要一个字节一个字节地写进去,但是如果我们在FileOutputStream流上面套上一个字符转换流,那我们就可以一个字符串一个字符串地写进去。




数据流


package cn.gacl.test;

import java.io.*;
public class TestDataStream{
    public static void main(String args[]){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //在调用构造方法时,首先会在内存里面创建一个ByteArray字节数组
        DataOutputStream dos = new DataOutputStream(baos);
        //在输出流的外面套上一层数据流,用来处理int,double类型的数
        try{
            dos.writeDouble(Math.random());//把产生的随机数直接写入到字节数组ByteArray中
            dos.writeBoolean(true);//布尔类型的数据在内存中就只占一个字节
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            System.out.println(bais.available());
            DataInputStream dis = new DataInputStream(bais);
            System.out.println(dis.readDouble());//先写进去的就先读出来,调用readDouble()方法读取出写入的随机数
            System.out.println(dis.readBoolean());//后写进去的就后读出来,这里面的读取顺序不能更改位置,否则会打印出不正确的结果
            dos.close();
            bais.close();
        }catch(Exception e){
                e.printStackTrace();
            }
    }
}

通过bais这个流往外读取数据的时候,是一个字节一个字节地往外读取的,因此读出来的数据无法判断是字符串还是bool类型的值,因此要在它的外面再套一个流,通过dataInputStream把读出来的数据转换就可以判断了。

注意了:读取数据的时候是先写进去的就先读出来,因此读ByteArray字节数组数据的顺序应该是先把占8个字节的double类型的数读出来,然后再读那个只占一个字节的boolean类型的数,因为double类型的数是先写进数组里面的,读的时候也要先读它。这就是所谓的先写的要先读。如果先读Boolean类型的那个数,那么读出来的情况可能就是把double类型数的8个字节里面的一个字节读了出来。 

打印流



对象流


package cn.gacl.test;

import java.io.*;

public class TestObjectIo {
    public static void main(String args[]) {
        T t = new T();
        t.k = 8;// 把k的值修改为8
        try {
            FileOutputStream fos = new FileOutputStream(
                    "D:/java/TestObjectIo.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            // ObjectOutputStream流专门用来处理Object的,在fos流的外面套接ObjectOutputStream流就可以直接把一个Object写进去
            oos.writeObject(t);// 直接把一个t对象写入到指定的文件里面
            oos.flush();
            oos.close();
            FileInputStream fis = new FileInputStream(
                    "D:/java/TestObjectIo.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            // ObjectInputStream专门用来读一个Object的
            T tRead = (T) ois.readObject();
            // 直接把文件里面的内容全部读取出来然后分解成一个Object对象,并使用强制转换成指定类型T
            System.out.print(tRead.i + "\t" + tRead.j + "\t" + tRead.d + "\t"
                    + tRead.k);
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/*
 * 凡是要将一个类的对象序列化成一个字节流就必须实现Serializable接口
 * Serializable接口中没有定义方法,Serializable接口是一个标记性接口,用来给类作标记,只是起到一个标记作用。
 * 这个标记是给编译器看的,编译器看到这个标记之后就可以知道这个类可以被序列化 如果想把某个类的对象序列化,就必须得实现Serializable接口
 */
class T implements Serializable {
    // Serializable的意思是可以被序列化的
    int i = 10;
    int j = 9;
    double d = 2.3;
    int k = 15;
    // transient int k = 15;
    // 在声明变量时如果加上transient关键字,那么这个变量就会被当作是透明的,即不存在。
}

直接实现Serializable接口的类是JDK自动把这个类的对象序列化,而如果实现public interface Externalizable extends Serializable的类则可以自己控制对象的序列化,建议能让JDK自己控制序列化的就不要让自己去控制。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java语言编写的汉诺塔程序,形象直观; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Tower extends Frame implements ActionListener,Runnable { HannoiTower tower=null; Button renew,auto=null; char towerName[]={'A','B','C'}; int 盘子数目,盘宽,盘高; Thread thread; TextArea 信息条=null; public Tower() { thread=new Thread(this); 盘子数目=5; 盘宽=80; 盘高=18; 信息条=new TextArea(12,12); 信息条.setText(null); tower=new HannoiTower(盘子数目,盘宽,盘高,towerName,信息条); renew=new Button("重新开始"); auto=new Button("自动演示搬盘子"); renew.addActionListener(this); auto.addActionListener(this); add(tower,BorderLayout.CENTER); add(renew,BorderLayout.SOUTH); add(auto,BorderLayout.NORTH); add(信息条,BorderLayout.EAST); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); setVisible(true); setBounds(60,20,670,540); validate(); } public void actionPerformed(ActionEvent e) { if(e.getSource()==renew) { if(!(thread.isAlive())) { this.remove(tower); 信息条.setText(null); tower=new HannoiTower(盘子数目,盘宽,盘高,towerName,信息条); add(tower,BorderLayout.CENTER); validate(); } else { } } if(e.getSource()==auto) { if(!(thread.isAlive())) { thread=new Thread(this); } try { thread.start(); } catch(Exception eee) { } } } public void run() { this.remove(tower); 信息条.setText(null); tower=new HannoiTower(盘子数目,盘宽,盘高,towerName,信息条); add(tower,BorderLayout.CENTER); validate(); tower.自动演示搬运盘子(盘子数目,towerName[0] ,towerName[1],towerName[2]); } public static void main(String args[]) { new Tower(); } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值