为了提高数据读写的速度,Java API提供了带缓冲功能的流类:缓冲流。
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
缓冲流的基本原理:在创建流对象时,内部会创建一个缓冲区数组(缺省使用8192个字节(8Kb)的缓冲区),通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("abc.jpg"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("abc_copy.jpg"));
package com.suyv.test1;
import org.junit.Test;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 09:46
* @Description: 字节缓冲输出流的使用--BufferedOutputStream
*/
public class BufferedOutputStreamDemo {
@Test
public void Test01() throws IOException {
//相当于把字节流进行了一次装
FileOutputStream fos = new FileOutputStream("file\\hello.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
//简单写法
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("file\hello.txt"));
//也可以进行追加
//BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("file\hello.txt",true));
// 写一个字节
bos.write(107);
// 写一个字节数组
byte[] buffer = {97,98,99};
bos.write(buffer);
bos.write("hello".getBytes());
/*写入内容为:kabchello*/
// 关闭流资源
bos.close();
}
}
package com.suyv.test1;
import org.junit.Test;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 09:54
* @Description: 字节缓冲输入流的使用--BufferedInputStream
*/
public class BufferedInputStreamDemo {
@Test
public void Test01() throws IOException {
// 创建流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file\\hello.txt"));
// 操作流
// 一次读一个字节
System.out.println((char) bis.read());
// 一次读一个字节数组
byte[] buffer = new byte[5];
int len;
while ((len = bis.read(buffer)) != -1){
System.out.println(new String(buffer,0,len));
}
// 关闭流资源
bis.close();
}
}
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 10:48
* @Description: 使用字节缓冲流复制文件--BufferedInputStream-BufferedOutputStream
*/
public class BufferedInputOutputStream {
@Test
public void Test01() throws IOException {
// 创建流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file\\cat.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("file\\cat_copy.jpg"));
// 操作流
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
// 关闭流
bis.close();
bos.close();
}
}
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
package com.suyv.test1;
import org.junit.Test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 11:03
* @Description: 字符缓冲输出流的使用--BufferedWriter
*/
public class BufferedWriter01 {
@Test
public void Test01() throws IOException {
//创建流
BufferedWriter bw = new BufferedWriter(new FileWriter("file\\bw.txt"));
// 操作流
bw.write(97);
bw.write("hello");
char[] chs = {'j','a','v','a',};
bw.write(chs);
/*文件内容为:ahellojava*/
// 关闭流
bw.close();
}
}
package com.suyv.test1;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 11:07
* @Description: 字符缓冲输入流的使用--BufferedReader
*/
public class BufferedReader01 {
@Test
public void Test01() throws IOException {
// 创建流
BufferedReader br = new BufferedReader(new FileReader("file\\bw.txt"));
// 操作流
//读一个字节
System.out.println((char) br.read());
//循环读字符数组
char[] buffer = new char[5];
int len;
while ( (len = br.read(buffer)) != -1){
System.out.print(new String(buffer,0,len));
}
// 关闭流
br.close();
}
}
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 11:37
* @Description: 使用字符缓冲流复制文件--BufferedInputStream-BufferedOutputStream
*/
public class BufferedReaderWriter {
/*
* 需求:把当前项目目录下的snow.txt内容复制到当前项目目录下的snow_copy.txt中
*
* 数据源:
* a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader -- FileReader -- BufferedReader
* 目的地:
* b.txt -- 写出数据 -- 字符转换流 -- OutputStreamWriter -- FileWriter -- BufferedWriter
*/
@Test
public void Test01() throws IOException {
// 创建缓冲流
BufferedReader br = new BufferedReader(new FileReader("file\\snow.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("file\\snow_copy.txt"));
// 操作流
char[] buffer = new char[1024];
int len;
while ((len = br.read(buffer)) != -1){
bw.write(buffer,0,len);
}
// 关闭流
bw.close();
br.close();
}
}
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 12:15
* @Description: 字符缓冲流特有方法
*/
public class BufferedReaderWriter01 {
// BufferedReader:public String readLine(): 读一行文字。
// BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。
@Test
public void Test01() throws IOException {
// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("file\\snow.txt"));
// 定义字符串,保存读取的一行文字
String line;
// 循环读取,读取到最后返回null
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// 释放资源
br.close();
}
@Test
public void Test02() throws IOException {
// 创建流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("file\\out.txt"));
// 写出数据
bw.write("Hello");
// 写出换行
bw.newLine();
bw.write("中国");
bw.newLine();
bw.write("欢迎你");
bw.newLine();
// 释放资源
bw.close();
}
}
说明:
查询API,缓冲流读写方法与基本的流是一致的,我们通过复制大文件(375MB),测试它的效率。
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 12:24
* @Description: TODO
*/
public class EfficiencyDemo {
/*节点流:FileInputStream、FileOutputStream
缓冲流:BufferedInputStream、BufferedOutputStream
效率测试*/
//方法1:使用节点流FileInputStream\FileOutputStream实现非文本文件(260MB)的复制
public void copyFileWithFileStream(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件-造流
fis = new FileInputStream(new File(srcPath));
fos = new FileOutputStream(new File(destPath));
//2. 复制操作(读、写)
byte[] buffer = new byte[100];
int len;//每次读入到buffer中字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//3. 关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void test1(){
String srcPath = "E:\\JavaTest\\test.mp4";
String destPath = "E:\\JavaTest\\test_copy.mp4";
long start = System.currentTimeMillis();
copyFileWithFileStream(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//7645毫秒
}
//方法2:使用缓冲流BufferedInputStream\BufferedOuputStream实现非文本文件(260MB)的复制
public void copyFileWithBufferedStream(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1. 造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2. 造流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3. 读写操作
int len;
byte[] buffer = new byte[100];
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源(如果有多个流,我们需要先关闭外面的流,再关闭内部的流)
try {
if (bos != null)
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void test2(){
String srcPath = "E:\\JavaTest\\test.mp4";
String destPath = "E:\\JavaTest\\test_copy1.mp4";
long start = System.currentTimeMillis();
copyFileWithBufferedStream(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//460毫秒
}
}
引入情况1:
使用FileReader 读取项目中的文本文件。由于IDEA设置中针对项目设置了UTF-8编码,当读取Windows系统中创建的文本文件时,如果Windows系统默认的是GBK编码,则读入内存中会出现乱码。
package com.suyv.test2;
import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 14:29
* @Description: 字符输入流乱码
*/
public class Demo01 {
@Test
public void Test01() throws IOException {
// 创建FileReader对象
FileReader fr = new FileReader("file\\gbk.txt");
// 操作流
int ch;
while ((ch = fr.read()) != -1){
System.out.print((char) ch);
}
/*输出内容:
���?��
����*/
// 关闭流
fr.close();
}
}
作用:转换流是字节与字符间的桥梁!
具体来说:
转换流java.io.InputStreamReader
,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造器:
举例:
//使用默认字符集
InputStreamReader isr1 = new InputStreamReader(new FileInputStream("in.txt"));
//使用指定字符集
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
示例代码:
package com.suyv.test2;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 14:43
* @Description: 转换输入流--InputStreamReader
*/
public class InputStreamReader01 {
@Test
public void Test01() throws IOException {
// 定义文件路径,文件为gbk编码
String fileName = "file\\gbk.txt";
//方式1:
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName));
// 定义变量,保存字符
int ch;
// 使用默认编码字符流读取,乱码
while ((ch = isr.read()) != -1) {
System.out.print((char)ch);
}
isr.close();
}
@Test
public void Test02() throws IOException {
// 定义文件路径,文件为gbk编码
String fileName = "file\\gbk.txt";
//方式2:
// 创建流对象,指定GBK编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName) , "GBK");
// 使用指定编码字符流读取,正常解析
int ch;
while ((ch = isr.read()) != -1) {
System.out.print((char)ch);
}
isr.close();
}
}
转换流java.io.OutputStreamWriter
,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造器:
举例:
//使用默认字符集
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
//使用指定的字符集
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
示例代码:
package com.suyv.test2;
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-21 14:47
* @Description: 转换输出流--OutputStreamWriter
*/
public class OutputStreamWriter01 {
@Test
public void Test01() throws IOException {
// 定义文件路径
String FileName = "file\\out_utf8.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("你好"); // 保存为6个字节
// 关闭流
osw.close();
}
@Test
public void Test02() throws IOException {
// 定义文件路径
String FileName2 = "file\\out_gbk.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("你好");// 保存为4个字节
// 关闭流
osw2.close();
}
}
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。
字符编码(Character Encoding) : 就是一套自然语言的字符与二进制数之间的对应规则。
编码表:生活中文字和计算机中二进制的对应规则
乱码的情况:按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
编码:字符(人能看懂的)--字节(人看不懂的)
解码:字节(人看不懂的)-->字符(人能看懂的)
字符集Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。
ASCII字符集 :
ISO-8859-1字符集:
GBxxx字符集:
Unicode字符集 :
UTF-8字符集:
小结:
如果需要将内存中定义的变量(包括基本数据类型或引用数据类型)保存在文件中,那怎么办呢?
int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;
String name = "巫师";
Student stu = new Student("张三",23,89);
Java提供了数据流和对象流来处理这些类型的数据:
数据流:DataOutputStream、DataInputStream
对象流DataInputStream中的方法:
byte readByte() ????????????????????????????????short readShort()
int readInt() ????????????????????????????????????????long readLong()
float readFloat() ????????????????????????????????double readDouble()
char readChar() ????????????????????????????????boolean readBoolean()
String readUTF() ????????????????????????????????void readFully(byte[] b)
对象流:ObjectOutputStream、ObjectInputStream
说明:对象流的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
ObjectOutputStream中的构造器:
public ObjectOutputStream(OutputStream out)
: 创建一个指定的ObjectOutputStream。
FileOutputStream fos = new FileOutputStream("game.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
ObjectOutputStream中的方法:
ObjectInputStream中的构造器:
public ObjectInputStream(InputStream in)
: 创建一个指定的ObjectInputStream。
FileInputStream fis = new FileInputStream("game.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
ObjectInputStream中的方法:
1、何为对象序列化机制?
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
2、序列化机制的重要性
序列化是 RMI(Remote Method Invoke、远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。
序列化的好处,在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
3、实现原理
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制。方法为:
public final void writeObject (Object obj)
: 将指定的对象写出。反序列化:用ObjectInputStream类读取基本类型数据或对象的机制。方法为:
public final Object readObject ()
: 读取一个对象。如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现java.io.Serializable
接口。Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。
举例1:序列化变量
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 11:26
* @Description: 对象流的使用--ObjectInputStream和 ObjectOutputStream
*/
public class Demo01 {
@Test
public void save() throws IOException {
String name = "巫师";
int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file\\game.dat"));
oos.writeUTF(name);
oos.writeInt(age);
oos.writeChar(gender);
oos.writeInt(energy);
oos.writeDouble(price);
oos.writeBoolean(relive);
oos.close();
}
@Test
public void reload()throws IOException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file\\game.dat"));
String name = ois.readUTF();
int age = ois.readInt();
char gender = ois.readChar();
int energy = ois.readInt();
double price = ois.readDouble();
boolean relive = ois.readBoolean();
System.out.println(name+"," + age + "," + gender + "," + energy + "," + price + "," + relive);
ois.close();
}
}
举例2:序列化对象
package com.suyv.test1;
import java.io.Serializable;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 11:31
* @Description: 实体类 -- Employee
*/
public class Employee implements Serializable {
private static final long serialVersionUID = -1446398935944895849L;
public static String company; //static修饰的类变量,不会被序列化
public String name;
public String address;
public transient int age; // transient瞬态修饰成员,不会被序列化
public Employee(String name, String address, int age) {
this.name = name;
this.address = address;
this.age = age;
}
public static String getCompany() {
return company;
}
public static void setCompany(String company) {
Employee.company = company;
}
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 +
", company=" + company +
'}';
}
}
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 11:45
* @Description: 序列化流对象的使用
*/
public class Demo02 {
@Test
public void save() throws IOException {
Employee.setCompany("云和数据");
Employee e = new Employee("三石", "河南郑州", 23);
// 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file\\employee.dat"));
// 写出对象
oos.writeObject(e);
// 释放资源
oos.close();
System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
}
@Test
public void reload() throws IOException, ClassNotFoundException {
// 创建反序列化流
FileInputStream fis = new FileInputStream("file\\employee.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
// 读取一个对象
Employee e = (Employee) ois.readObject();
// 释放资源
ois.close();
fis.close();
System.out.println(e);
}
}
举例3:如果有多个对象需要序列化,则可以将对象放到集合中,再序列化集合对象即可。
package com.suyv.test1;
import org.junit.Test;
import java.io.*;
import java.util.ArrayList;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 11:48
* @Description: 序列化多个对象--序列化集合
*/
public class Demo03 {
@Test
public void save() throws IOException {
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee("张三", "周口", 23));
list.add(new Employee("李四", "郑州", 24));
list.add(new Employee("王五", "洛阳", 25));
// 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file\\employees.dat"));
// 写出对象
oos.writeObject(list);
// 释放资源
oos.close();
}
@Test
public void reload() throws IOException, ClassNotFoundException {
// 创建反序列化流
FileInputStream fis = new FileInputStream("file\\employees.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
// 读取一个对象
ArrayList<Employee> list = (ArrayList<Employee>) ois.readObject();
// 释放资源
ois.close();
fis.close();
System.out.println(list);
}
}
问题1:
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException
异常。
问题2:
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。发生这个异常的原因如下:
解决办法:
Serializable
接口给需要序列化的类,提供了一个序列版本号:serialVersionUID
。凡是实现 Serializable接口的类都应该有一个表示序列化版本标识符的静态变量:
static final long serialVersionUID = 234242343243L; // 它的值由程序员随意指定即可。
package com.atguigu.object;
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1324234L; //增加serialVersionUID
//其它结构:略
}
System.in
和System.out
分别代表了系统标准的输入和输出设备
默认输入设备是:键盘,输出设备是:显示器
System.in的类型是InputStream
System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
重定向:通过System类的setIn,setOut方法对默认设备进行改变。
举例:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。
package com.suyv.test02;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 11:52
* @Description: 标准输入流输出流的使用
*/
public class Demo01 {
@Test
public void Test01(){
System.out.println("请输入信息(退出输入e或exit):");
// 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { // 读取用户输入的一行数据 --> 阻塞程序
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
// 将读取到的整行字符串转成大写输出
System.out.println("-->:" + s.toUpperCase());
System.out.println("继续输入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
拓展:System类中有三个常量对象:System.out、System.in、System.err
查看System类中这三个常量对象的声明:
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
奇怪的是,
final声明的常量,表示在Java的语法体系中它们的值是不能修改的,而这三个常量对象的值是由C/C++等系统函数进行初始化和修改值的,所以它们故意没有用大写,也有set方法。
public static void setIn(InputStream in) {
checkIO();
setIn0(in);
}
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
public static void setErr(PrintStream err) {
checkIO();
setErr0(err);
}
private static void checkIO() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setIO"));
}
}
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream
类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
打印流的分类
名称 | 类名 |
字节打印流 | PrintStream |
字符打印流 | PrintWriter |
打印流的特点
只负责输出数据,不负责读取数据
永远不会抛出IOException
有自己的特有方法
PrintWriter特点
自动换行 println()
不能输出字节 可以输出字节以外的内容
必须是通过配置 自动刷新 (println,printf,format)
boolean autoFlush: true 自动刷新 false,不自动刷新
包装流本身没有写出功能
将字节输出流转换字符输出流
PrintWriter构造方法
方法名 | 说明 |
PrintWriter(String fileName) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
PrintWriter(Writer out, boolean autoFlush) | 创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果为真,则println , printf ,或format方法将刷新输出缓冲区 |
PrintWriter特有方法
方法名 | 说明 |
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) | 使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据 |
代码举例1
package com.suyv.test02;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 12:25
* @Description: TODO
*/
public class Demo02 {
@Test
public void Test01() throws FileNotFoundException {
PrintStream ps = new PrintStream("file\\io.txt");
ps.println("hello");
ps.println(1);
ps.println(1.5);
ps.close();
}
}
代码举例2
package com.suyv.test02;
import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 12:26
* @Description: TODO
*/
public class Demo03 {
@Test
public void Test03(){
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("file\\text.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {// 把标准输出流(控制台输出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
}
}
代码举例3:自定义一个日志工具
package com.suyv.test02;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 12:28
* @Description: 日志工具类
*/
public class Logger {
/*
记录日志的方法。
*/
public static void log(String msg) {
try {
// 指向一个日志文件
PrintStream out = new PrintStream(new FileOutputStream("log.txt", true));
// 改变输出方向
System.setOut(out);
// 日期当前时间
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime = sdf.format(nowTime);
System.out.println(strTime + ": " + msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
package com.suyv.test02;
import org.junit.Test;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 12:29
* @Description: TODO
*/
public class LogTest {
@Test
public void Test01(){
//测试工具类是否好用
Logger.log("调用了System类的gc()方法,建议启动垃圾回收");
Logger.log("调用了TeamView的addMember()方法");
Logger.log("用户尝试进行登录,验证失败");
}
}
Properties集合概述
是一个Map体系的集合类
Properties可以保存到流中或从流中加载
属性列表中的每个键及其对应的值都是一个字符串
Properties基本使用
package com.test03;
import org.junit.Test;
import java.util.Properties;
import java.util.Set;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 12:32
* @Description: Properties的使用
*/
public class Demo01 {
@Test
public void Test01(){
// 创建集合对象
// Properties<String,String> prop = new Properties<String,String>(); //错误
Properties prop = new Properties();
// 存储元素
prop.put("itfxp001", "林青霞");
prop.put("itfxp002", "张曼玉");
prop.put("itfxp003", "王祖贤");
// 遍历集合
Set<Object> keySet = prop.keySet();
for (Object key : keySet) {
Object value = prop.get(key);
System.out.println(key + "," + value);
}
}
}
Properties集合的特有方法
方法名 | 说明 |
Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set<String> stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
代码演示
package com.test03;
import org.junit.Test;
import java.util.Properties;
import java.util.Set;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 13:06
* @Description: TODO
*/
public class Demo02 {
/*
* 特殊功能:
* public Object setProperty(String key,String value):添加元素
* public String getProperty(String key):获取元素
* public Set<String> stringPropertyNames():获取所有的键的集合
*/
@Test
public void Test01(){
// 创建集合对象
Properties prop = new Properties();
// 添加元素
prop.setProperty("张三", "30");
prop.setProperty("李四", "40");
prop.setProperty("王五", "50");
// public Set<String> stringPropertyNames():获取所有的键的集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);//获取元素(value)
System.out.println(key + "---" + 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)方法的格式写入输出字符流 |
代码示例
package com.test03;
import org.junit.Test;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 13:07
* @Description: TODO
*/
public class Demo03 {
// 写入数据
@Test
public void Test01() throws IOException {
// 创建集合对象
Properties prop = new Properties();
prop.setProperty("江一燕", "27");
prop.setProperty("jack", "30");
prop.setProperty("肉丝", "18");
//public void store(Writer writer,String comments) 把集合中的数据存储到文件
FileWriter w = new FileWriter("file\\name.properties");
//写
prop.store(w, "写入文件的说明:因为我要学习所有我要写入");//comments:信息的描述
w.close();
}
// 展示数据
@Test
public void Test02() throws IOException {
Properties prop = new Properties();
// public void load(Reader reader) 把文件中的数据读取到集合中
// 注意:这个文件的数据必须是键值对形式
FileReader r = new FileReader("file\\name.properties");
//读
prop.load(r);
r.close();
System.out.println("prop:" + prop);
}
}
IO技术开发中,代码量很大,而且代码的重复率较高,为此Apache软件基金会,开发了IO技术的工具类commonsIO
,大大简化了IO开发。
Apahce软件基金会属于第三方,(Oracle公司第一方,我们自己第二方,其他都是第三方)我们要使用第三方开发好的工具,需要添加jar包。
在导入commons-io-2.5.jar包之后,内部的API都可以使用。
package com.suyv.test04;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 13:23
* @Description: commons-io包下IOUtils类的使用
*/
public class Demo01 {
// 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
// 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
@Test
public void Test01() throws IOException {
IOUtils.copy(new FileInputStream("file\\text.txt"),new FileOutputStream("file\\text_copy.txt"));
System.out.println("复制成功");
}
@Test
public void Test02() throws IOException {
FileWriter fw = null;
try {
fw = new FileWriter("file\\writer.txt");
fw.write("hahah");
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeQuietly(fw);
}
}
}
参数:src:要复制的文件夹路径 dest:要将文件夹粘贴到哪里去
package com.suyv.test04;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import java.io.IOException;
import java.io.File;
/**
* @Author: 憨憨浩浩
* @CreateTime: 2023-12-22 13:31
* @Description: commons.io 包下 FileUtils 类的使用
*/
public class Demo02 {
@Test
public void Test01(){
try {
// 静态方法:void copyDirectoryToDirectory(File src,File dest);
FileUtils.copyDirectoryToDirectory(new File("E:\\Idea\\io\\aa"),new File("E:\\Idea\\io\\file"));
// 静态方法:writeStringToFile(File file,String str)
FileUtils.writeStringToFile(new File("day21\\io\\commons.txt"),"柳岩你好");
// 静态方法:String readFileToString(File file)
String s = FileUtils.readFileToString(new File("day21\\io\\commons.txt"));
System.out.println(s);
// 静态方法:void copyFile(File srcFile,File destFile)
FileUtils.copyFile(new File("io\\yangm.png"),new File("io\\yangm2.png"));
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}