IO流
缓冲流
缓冲流,也叫高效流,是对4个基本的
FileInputSream,FileOutputSrteam,FileReader,FileWriter
流的增强,所以也是4个流,按照数据类型分类可分为:字节缓冲流:BufferedInputStream,BufferedOutputStream;字符缓冲流:BufferedReader,BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
字节缓冲流
方法名 | 说明 |
---|---|
public BufferedInputStream(InputStream in) | 创建一个 新的缓冲输入流。 |
public BufferedOutputStream(OutputStream out) | 创建一个新的缓冲输出流。 |
一次读写一个字节
public class BufferedInPutStreamDemo01 {
public static void main(String[] args) throws IOException {
/**
* public BufferedInputStream(InputStream in)创建一个 新的缓冲输入流。
* public BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流。
*/
//一次读取一个字节
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day150429\\bb.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day150429\\buffered.txt"));
int b;
while ((b = bis.read()) != -1){
bos.write(b);
}
bos.close();
bis.close();
}
}
一次读写一个数组字节
public class BufferedInPutStreamDemo02 {
public static void main(String[] args) throws IOException {
/**
* public BufferedInputStream(InputStream in)创建一个 新的缓冲输入流。
* public BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流。
*/
//一次读取一个数组
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream("day150429\\bb.txt"));
bos = new BufferedOutputStream(new FileOutputStream("day150429\\buffered.txt"));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
bos.write(bytes,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if (bos != null){
try {
bos.close();
}catch (IOException e){
e.printStackTrace();
}
}
if (bis != null){
try {
bos.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
字符缓冲流
方法名 | 说明 |
---|---|
public BufferedReader(Reader in) | 创建一个 新的缓冲输入流。 |
public BufferedWriter(Writer out) | 创建一个新的缓冲输出流。 |
字符缓冲流特有方法
类名 | 方法名 | 说明 |
---|---|---|
BufferedReader | public String readLine() | 一次读取一行文字 |
BufferedWriter | public void newLine() | 写一行行分隔符,由系统属性定义符号 |
一次读取一行数据
public class BufferedReaderDemo01 {
public static void main(String[] args) throws IOException {
/**
* BufferedReader(Reader in)
* 构造方法中的参数是一个 Reader,该类是一个抽象类,我们传递的是该抽象类的子类FileReader
* BufferedReader提供了一种更为便捷的读取方式,可以一次读取一行数据
* 提供了一个方法readLine() 方法可以这个功能
* 该方法返回的是一个字符串
* 使用这种方式进行读取数据,那么的结束的方式判断使用 !=null
* readLine方法,读取数据时,读取不到换行符
*/
BufferedReader br = new BufferedReader(new FileReader(new File("day150429\\ab.java")));
//method01(br);
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
private static void method01(BufferedReader br) throws IOException {
int len;
char[] bytes = new char[1024];
while ((len = br.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
}
br.close();
}
}
缓冲字节流,缓冲字符流复制文件的效率比较
public class BufferedStreamCopyFile {
public static void main(String[] args) throws IOException {
long start1 = System.currentTimeMillis();
method1();
long end1 = System.currentTimeMillis();
System.out.println("BufferedInputStream 一次读取一个字符:"+(end1-start1));
long start2 = System.currentTimeMillis();
method2();
long end2 = System.currentTimeMillis();
System.out.println("BufferedInputStream 一次读取一组字符:"+(end2-start2));
//太慢
/* long start3 = System.currentTimeMillis();
method3();
long end3 = System.currentTimeMillis();
System.out.println("FileInputStream一次读取一个字节:"+(end3-start3));*/
long start4 = System.currentTimeMillis();
method4();
long end4 = System.currentTimeMillis();
System.out.println("FileInputStream一次读取一组字节:"+(end4-start4));
}
/**
* FileInputStream 一次读取一个数组字节 太慢
* @throws IOException
*/
public static void method4() throws IOException {
FileInputStream fis = new FileInputStream(new File("day150429\\01.mp4"));
FileOutputStream fos = new FileOutputStream("day150429\\05.mp4");
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}
/**
* FileInputStream 一次读取一个字节
* @throws FileNotFoundException
*/
/* public static void method3() throws IOException {
FileInputStream fis = new FileInputStream(new File("day150429\\01.mp4"));
FileOutputStream fos = new FileOutputStream("day150429\\04.mp4");
int len;
while ((len = fis.read()) != -1){
fos.write(len);
}
fos.close();
fis.close();
}*/
/**
* BufferedInputStream 一次读取一组字符
* @throws IOException
*/
public static void method2() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("day150429\\01.mp4")));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("day150429\\03.mp4")));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1){
bos.write(bytes,0,len);
}
//关闭资源
bos.close();
bis.close();
}
/**
* BufferedInputStream 一次读取一个字符
* @throws IOException
*/
public static void method1() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("day150429\\01.mp4")));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("day150429\\02.mp4")));
int len;
while ((len = bis.read()) != -1){
bos.write(len);
}
//关闭资源
bos.close();
bis.close();
}
}
使用BufferedReader和BufferedWriter读写文件(文件的复制)
public class CopyCharFileDemo {
public static void main(String[] args) throws IOException {
/**
* 将ab.java拷贝到新文件bds.java中
* 使用BufferedReader和BufferedWriter
*/
BufferedReader br = new BufferedReader(new FileReader(new File("day150429\\ab.java")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("day150429\\dbs.java")));
String line;
while ((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
}
bw.close();
br.close();
}
}
转换流
字符编码和字符集
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
字符集
ASCII字符集,美国信息交换标准码
Unicode字符集,万国码
UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
InputStreamReader类
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的
读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
charset
每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。
构造方法
方法名 | 说明 |
---|---|
InputStreamReader(InputStream in) | 创建一个使用默认字符集的字符流。 |
InputStreamReader(InputStream in, String charsetName) | 创建一个指定字符集的字符流。 |
public class InputStreamReaderDemo01 {
public static void main(String[] args) throws IOException {
/**
* InputStreamReader(InputStream in) : 创建一个使用默认字符集的 InputStreamReader
* InputStreamReader(InputStream in, String charsetName): 创建使用指定字符集的 InputStreamReader。
*/
BufferedReader br = new BufferedReader(new FileReader(new File("day150429\\hello.java")));
//��ð�
System.out.println(br.readLine());
InputStreamReader isr = new InputStreamReader(new FileInputStream(new File("day150429\\hello.java")), "GBK");
System.out.println(isr.read());
BufferedReader brs = new BufferedReader(new InputStreamReader(new FileInputStream(new File("day150429\\hello.java")), "GBK"));
String s = brs.readLine();
System.out.println(s);
}
}
OutputStreamWriter类
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的
将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
charset
每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。
构造方法
方法名 | 说明 |
---|---|
OutputStreamWriter(OutputStream in) | 创建一个使用默认字符集的字符输出流。 |
OutputStreamWriter(OutputStream in, String charsetName) | 创建一个指定字符集的字符输出流。 |
public class InputStreamReaderDemo03 {
public static void main(String[] args) throws IOException {
/**
* OutputStreamWriter(OutputStream out)
* 创建使用默认字符编码的 OutputStreamWriter。
* OutputStreamWriter(OutputStream out, String charsetName)
* 创建使用指定字符集的 OutputStreamWriter。
*/
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File("day150429\\ows.txt")),"GBK");
osw.write("我是utf-8数据");
osw.close();
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("day150429\\ows.txt")),"GBK"));
String s = br.readLine();
System.out.println(s);
}
}
转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
指定GBK编码的转换流,读取文本文件。
使用UTF-8编码的转换流,写出文本文件。
public class TransDemo {
public static void main(String[] args) {
// 1.定义文件路径
String srcFile = "file_gbk.txt";
String destFile = "file_utf8.txt";
// 2.创建流对象
// 2.1 转换输入流,指定GBK编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");
// 2.2 转换输出流,默认utf8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));
// 3.读写数据
// 3.1 定义数组
char[] cbuf = new char[1024];
// 3.2 定义长度
int len;
// 3.3 循环读取
while ((len = isr.read(cbuf))!=-1) {
// 循环写出
osw.write(cbuf,0,len);
}
// 4.释放资源
osw.close();
isr.close();
}
}
序列流
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该
对象的数据
、对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。
ObjectOutputStream流
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。
还可以使用 DataOutput 中的适当方法将基本数据类型写入流中。还可以使用 writeUTF 方法写入字符串。
构造方法
方法名 | 说明 |
---|---|
public ObjectOutputStream(OutputStream out) | 创建一个指定OutputStream的ObjectOutputStream。 |
序列化常用方法
方法名 | 说明 |
---|---|
public final void writeObject (Object obj) | 将指定的对象写出。 |
一个对象要想序列化,必须满足两个条件:
该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws IOException {
Employee employee = new Employee("zhangsan", "zhengzhou", 18);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day150429\\employee.txt"));
oos.writeObject(employee);
oos.close();
System.out.println("Serialized data is saved");
//java.io.NotSerializableException
//employee类必须实现java.io.serializable接口。
// `Serializable` 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,
// 会抛出`NotSerializableException`
}
}
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day150429\\person.txt"));
Person person = new Person("张三", 13, "郑州");
Person person1 = new Person("李四", 14, "深圳");
ArrayList<Person> list = new ArrayList<>();
list.add(person);
list.add(person1);
oos.writeObject(list);
oos.close();
}
}
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String address;
private int age;
public void addressCheck(){
System.out.println("Address check : " + name + " -- " + address);
}
public Employee() {
}
public Employee(String name, String address, int age) {
this.name = name;
this.address = address;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", age=" + age +
'}';
}
}
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String address;
public Person() {
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
ObjectInputStream类
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
ObjectInputStream 确保从流创建的图形中所有对象的类型与 Java 虚拟机中显示的类相匹配。使用标准机制按需加载类。
只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
readObject
方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。在 Java 中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。可以使用 DataInput 上的适当方法从流读取基本数据类型。
构造方法
方法名 | 说明 |
---|---|
public ObjectInputStream(InputStream in) | 创建一个指定InputStream的ObjectInputStream。 |
反序列化常用方法
方法名 | 说明 |
---|---|
public final Object readObject () | 读取一个对象。 |
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day150429\\employee.txt"));
Object o = ois.readObject();
System.out.println(o);
ois.close();
}
}
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day150429\\person.txt"));
ArrayList<Person>list = (ArrayList<Person>) ois.readObject();
for (Person person : list) {
System.out.println(person);
}
ois.close();
}
}
注意事项:
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
InvalidClassException
异常。**发生这个异常的原因如下:该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
打印流
平时我们在控制台打印输出,是调用
println
方法完成的,这两个方法都来自于java.io.PrintStream
类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
分类
名称 | 类名 |
---|---|
字节打印流 | PrintStream |
字符打印流 | PrintWriter |
打印流特点
- 只负责输出数据,不负责读取数据
- 永远不会抛出IOException
- 有自己的特有方法
字符打印流 printWriter特点
* 自动换行 println()
* 不能输出字节 可以输出字节以外的内容
* 必须是通过配置 自动刷新 (println,printf,format)
boolean autoFlush: true 自动刷新 false,不自动刷新
* 包装流本身没有写出功能
* 将字节输出流转换字符输出流
构造方法
方法名 | 说明 |
---|---|
PrintWriter(String fileName) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
PrintWriter(Writer out, boolean autoFlush) | 创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果为真,则println , printf ,或format方法将刷新输出缓冲区 |
特有方法
方法名 | 说明 |
---|---|
void write(String s) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
void print(String s) | 输出字符串, 没有换行 |
void println(String s) | 输出字符串并换行. 如果启动了自动刷新, 则会执行自动刷新写入数据 |
void printf(Locale l, String format, Object... args) | 使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据 |
void format(Locale l, String format, Object... args) | 使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据 |
public class PrintWriterDemo01 {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw = new PrintWriter(new File("day150429\\pw.txt"));
// void write(int c)
// 写入单个字符。
pw.write(97);
//print(int i)
// 打印整数。
pw.print(97);
pw.close();
}
}
Properties集合
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 属性列表中的每个键及其对应的值都是一个字符串
基本使用
public class PropertiesDemo01 {
public static void main(String[] args) {
//创建properties对象
Properties properties = new Properties();
//添加数据
properties.put("01","张三");
properties.put("02","李四");
properties.put("03","王五");
Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
for (Map.Entry<Object, Object> entry : entrySet) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
Set<Object> keySet = properties.keySet();
for (Object key : keySet) {
System.out.println(key+":"+properties.get(key));
}
}
}
Properties作为Map集合的特有方法
方法名 | 说明 |
---|---|
Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set<String> stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
public class PropertiesDemo02 {
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("001","张三");
properties.setProperty("002","李四");
properties.setProperty("003","王五");
String property1 = properties.getProperty("001");
String property2 = properties.getProperty("002");
System.out.println(property1);
System.out.println(property2);
// stringPropertyNames() : 获取Properties中的所有的Key 返回的是Set集合
Set<String> set = properties.stringPropertyNames();
for (String s : set) {
String value = properties.getProperty(s);
System.out.println(s+":"+value);
}
}
}
Properties和IO流结合的方法
方法名 | 说明 |
---|---|
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
public class PropertiesDemo04 {
public static void main(String[] args) throws IOException {
/**
* void load(InputStream inStream)
* 从输入流中读取属性列表(键和元素对)。
* void load(Reader reader)
* 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
*/
Properties pro = new Properties();
//void load(InputStream inStream)
// 从输入流中读取属性列表(键和元素对)。
pro.load(new FileInputStream(new File("day150429\\properties")));
String property1 = pro.getProperty("001");
String property2 = pro.getProperty("002");
String property3 = pro.getProperty("003","默认值");
System.out.println("001="+property1);
System.out.println("002="+property2);
System.out.println("003="+property3);
//void load(Reader reader)
// 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
pro.load(new BufferedReader(new FileReader(new File("day150429\\propertie"))));
Set<Object> keySet = pro.keySet();
for (Object key : keySet) {
System.out.println(key+"="+pro.get(key));
}
}
}
public class PropertiesDemo03 {
public static void main(String[] args) throws IOException {
/**
* void store(OutputStream out, String comments)
* 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
* void store(Writer writer, String comments)
* 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。
*/
Properties pro = new Properties();
pro.setProperty("001","战三干");
pro.setProperty("002","找大号");
pro.store(new FileOutputStream("day150429\\properties"),null);//写入的是ASCII的十六进制编码
pro.store(new FileWriter("day150429\\propertie"),null);//写入的是原内容
}
}