字符输入流
当使用字节流读取汉字时,会一个字节一个字节读取,读取到的数据为int类型。如果读取的文本文件有汉字,则有可能会出现问题,因为不同的编码格式,每个汉字占用的字节数是不同的,所以如果要把读取到的数据转换为汉字时,可能会报错。如果读取后直接使用字节流写入到一个文件里,则不会出现问题。
public class ReadDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("a.txt");
//读取a.txt中的内容,直接写入到b.txt中
byte[] bytes = new byte[1024];
int length;
FileOutputStream fos=new FileOutputStream("b.txt");
while ((length = fis.read(bytes))!=-1){
fos.write(bytes,0,length);
}
}
}
字符输入流则不会出现以上问题。如果读完数据要查看,则使用字符流,如果单纯的拷贝 推荐使用字节流,如果使用字符流copy非文本文件可能会出现问题
FileReader reader = new FileReader("a.txt");
int len ;
char [] chars = new char[1024];
while ((len=reader.read(chars))!=-1){
System.out.println(new String(chars, 0, len));//注意如果使用println则会每隔1024字符换行
}
字符输出流
可以使用writer() 方法写入文件一段内容。
可以写入单个字符,或者字符串,或者char类型的数组,还可以指定写入长度。
调用writer()方法以后,内容不会立刻写入到文件,此时需要调用flush()或者close()方法刷新文件。
package com.atguigu.io;
import java.io.FileWriter;
import java.io.IOException;
public class WriterDemo {
public static void main(String[] args) throws IOException {
FileWriter writer = new FileWriter("b.txt");
writer.write(20320);//在b.txt中写入“你”这个汉字。
writer.write("好好学习,天天向上");//也可以直接写入字符串
writer.flush();//调用flush方法刷新文件
}
}
用writer 指定长度copy文件内容
package com.atguigu.io;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class WriterDemo {
public static void main(String[] args) throws IOException {
FileReader fr= new FileReader("a.txt");
FileWriter writer = new FileWriter("b.txt");
char [] chars = new char[1024];
int length;
while ((length=fr.read(chars))!=-1){
writer.write(chars,0,length);
}
}
}
缓冲流
缓冲流也被称为高效流,能够提高IO操作的效率,缓冲流其实是对普通IO流的封装
字节缓冲流:BufferedInputStream,BufferOutputStream、
字符缓冲流:BufferedReader,BufferedWriter
字节缓冲流,测试使用缓冲流和普通字节流的拷贝速度
package com.atguigu.bufferedio;
import java.io.*;
public class BufferedDemo1 {
public static void main(String[] args) throws IOException {
//两个普通的字节流
FileInputStream fis = new FileInputStream("1.exe");
FileOutputStream fos = new FileOutputStream("2.exe");
//缓冲流是对普通流的包装
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bot = new BufferedOutputStream(fos);
long start = System.currentTimeMillis();//返回以毫秒为单位的当前时间
// test1(fis, fos);//23160
// test2(fis,fos);//9
test3(bis,bot);//170
long end = System.currentTimeMillis();
System.out.println(end-start);
//关闭IO流(先开的后关闭,后开的先关闭)
bot.close();
bis.close();
fos.close();
fis.close();
}
//测试使用普通字节流一次读取一个字节的拷贝速度
public static void test1(FileInputStream fis, FileOutputStream fos) throws IOException {
int content;
while ((content = fis.read()) != -1) {
fos.write(content);
}
}
//测试使用普通字节流一次读取1M的速度
public static void test2(FileInputStream fis, FileOutputStream fos) throws IOException {
int length;
byte[] content = new byte[1024*1024];
while ((length = fis.read(content)) != -1) {
fos.write(content,0,length);
}
}
//测试使用缓冲流拷贝速度
public static void test3(BufferedInputStream bis, BufferedOutputStream bos) throws IOException {
int content;
while ((content = bis.read()) != -1) {
bos.write(content);
}
}
}
可以发现使用缓冲流也不一定会提高拷贝速度,程序优化有个最优方式,IO流使用之后都要关闭(先开的后关闭,后开的先关闭)。
字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。
- BufferedReader:
public String readLine()
: 读一行文字。 - BufferedWriter:
public void newLine()
: 写一行行分隔符,由系统属性定义符号。
转换流
将字节流和字符流相互转换
InputStreamReader / OutputStreamWriter
在创建FileReader对象读取一个文件的时候,是无法指定文件的编码方式的,有可能出现写入时的编码方式和读取时的编码方式不一致的情况,导致乱码
package com.atguigu.convertio;
import java.io.*;
public class convertDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("b.txt");
FileOutputStream fos = new FileOutputStream("a.txt");
//指定以字节流的形式打开GBK编码的文本文件
InputStreamReader isr = new InputStreamReader(fis,"utf8");
OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk");
char[] chars = new char[10];
int length;
while ((length=isr.read(chars))!=-1){
osw.write(chars,0,length);
}
osw.close();
isr.close();
fos.close();
fis.close();
}
}
序列化和反序列化
如何把Java对象写入到文件里?字节流 字符流
序列化:将Java对象转换为字节或者字符,用来保存或者传输的过程。
ObjectOutputStream 将对象转换成为二进制并写入到文件。
Java对象转换为字节,对象所有的信息都会保留,可以用来保留对象。
Java对象转换为字符串,信息会丢失,用来保留对象的属性以及跨平台传输数据。
被transient 关键字修饰的变量,在对象变成二进制的时候不会被写入。
反序列化:将字节或者字符串加载成为Java对象来使用的过程。
ObjectInputStream 将二进制的文件加载成Java对象。
使用 ObjectInputStream 和 ObjectOutputStream 实现 Java对象 和 字节之间相互转换.
不是所有的对象都能直接变成二进制被写入到文件里。对象需要实现 Serializable 接口
Serializable 接口没有抽象方法,只是对类进行一个标记,表示这个类可以被序列化!
序列化和反序列化的注意事项:
1.将对象写入到二进制文件以后,如果直接使用 ObjectInputStream 加载是可以加载成功的
2. 生成二进制文件以后,手动修改了这个文件,会报错!
3. 生成二进制文件以后,修改了代码,添加或者删除了一个属性,再重新加载文件。
InvalidClassException出现的三种情况:
前提是生成 二进制文件以后,如果做一下几种操作,会触发异常
- 修改了 java文件,添加了不以 transient 修饰的变量,或者删除了任意的变量。
但是如果是修改变量的值,而没有新增变量,不会报错。 - java类里的 SerialVersionUID 版本和 文件里读取的版本不一致。
- 如果一个类它的父类没有可以访问的无参构造方法,也会报错。
如果子类实现了 Serializable 接口,子类序列化时,父类里定义的属性不会被写入;
如果父类实现了 Serializable 接口,子类序列化时,父类里定义的属性会被写入。
Properties 类
HashTable 是线程安全的效率低,HashMap线程不安全效率高
HashTable不允许放置null,HashMapkeyifangnull值键值对。
java.util.Properties
继承于Hashtable
,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties
方法就是返回一个Properties
对象。
Properties 可以像Map一样存入键值对,但是通常不手动调用put给键值对赋值。
Properties 通常用来读取一个 配置文件,后缀名通常是 .properties
通常是以键值对的形式保存数据,键和值之间可以使用 = 或者 : 连接
FileReader reader = new FileReader("config.properties");
prop.load(reader);//按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
getProperty(String key, String defaultValue)
用指定的键在属性列表中搜索属性。