------- android培训、java培训、期待与您交流! ----------
java IO流操作
流可以理解为计算机数据之间的流动,按照流动的方向,可以理解为cpu读取的数据流,我们称为输入流,还有cpu写入的数据流,我们称为输出流,在细致的划分的话,我们可以将输入流分为字节输入流和字符输入流,输出流分为字节输出流和字符输出流,从字面上来理解,字节流是用来处理原始二进制数据的,字符流是用来处理经编码处理后的字符数据的,也就是文本数据,我们如果想要读取文本文件的话,可以使用字节流也可以使用字符流,但是如果想要读取图片,视频,音频,就得用字节流了,流读取文件的源可以是从文件(本地硬盘),内存,键盘输入,网络等,
输出目的可以是硬盘,内存,显示器,打印机,还用java中的控制台等.
javaIO这一章主要介绍io流常用的几个类,这些类位于java.io包中
这一章我认为难点在于大量的类,你得掌握清楚各个类之间的继承关系,知道在什么时候使用哪些类,每个类具体的特色有哪些
1.先介绍以下字符输入流和字符输出流
java中字符流虽然有很多,但是常用的有以下三类
用于字符流输入和输出的
Reader
|----------BufferedReader
|----------FileReader
|----------InputStreamReader
Writer
|----------BufferedWriter
|----------FileWriter
|----------OutputStreamWriter
Reader 用于读取字符流的抽象类,子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能
BufferedReader :可用于提高读取字符流的效率
FileReader :可用于读取硬盘中的文件
InputStreamReader :可将字节流转换成字符流读入
Writer 写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能
BufferedWriter :可用于提高写入字符流的效率
FileWriter :可以将文件写入硬盘中
OutputStreamWriter :可将字节流转换成字符写出
实例1
/*
*用于测试文件读入与写出的小程序
*
*/
import java.io.*;
public class TestFileReadWriter {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("G:\\javaProject\\biXinagDong\\src\\TestFileReadWriter.java");
//因为在java中\被认为是转义字符所以要写\\,但是在unix系统下这样写就不行了,为了提高程序的可移植性,可以将\\替换成File。separator,这样的话
//无论是Windows还是Unix都不会出错
/* int i = 0;
while((i = fr.read()) != -1) {
System.out.print((char)i);
} //第一种方法读一次,打印一次
*/ char[] c = new char[1024]; //初始化字符数组大小为1024个字符
fw = new FileWriter("G:\\testIO\\TestFileReadWriter.java");
int i = 0;
while(( i = fr.read(c, 0, c.length)) != -1) {
//System.out.println(c);
fw.write(String.copyValueOf(c, 0, i));
//fw.write(c, 0, i);
}
//第二种方法,将数据读入指定的数组中,再将数组打印出来
//fw.write(c, 0, c.length);
//fw.write(String.copyValueOf(c, 0, i));
} catch (IOException e) {
throw new RuntimeException("指定的文件未找到");
} finally {
try {
if(fr != null) {
fr.close();
fr = null;
}
if(fw != null) {
fw.close();
fw = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这段小程序最后在关闭写入流时,内部自动调用了flush()方法,因为 Writer 的子类是用来写入字符的,他不能一个一个字节的写入,所以暂时将数据保存在流中,最后一次性写出,就
不会出错
BufferedReader 与 BufferedWriter
这两个类是为了提高效率而存在的
BufferedReader 特有的方法:public String readLine()
BufferedWriter 特有的方法:public void newLine()
像文件分隔符一样windows和Unix是以不同的字符作为终止符windows是以\r和\n作为结束行标志,而Unix是以\n作为结束标志 newLine()方法是跨平台的
实例2
/*
*用于测试BufferedReader和BufferedWriter的方法
*
*/
import java.io.*;
public class TestBufferedWriteReader {
public static int COUNT;
public static void main(String[] args) {
int b = 0;
FileReader fr = null;
FileWriter fw = null;
BufferedReader br = null;
BufferedWriter bw = null;
try {
fr = new FileReader("G:\\javaProject\\biXinagDong\\src\\TestBufferedWriteReader.java");
fw = new FileWriter("G:\\testIO\\TestBufferedWriteReader.java");
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
String str = null;
while((str = br.readLine()) != null) {
for(int i=0; i<str.length(); i++) {
COUNT++;
}
System.out.println(COUNT);
COUNT = 0;
bw.write(str);
//bw.newLine();
}
} catch (IOException e) {
System.out.println("-----");
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流与此类似,但是字节流没有与字符流对应的 InputStreamReader 和 OutputStreamWriter
2.Properties 类
该类将集合和流结合在一起,Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
该类的典型应用
实例3
/*
*该程序可用Properties的对象对程序配置文件进行读取和存入
*
*/
import java.util.*;
import java.io.*;
public class FileProperties {
public static void main(String[] args) throws IOException{
File f = new File("g:\\java\\count.ini");
Properties pro = new Properties();
if(!f.exists()) {
f.createNewFile();
}
FileInputStream fis = new FileInputStream(f);
pro.load(fis);
String value = pro.getProperty("time");
int count = 0;
if(value != null) {
count = Integer.valueOf(value);
if(count >= 3) {
System.out.println("使用次数已到,请续费");
return;
}
}
count++;
pro.setProperty("time", count+"");
FileOutputStream fos = new FileOutputStream(f);
pro.store(fos, "ok");
fis.close();
fos.close();
}
}
3.SequenceInputStream 类的使用
SequenceInputStream 用于将多个输入流关联起来,并统一对其进行输出
SequenceInputStream 的两个构造方法
1.SequenceInputStream(Enumeration<? extends InputStream> e) //构造参数是输入流的一个枚举
2.SequenceInputStream(InputStream s1, InputStream s2)//构造参数是两个输入流
实例4
/*
* 该程序定义了两个方法,用于文件的分离和合并
*/
import java.io.*;
import java.util.*;
public class TestMySplit {
public static void main(String[] args) throws IOException{
splitPic();
merga();
}
public static void splitPic() throws IOException{ //定义一个方法,该方法用于分离图片
int count = 1;
FileInputStream fis = new FileInputStream("F:\\图片\\壁纸\\福尔摩斯.jpg");
byte[] b = new byte[1024 * 50]; //定义一个初始值是50kb的字节数组
int line = 0;
while((line = fis.read(b)) != -1) {
FileOutputStream fos = new FileOutputStream("g:\\java\\" + count++ + ".part");
fos.write(b, 0, line);
fos.flush();
fos.close();
}
fis.close();
}
public static void merga() throws IOException { //定义一个方法,该方法用于合并文件
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
al.add(new FileInputStream("G:\\java\\1.part"));
al.add(new FileInputStream("G:\\java\\2.part"));
al.add(new FileInputStream("G:\\java\\3.part"));
al.add(new FileInputStream("G:\\java\\4.part"));
final Iterator<FileInputStream>i = al.iterator();
Enumeration<FileInputStream> e = new Enumeration<FileInputStream>() { //创建了一个匿名类的对象,并覆写接口的两个方法
public boolean hasMoreElements() {
return i.hasNext();
}
public FileInputStream nextElement() {
return i.next();
}
};
SequenceInputStream sis = new SequenceInputStream(e);
FileOutputStream fos = new FileOutputStream("g:\\java\\pic.jpg");
int line = 0;
byte[] b = new byte[1024 * 1024]; //定义一个初始值是1mb的字节数组
while((line = sis.read(b)) != -1) {
fos.write(b, 0, line);
fos.flush();
}
fos.close();
sis.close();
}
}
4.对象的序列化与反序列化
对象的序列化就是把一个对象变为二进制数据流的一种方法,通过对对象的序列化可以实现对对象的传输和存储,在java io 包中要使用到的类是 ObjectOutputStream 和 ObjectInputStream
另外,如果一个类的对象想要被序列化,则该类必须实现 Serializable 接口 ,该接口没有任何方法,没有任何方法的接口叫标识接口,只有实现了这个类的对象才能被序列化
实例5
/*
*该程序会对指定的对象进行存储和读入
*
*
*/
import java.util.*;
import java.io.*;
public class TestObjectStream {
public static void main(String[] args) throws Exception {
//writeObject();
readObject();
}
public static void writeObject() throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("g:\\java\\myObject.txt"));
Animal a1 = new Animal("pig", 3000);
Animal a2 = new Animal("cat", 2000);
Animal a3 = new Animal("dog", 1000);
oos.writeObject(a1);
oos.writeObject(a2);
oos.writeObject(a3);
oos.close();
}
public static void readObject() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("g:\\java\\myObject.txt"));
Animal a1 = (Animal)ois.readObject();
System.out.println(a1);
Animal a2 = (Animal)ois.readObject();
System.out.println(a2);
Animal a3 = (Animal)ois.readObject();
System.out.println(a3);
ois.close();
}
}
class Animal implements Serializable { // Serializable 是一个标识接口,实现了此接口的类的对象才可以被序列化
//public static final long serialVersionUID = 1l; //由了这句声明之后,该类修改后也能运行
public transient String name; //加入transient这个关键字修饰的将没有被序列化的资格
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return this.name + " " + this.age;
}
}
值得一提的是,在序列化过程中引入了 serialVersionUID 这个类型为long的变量,程序员可以对此变量进行指定,如果没有对其指定,java在编译时会自动生成serialVersionUID,并在写了
对象时,将相关信息写入文件,在进行反序列化时,就会把文件字节流中的 serialVersionUID 本地相应实体类的 serialVersionUID 进行比较,如果相同就可以进行反序列化,否者就会抛出
InvalidClassException 异常,一旦类中的相关信息被修改,重新编译后 serialVersionUID 就不会相同,此时只有在类中对其进行显示的声明指定,这样程序在编译时就不会自动生成该
变量,修改类后,程序运行就不会出错
5.管道流
管道流的作用是可以进行两个进程之间的通信使用输出流和输入流时无需中转站,就像管道一样,可以把它们连接在一起,千万不要在单线程中使用管道流,因为这样可能造成死锁的产生
实例6
import java.io.*;
import java.util.*;
public class TestPipedStream {
public static void main(String[] args) {
try {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
pis.connect(pos);
Send s = new Send(pos);
Receive r = new Receive(pis);
Thread t1 = new Thread(s);
Thread t2 = new Thread(r);
t1.start();
t2.start();
} catch(IOException e) {
e.printStackTrace();
}
}
}
class Receive implements Runnable {
PipedInputStream pis = null;
public Receive(PipedInputStream pis) {
this.pis = pis;
}
public void run() {
try {
byte[] b = new byte[1024];
System.out.println("没有数据,读取阻塞");
int leng = pis.read(b);
System.out.println("读取结束");
String str = new String(b, 0, leng);
System.out.println(str);
pis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Send implements Runnable {
PipedOutputStream pos = null;
public Send(PipedOutputStream pos) {
this.pos = pos;
}
public void run() {
try {
String str = "hello";
byte[] b = str.getBytes();
System.out.println("等待六秒后写入");
Thread.sleep(6000);
pos.write(b);
pos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
6.RandomAccessFile 类的使用
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针
实例7
import java.io.*;
public class TestRandomAccessFile {
public static void main(String[] args) throws Exception {
writeFile();
readFile();
}
public static void writeFile() throws Exception {
RandomAccessFile raf = new RandomAccessFile("g:\\java\\raf.txt", "rw"); //以读写方式创建文件,如果文件不存在,则创建
raf.write("张三25".getBytes());
//raf.write("87".getBytes());
raf.write("李四25".getBytes());
raf.close();
}
public static void readFile() throws Exception{
RandomAccessFile raf = new RandomAccessFile("g:\\java\\raf.txt", "r"); //以只读方式创建文件,如果文件不存在,则报异常
raf.seek(1*6); //该方法能够根据偏移量移动指针
//raf.seek(0*6); //该方法的指针也能进行反方向移动
raf.skipBytes(4); //该方法可以根据参数的大小跳过相应的字节
System.out.println(raf.getFilePointer()); //该方法用于获取指针的下标位置
byte[] b = new byte[6];
raf.read(b);
String str = new String(b);
System.out.println(str);
raf.close();
}
}
7.类总结
常用的类还有很多,我们现在来理一理他们之间的继承关系
输入字节流
InputStream (字节输入流的终极父类,是抽象类)
|-------------FileInputStream (文件字节输入流)
|-------------PipedInputStream (管道字节输入流)
|-------------SequenceInputStream (顺序字节输入流,这个类没对应的输出流和字符流)
|-------------ByteArrayInputStream (字节数组输入流,字符流相对应的是 CharArrayReader )
|-------------ObjectInputStream (用于对象反序列化的,没有对应字符流的)
|-------------FilterInputStream (这是个很多字节输入流修饰类的父类,一般不用他创建对象,而是使用它的子类,读取数据同时对数据进行处理)
|-------------BufferedInputStream (字节缓冲输入流,用于提高效率)
|-------------DataInputStream (数据输入流,没有对应字符流的)
|-------------LineNumberInputStream (已过时,由 LineNumberReader 替换)
|-------------PushbackInputStream (压缩字节输入流,没有对应的字节输出流)
输出字节流
OutputStream (字节输出流的终极父类,是抽象类)
|-------------FileOutputStream (文件字节输出流)
|-------------PipedOutputStream (管道字节输出流)
|-------------ByteArrayOutputStream (字节数组输出流,字符流相对应的是 CharArrayWriter )
|-------------ObjectOutputStream (用于对象序列化的,字符流没有对应的)
|-------------FilterOutputStream (这是个很多字节输出流修饰类的父类,一般不用他创建对象,而是使用它的子类,写入数据同时对数据进行处理)
|-------------BufferedOutputStream (字节缓冲输出流,用于提高效率)
|-------------DataOutputStream (数据输出流,没有对应字符流的)
|-------------PrintStream (打印输出字节流,没有对应的输入流)
输入字符流
Reader
|-------------PipedReader (管道字符输入流)
|-------------BufferedReader (字符缓冲输入流,用于提高效率)
|-------------LineNumberReader (行数字符输入流)
|-------------CharArrayReader (字符数组输入流,字节流对应的是 ByteArrayInputStream)
|-------------FilterReader (没有任何子类)
|-------------StringReader (字符串字符输入流,没有对应的字节流)
|-------------InputStreamReader (他是字节流通向字符流的一个桥梁,用于输入)
|------------FileReader (文件字符输入流)
输出字符流
Writer
|-------------PipedWriter (管道字符输出流)
|-------------BufferedWriter(字符缓冲输出流,用于提高效率)
|-------------CharArrayWriter (字符数组输出流,字节流对应的是 ByteArrayOutputStream )
|-------------FilterWriter (没有任何子类)
|-------------StringWriter (字符串字符输出流,没有对应的字节流)
|-------------PrintWriter (打印输出字符流,没有对应的输入流)
|-------------OutputStreamWriter (他是字节流通向字符流的一个桥梁,用于输出)
|-------------FileWriter (文件字符输出流)
其他的还有
File (文件流类)
RandomAccessFile (随机访问类)
另外还有一个 Properties 该类位于java.util包中
总结:
1.字节流有的字符流没有的:ObjectInputStream ObjectOutputStream DataInputStream DataOutputStream SequenceInputStream 等
2.输入流有的输出流没有的:SequenceInputStream LineNumberReader LineNumberReader 等
3.输出流有的输入流没有的:PrintWriter PrintStream
4.字符流有的字节流没有的:StringReader StringWriter InputStreamReader OutputStreamWriter