字节流
用来做文件复制
以单位来分: 字节流 字符流
以层级来分: 底层流 包装流
字节流: InputStream/OutputStream
子类: FileInputStream/FileOutputStream
构造方法
@Test
public void test01InputStream() throws FileNotFoundException {
InputStream is = new FileInputStream(new File("a.txt"));
InputStream is1 = new FileInputStream("a.txt");
}
@Test
public void test01OutputStream() throws FileNotFoundException {
OutputStream os = new FileOutputStream(new File("a.txt"));
OutputStream os1 = new FileOutputStream("a.txt");//默认false替换
// true在原有的文件末尾上追加内容
OutputStream os2 = new FileOutputStream("a.txt", true);
OutputStream os3 = new FileOutputStream(new File("a.txt"), true);
}
注意:
1.IO所有跟文件相关的流中, 构造方法中需要File作为参数的都可以使用文件路径直接取代
2.字节流写和读都是以字节为单位的, 单个字节能不能正常显示出来, 是不确定的
API
write(int)
写单个字节
void write(int) : 写入这个int值的低八位,比如写入-1,最后八位都是1,读出来就是255
@Test
public void test01Write() throws IOException {
// a.txt 现在不存在
/*
如果文件不存在, 会创建新的文件, 然后再写入内容
如果文件存在, 会清空原来的内容, 然后再写入
*/
OutputStream os = new FileOutputStream("a.txt");
// 以字节为单位写入, 只写入一个字节
// 00000000 00000000 00000001 00000000
os.write(229);
os.write(147); // 'a' = 97
os.write(136); // 'A' = 65
os.write(48); // '0' = 48
// 10000000 00000000 00000000 00000001
// 反:11111111 11111111 11111111 11111110
// 补:11111111 11111111 11111111 11111111
os.write(-1); // 11111111
}
read()
读文件中单个字节, 并且存入int的低八位, 其余空位补0
当返回 -1 的时候, 说明文件读到了末尾
@Test
public void test01Read() throws IOException {
/*
如果文件不存在, 就会抛出 FileNotFoundException
*/
InputStream is = new FileInputStream("a.txt");
// 读一个字节 8位 放入int的 32位 中的低八位, 其他空位都补0
int i ;
while((i = is.read()) != -1) {
System.out.println("i:" + i);
}
}
write(byte[])
将这个字节数组中所有的字节一起写入,编码
@Test
public void test02Write() throws IOException {
OutputStream os = new FileOutputStream("a.txt");
String str = "你好, 你吃了吗?";
// 字符串 -> 字节数组 [编码]
byte[] bs = str.getBytes("gbk");//编码
os.write(bs);
//[-60, -29, -70, -61, 44, 32, -60, -29, -77, -44, -63, -53, -62, -16, 63]
System.out.println(Arrays.toString(bs));
}
read(byte[] bs)
读出字节数组,返回的是长度,最多一次读出bs.length个字节
读到文件末尾,返回-1
@Test
public void test02Read() throws IOException {
InputStream is = new FileInputStream("a.txt");
byte[] bs = new byte[20];
int i = is.read(bs);//返回的是数组的长度
System.out.println("i: " + i);
System.out.println("bs: " + Arrays.toString(bs));
}
//
i: 15
bs: [-60, -29, -70, -61, 44, 32, -60, -29, -77, -44, -63, -53, -62, -16, 63, 0, 0, 0, 0, 0]
InputStream is = new FileInputStream("a.txt");
byte[] bs = new byte[6];
int len ;
while((len = is.read(bs)) != -1) {
System.out.println("len: " + len);
System.out.println("bs: " + Arrays.toString(bs));
}
//
len: 6
bs: [-60, -29, -70, -61, 44, 32]
len: 6
bs: [-60, -29, -77, -44, -63, -53]
len: 3
bs: [-62, -16, 63, -44, -63, -53]//最后两位是没替换之前的
write(byte[],int offset,int length)
截取一部分字节数组写入
public void test03Write() throws IOException {
OutputStream os = new FileOutputStream("a.txt");
String str = "你好, 你吃了吗?";
// 字符串 -> 字节数组 [编码]
byte[] bs = str.getBytes("gbk");
os.write(bs, 0, bs.length); // 等同于 os.write(bs);
os.write(bs, 2, 3); // 好,
System.out.println(Arrays.toString(bs));
//[-60, -29, -70, -61, 44, 32, -60, -29, -77, -44, -63, -53, -62, -16, 63]
}
Buffered缓冲区
包装 -> 高级流: BufferedInputStream/BufferedOutputStream
Buffered: 缓冲/缓存,提高效率
BufferedOutputStream需要手动刷新缓冲区flush()
关闭流的时候也会自动刷新缓冲区close
关闭流一般放在try/catch的finally中
案例:文件复制
方法一:单个字节读出然后写入
方法一:单个字节读出然后写入
@Test
public void copy() throws IOException {
InputStream is = new FileInputStream("a.txt");
OutputStream os = new FileOutputStream("a_bak.txt");
int i ;
while((i = is.read()) != -1) {
os.write(i);
}
}
方法二:读出字节数组然后写入,效率比上面高
//write(byte[])
@Test
public void mc() throws IOException {
InputStream is=new FileInputStream("a.txt");
OutputStream os=new FileOutputStream("ab.txt");
int len;
byte[] bs=new byte[1024];
while ((len=is.read(bs))!=-1){
os.write(bs,0,len);
}
}
方法三:缓冲区,效率最高
//Buffered
@Test
public void copy02Buffered() throws IOException {
InputStream is = new FileInputStream("a.mp3");
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream("a_bak.mp3");
BufferedOutputStream bos = new BufferedOutputStream(os);
int len ;
byte[] bs = new byte[1024];
long time1 = System.currentTimeMillis();
while ((len = bis.read(bs)) != -1) {
bos.write(bs, 0, len);
}
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
}
案例:文件加密解密
文件加密 -> 原理, 将读出来的字节做了运算后写入到新文件中
解密 -> 将加密过的文件, 再读出, 做一个还原运算, 重新写入
运算 -> 为了避免byte取值范围溢出, 同时为了避免加密解密两套代码
一般使用 ^ 运算
只能单个字节异或加密
public void copy01Buffered() {
InputStream is = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
is = new FileInputStream("a_bak.txt");
bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream("a_bak_back.txt");
bos = new BufferedOutputStream(os);
int a;
long time1 = System.currentTimeMillis();
while ((a = bis.read()) != -1) {
bos.write(a ^ 1);
}
// 手动将缓冲区的内容写出到文件
// bos.flush();
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
// 流在关闭的时候, 也会自动刷新缓冲区内容
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
try/catch处理异常(了解)
try(resource) {} 不管有没有异常, resource都会自动关闭, 不需要finally手动关闭
finally
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a_bak.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a_bak_back.txt"))) {
字符流
字符流: Reader/Writer
Writer
OutputStreamWriter(OutputStream out, String charsetName)- 指定字符集
OutputStreamWriter(OutputStream out)- 默认字符集
public void write01() throws IOException {
Writer w = new OutputStreamWriter(new FileOutputStream("a.txt"));
// 写入单个字符
// 11111111 11111111
w.write(-1); // 65535
w.write(97); // a
w.write('你');
w.close();
}
public void write02() throws IOException {
Writer w = new OutputStreamWriter(new FileOutputStream("a.txt"));
//写字符数组
char[] ch = {'h', 'e', 'l', 'l', 'o'};
w.write(ch);
w.write('\n');
w.write("hello");
w.close();
}
public void write03() throws IOException {
Writer w = new OutputStreamWriter(new FileOutputStream("a.txt"));
//写字符数组的一部分
char[] ch = {'h', 'e', 'l', 'l', 'o'};
w.write(ch, 2, 3); // llo
w.write("\n");
w.write("hello", 1, 4);
w.close();
Reader
InputStreamReader(InputStream in, String charsetName) - 指定字符集
InputStreamReader(InputStream in) - 默认字符集
注:虽然读取了⼀个字符,但是会⾃动提升为int类型。
public void read01() throws IOException {
Reader r = new InputStreamReader(new FileInputStream("a.txt"));
int c = r.read();
System.out.println((char) c);//读出来还是int型,需要转换
}
public void read02() throws IOException {
Reader r = new InputStreamReader(new FileInputStream("a.txt"));
char[] ch = new char[10];
int len = r.read(ch);
System.out.println(len);
System.out.println(ch);
}
缓冲(包装)字符流
通常用来读写文件内容,效率>>>>
BufferedReader(Reader)
readLine()
读一整行,读到末尾返回null
public void test01() throws IOException {
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("a.txt"), "gbk"));
String str;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
br.close();
}
PrintWriter
PrintWriter(String fileName, String csn)
只能清空原文件内容, 但是可以指定字符集
PrintWriter(OutputStream out, boolean autoFlush)
可以在原文件上追加内容, 但是不可以指定字符集, 可以自动刷新缓冲区
PrintWriter(Writer out, boolean autoFlush)
可以在原文件上追加内容, 可以指定字符集, 可以自动刷新缓冲区
@Test
public void test02() throws IOException {
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("a.txt", true), "gbk"), true);
pw.print("?");
pw.println();
pw.println("没有");
// pw.flush();
}
@Test
public void test03() throws IOException {
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("a.txt", true), "gbk"), true);
pw.print("你怎么还不找!");
pw.print("都多大人了!");
pw.flush();
}
print和println
void print(Object): 写出内容, 不加换行
void println(): 写出内容, 并且换行
注意: 自动刷新的功能, 只有println方法具有,print方法 必须手动刷新
对象流
对象 -> 文件[字节] : 序列化
文件[字节] -> 对象 : 反序列化
API
public void test01Init() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
}
public void test02Write() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
Date date = new Date();
oos.writeObject(date);
String str = "hello";
oos.writeObject(str);
oos.writeObject(null);
oos.close();
}
public void test03Read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Date d = (Date) ois.readObject();
System.out.println(d);
String s = (String) ois.readObject();
System.out.println(s);
// 没有对象了, 读出来的是 EOFException End Of File
Object o = ois.readObject();
System.out.println(o);
}
Serializable: 可序列化的
内部没有任何字段方法
serialVersionUID
给类添加固定的序列版本号
transient
修饰的变量, 在序列化的时候, 会被忽略掉
public class Student implements Serializable {
// 序列化后的版本编号, 不指定的话,
// 会自动生成一串, 会随着类内容的修改而改变
public static final long serialVersionUID = 1234l;
private String name;
private int age;
// 序列化的时候, sex对应的值会被忽略
private transient char sex;
public void test01Write() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("stu.obj"));
Student s = new Student();
s.setName("杨幂");
s.setAge(38);
s.setSex('女');
// NotSerializableException
oos.writeObject(s);
oos.close();
}
public void test02Read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("stu.obj"));
// NotSerializableException
Student s = (Student) ois.readObject();
ois.close();
System.out.println(s);
}
流的选择
文件复制:
BufferedInputStream/BufferedOutputStream
文件内容读写:
BufferedReader/PrintWriter
FileReader/FileWriter