JavaIO流2
- BufferedReader与BufferedWriter
- 使用字节流实现对二进制文件复制
- 对象流
- 标准输入输出流
- 转换流
- 打印流
- Properties类
BufferedReader与BufferedWriter
-
BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的。
-
关闭处理流的时候,只需要关闭外层流即可
演示bufferedReader的使用
package com.java_io; import java.io.BufferedReader; import java.io.FileReader; public class BufferedReaderTest { public static void main(String[] args) throws Exception{ String path = "E://note.txt"; //创建BufferedReader对象 BufferedReader bufferedReader = new BufferedReader(new FileReader(path)); //按行读取,提高效率 String line; /* * bufferedReader.readLine()是按行读取文件的 * 当返回null时,表示已读取完毕 */ while ((line =bufferedReader.readLine())!=null){ System.out.println(line); } bufferedReader.close(); } }
-
需要注意的是,关闭流时只需要关闭BufferedReader。因为底层会自动关闭节点流
底层代码
public void close() throws IOException { synchronized (lock) { if (in == null) return; try { in.close(); } finally { in = null; cb = null; } } }
-
演示BufferedWriter
package com.java_io; import java.io.BufferedWriter; import java.io.FileWriter; public class BufferedWriterTest { public static void main(String[] args) throws Exception{ String path = "E://a.txt"; //创建对象,把FileWriter的Boolean值置为true可以实现追加功能 //默认为false,会覆盖原来的内容 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path,true)); bufferedWriter.write("hello,成志恒"); //插入一个当前系统的换行符 bufferedWriter.newLine(); bufferedWriter.write("hello2,成志恒"); bufferedWriter.newLine(); bufferedWriter.write("hello3,成志恒"); bufferedWriter.newLine(); bufferedWriter.close(); } }
-
使用bufferd进行文件拷贝
package com.bufferd; import java.io.*; public class BufferedCopy_ { public static void main(String[] args) { String path1 = "e://a.txt"; String path2 = "e://a1.txt"; BufferedReader br = null; BufferedWriter bw = null; String line; try { br = new BufferedReader(new FileReader(path1)); bw = new BufferedWriter(new FileWriter(path2)); while ((line = br.readLine())!=null){ bw.write(line); bw.newLine(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(br != null){ br.close(); } if(bw != null){ bw.close(); } } catch (Exception e) { e.printStackTrace(); } } } }
使用说明:
- BufferedReader和BufferedWriter是按照字符操作的
- 如果去操作二进制文件(声音,视频,doc,pdf),可能造成文件损坏
使用字节流实现对二进制文件复制
-
BufferedOutputstream和BufferedInputStream结合实现对图片的拷贝
package com.bufferd; import com.sun.xml.internal.ws.api.ha.StickyFeature; import java.io.*; public class BufferedOutputStream_ { public static void main(String[] args) { String path1 = "e://567.jpg"; String path2 = "e://678.jpg"; BufferedInputStream bis = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(new FileInputStream(path1)); bos = new BufferedOutputStream(new FileOutputStream(path2)); byte[] b = new byte[1024]; int len; while((len = bis.read(b))!=-1){ bos.write(b,0,len); } System.out.println("拷贝成功..."); } catch (IOException e) { e.printStackTrace(); } finally { try { if(bis!=null){ bis.close(); } if (bos!=null){ bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
-
字节流可以操作二进制文件,也可以操作文本文件
对象流
-
序列化和反序列化
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,改类必须实现两个接口之一:
- Serializable //这是一个标记接口,没有方法
- Externalizable //该接口有方法需要实现,一般不使用
-
ObjectOutputStream提供序列化功能
-
序列化后保存的文件格式不是纯文本的,而是按照序列化的格式来保存
-
常用的几种基本类型都实现了Serializable接口,所以可以直接保存其数据类型
-
如果需要实例化某个类的对象,该类需要实现Serializable
代码演示
package com.outputstream; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ObjectOutputStream_ { public static void main(String[] args) throws Exception{ String filePath = "e:\\a.bat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); oos.write(100);//int -> Integer(实现了Serializable接口) oos.writeBoolean(true);//boolean -> Boolean(实现了Serializable接口) oos.writeChar('a');//char -> Character(实现了Serializable接口) oos.writeUTF("成志恒");//string -> String(实现了Serializable接口) oos.writeDouble(9.5);//double -> Double(实现了Serializable接口) //保存一个dog对象 oos.writeObject(new Dog("dog",10)); oos.close(); System.out.println("保存成功..."); } } class Dog implements Serializable { public Dog(String name, int age) { this.name = name; this.age = age; } private String name; private int age; }
-
-
ObjectInputStream提供反序列化功能
- 读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
- 在反序列化的时候,如果需要调用对象(dog)的方法,则需要向下转型,而实现向下转型的关键是当前类可以引用dog类,即在当前类下引用类。
代码演示
package com.inputstream_; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import com.outputstream.Dog;8//引用dog类 public class ObjectInputStream_ { public static void main(String[] args) throws IOException, ClassNotFoundException { String filePath = "e:\\a.bat"; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath)); System.out.println(ois.readInt()); System.out.println(ois.readBoolean()); System.out.println(ois.readChar()); System.out.println(ois.readUTF()); System.out.println(ois.readDouble()); Object dog = ois.readObject(); System.out.println("类型为:"+dog.getClass()); System.out.println(dog); //向下转型 Dog dog1 = (Dog)dog; System.out.println(dog1.getAge()); ois.close(); System.out.println("反序列成功"); } }
-
注意事项和细节说明
-
读写顺序要一致
-
要求实现序列化或反序列化对象,需要实现Serializable接口
-
序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
//SerialVersionUID序列化的版本号,可以提高兼容性 private static final long serialVersionUID = 1L;
-
序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
//下面两个属性都不会被序列化 private static String color; private transient String nation;
-
序列化对象时,要求里面属性的类型也需要实现序列化接口
代码演示
Master类
package com.outputstream; public class Master { }
Dog类新增加一个Master类型的属性
private Master master = new Master();
此时运行ObjectOutputStream_系统会报错
如果要解决上述问题,需要Master类实现Serializable接口
package com.outputstream; import java.io.Serializable; public class Master implements Serializable { }
此时再运行ObjectOutoutStream_,保存成功
-
序列化具备可继承性,也就是如果某类已经 实现了序列化,则它的所有子类也已经默认实现了序列化
-
标准输入输出流
-
System.in(标准输入:键盘)
- 该流为System类中的public final static InputStream in = null;
- 编译类型:InputStream
- 运行类型:BufferedInputStream
-
System.out(标准输出:显示器)
- 该流为System类中的public final static PrintStream out = null;
- 编译类型:PrintStream
- 运行类型:PrintStream
-
代码演示
package com.standard; import java.util.Scanner; public class OutAndIn { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入数据"); String next = scanner.next(); System.out.println("next="+next); } }
转换流
-
在读取文件内容时,会默认文件为utf-8方式编码的,一旦文件编码方式改变了,所读取到的信息就会产生乱码,所以有转换流的出现
-
转换流可以把一个字节流转换成字符流,而字节流读取文件信息可以按照其编码方式来读取,所以能很好解决乱码问题
-
转换流有InputStreamReader与OutputStreamWriter
-
InputStreamReader:Reader的子类,可以将InputStream包装成Reader
-
OutputStreamWriter:Writer的子类,可以将OutputStream包装成Writer
-
当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
-
可以在使用是指定编码格式(比如utf-8,gbk,gb2312等)
-
-
演示使用InputStreamReader转换流解决中文乱码问题
将字节流FileInputStream转换成字符流InputStreamReader,指定编码gbk/utf-8
package com.inputstream_; import java.io.*; public class InputStreamReader_ { public static void main(String[] args) throws IOException { String filePath = "e:\\a.txt"; //1.将FieleInputStream转换成InputStreamReader,编码方式为gbk InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"gbk"); //2.将InputStreamReader传入BufferedReader BufferedReader br = new BufferedReader(isr); //3.读取文件 String s = br.readLine(); //4.打印文件内容 System.out.println(s); //5.关闭流 br.close(); } }
为了减少代码行数,在开发中经常把1和2合在一起写
BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream(filePath),"gbk"));
-
OutStreamWriter同理
package com.outputstream; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; public class OutStreamWriter_ { public static void main(String[] args) throws IOException { String filePath = "e:\\a.txt"; String charSet = "utf-8"; OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath),charSet); osw.write("hello,成志恒11"); osw.close(); } }
打印流
-
打印流只有输出流没有输入流
-
PrintStream(字节打印流)
在默认情况下,PrintStream输出数据的位置是标准输出(显示器)
演示代码
package com.print; import java.io.PrintStream; public class PrintStream_ { public static void main(String[] args) { PrintStream printStream = System.out; printStream.println("hello,word"); printStream.close(); } }
通过查看Print的底层源码
public void print(String s) { if (s == null) { s = "null"; } write(s); }
可以知道print底层使用的是write,所以我们可以直接调用write进行打印/输出
printStream.write("hello,word",getBytes());
所得运行结果跟上述一样。
另外,通过System.setOut()方法可以将内容输出到指定的的设备上
String filePath = "e:\\a.txt"; System.setOut(new PrintStream(filePath)); System.out.println("hi,word!");
运行结果
-
PrintWriter(字符字符打印流)
- PrintWriter使用完之后必须关闭流,否则写入的内容不会刷新
使用PrintWriter将内容输出到指定设备
package com.print; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class PrintWriter_ { public static void main(String[] args) throws IOException { PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\a.txt")); printWriter.print("hello.word!!!"); //必须关闭流! printWriter.close();//相当于flush+关闭流 } }
Properties类
-
Properties类主要用于读取Java的配置文件。
-
传统的读取配置文件信息的方法
代码演示
package com.properties; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class Properties_ { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties")); String line = ""; while((line = br.readLine()) != null){ //利用split将等号前后的内容划分成两个数组 String[] split = line.split("="); System.out.println(split[0]+"值为"+split[1]); } } }
如果我们使用传统方法获取指定的ip值,会有很多问题,所以一般使用Properties类去读取配置文件
-
Properties类读取配置文件
-
该类是专门用于读取配置文件的集合类,配置文件的格式需要按照一下格式编写:
键=值
键=值
-
注意:键值对不需要有空格,值不需要用引号括起来,默认类型为String
-
Properties的常见方法
- load:加载配置文件的键值对到Priperties对象
- list:将数据显示到指定设备
- getProperty(key):根据键获取值
- setProperty(key,value):设置键值对到Properties对象
- store:将Properties中的键值对存储到配置文件。在idea中,保存信息到配置文件,如果信息含有中文,会存储为Unicode码
- http://tool.chinaz.com/tools/unicode/aspx Unicode码查询工具
代码演示
package com.properties; import java.io.FileReader; import java.io.IOException; import java.util.Properties; public class Properties_01 { public static void main(String[] args) throws IOException { //1.创建Properties对象 Properties properties = new Properties(); //2.加载指定文件 properties.load(new FileReader("src\\mysql.properties")); //3.把k-v输出到控制台 properties.list(System.out); //4.根据key值获取相对应的value String user = properties.getProperty("user"); System.out.println("用户名="+user); } }
运行结果
-
-
使用Properties类来创建配置文件
package com.properties; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; public class Properties_02 { public static void main(String[] args) throws IOException { Properties properties = new Properties(); properties.setProperty("charset","utf8"); properties.setProperty("user","志恒");//中文保存为Unicode码 properties.setProperty("pwd","11111"); properties.store(new FileWriter("src\\mysql2.properties"),null); System.out.println("保存成功"); } }
运行结果
-
使用Properties来修改文件内容
- 如果该文件没有key,就会自动创建
- 如果该文件有key,就会修改
properties.setProperties("pwd",888888);
-
Properties父类是Hashtable,所以其核心代码底层就是Hashtable 的核心方法
public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value;//如果key存在,就替换 return old; } } addEntry(hash, key, value, index);//如果是新的值,就addEntry return null; }