文件
文件流
文件在程序中是以流的形式来操作的
流:数据在数据源(文件/磁盘)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
构造器
获取文件的相关信息
api:
- getName
- getAbsolutePath
- getParent
- length
- exists
- isFile
- isDirectory
目录的操作和文件删除
api:
- mkdir
- mkdirs
- delete
IO流
流的原理
- I/O是Input/output的缩写,I/O技术是非常实用的技术,用于处理数据传输
如读写文件,网络通讯等。 - Java程序中,对于数据的输入/输出操作以’’流(Stream)"的方式进行。
- java.io包下提供了各种‘’流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
流的分类
文件和流
字节流
FileInputStream 介绍
//字节输入流
public class FileInputStream_ {
@Test//单个字节读取
public void test1() throws IOException {
String path = "f:\\hello.txt";
int readresult ;
FileInputStream fis = null;
fis = new FileInputStream(path);
while ((readresult = fis.read()) != -1) {//方法返回值为下一个字节/-1(读完)
System.out.print((char) readresult);//中文字符转换成三个字节,每次只取其一,所以文本有中文的话会打印拆分出的字符
}
fis.close();//关闭资源不浪费
}
@Test//加入字节数组作为缓冲每次读取指定长度
public void test2() throws IOException {
String path = "f:\\hello.txt";
byte[] readresult = new byte[8];
int length;
FileInputStream fis = new FileInputStream(path);
while ((length = fis.read(readresult)) != -1) {//文本有中文的话会打印乱码
//最后一次缓冲数组接收:不足指定长度的空白,由上一次的同位置的内容填补
//解决方案:每次按方法返回的长度值来使用(转换字符串或写入)(拷贝的关键)
System.out.print(new String(readresult,0,length));
}
fis.close();
}
}
FileOutputStream 介绍
//字节输出流
public class FileOutputStream01 {
@Test//每次写入一个字节/一个字节构成的字符/字节数组
public void test() throws IOException {
String path = "f:\\a.txt";
//没有append参数的构造器写入会覆盖之前的内容,有就不覆盖
FileOutputStream fos = new FileOutputStream(path,true);
String str = "zxk,world!周小坤";
byte[] buf=str.getBytes();//String类的方法,字符串->字节数组
//找不到文件路径会自动创建,字节自动组合成字符写入,不会有乱码
fos.write('周');//方法形参可以加偏移量和字节数组长度(拷贝会用)
fos.close();//节约资源
}
}
字节流拷贝
//完成二进制文件的拷贝(字节流)
//同样可以拷贝含中文字符的文本文件
public class FileCopy {
public static void main(String[] args) throws IOException {
/*String srcFilePath = "f:\\girl.jpg";
String destFilePath = "f:\\io\\goodGirl.jpg";*/
String srcFilePath = "C:\\Users\\Administrator\\Desktop\\io流.txt";
String destFilePath = "f:\\io\\io流.txt";
FileInputStream fis = new FileInputStream(srcFilePath);
FileOutputStream fos = new FileOutputStream(destFilePath,true);
byte[] buf = new byte[1024];
int length;
while ((length = fis.read(buf) )!= -1) {
fos.write(buf,0,length);//注意使用buf的长度为length(读取方法返回值)
}
System.out.println("拷贝完成~");
fis.close();
fos.close();
}
}
字符流
FileReader 相关方法:
- new FileReader(File/String)
- read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
- read(char[]): 批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
相关api:
- new String(char[]):将char[ ]转换成String
- new String(char[] ,off,len):将char[]的指定部分转换成String
FileWriter 常用方法
- new FileWriter(File/String):覆盖模式,相当于流的指针在首端
- new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off,len):写入指定数组的指定部分
- write(string):写入整个字符串
- write(string,off,len):写入字符串的指定部分
相关API:String类:toCharArray:将String转换成char[]
注意:
Filelriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!
输入流案例:
//字符输入流
public class FileReader_ {
@Test
public void test1() throws IOException {
String filePath = "f:\\story.txt";
int data;
FileReader fr = new FileReader(filePath);
//循环读取下个字符
while ((data = fr.read()) != -1) {
//方法默认读取字符是按照utf-8编码,而当文本是按ANSI即中文gbk保存时,
//读取就会出现乱码
//解决方案:将文本文件按照utf-8保存
System.out.print((char)data);
}
fr.close();
}
@Test
public void test2() throws IOException {
String filePath = "f:\\story.txt";
char[] buf = new char[8];//缓冲
int length;
FileReader fr = new FileReader(filePath);
//循环读取指定长度的字符数组
while ((length = fr.read(buf)) != -1) {
System.out.print(new String(buf,0,length));
}
fr.close();//节约资源
}
}
输出流案例:
//字符输出流(必须关流)
public class FileWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "f:\\note.txt";
FileWriter fw = new FileWriter(filePath);//一样可以选择不覆盖的构造方法
char[] chars = {'a', 'b', 'c'};
//3) write(int):写入单个字符
fw.write('周');
//4) write(char[]):写入指定数组
fw.write(chars);
//5) write(char[],off,len):写入指定数组的指定部分
fw.write(chars,0,1);
//6) write(string):写入整个字符串
fw.write("周小坤");
//7) write(string,off,len):写入字符串的指定部分
fw.write("周小坤",0,1);
fw.close();//关闭流或者刷新流才能真正写入
// fw.flush();
}
}
节点流和处理流(修饰器设计模式)
节点流和处理流的区别和联系
- 节点流是底层流/低级流,直接跟数据源相接。
- 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
- 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连[模拟修饰器设计模式]
代码模拟修饰器设计模式
//模拟修饰器设计模式
public class Test_ {
public static void main(String[] args) {
BufferedReader_ br1 = new BufferedReader_(new FileReader_());
br1.readFiles(10);
BufferedReader_ br2 = new BufferedReader_(new StringReader_());
br2.readStrings(5);
}
}
//处理流
public class BufferedReader_ extends Reader_{
private Reader_ reader_;//属性是Reader_类型
//接收子类对象
public BufferedReader_(Reader_ reader_){
this.reader_ = reader_;
}
//方法优化
public void readFiles(int num) {
for (int i = 0; i < num; i++) {
reader_.readFile();
}
}
public void readStrings(int num) {
for (int i = 0; i < num; i++) {
reader_.readString();
}
}
}
//抽象类
public abstract class Reader_ {
public void readFile(){
}
public void readString(){
}
}
//低级流
public class FileReader_ extends Reader_{
public void readFile(){
System.out.println("读取文件..");
}
}
public class StringReader_ extends Reader_ {
public void readString(){
System.out.println("读取字符串..");
}
}
处理流的功能主要体现在以下两个方面:
1.性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
2.操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
处理流-BufferedReader 和 BufferedWriter
- BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的
- 关闭处理流时,只需要关闭外层流即可
案例:
使用BufferedReader读取文本文件,并显示在控制台
输入流:
//包装字符输入流
public class BufferedReader_ {
public static void main(String[] args) throws IOException {
String filePath = "f:\\io\\a.java";
BufferedReader br = new BufferedReader(new FileReader(filePath));
String line;//缓冲
while ((line = br.readLine()) != null) {//按行读取,当方法返回null时,文件读取完毕
System.out.println(line);
}
br.close();//关闭处理流,底层会自动关闭节点流
}
}
输出流:
//包装字符输出流
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "f:\\io\\ok.txt";
//可覆盖/追加写
BufferedWriter bw = new BufferedWriter(new FileWriter(filePath,true));
bw.write("hello,韩顺平教育!");
bw.newLine();//插入一个和系统相关的换行
bw.write("hello2,韩顺平教育!");
bw.newLine();
bw.write("hello3,韩顺平教育!");
bw.newLine();
bw.close();//关闭外层流即可,底层会真正写入然后关闭
}
}
包装字符流拷贝文本文件:
//包装字符流拷贝文本文件
//优化:包装流优化了读写方法,按行读写字符串
//1. BufferedReader 和 BufferedWriter 是按照字符操作
//2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏
public class BufferedCopy_ {
public static void main(String[] args) throws IOException {
String srcFilePath = "f:\\io\\a.java";
String destFilePath = "f:\\io\\a2.java";
String line = null;
BufferedReader br = new BufferedReader(new FileReader(srcFilePath));
BufferedWriter bw = new BufferedWriter(new FileWriter(destFilePath));
while ((line = br.readLine()) != null) {//循环读取:读完一行->写入一行->换行
bw.write(line);
bw.newLine();
}
System.out.println("拷贝完成~");
br.close();
bw.close();//关闭外层流,字节流完成写入关闭
}
}
处理流-BufferedInputStream 和 BufferedOutputStream
包装字节流拷贝文件:
//包装字节流拷贝, 同字节流拷贝一样,未使用优化方法
//同样:既可以拷贝二进制文件,也可以拷贝文本文件
public class BufferedCopy02 {
public static void main(String[] args) throws IOException {
String srcFilePath = "f:\\io\\尚硅谷_沙诗博_JavaSE_2021-07-28_001_计算机语言介绍.wmv";
String destFilePath = "f:\\io\\尚硅谷1.wmv";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFilePath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
byte[] buf = new byte[1024];
int length;
while ((length = bis.read(buf)) != -1) {//当返回-1时,拷贝结束
bos.write(buf,0,length);
}
System.out.println("文件拷贝完毕~");
bis.close();
bos.close();//关闭流,节约资源
}
}
对象流
功能:提供了对基本类型或对象类型的序列化和反序列化的方法
ObjectOutputStream 提供 序列化功能
ObjectInputStream 提供 反序列化功能
序列化代码
//序列化形式写入
public class ObjectOutStream_ {
public static void main(String[] args) throws IOException {
//指定文件写入路径
String filePath = "f:\\io\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
//序列化数据到路径文件里
oos.writeInt(100);//这些基本数据类型的包装类都实现了Serializable接口
oos.writeBoolean(true);
oos.writeChar(97);
oos.writeDouble(3.14);
oos.writeUTF("周小坤");
//保存一个Dog类对象
oos.writeObject(new Dog("小黑", 3,"black","墨西哥"));
oos.close();
System.out.println("数据保存完毕(序列化形式)");
}
}
//序列化对象先实现接口,属性类也都要实现
public class Dog implements Serializable {
private String name;
private int age;
//序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
private static String nation;//如果直接声明初始化静态变量,反序列化时可以得到
private transient String color;
//序列化对象时,要求里面属性的类型也需要实现序列化接口
private Master master = new Master();//否则会报异常
//serialVersionUID 序列化的版本号,可以提高兼容性
private static final long serialVersionUID = 1L;
public Dog(String name, int age,String color,String nation) {
this.name = name;
this.age = age;
this.color = color;
this.nation = nation;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}'+nation+""+color+""+master;
}
}
反序列化:
//反序列化形式读取
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//指定反序列化的文件
String filePath = "f:\\io\\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();//编译类型Object,运行类型Dog
System.out.println(dog);
//想调用对象的方法,需要先将其向下转型
//并且将Dog类放在可以引用的位置
//每次更新类中方法,需要先重新序列化再反序列化再使用新方法
Dog dog2 = (Dog) dog;
System.out.println(dog2.getName());
ois.close();
}
}
注意事项:
- 读写顺序要一致
- 要求序列化或反序列化对象,需要实现Serializable
- 序列化的类中建议添加SeriaIVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有属性都进行序列化。但除了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化
标准输入输出流
类型 | 默认设备 | |
---|---|---|
System.in 标准输入 | InputStream | 键盘 |
System.out 标准输出 | PrintStream | 显示器 |
转换流
- InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成(转换)Reader(字符流)
- OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
- 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
- 可以在使用时指定编码格式(比如 utf-8,gbk,gb2312,ISO8859-1等)gbk即文本文档默认保存格式ANSI
输入流案例
//转换输入流InputStreamReader
//按指定编码(构造器)读取以解决乱码问题
public class InputStreamReader_ {
@Test
//用普通的字符输入流读取因为文件不是按照默认的UTF-8编码保存导致读出乱码
public void test1 ()throws IOException {
String filePath = "f:\\io\\story.txt";
FileReader fileReader = new FileReader(filePath);
char[] buf=new char[8];
int length;
while ((length = fileReader.read(buf)) != -1) {
System.out.print(new String(buf,0,length));
}
fileReader.close();
}
@Test
public void test2() throws IOException {
String filePath = "f:\\io\\story.txt";
//外层:包装流(优化方法),中层:转换流(指定编码),底层:字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(filePath), "gbk"));
String s = br.readLine();
System.out.println(s);
br.close();
}
}
输出流案例:
//转换输出流OutputStreamWriter
//按指定编码(构造器)写入(gbk/utf-8/utf8)
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "f:\\io\\story2.txt";
String charSet = "utf-8";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
osw.write("hi,周小坤去学习");
osw.close();
}
}
打印流
字节输出流:
//打印流(字节输出流)
public class PrintStream_ {
@Test
public void test1()throws IOException{
//默认的输出位置:标准输出(显示器)
//设置标准输出流的路径(注意打印流类的引用的位置)使用setOut方法
System.setOut(new PrintStream("f:\\io\\so.txt"));
PrintStream so = System.out;
//以下三部分全部输出到指定路径文件中
so.print("hello,john");//print底层调用的也是write
so.write("周小坤,你好".getBytes());//直接调用write输出到文件
System.out.println("hello,周小坤~");
so.close();
//修改打印流输出数据的位置/设备
PrintStream ps = new PrintStream("f:\\io\\print.txt");
ps.write("hello,周小坤".getBytes());//hello,周小坤输出到f:\io\print.txt
ps.close();
}
@Test
public void test2()throws IOException{
//默认的输出位置:标准输出(显示器)
//设置标准输出流的路径(注意打印流类的引用的位置)使用setOut方法
PrintStream so = System.out;
//如果在标准输出声明后再改变System.out的输出路径,则两者有着不同的路径
System.setOut(new PrintStream("f:\\io\\so.txt"));
so.print("hello,john");//标准输出
so.write("周小坤,你好".getBytes());//标准输出
System.out.println("hello,周小坤~");//指定文件
so.close();
}
}
字符输出流:
//字符包装流打印流
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
// PrintWriter pw = new PrintWriter(System.out);
PrintWriter pw = new PrintWriter(new FileWriter("f:\\io\\f2.txt"));
pw.write("你好上海");
pw.close();//关闭或刷新才是真正写入
}
}
properties类
基本介绍:
- 专门用于读写配置文件的集合类
配置文件的格式:
键=值
键=值 - 注意:键值对不需要有空格,值不需要用引号,默认类型是String
- Properties的常见方法
- load:加载配置文件的键值对到Properties对象
- list:将数据显示到指定设备
- getProperty(key):根据键获取值
- setProperty(key,value):设置健值对到Properties对象
- store:将Properties中的健值对存储到配置文件在idea中,保存信息到配置文件,如果
含有中文,会存储为unicode码
Properties类文件的读写:(两种方式)
案例:
如下一个配置文件 mysql.properties
ip=192.168.0.13
user=root
pwd=12345
编程读取ip,user,pwd的值
- 传统方式(IO流):
//传统方法读取properties类的值
public class Properties01 {
public static void main(String[] args) throws IOException {
//读取mysql.properties 文件,并得到ip, user 和 pwd
BufferedReader br = new BufferedReader(new FileReader("chapter19\\src\\mysql.properties"));//文件找不到注意相对路径的位置
String line;
while ((line = br.readLine()) != null) {
//只要ip
String[] split = line.split("=");
if ("ip".equals(split[0])) {//String的分割方法:返回字符串数组
System.out.println(split[0]+"值是:"+split[1]);
}
}
br.close();
}
}
- 使用类的方法:
//使用Properties类读取mysql.properties文件
public class Properties02 {
public static void main(String[] args) throws IOException {
//1. 创建Properties 对象
Properties properties = new Properties();
//2. 加载指定配置文件(读取)
properties.load(new FileReader("chapter19\\src\\mysql.properties"));
//3. 把k-v显示控制台(写入)
properties.list(System.out);
//4. 根据key 获取对应的值
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("用户名="+user);
System.out.println("密码="+pwd);
}
}
案例(mysql2.properties):
- 使用Properties类完成对mysql.properties的读取,看老师代码演示
- 使用Properties类添加key-val到新文件mysql2.properties中
- 使用Properties类完成对mysql2.properties的读取,并修改某个key-val
//使用Properties 类来创建 配置文件, 修改配置文件内容
public class Properties03 {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
//没有key 就是创建,有key 就是修改
properties.setProperty("charset", "gbk");
properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值(我的为啥是中文字符)
properties.setProperty("pwd", "888888");
//写入到文件中store(字符/字节输出流,注解)
properties.store(new FileOutputStream("chapter19\\src\\mysql2.properties"), null);
System.out.println("保存配置文件成功~");
}
}