如图1所示,程序在运行期间,可能需要从外部的存储媒介或其他程序中读入所需要的数据,这就需要使用输入流。另一方面,程序在处理数据后,可能就需要将处理的结果写入到永久的存储媒介或传送给其他的应用程序,这就需要使用输出流。
特别注意:。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
2,字符流和字节流的区别
2.1 IO流的分类
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
2.2字符流和字节流
字符流的由来:因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8位)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片,AVI等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论:。优先选用字节流首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
3,文件类:在整理IO流的知识前,我们需要先了解往往作为流起点或者终点的文件类特别注意的是,文件类的对象主要用来获取文件本身的一些信息,例如文件所在的目录,文件的长度和文件读写权限等,不涉及对文件的读写操作。
创建一个文件对象的构造方法有三种:
File(String filename);//利用文件名
File(String directoryPath,String filename);//利用文件的路径和文件名
File(File dir,String filename);//利用文件所在的目录和文件名
注意使用第三种构造方法时,文件被认为与当前应用程序在同一目录中。
另外也注意到,三种构造方法都需要使用到的字符串型参数 - 文件名。
4,目录
4.1目录的创建:文件对象调用方法public boolean mkdir()方法创建一个目录,如果创建成功返回true,反之返回false(如果该目录已经存在)。
4.2列出目录中的文件如果文件对象是一个目录,那么该对象调用以下方法列出该目录下的文件和子目录。
·public String [] list()用字符串形式返回目录下的全部文件。
·public File [] listFile()用File对象形式返回目录下的全部文件。
5,文件字节输入流(InputStream的使用类的子类:的FileInputStream类(文件字节输入流))
5.1 使用输入流通常包括以下四个基本步骤:
·设定输入流的源
·创建指向源的输入流
·让输入流读取源中的数据
·关闭输入流
5.2 构造FileInputStream类的对象
FileInputStream(String name)
FileInputStream(File file)
第一个构造方法使用给定名、第二个构造方法使用File对象创建FileInputStream流,其中参数name和File指定的文件成为输入流的源。
特别需要注意,FileInputStream输入流指定的文件可能不存在,出现I/O错误(Java使用IOException(IO异常)对象来表示这个出错信号)。因此程序必须在try-catch语句块中的try部分创建输入流(FileInputStream),在catch部分中检测并处理这个异常。
注意:当使用父类InputStream类声明FileInputStream对象时(即FileInputStream的上转型对象),可以不用try-catch语句块。
5.3使用输入流读取字节
文件字节流可以调用从父类继承的read方法顺序地读取文件,只要不关闭流,每次调用read方法就顺序地读取文件中的其余内容,直到文件的末尾或文件字节输入流被关闭。
字节输入流(InputStream)的read方法以字节为单位读取源中的数据。
·int read() 输入流调用该方法从源中读取单个字节的数据,该方法返回字节值,如果未读出就返回-1。
·int read(byte b[]) 输入流调用该方法从源中读取b.length个字节的数据,该方法返回实际读取的字节数目,如果未读出就返回-1。
·int read(byte b[],int off,int len) 输入流调用该方法从源中读取len个字节的数据到字节数组b中,该方法返回实际读取的字节数目,如果未读出就返回-1,参数off指定开始读取数据的偏移量。
import java.io.*;
public class Example10_4 {
public static void main(String args[]) {
int n=-1;
byte [] a=new byte[100];
try{ File f=new File("Example10_4.java");
InputStream in = new FileInputStream(f);
while((n=in.read(a,0,100))!=-1) {
String s=new String (a,0,n);
System.out.print(s);
}
in.close();
}
catch(IOException e) {
System.out.println("File read Error"+e);
}
}
}
需要特别注意的是,当把字节转化为字符串时,要把实际读入的字节转化为字符串。
5.4 关闭流
使用close()方法显式地关闭打开的流,如果没有关闭那些被打开的流,那么就不可能不允许另一个程序操作这些流所用的资源。
6、文件字节输出流(使用OutputStream类的子类:FileOutputStream类(文件字节输出流))
6.1 使用输出流通常包括以下四个基本步骤:
·设定输出流的目的地
·创建指向目的地的输出流
·让输出流读取目的地中的数据
·关闭输出流
6.2 构造FileOutputStream类的对象
FileOutputStream(String name);
FileOutputStream(File file);
第一个构造方法使用给定名、第二个构造方法使用File对象创建FileOutputStream流,其中参数name和File指定的文件成为目的地。
特别需要注意,FileOutputStream输出流指向的文件可能不存在,出现I/O错误(Java使用IOException(IO异常)对象来表示这个出错信号)。因此程序必须在try-catch语句块中的try部分创建输出流(FileOutputStream),在catch部分中检测并处理这个异常。
可以使用FileOutputStream类的下列能选择是否具有刷新功能的构造方法创建指向文件的输出流。
FileOutputStream(String name,boolean append);
FileOutputStream(File file,boolean append);
其中参数append取值为true,输出流不会刷新所指向的文件,输出流write方法将从文件的末尾开始向文件写入数据,若参数append去false,输出流将刷新所指向的文件。
注意:当使用父类OutputStream类声明FileOutputStream对象时(即FileOutputStream的上转型对象),可以不用try-catch语句块。
6.3 使用输出流写出字节
文件字节流可以调用从父类继承的write方法顺序地写文件,只要不关闭流,每次调用write方法就顺序地向文件写入内容,直到文件字节输出流被关闭。
字节输入流(InputStream)的read方法以字节为单位读取源中的数据。
·void write() 输出流调用该方法向目的文件写入单个字节的数据。
·void write(byte b[]) 输出流调用该方法向目的地写入一个字节数组b。
·void write(byte b[],int off,int len) 给定字节数组b中起始于偏移量off处取len个字节写入目的地。
·void close() 关闭输出流
6.4 显式关闭输出流。
import java.io.*;
public class Example10_5 {
public static void main(String args[]) {
byte [] a = "新年快乐".getBytes();
byte [] b = "Happy New Year".getBytes();
File file = new File("a.txt"); //输出的目的地
try{
OutputStream out=new FileOutputStream(file); //指向目的地的输出流
System.out.println(file.getName()+"的大小:"+file.length()+"字节");//a.txt的大小:0字节
out.write(a); //向目的地写数据
out.close();
out=new FileOutputStream(file,true); //准备向文件尾加内容
System.out.println(file.getName()+"的大小:"+file.length()+"字节");///a.txt的大小:8字节
out.write(b,0,b.length);
System.out.println(file.getName()+"的大小:"+file.length()+"字节");///a.txt的大小:8字节
out.close();
}
catch(IOException e) {
System.out.println("Error "+e);
}
}
}
7、文件字符输入、输出流(使用Reader、Write类的子类FileReader、FileWrite(文件字符输入、输出流))
7.1 字节流存在不能很好地操作Unicode字符,因此,如果使用字节流,读取不当会出现“乱码”的现象。
7.2 构造方法和参数append的使用与字节流类似,本处就不赘述。
7.3 对于Write流,write方法将数据首先写入到缓冲区,每当缓冲区溢出时,缓冲区的内容被自动写入到目的地,如果关闭流,缓冲区的内容会立刻被写入到目的地。流调用flush()方法可以立刻冲洗当前缓冲区,即将当前缓冲区的内容写入到目的地。
import java.io.*;
public class Example10_6 {
public static void main(String args[]) {
File sourceFile = new File("a.txt"); //读取的文件
File targetFile = new File("b.txt"); //写入的文件
char c[] =new char[19]; //char型数组
try{
Writer out = new FileWriter(targetFile,true); //指向目的地的输出流
Reader in = new FileReader(sourceFile); //指向源的输入流
int n = -1;
while((n=in.read(c))!=-1) {
out.write(c,0,n);
}
out.flush();
out.close();
}
catch(IOException e) {
System.out.println("Error "+e);
}
}
}
8、缓冲流(高级、上层流:BufferedReader、BufferedWrite类)
8.1 缓冲流能够增强读写文件的能力,需要注意的是,两者的源和目的地必须是字符输入流和字符输出流,
8.2 构造方法
BufferedReader(Reader in);
BufferedWrite(Write out);
BufferedReader流能够读取文本行,方法是readLine();
BufferedWrite流有一个独特的向文件写入一个回行符的方法:newLine();
import java.io.*;
import java.util.*;
public class Example10_7 {
public static void main(String args[]) {
File fRead = new File("english.txt");
File fWrite = new File("englishCount.txt");
try{ Writer out = new FileWriter(fWrite);
BufferedWriter bufferWrite = new BufferedWriter(out);
Reader in = new FileReader(fRead);
BufferedReader bufferRead =new BufferedReader(in);
String str = null;
while((str=bufferRead.readLine())!=null) {
StringTokenizer fenxi = new StringTokenizer(str);
int count=fenxi.countTokens();
str = str+" 句子中单词个数:"+count;
bufferWrite.write(str);
bufferWrite.newLine();
}
bufferWrite.close();
out.close();
in = new FileReader(fWrite);
bufferRead =new BufferedReader(in);
String s=null;
System.out.println(fWrite.getName()+"内容:");
while((s=bufferRead.readLine())!=null) {
System.out.println(s);
}
bufferRead.close();
in.close();
}
catch(IOException e) {
System.out.println(e.toString());
}
}
}
9、随机流(RandomAccessFile类流)
9.1 RandomAccessFile类创建的流的指向既可以作为流的源,也可以作为流的目的地,换句话说,当准备对一个文件进行读写操作时,创建一个指向该文件的随机流即可,这样既可以从这个流中读取文件的数据,也可以通过这个流写入数据到文件中。
以下是RandomAccessFile的类的两个构造方法:
RandomAccessFile(String name,String mode);
RandomAccessFile(File file,String mode);
参数名,文件给出流的源,也是流向的目的地,参数模式取R(只读)或RW(可读写),决定流对文件的访问权限。
特别注意:RandomAccessFile流指向文件时,不刷新文件。
import java.io.*;
public class Example10_9 {
public static void main(String args[]) {
RandomAccessFile in=null;
try{ in=new RandomAccessFile("Example10_9.java","rw");
long length=in.length(); //获取文件的长度
long position=0;
in.seek(position); //将读取位置定位到文件的起始
while(position
10,数组流(ByteArrayInputStream的和ByteArrayOutputStream(字节数组输入流,字节数组输出流))
10.1流的源和目的地除了可以是文件外,还可以是计算机内存。
import java.io.*;
public class Example10_10 {
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){}
}
}
11,数据流(DataInputStream类和DataOutputStream类)
11.1它们允许程序按着机器无关的风格读取Java原始数据。也就是说,读取一个数据时,不必再关心这个数值应当是多少个字节。
DataInputStream(InputStream in);
DataOutputStream(OutputSteam out);
import java.io.*;
public class Example10_11 {
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){}
}
}
11,对象流(ObjectInputStream的和ObjectOutputStrea)
11.1构建方法类似数据流
序列化:将对象的状态和信息转化为可存储的形式的过程。
11.2当使用对象流写入或读入对象时,要保证对象是序列化的,这是为了保证能把对象写入到文件,并能再把对象正确读回到程序中。
import java.io.*;
public class Example10_13 {
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);
}
}
}
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;
}
}
12,序列化与对象克隆
12.1使用对象流很容易获取一个序列化对象的克隆,只需将该对象写入对象输出流指向的目的地,然后将该目的地作为一个对象输入流的源,那么该对象输入流从源中读回的对象一定是原对象的一个克隆,即对象输入流通过对象的序列化信息来得到当前对象的一个克隆。
12.2当程序想以较快的速度得到一个对象的克隆时,可以用对象流将对象的序列化信息写入内存,而不是写入到磁盘文件中。对象流将数组流作为底层流就可以将对象的序列化信息写入内存。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
public class Example10_14 {
public static void main(String args[]) {
MyWin win=new MyWin();
}
}
class MyWin extends JFrame implements ActionListener {
JLabel label=null;
JButton 读入=null,写出=null;
ByteArrayOutputStream out = null;
MyWin() {
setLayout(new FlowLayout());
label=new JLabel("How are you");
读入=new JButton("读入对象");
写出=new JButton("写出对象");
读入.addActionListener(this);
写出.addActionListener(this);
setVisible(true);
add(label);
add(写出);
add(读入);
setSize(500,400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
validate();
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==写出) {
try{ out = new ByteArrayOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(out);
objectOut.writeObject(label);
objectOut.close();
}
catch(IOException event){}
}
else if(e.getSource()==读入) {
try{ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream objectIn = new ObjectInputStream(in);
JLabel temp=(JLabel)objectIn.readObject();
temp.setText("你好");
this.add(temp);
this.validate();
objectIn.close();
}
catch(Exception event){}
}
}
}