属性集【Properties】
java.util.Properties
类继承于Hashtable,用来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应的值都是一个字符串。
构造方法
- public Properties():创建一个空的属性集列表。
共性的api方法
- public Object setProperty(String key,String value):保存一对属性。
- public String getProperty(String key):使用此属性列表中的指定的键搜索对应的值。
- public Set stringPropertyNames():获取所有键的名称并封装到Set集合中。
public static void main(String[] args) {
// 创建属性集对象
Properties properties = new Properties();
// 添加键值对元素
properties.setProperty("name", "abc.txt");
properties.setProperty("size", "12000");
properties.setProperty("destination","D:\\abc.txt");
properties.put("data","小孙");
System.out.println(properties);// {destination=D:\abc.txt, name=abc.txt, size=12000}
// 通过键来获取值
String data = properties.getProperty("data");
System.out.println(data); // 小孙
String size = properties.getProperty("size");
System.out.println(size);// 12000
// 遍历该属性集
// public Set<String> stringPropertyNames():获取所有键的名称并封装到Set集合中。
Set<String> keys = properties.stringPropertyNames();
// 遍历keys
for (String key : keys) {
// 通过key获取value
String value = properties.getProperty(key);
System.out.println(key + "=" + value);
}
}
与流相关的方法
- public void load(InputStream input):从字节输入流中读取键值对
参数中使用了字节输入流,通过流对象,可以关联到某个文件上,这样既可以加载文件中的数据。文件中的数据的格式:key=value
例如:
data=小孙
size=12000
name=abc.txt
代码演示:
public static void show01() throws IOException {
// 0.构建一个流对象
FileReader fr = new FileReader("day29_IO\\abc.txt");
// 1.创建Properties集合
final Properties properties = new Properties();
// 2.使用Properties集合中的方法load读取保存在输入流中的数据
properties.load(fr);
// 3.遍历Properties集合
final Set<String> set = properties.stringPropertyNames();
for (String key : set) {
// 通过key获取value值
final String value = properties.getProperty(key);
System.out.println(key + "=" + value);
}
/*
name=abc.txt
size=12000
data=小孙
目的地=D:\abc.txt
*/
}
- public void store(OutputStream out,String comments):把集合当中数据写入字节输出流中
可以使用Properties集合当中的方法store,把集合当中的临时数据,持久化写入到硬盘文件中保存。
代码示例:
public static void show02() throws IOException {
// 1. 创建Properties集合对象,添加数据
final Properties properties = new Properties();
properties.setProperty("四大名著1","红楼梦");
properties.setProperty("四大名著2","西游记");
properties.setProperty("四大名著3", "水浒传");
properties.setProperty("四大名著4", "三国演义");
// 2. 创建字节输出流/字符输出流对象,构造方法中绑定需要写入数据的目的地
final FileWriter fw = new FileWriter("day29_IO\\abcd.txt", true);
// 3. 使用Properties集合中的方法store,把集合当中的临时数据,持久化写入到硬盘当中存储
properties.store(fw, "si da ming zhu");
// 4.释放资源。
fw.close();
}
缓冲流【Buffered】
缓冲流我们理解为对原来的使用数组方式进行数据传输的一种增强
按照类型分为:
- 字符缓冲流:BufferedReader,BufferedWriter
- 字节缓冲流:BufferedInputStream,BufferedOutputStream
缓冲流的基本原理,是在创建流对象的时候,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写数据,减少系统IO操作的次数,减少开销,提高程序的读写的效率。
字节缓冲流
构造方法
- public BufferedInputStream(InputStream input):创建一个新的缓冲输入流
- public BufferedOutputStream(OutputStream output):创建一个新的缓冲输出流
代码示例:
字符缓冲流
构造方法
-
public BufferedWriter(Writer out):创建一个新的字符缓冲输出流
-
public BufferedReader(Reader in):创建一个新的字符缓冲输入流。
特有方法
-
BufferedReader:public String readLine():读取整行的文本信息。
-
BufferedWriter:public void newLine():写入一行的行分隔符 ,由系统属性定义换行符号。
字符缓冲输入流代码演示
public static void main(String[] args) throws IOException {
//1. 创建一个字符缓冲输入流对象,构造方法中传递一个字符输入流
final BufferedReader br = new BufferedReader(new FileReader("day29_IO\\abc.txt"));
//2. 使用字符缓冲输入流对象中的read/readLine,读取文本信息
/* String str = br.readLine();
System.out.println(str);// data=小孙*/
// 循环的结束条件 readLine()返回值是null
String str = null;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
// 3.释放资源。
br.close();
}
字符缓冲输出流代码演示:
public static void main(String[] args) throws IOException {
//1.创建一个字符缓冲输出流对象,构造方法中传递一个字符输出流
final BufferedWriter bw = new BufferedWriter(new FileWriter("day29_IO\\two.txt"));
//2.调用字符缓冲输出流对象中的write,把数据写入到内存缓冲区中。
bw.write("我今天学习了PS");
bw.newLine();
bw.write("3d软件mmd");
bw.newLine();
bw.write("c4d");
//3.调用字符缓冲输出流对象中的flush方法,把内存缓冲区中的数据刷新到文件中。
bw.flush();
//4.释放资源。
bw.close();
}
练习:文件复制
代码演示:
// 使用缓冲流完成文件复制
public static void show02() throws IOException {
// 获取开始的时间
long start = System.currentTimeMillis();
// 1.构建一个字节缓冲输入流
final BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\admin\\Desktop\\3.gif"));
// 2.构建一个字节缓冲输出流
final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\documents\\day29_IO\\3.gif"));
// 3.使用字节缓冲输入流对象中的方法read(byte[] b),读取文件
byte[] bytes = new byte[1024];
// 确定while循环结束的条件 read() == -1
int len = 0;// 记录读取到的有效字节个数
while ((len = bis.read(bytes)) != -1) {
// 4. 把读取到的字节内容再次写入到目的地文件中,调用write
bos.write(bytes, 0, len);
}
// 5.释放资源
bos.close();
bis.close();
// 获取结束的时间
long end = System.currentTimeMillis();
System.out.println("文件复制耗费的时间为:" + (end - start) + "ms");// 文件复制耗费的时间为:91ms
}
转换流【字节流<—>字符流】
字符编码:
按照某种规则,将字符存储到计算机中,称为编码;反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。在进行编码和解码过程中,我们必须采用是同一种规则,才能数据正常,否则,会会导致乱码现象。
- 字符编码:就是一套自然语言中的字符与二进制数之间的对应规则。
字符集:
-
字符集:是一个系统可支持的所有字符的集合,包括各国文字,标点符号,图形符号,数字等,也叫编码表。
计算机中要准确的存储和识别各种文字的字符符号,需要进行字符编码,一套字符集至少有一个套字符编码
常见的字符编码集有ASCII字符集、GBK字符集、Unicode字符集。
ASCII字符集:
- ASCII是基于拉丁字母的一套编码系统,用于显示现代英语。
- 基本的ASCII字符集,使用7位(bit)表示一个字符,共128个字符。ASCII的扩展字符集使用8位(bit)表示一个字符,共256个字符。
ISO-8859-1字符集:
- 拉丁码表,别名–Lantin-1,用于显示欧洲使用的语言,包括荷兰,丹麦,德语,意大利语,西班牙语等。
- ISO-8859-1使用单字节编码,兼容ASCII编码。
GB系列字符集
- GB2312:称为简体中文码表,里面大概含有7000多个简体汉字,此外数学符号,罗马希腊的字母、日本的假名都编进去了,连在ASCII里的原来就与的数字、标点、字母都统统重新用的两个字节编写进去了。
- GBK:最常用的中文编码。是在原来GB23121码表基础上进行扩展。使用双字节编码。共收录了21000多个汉字,完全兼容GB2312,标准,同时支持繁体汉字以及日韩汉字等。
- GB18030:最新的中文码表,共收录了7万多个汉字,采用多字节编码,每个字可以由1个字节,2个字节或者4个字节组成,支持国内少数名族的文字,同时支持繁体字以及日韩汉字等。
Unicode字符集:
- Unicode编码系统为表达任意语言的任意字符而设计的,是业界的一种标准,也成为统一编码,标准万国码表。
- 它最多使用4个字节的数字来表示每个字母、符号、或者文字,有三种常见的编码方案:UTF-8,UTF-16,UTF-32。
- UTF-8编码表,用来表示Unicode标准中的任意字符,编码规则:
1. 128个US-ASCII字符,使用的是一个字节编码
2. 拉丁字的字母,需要两个字节编码
3. 大部分常见的汉字,使用的是三个字节编码
4. 其他极少数的辅助字符,采用的四个字节编码。
编码会引发的问题
由于编码规则不一致,导致引发乱码现象。
那么如何读取GBK编码的文件呢?
InputStreamReader类
转换流java.io.InputStreamReader
,是Reader的子类,它是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,或者可以使用平台默认的字符集。
构造方法
- public InputStreamReader(InputStream in):创建一个使用默认的字符集的字符流。
- public InputStreamReader(InputStream in,String charsetName):创建一个指定字符集的字符流。
代码演示:
// 读取一个使用UTF-8编码的文件
public static void show02() throws IOException{
//1. 创建InputStreamReader对象,构造方法中传递字节输入流和和指定的编码表名称
InputStreamReader isr = new InputStreamReader(new FileInputStream("day29_IO\\one.txt"), "GBK");
//2. 使用InputStreamReader对象中的方法read读取文件中的信息
int len = 0;
while ((len = isr.read()) != -1) {
System.out.print((char)len+" ");// H e l l o W o r l d _ J a v a
}
//3. 释放资源。
isr.close();
}
OutputStreamWriter类
转换流 java.io.OutputStreamWriter
是Writer的子类,它是字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以手动指定,也可以使用平台默认的字符集。
构造方法
-
public OutputStreamWriter(OutputStream out):创建一个使用平台默认的字符集的字符流。
-
public OutputStreamWriter(OutputStream out,String charsetName):创建一个指定的字符集的字符流。
练习:转换文件的编码
将GBK编码的文本文件,转换为UTF-8编码的文件。
分析:
- 指定GBK编码的转换流,读取文本文件。InputStreamReader
- 使用UTF-8编码的转换流,写入到文本文件中。OutputStreamWriter
代码示例:
public static void main(String[] args) throws IOException {
//1.1 使用指定GBK编码的转换流,读取文本文件。InputStreamReader
final InputStreamReader isr = new InputStreamReader(new FileInputStream("day30_IO\\GBK.txt"), "GBK"); //1.2 使用UTF-8编码的转换流,写入到文本文件中。OutputStreamWriter 此时不写默认就是UTF-8
final OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day30_IO\\UTF-8.txt"));
//2. 使用转换流读取数据源文件
//2.1 定义一个指定长度的字符数组
char[] chars = new char[1024];
//2.2 定义一个变量,记录读取到的有效字符个数
int len = 0;
//2.3 循环读取
while ((len = isr.read(chars)) != -1) {
// 读取出来的数据要写入到目的地文件中
osw.write(chars, 0, len);
}
//3. 释放资源
osw.close();
isr.close();
}
序列化流
Java提供了一种对象序列化的机制,用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写入到文件中后,就相当于在文件中保存了一个对象信息。
反之,该字节序列还可以从文件读取出来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
ObjectOutputStream类
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写入到文件中,实现对象的持久化存储。
构造方法
- public ObjectOutputStream(OutputStream out):创建一个指定的OutputStream的ObjectOutputStream类对象
特有的独有方法:
- void writeObject(Object obj):将指定的对象写入到ObjectOutputStream类对象中。
序列化操作
- 一个对象想要能够序列化和反序列化,必须满足两个条件:
- 该类必须实现
java.io.Serializable
接口,Serializable接口,是一个标记型接口,如果该类没有实现Serializable接口,将会抛出NotSerializableException。 - 该类的所有属性必须是可以实现序列化或者反序列化。如果有一个属性不想让它参与序列化,则该属性必须标明是瞬态的,瞬时的,这个关键字是
transient
。
- 该类必须实现
public class Student implements Serializable {
private String name;
private transient Integer age;// 不让age属性参与序列化
}
ObjectInputStream类
java.io.ObjectInputStream
类是反序列化流,将之前使用ObjectOutputStream序列化流的原始数据恢复为对象。
构造方法
- public ObjectInputStream(InputStream in):创建一个指定的InputStream的对象反序列化流对象。
特有的方法:
- public final Object readObject():从反序列化流中读取一个对象。
对于JVM来说,能够进行反序列的对象 ,前提条件是必须能够找到class文件的类,如果找不到该类的class文件,则会抛出一个ClassNotFoundException异常。
另外,当JVM序列化对象时,能够找到class文件,但是class文件在序列化对象时,发生了修改,那么反序列化操做会抛出一个InvalidClassException异常。原因如下:
- 该类的序列化版本号与从流中读取出来描述该类的版本号不一致。
- 该类包含了未知数据类型。
- 该类没有可访问的无参构造方法。
Serializable接口给需要序列化的类,提供了一个序列化版本号,serialVersionUID 该版本号的目的就是在于验证序列化的对象和对应的类是否是版本一致的。
代码演示:
// 序列化操作类
public class Demo01ObjectOutputStream {
public static void main(String[] args) throws IOException {
//1.创建ObjectOutputStream流对象,构造方法中传递指定的字节输出流。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day30_IO\\student.txt"));
//2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中。
//2.1 先创建一个对象
Student s = new Student("小孙", 30);
s.score = 60;
//2.2写对象到文件中
oos.writeObject(s);
//3.释放资源。
oos.close();
}
}
// 反序列化类操作
public class Demo02ObjectInputStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1. 创建一个ObjectInputStream流对象,构造方法中传递一个字节输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day30_IO\\student.txt"));
// 2. 使用ObjectInputStream流对象中的方法readObject,读取保存在文件中的对象数据
Object obj = ois.readObject();
// 3.释放资源。
ois.close();
// 4. 查看对象的数据
System.out.println(obj);// Student{name='小孙', age=30}
if ( obj instanceof Student) {
Student student = (Student)obj;
System.out.println(student.getAge() + "--" + student.getName());
} else {
System.out.println("转换失败");
}
}
}
// 需要被序列化的类
import java.io.Serializable;
public class Student implements Serializable {
// 可以选择手动自定义一个序列化版本号
private static final long serialVersionUID = 1L;
//private static String name;
private String name;
private Integer age;
private transient String address = "郑州市";
transient int score;// 0
}
原理分析:
打印流
java.io.PrintStream
类能够很方便打印各种数据类型的值。
构造方法
- public PrintStream(String filename):使用指定的文件名创建一个新的打印流对象。
改变打印流的方向
正常System.out就是PrintStream类型的,数据的流动的位置在控制台中。改变数据的流动位置。通过System.setOut(PrintStream print)来改变流向。
PrintStream out = System.out;
out.print(123);// 在控制台中
// 构造方法创建一个打印流对象
PrintStream printStream = new PrintStream("day30_IO\\print.txt");
// 改变打印流的方向为"day30_IO\\print.txt"路径
System.setOut(printStream);
System.out.println("我已经改变了输出数据的位置");
System.out.println("我想在控制台中输出数据");
System.out.println("啦啦啦啦啦");