什么是IO流?
byte序列的读写,Java中的IO流是实现输入/输出的基础.
1)
InputStream : 抽象类读取数据的过程 包含读取方法read();
in 模仿了读取小说的过程
简单说 : in是读取文件的
OutputStream:抽象了写出数据的过程
包含写出方法write();
out模仿了写笔记记录的过程
简单说 : out是写入文件的
基本的byte流
InputStream(抽象方法read())
|---
FileInputStream(read()在文件上读取) 节点流
|
|--- FilterInputStream
过滤器流,输入流功能的扩展
| |--- DataInputStream(readInt()) 基本类型数据的读取
| |---
BufferedInputStream 提供读取缓冲区管理
| --- ObjectInputStream
过滤器流,依赖基本byte流,扩展对象的反序列化
OutputStream(抽象方法write())
|--- FileOutputStream(write()在文件上写实现写入)
节点流
|
|--- FilterOutputStream 过滤器流,输出流功能的扩
| |---
DataOutputStream(writerInt()) 基本类型数据的写出
| |--- BufferedOutputStream
提供了输出缓冲区管理
| --- ObjectOutputStream 过滤器流,依赖基本byte流,扩展对象的序列化
注意:除节点流外都是过滤器流
字符流,可以处理字符编码,底层依赖于byte流
Reader
读取文本
| --- InputStreamReader 过滤去,依赖基本byte输入流
|
实现文本编码的解析
|
| --- BufferedReader 过滤器, 需要依赖Reader 实例
| 提供了readLine()方法, 可以在文本文件中读取一行
| 是常用的文本读取方法
Writer
| --- OutputStreamWriter 过滤器,,依赖基本byte输出流
| 实现文本编码
| ---
PrintWriter 过滤器,依赖于Writer 流
|
提供了输出文本常有方法println()
2) EOF = End of File = -1 (文件读到末尾会返回-1)
3) 输入流的基本方法
InputStream
in = new InputStream(file) / /file是文件名
int b = in.read();
读取一个byte无符号填充到int底八位,-1是EOF
int.read(byte[] buf) 读取数据填充到buf中
int.read(byte[] buf,int start,int size)
读取数据填充到buf中
in.close 关闭输入流
4)输出流的基本方法:
OutputStream
out = new OutputStream(file) / /file是文件名
out.write(int b) 写出一个byte 到流 b
的底八位写出
out.write(byte[]
buf) 将buf的一部分写入流中
out.write(byte[]
buf, int start, int size) 将buf的一部分写入流中
out.flush() 清理缓存
out.close
1.FileInputStream (read()在文件上读取)
节点流
方法: read() 从输入流中读取数据的下一个字节
read(byte[] buf) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组
buf中
read(byte[] b, int off, int
len) 将输入流中最多 len 个数据字节读入 byte 数组。
例
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class InputStreamDemo { public static void main(String[] args) throws IOException { String file = "out.txt"; InputStream in = new FileInputStream(file); int b; while((b=in.read())!=-1){//read()方法 System.out.print(Integer.toHexString(b) + " "); } in.close(); in = new FileInputStream(file); //in.available() 可以读取的数据个数,小文件一般是文件长度 byte[] buf = new byte[in.available()]; in.read(buf);//read(byte[] buf)方法重载 in.close(); for (byte c : buf) { System.out.print(Integer.toHexString(c & 0xff) + " "); // c & 0xff --->将16进制写成0xff的格式 //ffffffd6---> d6 //11111111 11111111 11111111 11010110 &对应相乘 //00000000 00000000 00000000 11111111 0xff //00000000 00000000 00000000 11010110 } } }
2
FileOutputStream(write()在文件上写实现写入) 节点流
方法 :write(int b) 将指定的字节写入此输出流。
write(byte[] buf) 将 b.length 个字节从指定的 byte
数组写入此输出流。
write(byte[] b, int off, int
len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
例子
import java.io.*; public class OutputStreamDemo { public static void main(String[] args) throws IOException{ String file = "out.txt"; OutputStream out = new FileOutputStream(file); out.write(65);//在文件中是以16进制存储的,对应0x41 out.write(66);//0x42 byte[] buf = {(byte)0xd6,(byte)0xd0}; out.write(buf); out.flush();//刷出缓存,清理缓冲区,保证可靠写 out.close(); } }
3.BufferedInputStream和BufferedOutputStream 的 用法
BufferedInputStream(FileInputStream in)
BufferedOutputStream(FileOutputStream
out)
可以提高性能
例子
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class BufferedStreamDemo { public static void main(String[] args) throws IOException { //BufferedInputStream普通写法 String file = "out.txt"; InputStream ins = new FileInputStream(file); BufferedInputStream bufin= new BufferedInputStream(ins); int b; while((b=bufin.read())!=-1){ System.out.println(Integer.toHexString(b)); } //常用写法,只要用到FileInputStream的地方都可以套一个BufferedInputStream用来提升性能 BufferedInputStream in = new BufferedInputStream( new FileInputStream("out.txt")); //BufferedOutputStream BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream("out.txt")); out.write(65); } }
4.基本类型数据的写出和读入
DataOutputStream 方法:readInt() readLong()
readBoolean()等
写出(写)
//例子 import java.io.*; public class DataOutDemo { public static void main(String[] args) throws IOException{ String file = "data.dat";//项目文件夹 OutputStream out = new FileOutputStream(file); //DataOutputStream 实现基本类型数据的序列化 //将基本类型数据拆开为byte序列,写入到out流中 DataOutputStream dos = new DataOutputStream(out); dos.write(-2); dos.writeInt(-2); dos.writeLong(-2); dos.writeByte(-2); dos.writeDouble(-2); dos.writeShort(-2); dos.writeFloat(-2); dos.writeBoolean(true); dos.writeChar('中'); dos.close(); } }
DataInputStream 方法: writeInt() writeChar()
等8种
读入(读)
import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class DataInDemo { public static void main(String[] args) throws IOException{ String file = "data.dat"; InputStream in = new FileInputStream(file); //DataInputStream 从基本流中读取基本类型数据,实现基本 //类型数据的反序列化 DataInputStream dis = new DataInputStream(in); int b = dis.read(); int i = dis.readInt(); long l= dis.readLong(); byte bx = dis.readByte(); double d = dis.readDouble(); short s = dis.readShort(); float f = dis.readFloat(); boolean bol = dis.readBoolean(); char c = dis.readChar(); dis.close(); System.out.print( b +" ");//254 fe System.out.print(i+" "); System.out.print(l+" "); System.out.print(bx+" "); System.out.print(d+" "); System.out.print(s+" "); System.out.print(f+" "); System.out.print(bol+" "); System.out.print(c+" "); } }
5
字符串的序列化(文字的编码方案)
从char序列到byte序列
的转换,叫"编码"
1) String
字符串本质上是Char
2)utf-16be
编码-----将16位char从中间切开为2个byte
utf -16be是将 unicode char[]
序列化为byte[]的编码方案
能够支持65535个字符编码,英文浪费空间
如:
char[] = ['A', 'B', '中']
对应 0041,0042,4e2d
utf-8:国际标准,是将unicode编码为byte序列的方案,采用变长编码
1-N方案,其中英文1个byte,中文3个byte
unicoded的" 中": 4e 2d = 01001110 00101101
utf-8的"中":e4 b8 ad =11100100 10111000 10101101
1110xxxx 10xxxxxx
10xxxxxx
以0开头的是英文(0-127)
110表示连续2字节表示一个字符
1110表示连续3字节表示一个字符
每个数据字节以10开头
GBK:
中国标准,支持20000+中日韩文,英文编码1byte,中文2byte
与unicode不兼容,中文windows默认gbk
ISO8859-1:只支持255个英文字符,不支持中文(Sun服务器默认编码,如tomcat等)
例子
import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class CharIODemo { public static void main(String[] args) throws IOException{ String str = "ABCD中国"; System.out.println(str); //Java 的字符是16位 Unicode值,而文件是8位Byte序列 //GBK System.out.println("GBK编码方案,对字符编码"); String file = "gbk.txt"; OutputStream out = new FileOutputStream(file);//默认GBK编码方案 byte[] gbk = str.getBytes("GBK"); out.write(gbk); out.close(); IOUtils.print(file); //UTF-16BE,每个编码是2个字节 System.out.println("UTF-16BE编码方案,对字符编码"); file = "utf-16be.txt"; out = new FileOutputStream(file); byte[] utf16be = str.getBytes("UTF-16BE"); out.write(utf16be); out.close(); IOUtils.print(file); //UTF-8,英文是1个字节,中文是3个字节 System.out.println("UTF-8编码方案,对字符编码"); file = "utf-8.txt"; out = new FileOutputStream(file); byte[] utf8 = str.getBytes("UTF-8");//编码string -> byte[] out.write(utf8); out.close(); IOUtils.print(file); byte[] buf = IOUtils.read("utf-8.txt"); //new String(buf,"UTF-8"),构造器可以将 byte编码序列 //解码为 char序列(字符串) String s = new String(buf,"UTF-8");//解码byte-> String System.out.println(s); } }
6 字符流IO(Reader
Writer)
1)
字符的处理,一次处理一个字符(unicode编码)
2)
字符的底层仍然是基本的字节流
3) 字符流的基本实现
InputStreamReader 完成byte流解析为char流,按照编码解析
OutputStreamWriter 提供char流到byte流,按照编码处理
4)
字符流的过滤器
是字符读写的功能扩展,极大的方便了文本的读写操作
BufferedReader : readLine() 一次读取一行
PrintWriter : println() 一次打印一行
5)读取一个文本文件
InputStream is = new
FileInputStream("test.txt");
Reader in =
new InputStreamReader(is);
BufferedReader reader = new
BufferedReader(in);
或者
BufferedReader in = new BufferedReader(new
FileReader(filename));
例子:
import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class TestReaderDemo { public static void main(String[] args) throws IOException{ //Scanner BufferedReader都是流的功能扩展,是过滤器 // 不能单独使用,最终需要依赖于基本byte流(in) //Scanner 提供了nextLine()方法//Java5以后 //BufferedReader 提供了 readLine()方法,读取一行 //readLine()读取到文件末尾返回null //逐行读取文本文件,显示到系统控制台 //工作中常用 String file = "in.txt"; //为当前工作区workspace/项目名/in.txt BufferedReader in = new BufferedReader( new InputStreamReader( new BufferedInputStream( new FileInputStream(file)),"gbk")); String str; while((str = in.readLine()) != null){ System.out.println(str); } in.close(); }
6)写出一个文本文件
PrintWriter
out = new PrintWriter(new FileWriter(new
FileOutputStream(filename)));
或者
PrintWriter
out = new PrintWriter(
new
OutputStreamWriter(
new
FileOutputStream(filename)))
例子
import java.io.IOException; import java.io.PrintWriter; import java.util.Scanner; public class SyncWriteDemo { public static void main(String[] args) throws IOException{ Scanner in = new Scanner(System.in); String file = "sync.txt"; PrintWriter out = new PrintWriter(file,"UTF-8"); while(true){ String str = in.nextLine(); out.println(str); if("q".equalsIgnoreCase(str)){ break; } } out.close(); } }
7)系统的默认编码,中文一般是GBK
如何查看默认编码?
String encoding = System.getProperty("file.encoding");
7 对象的IO序列化和深层复制
什么是对象序列化:
将对象Object转换为byte序列,反之叫做对象的反序列华
1)序列化流,是过滤流
ObjectOutputStream 方法 writeObject()
对象的序列化
ObjectInputStream
方法readObject() 对象的反序列化
2)序列化接口(Serializable)
对象必须实现"序列化接口Serializable"才能进行序列化,否则将出现不能序列化的异常
Serializable是一个空的接口,没有任何方法 ,仅作为序列化的一个标识
3)JavaBean 规范规定,Java类必须实现Serializable接口
Java API中的类大多是符合Java Bean规范的,基本都实现了Serializable
4) 对象的序列化可以变相实现对象的深层复制
例子
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ObjectIODemo { public static void main(String[] args) throws Exception{ String file = "obj.dat"; ObjectOutputStream out = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream(file))); Foo foo =new Foo(); out.writeObject(foo);//将foo引用的对象,序列化到文件中 out.close(); //读出 ObjectInputStream in = new ObjectInputStream( new BufferedInputStream( new FileInputStream(file))); Foo foo1 = (Foo)in.readObject();//对象反序列化 in.close(); System.out.println(foo1.name); System.out.println("深层复制:对象被复制,对象属性也被复制"); System.out.println(foo==foo1);//false 对象复制了(一层) System.out.println(foo.name == foo1.name);//false ,属性被复制了(二层) //利用序列化 和 反序列化 可以简洁的实现 对象的深层复制 } } class Foo implements Serializable{//Serializable没有声明方法 String name = "Tom"; }
浅层复制与深层复制
1)java的默认规则是浅层复制,性能好,但隔离性差,如(clone(),Arrays.copyOf)
浅层复制 :
对象的引用不同,但对象中属性的引用相同
2)利用序列化可以实现深层复制
深层复制:
对象的引用不同,但对象中的属性的引用也不相同
例
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class DeepcopyDemo { public static Object deepCope(Object obj){ try{ //1. 对象序列化 // 缓冲流: 字节数组输出流 ByteArrayOutputStream buf = new ByteArrayOutputStream(); //对象输出流 ObjectOutputStream out = new ObjectOutputStream( new ByteArrayOutputStream()); out.writeObject(obj);//序列化对象到buf中 out.close(); //2 .对象的反序列化 byte[] ary = buf.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(ary); ObjectInputStream in = new ObjectInputStream(bais); Object o = in.readObject();//从ary反序列化 in.close(); return o; }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(e); } } }
以上用到的ByteArrayInputStream和ByteArrayOutputStream
下面有一个ByteArrayInputStream和ByteArrayOutputStream的例子
例子
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import com.tarena.day18.IOUtils; public class ByteArrayIODemo { public static void main(String[] args) throws IOException{ byte[] ary = {1,-1,127,-128}; // {00000001, 11111111, 01111111, 10000000} ByteArrayInputStream in = new ByteArrayInputStream(ary); int b = in.read();//1 00000000 00000000 00000000 00000001 System.out.println(b); b = in.read(); System.out.println(b);//255 00000000 00000000 00000000 11111111 b = in.read(); System.out.println(b);//127 00000000 00000000 00000000 01111111 b = in.read(); System.out.println(b);//128 00000000 00000000 00000000 10000000 b = in.read(); System.out.println(b);//-1 11111111 11111111 11111111 11111111 in.close(); ByteArrayOutputStream out = new ByteArrayOutputStream();//默认开辟32byte的数组作为输出目标 //如果满了就自动扩容 //out:[0,0,0,0,0,0,0,.....] // out.write(1);//[1,0,0,0,0,0,....] out.write(-2);//[1,fe,0,0,0,0,0,....] out.write(-1);//[1,fe,ff,0,0,0,0,....] out.close(); byte[] buf = out.toByteArray();//复制有效部分 IOUtils.print(buf);//[01, fe, ff ] } }
原文:http://www.cnblogs.com/hqr9313/archive/2012/04/23/2467294.html