# Java-IO流
# 文件
# 创建文件
三种重载方式
new File(String pathname); // 根据路径构建一个file对象
new File(File parent,String child); // 根据父目录文件+子路径创建
new File(String parent,String child); // 根据父目录+子路径创建
注意:在java中目录也是一种文件,这样可以提供一致的API
# 文件的常见操作
文件的常用信息:
getName
getAbsolutePath
getParent
getPath(创建文件时传入的路径)
length
isFile
isDirectoryexists
创建与删除文件夹:
- mkdir:创建单级目录
- mkdirs:创建多级目录(推荐)
- delete:删除空目录或文件
# IO流原理及分类
# 流的分类
- 按操作数据单位的不同分为:字节流(8bit)二进制文件,字符流(按字符)文本文件
- 按数据流的流向不同分为:输入流、输出流
- 按流的角色不同分为:节点流,处理流和包装流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
Java的IO涉及40多个类,都是派生自上面的4个基类
# 流的体系图
# IO流体系图-常用类
# FileInputStream
- 单个字节读取
/**
* 单个字节读取,效率比较低
*/
@Test
public void test01(){
String path = "g:/hello.txt";
int readData = 0;
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
// 每次读取一个字节,如果到文件末尾会返回-1
while( (readData = inputStream.read()) != -1){
System.out.print((char)readData);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流,释放资源
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 每次读取多个字节
/**
* 使用read(byte[] b)方法,每次读取多个字节
*/
@Test
public void test02(){
String path = "g:/hello.txt";
int readLen = 0;
// 字节数组
byte[] buffer = new byte[8];
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
// 每次读取buffer的长度的数据,存入buffer
// 如果读取正常,返回实际读取的字节数
// 返回-1表示读取完毕
while( (readLen = inputStream.read(buffer)) != -1){
// String构造器可以传入字节数组,偏移量,数组长度
System.out.print(new String(buffer,0,readLen));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流,释放资源
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
# FileOutputStream
@Test
public void writeFile(){
// 创建Fileout
String path = "g:/a.txt";
FileOutputStream fileOutputStream = null;
try {
// 得到FileInputStream对象
// 1. new FileOutputStream(path) 写入内容会覆盖原来的内容
// 2. new FileOutputStream(path,true) 写入内容会追加原来的内容
fileOutputStream = new FileOutputStream(path,true);
// 写入一个字节
//fileOutputStream.write('H');
// 写入一个字符串
String str = "hello,world!";
// str.getBytes()把字符串转为字节数组
//fileOutputStream.write(str.getBytes());
// write(byte[] b, int off, int len) 从off位置开始读取len长度的字节到输出流
fileOutputStream.write(str.getBytes(),0,str.length());
} catch (IOException e){
e.printStackTrace();
}finally {
try {
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
注意:
- UTF8编码:一个英文字符占1个字节,一个中文字符占3个字节(扩展汉字可能为4个)
- GBK编码:一个英文字符占1个字节,一个中文字符占2字节
# FileReader
- 每次读取单个字符
@Test
public void test01(){
String filePath = "g:/a.txt";
FileReader fileReader = null;
int data = 0;
try {
fileReader = new FileReader(filePath);
// 每次读取一个字符
while((data = fileReader.read()) != -1){
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- 每次读取多个字符
@Test
public void test02(){
String filePath = "g:/a.txt";
FileReader fileReader = null;
char[] buffer = new char[8];
int readLen = 0;
try {
fileReader = new FileReader(filePath);
// 每次读取一个字符数组/字符串
while((readLen = fileReader.read(buffer)) != -1){
System.out.print(new String(buffer,0,readLen));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
# FileWriter
@Test
public void test01(){
String filePath = "g:/note.txt";
// 创建FileWriter
FileWriter fileWriter = null;
char[] chars = {'h','u','y','a'};
try {
fileWriter = new FileWriter(filePath,true);
fileWriter.write('H');
fileWriter.write(chars);
fileWriter.write("你好,未来的自己");
fileWriter.write("孤独的鹿角虫".toCharArray(),0,6);
fileWriter.write("上海天津",2,2);
} catch (IOException e) {
e.printStackTrace();
} finally {
// FileWriter必须关闭或刷新,数据才能写入文件
try {
//fileWriter.close();
fileWriter.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
# 节点流和处理流
# 概念
节点流:就是从一个特定的数据源读写数据,如FileReader,FileWriter
处理流:又称包装流
,可连接在已存在的流(节点流或处理流),为程序提供更强大的读写能力,也更加灵活,如BufferedReader、BufferedWriter
处理流的作用:
- 性能提高:增加缓冲来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列便捷的方法,一次性输入输出大批量数据,使用更加方便灵活
处理流使用了修饰器设计模式,不会直接与数据源相连
# 字符处理流
- BufferedReader
缓冲区的作用:减少类对底层输入流的访问频率,提高读取效率
@Test
public void test01() throws Exception {
String path = "g:/note.txt";
// 创建
BufferedReader br = new BufferedReader(new FileReader(path));
// 读取
String line;
// 当返回空表示文件读取完毕
while((line = br.readLine()) != null){
System.out.println(line);
};
// 关闭流,只需要关闭外层的BufferedReader,底层会自动关闭节点流
br.close();
}
- BufferedWriter
@Test
public void test01() throws IOException {
String path = "g:/ok.txt";
// 创建
BufferedWriter bw = new BufferedWriter(new FileWriter(path));
bw.write("hello,未来的自己!");
// 插入一个换行,和系统相关
bw.newLine();
bw.write("hello1,未来的自己!");
bw.newLine();
bw.write("hello2,未来的自己!");
bw.newLine();
// 关闭外层流即可
bw.close();
}
# 字节处理流
- BufferedInputStream
- BufferedOutputStream
@Test
public void test(){
String oriPath = "f:/fugang.png";
String tarPath = "f:/test.png";
BufferedInputStream is = null;
BufferedOutputStream os = null;
byte[] b = new byte[1024];
int readLen = 0;
try {
is = new BufferedInputStream(new FileInputStream(oriPath));
os = new BufferedOutputStream(new FileOutputStream(tarPath));
while((readLen = is.read(b)) != -1){
os.write(b,0,readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
try {
if(is != null){
is.close();
}
if(os != null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:字节流既可以处理二进制文件,也能处理文本文件
# 对象处理流
对象处理流是一种字节流
序列化
就是指把保存数据时,同时保存数据值和数据类型;反序列化
,就是指恢复数据时能同时恢复数据的值和数据类型
需要序列化的类必须实现以下两个接口之一:
- Serializable
- Externalizable
案例:
- ObjectInputStream
public class ObjectOutputStream_ {
@Test
public void test() throws IOException {
String filePath = "f:/data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
oos.writeInt(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(9.5);
oos.writeUTF("未来可期");
oos.writeObject(new Dog("旺财",10));
oos.close();
System.out.println("数据保存完毕");
}
}
- ObjectOutputStream
public class ObjectInputStream_ {
@Test
public void test() throws Exception {
String filePath = "f:/data.dat";
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.readDouble());
System.out.println(ois.readUTF());
Object dog = ois.readObject();
System.out.println("运行类型="+dog.getClass());
System.out.println("对象内容="+dog);
ois.close();
System.out.println("数据读取完毕");
// 对象在序列化和反序列化是所在的包必须保持一致,可以将Dog类的定义放在可以引用的位置
// 要调用dog的方法,需要向下转型
Dog dogObj = (Dog )dog;
System.out.println(dogObj.getName());
}
}
- Dog类
public class Dog implements Serializable {
private String name;
private Integer age;
public Dog(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
注意:
- 读写顺序要一致
- 序列化和反序列化对象需要实现,Serializable
- 序列化的类建议添加
SerialVersionUID
,为了提高版本兼容性- 序列化对象时,默认将所有属性序列化,除了
static
或transient
修饰的成员- 序列化对象时,要求
对象属性
的类型也要实现序列化接口- 序列化具备可继承性,如果某个类已经实现了序列化,则它的所有子类也默认实现了序列化
# 标准输入输出流
标准流也是一种字节流
public class InputAndOutput {
public static void main(String[] args) {
// System.in 的编译类型 InputStream
// System.in 的运行类型 BufferedInputStream
// 表示标准输入 键盘
System.out.println(System.in.getClass());
// System.out 的编译类型 PrintStream
// System.out 的运行类型 PrintStream
// 表示标准输出 显示器
System.out.println(System.out.getClass());
}
}
# 转换流
可以把字节流转字符流
- InputStreamReader:
@Test
public void test01() throws IOException {
String filePath = "f:/a.txt";
// 字节流转字符流,并制定编码为gbk
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"gbk");
// 转BufferedReader
BufferedReader br = new BufferedReader(isr);
// 读取
String s = br.readLine();
System.out.println("读取到的内容:"+s);
br.close();
}
- OutputStreamWriter:可以传入一个输出流,并指定字符集
@Test
public void test() throws IOException {
String filePath = "f:/rong.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
osw.write("hello,你好呀!");
osw.close();
System.out.println("按照gbk保存文件");
}
# 打印流
打印流只有输出流
# PrintStream
@Test
public void test() throws IOException {
PrintStream out = System.out;
out.print("huya dish");
// print底层调用的write
out.write("你好".getBytes());
out.close();
// 可以控制输出位置
System.setOut(new PrintStream("f:/sout.txt"));
// 这句话会输出到文件中
System.out.println("hello 鹿角虫");
}
# PrintWriter
@Test
public void test() throws IOException {
PrintWriter pw = new PrintWriter(new FileWriter("f:/out.txt"));
pw.print("你好,未来");
pw.close();
}
# Properties类
用于读取.properties
配置文件
@Test
public void test() throws IOException {
// 使用Properties类读取配置文件
Properties properties = new Properties();
// 加载指定配置文件
properties.load(new FileReader("src/mysql.properties"));
// 把k-v打印到控制台
properties.list(System.out);
// 根据key获取指定值
System.out.println(properties.getProperty("user"));
// 根据key设置指定值
properties.setProperty("password","鹿角虫");
properties.setProperty("ip","192.168.0.1");
// 重新输出
properties.list(System.out);
// 输出到文件,如果有中文,默认会保存为unicode,第二个参数为注释
properties.store(new FileOutputStream("src/test.pproperties"),"hello world");
System.out.println("保存配置文件成功");
}