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(); } }
1. Bruce Eckel. "Thinking in Java" (4th Edition). Prentice Hall, 2006. 2. James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. "The Java Language Specification" (3rd Edition). Addison-Wesley Professional, 2005. 3. Herbert Schildt. "Java: A Beginner's Guide" (6th Edition). McGraw-Hill Education, 2014. 4. Cay S. Horstmann and Gary Cornell. "Core Java" (Volume 1 and 2). Prentice Hall, 2013. 5. Joshua Bloch. "Effective Java" (2nd Edition). Addison-Wesley Professional, 2008. 6. Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, and Doug Lea. "Java Concurrency in Practice". Addison-Wesley Professional, 2006. 7. Elliotte Rusty Harold. "Java I/O" (2nd Edition). O'Reilly Media, 2006. 8. Doug Lea. "Concurrent Programming in Java: Design Principles and Pattern" (2nd Edition). Addison-Wesley Professional, 2000. 9. Martin Fowler. "Refactoring: Improving the Design of Existing Code" (2nd Edition). Addison-Wesley Professional, 2018. 10. Bruce Tate, Justin Gehtland, and Erik Hatcher. "Better, Faster, Lighter Java" (1st Edition). O'Reilly Media, 2004. 文翻译: 1. Bruce Eckel. "Java编程思想" (第4版). 机械工业出版社, 2008. 2. James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. "Java语言规范" (第3版). 机械工业出版社, 2006. 3. Herbert Schildt. "Java程序设计教程" (第6版). 机械工业出版社, 2015. 4. Cay S. Horstmann and Gary Cornell. "Java核心技术" (卷1和2). 机械工业出版社, 2013. 5. Joshua Bloch. "Effective Java" (第2版). 机械工业出版社, 2009. 6. Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, and Doug Lea. "Java并发编程实践". 机械工业出版社, 2009. 7. Elliotte Rusty Harold. "Java I/O" (第2版). 国电力出版社, 2014. 8. Doug Lea. "Java并发编程:设计原则与模式" (第2版). 机械工业出版社, 2009. 9. Martin Fowler. "重构:改善既有代码的设计" (第2版). 人民邮电出版社, 2019. 10. Bruce Tate, Justin Gehtland, and Erik Hatcher. "轻量级Java企业应用开发" (第1版). 机械工业出版社, 2005.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值