一、字符流与字节流
1.标准的输入输出流概述
标准输入流in:
public static final InputStream in in为System类中的一个静态成员变量,为字节输入流,用来读取键盘录入的数据;
使用:
InputStream is = System.in; //创建InputStream类型的对象
Scanner sc = new Scanner(System.in);
注意:这里的标准输入流in作为引用类型修饰的成员变量,区别于普通的:public static final int x;
标准输出流out:
public static final PrintStream out out也为System类中的一个静态变量,为字节输出流,将数据输出到命令行;
使用:
System.out.println(); //比较熟悉了
2.OutputStreamWriter的概述和使用
概述:由于标准输出流是一个字节输出流,所以只能输出字节或者字节数组,这里的高效输入流BufferedReader从硬盘中读取到的是字符串,存入内存中,如果想输出,还需要转换成字节数组。因此要想通过标准输出流输出字符串,可使用OutputStreamWriter类将标准输出流转换成字节输出流。
public static void main(String[] args) throws IOException {
//创建输入流对象
BufferedReader br = new BufferedReader(new FileReader("Student.java"));
//创建输出流对象
Writer w = new OutputStreamWriter(System.out);
BufferedWriter bw = new BufferedWriter(w);
//进行数据的读写
String line; //定义字符串存储读取的字符串
while((line = br.readLine()) != null) {
bw.write(line); //将字符串输出到命令行
bw.newLine(); //解决换行符问题
}
//释放资源
br.close();
bw.close();
}
3.InputStreamReader的概述和使用
概述:标准输入流是一个字节输入流,所以在使用字符输出流将读取到的字节写入文件时,需要将字节数组转换成字符串,这里使用创建String对象的方式实现,然后相当于对字符串进行写入操作;
public static void main(String[] args) throws IOException {
//创建输入流对象
InputStream is = System.in; //这里创建字节输入流对象
//创建输出流对象
FileWriter fw = new FileWriter("a.txt"); //这里创建字符输出流对象
//进行数据的读写
byte[] bys = new byte[1024]; //以字节数组的方式读写
int len;
while((len = is.read(bys)) != -1) {
fw.write(new String(bys, 0, len));//写数据,这里的new String(bys, 0, len)是将读取到的字节数组转换成字符串,方便write写入
fw.flush(); //刷新缓冲区
}
//释放资源
is.close();
fw.close();
}
使用InputStreamReader类将字节流对象转换成字符输入流对象,直接利用字符数组方式进行数据的读写:
public static void main(String[] args) throws IOException {
//创建输入流对象
InputStream is = System.in; //这里创建字节输入流对象
Reader r = new InputStreamReader(is); //将字节输入流对象转换成字符输入流对象
//创建输出流对象
FileWriter fw = new FileWriter("a.txt"); //这里创建字符输出流对象
//进行数据的读写
char[] chs = new char[1024];
int len;
while((len = r.read(chs)) != -1) {
fw.write(chs, 0 ,len);//写数据,这里直接写入字符数组
fw.flush(); //刷新缓冲区
}
//释放资源
is.close();
fw.close();
}
二、打印流
1.打印流的概述
概述:向文本输出流打印对象的格式化表示形式,此类包含 PrintStream 中的所有 print 方法,它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。 与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。
此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 checkError() 是否出现错误。
public static void main(String[] args) throws IOException {
//创建打印流对象
PrintWriter pw = new PrintWriter("a.txt");
//写出数据
pw.write("hello");
pw.write("world");
pw.write("java");
//释放资源
pw.close();
}
2.打印流特有的功能
自动换行:使用方法println()取代write()实现自动换行;
自动刷新:创建PrintWriter对象时,可以利用构造方法启动刷新开关,同时必须使用println等3方法才能实现自动刷新;
注意:创建FileWriter对象时构造方法中的boolean类型参数是是否追加;
创建打印流对象时构造方法中的boolean类型参数是是否自动刷新;
代码如下:(实现的功能:将几个字符串hello、world、java打印到文件a.txt中)
public static void main(String[] args) throws IOException {
//创建打印流对象
PrintWriter pw = new PrintWriter(new FileWriter("a.txt"),true);
//写出数据
pw.println("hello");
pw.println("world");
pw.println("java");
//释放资源
pw.close();
}
3.使用打印流复制文本文件
注:对于文本文件的复制,首先要明确数据源和目的地
数据源:a.txt
目的地:d:\a.txt
public static void main(String[] args) throws IOException {
//创建输入流
BufferedReader br = new BufferedReader(new FileReader("a.txt")); //从数据源输入到缓冲区
//创建打印流对象
PrintWriter pw = new PrintWriter(new FileWriter("d:\\a.txt"),true); //从缓冲区输出到目的地
//读写数据
String line;
while((line = br.readLine())!= null) {
pw.println(line);
}
//释放资源
pw.close();
}
三、对象操作流
1.对象操作流概述
对象操作流:可以用于读写任意类型的对象;
ObjectOutputStream:
writeObject:用于将内存中的对象写入流中(这里的流指对应的硬盘中文件,比如.txt)
ObjectOutputStream(OutputStream out):构造方法
ObjectInputStream:
readObject:用于从流中读取对象到内存中(这里的流指对应的硬盘中文件,比如.txt)
ObjectInputStream(InputStream in):构造方法
注意:只能使用对象输出流写出对象,使用对象输入流来读取对象;
只能将支持实现 java.io.Serializable 接口的对象写入流中。
2.使用对象操作流读写对象
使用对象输出流写对象(写到.txt文件中):
注:这里的a.txt文件在执行代码后,会直接在项目根目录创建,不用自己另外创建
public static void main(String[] args) throws IOException {
//创建对象输出流的对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
//创建学生对象
Student s1 = new Student("hxf",25);
Student s2 = new Student("ddd",24);
//写学生对象到.txt文件
oos.writeObject(s1);
oos.writeObject(s2);
//释放资源
oos.close();
}
使用对象输入流读对象(将.txt文件中的student对象写到内存中,并输出到命令行):
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建对象输入流的对象
ObjectInputStream iis = new ObjectInputStream(new FileInputStream("a.txt"));
//读取对象
try {
while(true) {
Object obj = iis.readObject(); //这里抛出找不到类异常,即有可能Student类被删掉了
System.out.println(obj);
}
}
catch(EOFException e) {
System.out.println("读到结尾了");
}
//释放资源
iis.close();
}
分析:首先创建对象输入流对象,然后使用try…catch…语句,因为可能会出现对象读取到结尾继续往后读的异常,我们需要捕获该异常,try中使用while死循环,当读取到结尾后出现异常,即跳出该死循环,最后释放资源。
3.使用对象操作流读写集合对象
使用对象输出流写集合对象(写到.txt文件中):
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));//创建对象输出流的对象
ArrayList<Student> list = new ArrayList<Student>(); //创建集合
list.add(new Student("hxf",25));
list.add(new Student("ddd",24));
oos.writeObject(list); //将集合写入内存(.txt文件)
oos.close();
使用对象输入流读集合对象(将集合对象读到内存中):
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建对象输入流的对象
ObjectInputStream iis = new ObjectInputStream(new FileInputStream("a.txt"));
//读取对象
Object obj = iis.readObject();
System.out.println(obj);
//获取集合对象,可以进行集合操作
ArrayList<Student> list = (ArrayList<Student>)obj;
//释放资源
iis.close();
}
4.序列化接口出现的ID不匹配的问题
需要进行对象读写的类必须实现接口java.io.Serializable,该接口作为一种表示(其中不含任何抽象方法),实现该接口的类会根据该类自身的成员变量、方法生成一个ID(serialVersionUID),若成员变量发生变化,则类中该ID也会发生变化;这时,如果我们将Student类对象写入.txt文件中,对应有一个serialVersionUID,若此时在Student类中增加一个成员变量,则Student类中的serialVersionUID发生变化,这时使用(读取也算使用).txt文件中的Student对象则会出现serialVersionUID与定义的Student不匹配问题(报错),所以采用以下方式解决,一开始就将serialVersionUID定义为一个常量:
private static final long serialVersionUID = 4414407504887034035L;
四、Properties
1.Properties概述
概述:Properties 类表示了一个持久的属性集(比如把内存中的键和值存到硬盘中的文件中),Properties 可保存在流中或从流中加载,属性列表中每个键及其对应值都是一个字符串。
构造方法:
继承自:类 Hashtable
与HashMap的区别:
Hashtable:非 null 对象可以用作键或值;是同步的,安全性高,效率低;
HashMap:可以存null的的对象作为键或值;是非同步的,安全性低,效率高;
Properties 类使用举例:
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建属性列表
Properties prop = new Properties();
//添加映射关系
prop.put("001", "hxf");
prop.put("002", "ddd");
//遍历属性列表,两种方法
//第一种,获取所有key,通过key获取value
Set<Object> keys = prop.keySet();
for(Object key:keys) {
Object value = prop.get(key);
System.out.println("key:"+key+" "+"value:"+value);
}
//第二种,利用Map.Entry
Set<Map.Entry<Object, Object>> entrys = prop.entrySet();
for(Map.Entry<Object, Object> entry:entrys) {
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println("key:"+key+" "+"value:"+value);
}
}
2.Properties和IO流结合的功能
1.void list(PrintWriter out) :将属性列表输出到指定的输出流。
//创建属性列表
Properties prop = new Properties();
//添加映射关系
prop.setProperty("001", "hxf");
prop.setProperty("002", "ddd");
//创建打印流对象
PrintWriter out = new PrintWriter("c.txt");
prop.list(out);
//释放资源
out.close();
运行结果:
2. void load(Reader reader) :按简单的面向行的格式从输入字符流中读取属性列表(键值对)。
//创建属性列表
Properties prop = new Properties();
//创建输入流对象
FileReader fr = new FileReader("c.txt");
prop.load(fr);
System.out.println(prop);
//释放资源
fr.close();
运行结果:
3. void store(Writer writer, String comments) :将此 Properties 表中的属性列表(键和元素对)写入到字符输出流。
//创建属性列表
Properties prop = new Properties();
//添加映射关系
prop.setProperty("001", "hxf");
prop.setProperty("002", "ddd");
//创建输出流对象
FileWriter fw = new FileWriter("d.txt");
//使用void store(Writer writer, String comments)
prop.store(fw,"use store");
//释放资源
fw.close();
运行结果:
五、编码
1.编码表的概述
编码表作用:把计算机底层的二进制数据转换成我们能看到的字符;
常见编码表:
ASCII:最早的编码表
GB2312 - - GBK:中国使用的码表
Unicode:所有字符都占2个字节
UTF - 8:长度可变的码表
Java中默认使用:Unicode
注:ANSI为本地编码表,跟操作系统的环境、语言等有关。
2.Java中字符串的编码
Java中字符串默认编码:ANSI(对于简体中文系统为:GBK)
解决乱码的方式:读写(或者说编解码)都采用同一种编码方式,则不会出现乱码。
public static void main(String[] args) throws IOException, ClassNotFoundException {
String s = "高薪就业"; //默认将字节以GBK的方式编码成字符串
byte[] bys = s.getBytes("UTF-8"); //通过指定"UTF-8"方式将字符串类型转换(解码)成字节数组,这里
//创建输出流对象
FileOutputStream fos = new FileOutputStream("a.txt");
fos.write(bys);
//创建输入流对象
FileInputStream fis = new FileInputStream("a.txt");
byte[] bys1 = new byte[1024];
int len;
while((len = fis.read(bys1))!=-1) {
System.out.println(new String(bys1,0,len,"UTF-8")); //将读取到的字节数组使用"UTF-8"的方式编码成字符串
}
//释放资源
fos.close();
fis.close();
}
分析:这里解码使用“UTF-8”的方式将字符串解码成字节数组,编码也用“UTF-8”的方式将字节数组编码成字符串,所以不会出现乱码。
3.Java字符流中的编码
问题:下面的代码是使用默认的GBK的编码方式,正常输出会在b.txt中写入“北京”,若此时将b.txt的编码方式改为“UTF-8”,则会出现乱码
FileWriter fw = new FileWriter("b.txt");
String s = "北京";
fw.write(s);
fw.close();
解决:
OutputStreamWriter 是字符流通向字节流的桥梁,其包含一个构造方法,可以指定编码方式:
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");
String s = "北京";
osw.write(s);
osw.close();
分析:由于将要写入的.txt文件的编码方式为“UTF-8”,所以输出流也需要以“UTF-8”的方式写入数据到该文件,标准输出流OutputStreamWriter 具有指定编码方式的功能。