IO
数据的输入与输出流
将内存中的数据持久化到硬盘中
或者从硬盘读取数据临时存放在内存
都需要用到IO操作
File类
数据一般存储在文件中
Java提供了File类支持对文件的操作
静态变量:
pathSeparator 与系统有关的路径分隔符 window 是 ; Linux 是 :
separator 与系统有关的目录分隔符 window 是 \ Linux 是 /
构造方法: 通过一个文件或路径得到一个对象
window下路径不区分大小写
Linux区分大小写
new File(Filrpath)
new File(参照路径,相对路径)
new File(File对象,相对路径)
创建方法:
createNewFile(): 创建文件的方法(当前文件夹存在的基础上)
boolean mkdir(): 创建单级目录文件夹
boolean mkdirs(): 创建多级目录文件夹
删除方法:
boolean delete(): 删除文件或文件夹,不走回收站,从硬盘直接删除
如果要删除的文件夹下有文件或子文件夹就无法删除
获取方法:
String[] list(): 将构造器传入的路径下当前目录下文件与文件夹的名字存入String数组并返回
不能获取子文件夹的文件名
File[] listFiles(): 获取当前目录下所有的文件夹和文件组成的File对象传入File数组并返回
String getName(): 返回路径中最后一级文件或文件夹的名字
long length(): 返回路径中表示文件的字节数,对文件夹无效
String getAbsolutePath():返回当前项目(非当前文件)的绝对路径,不区分.和..
String getCanonicalPath():返回当前项目(非当前文件)的绝对路径
File getAbsoluteFile():返回绝对路径的File对象
String getParent(): 获取上一级路径所在的绝对路径String
File getParentFile(): 获取上一级路径所在的绝对路径File对象
String lastModified():获取最后一次的修改时间,毫秒值
判断方法:
boolean exists(): 判断构造器中的路径参数是否真实存在此路径
boolean isDirectory(): 判断构造器中的路径是否是文件夹
boolean isFile(): 判断构造器中的路径是否是文件
boolean canRead():判断是否可读
boolean canWrite():判断是否可写
boolean isHidden():判断是否隐藏
File file = new File("D:\\sss\\a.txt");// 创建一个File对象
System.out.println(file);
file.mkdirs();//创建多级目录
file.createNewFile();
// 在当前文件夹存在的基础上,如果file对象中文件不存在就创建这个文件
// 如果文件文件存在就不在创建
//如果D:\\sss中sss文件夹不存在就会报错
System.out.println(file.length());
System.out.println(file.getAbsolutePath());
System.out.println(file.getAbsoluteFile());
System.out.println(file.getParent());
System.out.println(file.getParentFile());
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
file.delete();
1.new File("d:\\demo\\a.txt")表示指向这个地址不是创建文件
2.new File("d:\\demo",“a.txt”)
3.File f = new File("d:\\demo")
new File(f,"a.txt”)
以上三种效率是一样的,我们一般使用第一种
文件地址的获取
类名.class 获取class对象
class对象.getResource(path) 获取URL对象
path不加/,就是当前路径的相对路径
path加/,就是当前项目的根路径
URL.getFile() 获取String类型的文件地址
src包下有path包
path包下有a包
a包下有b包和1文件
b包下c包和2文件
c包下有3文件
在b包使用一个TestPath类获取
不带/的相对路径写法
上一级1文件的内容 TestPath.class.getResource("../1").getFile()
同级的2文件的内容 TestPath.class.getResource("2").getFile();
下一级3文件的内容 TestPath.class.getResource("c/3").getFile();
带/的相对路径写法
上一级1文件的内容 TestPath.class.getResource("/path/a/1").getFile()
同级的2文件的内容 TestPath.class.getResource("/path/a/b/2").getFile();
下一级3文件的内容 TestPath.class.getResource("/path/a/b/c/3").getFile();
public class TestPath {
public static void main(String[] args) {
// String filepath = TestPath.class.getResource("../1").getFile();
// String filepath = TestPath.class.getResource("2").getFile();
String filepath = TestPath.class.getResource("c/3").getFile();
try (FileInputStream in = new FileInputStream(new File(filepath));) {
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) > 0) {
System.out.println(new String(b, 0, len));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
文件地址包含中文
文件地址包含中文时,会抛出文件找不到的异常
需要对其进行转义
java.net.URLDecoder 类的 decode(filepath, "utf-8");
String filepath = TestPath.class.getResource("含有中文的文件名").getFile();
String decodePath = URLDecoder.decode(filepath, "utf-8");
try (FileInputStream in = new FileInputStream(new File(decodePath));) {
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) > 0) {
System.out.println(new String(b, 0, len));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
文件过滤器
file[] listFiles(fileFilter) file对象的listFiles方法传入一个fileFilter对象
fileFilter是一个接口,需要我们自定义来实现
重写其中唯一的accept方法
package coollections;
import java.io.File;
import java.io.FileFilter;
public class MyFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
boolean flag = false;
// File对象的最后一级文件/文件夹的名字
String name = pathname.getName();
if (name == null) {
return flag;
}
//判断是否是文件
if (name.contains(".")) {
flag = true;
}
return flag;
}
}
public static void main(String[] args) {
File file = new File("D:\\IO\\File");// 创建一个File对象
File[] listFiles = file.listFiles(new MyFilter());
//listFiles在遍历目录时会调用过滤器的accept方法
//将遍历的路径传入accept方法的参数中
//如果accept方法返回true,说明这个传入的路径指向一个文件,就存入File数组
for (File file2 : listFiles) {//遍历文件
System.out.println(file2);
}
}
目录的遍历
public static void main(String[] args) {
File file = new File("D:\\jar包\\IO\\File");// 创建一个File对象
getAllFile(file);
}
public static void getAllFile(File f) {
File[] listFiles = f.listFiles();// 获取当前目录下的文件及文件夹
for (File file : listFiles) {
if (file.isDirectory()) {// 如果是文件夹就继续遍历
getAllFile(file);//递归
} else {
System.out.println(file);
}
}
}
递归
方法内部调用自身
递归一定要有出口,不能是死循环
递归次数不能太多
构造方法禁止递归
斐波那契数列
不要在自身中调用两次递归
getArray(n):
getArray(n-1) + getArray(n-2) 不建议
public static void main(String[] args) {
getArray(9);
}
public static void getArray(int n) {
int a = 0;
int b = 1;
for (int i = 0; i < n; i++) {
int temp = b;
b = a + b;
a = temp;
System.out.print(a + " ");
}
//1 1 2 3 5 8 13 21 34
}
//递归实现
public static void main(String[] args) {
getNum(9);
}
public static void getNum(int num) {//取出第num位前所有的斐波那契数列的数值
for (int a = 1; a <= num; a++) {
int[] i = getArray(a);
System.out.print(i[1] + " ");//取出第a位斐波那契数列的数值
}
}
public static int[] getArray(int n) {//递归方法,每次返回斐波那契数列最后两位数
if(n < 1){
int[] array = { 0, 0 };
return array;
}
if (n == 1 || n == 2) {
// i -= 1;
int[] array = { 1, 1 };
return array;
} else {
int[] re = getArray(n - 1);// 递归调用自身
// i -= 1;
// if (i == 0) {
// return re;
// }
int temp = re[0];
re[0] = re[1];
re[1] = re[1] + temp;
return re;
}
}
字节流
字节流有输入和输出流,操作数据的读取与存储
可以操作文本,视频,音乐,图片等
字节输出流OutputStream
抽象类,以字节操作数据的输出
所有字节输出流的父类
方法:
write(int b):写入单个字节的内容
write(byte[] b):写入字节数组中的内容
write(byte[] b,int start , int len):写入字节数组的一部分
close(): 先刷新流,再关闭流对象,释放与流有关的资源
flush(): 刷新缓存区,将缓存区剩余输出流的内容强制输出
字节输出流FileOutputStream
OutputStream的子类之一
构造方法参数可以传入一个文件路径或者File对象,必须是一个文件
如果这个文件不存在会自动创建这个文件
实现文件的追加与换行
public static void main(String[] args) throws IOException {
File file = new File("D:\\IO\\File\\a.txt");
// 传入File对象,第二参数为true,表示在文件末尾追加内容
FileOutputStream fileOut = new FileOutputStream(file, true);
fileOut.write("hello\r\n".getBytes());// 数据的传输类型一般是byte类型
//\r\n是换行符
fileOut.close();
}
流的异常处理
package coollections;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Streams {
public static void main(String[] args) throws IOException {
File file = new File("D:\\IO\\File\\a.txt");
FileOutputStream fileOut = null;
try {
fileOut = new FileOutputStream(file, true);
fileOut.write("hello\r\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
throw new IOException("文件写入失败!");
} finally {
try {
if (fileOut != null) {
fileOut.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件流关闭失败!");
}
}
}
}
字节输入流InputStream
抽象类
所有字节输入流的父类,用于读取文件
方法:
int read(int i) 按照单个字节读取,返回读取数据的ASCII编码值,读取到末尾返回-1
int read(byte[] b) 按照字节数组长度读取字节存入数组中,返回读取的字节长度
没有数据存入数组时返回-1
数据存入数组,会覆盖之前存入数组的数据;
新数据读取的长度不足覆盖数组长度,剩余的位置显示上一次数据
构造方法为流对象绑定数据源
参数可以为File对象或者String类型的FileName
FileInputStream
InputStream抽象类的子类
用于读取数据到数组缓冲区(byte数组)
单个字节读取文件内容
FileInputStream fileInputStream = new FileInputStream("D:\\IO\\File\\a.txt");
int num = 0;
while ((num = fileInputStream.read()) != -1) {
System.out.print((char) num);
}
fileInputStream.close();
以数组读取文件内容
FileInputStream fileInputStream = new FileInputStream("D:\\IO\\File\\a.txt");
byte[] b = new byte[1024];
int len = 0;
while ((len = fileInputStream.read(b)) != -1) {//按照数组的形式读取
System.out.print(new String(b, 0, len));//将byte数组转为String
}
fileInputStream.close();
获取文件编码的方法
转载https://blog.csdn.net/ardo_pass/article/details/86157619
gbk编码没问题,其他未测试,慎用
public static String codeString(String fileName) throws Exception {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fileName));
int p = (bin.read() << 8) + bin.read();
bin.close();
String code = null;
switch (p) {
case 0xefbb:
code = "UTF-8";
break;
case 0xfffe:
code = "Unicode";
break;
case 0xfeff:
code = "UTF-16BE";
break;
default:
code = "GBK";
}
return code;
}
文件的复制
最经典的文件复制,但效率低
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("D:\\jquery-1.10.2.zip");
out = new FileOutputStream("E:\\a.zip");
// 定义字节数组,缓冲
byte[] b = new byte[1024];//数组大小越大,消耗时间会相对缩短(有极限)
// 每次读取的数据长度
int len;
while ((len = in.read(b)) > 0) {// 将文件数据读取放入数组
out.write(b, 0, len);// 将数组数据写入另一个文件
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件复制失败!");
} finally {//嵌套关闭流
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("资源关闭失败!");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("资源关闭失败!");
}
}
}
转载文件复制的方法https://blog.csdn.net/karlin999/article/details/79756397
字符流
为了方便操作文本数据,Java提供了字符流
字符流只能操作文本数据
Writer
Java.Io.Writer是一个抽象类
所有字符输出流父类
方法:
write(int i) : 写入一个字符
write(char[] c) :写入一个char数组
... 写入char数组的一部分
write(String s):写入字符串
... 写入字符串的一部分
FileWriter
文件输出流,继承OutputStreamWriter(Writer的子类)
构造方法接收File对象或者String 文件名
字符输出流必须使用一个刷新功能flush()
将内存缓冲区的数据刷新进文件中
FileWriter fileWriter = new FileWriter("D:\\jar包\\IO\\File\\a.txt");
fileWriter.write("abhkjcfh");
//如果不是以追加的方式写入会先清空数据再写入
fileWriter.flush();//刷新数据进文件中
fileWriter.close();
Reader
Java.Io.Reader是一个抽象类
所有字符输入流的父类
方法:
int read() : 读取一个字符
int read(char[] c) : 读取一个字符数组
没有读取String的方法
FileReader
文件输入流,继承InputStreamReader(Reader的子类)
构造方法接收File对象或者String 文件名
FileReader fileReader = new FileReader("D:\\jar包\\IO\\File\\a.txt");
char[] c = new char[1024];
int len = 0;
while ((len = fileReader.read(c)) > 0) {
//可能出现乱码问题
System.out.println(new String(c, 0, len));
}
字符转化流
为了解决字符流操作文本文件可能出现的乱码问题
OutputStreamWriter
继承Writer抽象类
字符流转字节流的桥梁
工作过程: 写入中文时.OutputStreamWriter用要写入数据去查询已知编码的编码表,
得到对应的字节,将字节交给对应字节输出流,用字节输出流去写入数据
作用就是改变数据的编码,解决中文乱码
构造器
OutputStreamWriter(OutputStream out)接收一个任意字节输出流对象
OutputStreamWriter(OutputStream out, String charsetName)接收字节输出流对象和编码
会清空原先文件数据
FileOutputStream fileInputStream = new FileOutputStream("D:\\a.txt");
OutputStreamWriter out = new OutputStreamWriter(fileInputStream, "GBK");
out.write("复赛放箭都");
out.close();
InputStreamReader
继承Reader抽象类
字符流转字节流的桥梁
InputStreamReader(FileInputStream out)接收一个任意字节输出流对象
InputStreamReader(FileInputStream out, String charsetName)接收字节输出流对象和编码
FileInputStream fileInputStream = new FileInputStream("D:\\jar包\\IO\\File\\a.txt");
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "GBK");
char[] c = new char[1024];
int len = 0;
while ((len = inputStreamReader.read(c)) > 0) {
System.out.println(new String(c, 0, len));
}
缓冲流
为了提高IO流的读写速度而存在的缓冲流
分为字节缓冲流和字符缓冲流
字节缓冲流
字节缓冲输出流:BufferedOutputStream
字节缓冲输入流:BufferedInputStream
内部都包含了一个缓冲区,通过缓冲区来读写数据,就提高了IO读写速度
BufferedOutputStream
字节缓冲输出流
继承FileOutputStream
接收一个OutputStream对象作为构造器参数,提升这个对象的传输速度
会清空原先文件数据(除非以追加形式写入)
BufferedOutputStream buff = new BufferedOutputStream(new FileOutputStream("D:\\jar包\\IO\\File\\a.txt"));
byte[] b = "helflo".getBytes();
buff.write(b);
buff.close();
BufferedInputStream
字节缓冲输入流
继承FilterInputStream
接收一个InputStream对象作为构造器参数,提升这个对象的传输速度
BufferedInputStream bu = new BufferedInputStream(new FileInputStream("D:\\jar包\\IO\\File\\a.txt"));
byte[] b = new byte[1024];
int len = 0;
while ((len = bu.read(b)) > 0) {
System.out.println(new String(b, 0, len));
}
在传输大数据文件时,字节缓冲流所用的时间是最少的
所以一般用字节缓冲流传输数据
字符缓冲流
实现文件的高速读取
字符缓冲输入流:BufferedReader
字符缓冲输出流:BufferedWriter
BufferedWriter
字符缓冲输出流
继承Writer抽象类
write() 可以传入单个字符,字符数组,字符串
构造方法
BufferedWriter(Writer w ) 传入任意字符输出流
FileWriter fileWriters = new FileWriter("D:\\jar包\\IO\\File\\a.txt", true);
BufferedWriter b = new BufferedWriter(fileWriters);
b.write("放得开".toCharArray());
b.flush();
b.close();
BufferedReader
字符缓冲输入流
继承Reader抽象类
readLine():读取一行数据
FileReader fileReader = new FileReader("D:\\jar包\\IO\\File\\a.txt");
BufferedReader bu = new BufferedReader(fileReader);
char[] c = new char[1024];
int len = 0;
while ((len = bu.read(c)) > 0) {
System.out.println(new String(c, 0, len));
}
用字符缓冲流完成文本文件的复制
简略
BufferedReader bu = new BufferedReader(new FileReader("D:\\jar包\\IO\\File\\a.txt"));
BufferedWriter wr = new BufferedWriter(new FileWriter("D:\\jar包\\IO\\File\\c.txt"));
String line = null;
while ((line = bu.readLine()) != null) {
wr.write(line);
wr.flush();
}
wr.close();
bu.close();
IO使用
对文件或文件夹操作
文件夹操作
File对象
文件操作
流对象
文本对象
字符流
要高效读写
缓冲流
非高效
视情况而定
防止乱码
转换流
非文本对象
字节流
要高效读写
缓冲流
非高效
视情况而定
总结
java.io包
字节流
InputStream(抽象类):所有字节输入流的父类,用于读取任意类型数据
常用方法int read(byte[] b) 按照字节数组长度读取字节存入数组中
常用子类FileInputStream
OutputStream(抽象类):所有字节输出流的父类,用于写入任意类型数据
常用方法write(byte[] b):写入字节数组中的内容
常用子类FileOutputStream
new FileOutputStream(fileName, true);//追加文件
字符流
Reader(抽象类):所有字符输入流的父类
常用方法int read(char[] c) : 读取一个字符数组,没有读取String的方法
常用子类FileReader:字符输入流,继承InputStreamReader(Reader的子类)
Writer(抽象类):所有字符输出流父类,用于写入文本类型数据
常用方法write(char[] c) :将文本数据写入char数组
常用子类FileWriter:字符输出流,继承OutputStreamWriter(Writer的子类)
字符对象写入数据需要flush刷新一下
字符转化流:
为了解决字符流操作文本文件可能出现的乱码问题,将字符流转为字节流
需要一个OutputStream对象和文本编码作为构造器的参数
OutputStreamWriter继承Writer抽象类
需要一个InputStream对象和文本编码作为构造器的参数
InputStreamReader继承Reader抽象类
缓冲流:
通过缓冲区来读写数据提高IO的读写速度
字节缓冲输出流,接受一个OutputStream对象作为构造器的参数
BufferedOutputStream(继承FileOutputStream)
字节缓冲输入流,接受一个InputStream对象作为构造器的参数
BufferedInputStream(继承FilterInputStream)
字符缓冲输出流,接受一个Writer对象作为构造器的参数
BufferedWriter(继承Writer抽象类)
字符缓冲输入流,接受一个Reader对象作为构造器的参数
BufferedReader(继承Reader抽象类)可以读取一行数据
以上是最常用的流,IO包中还有很多其它的流
字符串编码转换
new String(byte[] b , 0 , length , encodeing)
Properties
Map集合子类
继承Hashtable(继承Dictionary抽象类,实现Map接口)
无序的
配合IO完成数据的持久化
没有泛型,key与value都是字符串类型
对键值对数据进行操作,很适合配置文件的读写
方法:
void load(InputStream in):接收任意字节输入流对象,从中读取键值对
void load(Reader read) :接收任意字符输入流对象,从中读取键值对
store(OutputStream out,String 描述信息):接收任意字节输出流,通过输出流将数据持久化
store(Write it,String 描述信息):接收任意字符输出流,通过输出流将数据持久化
setProperties(key,value):存放数据
getProperties(key):获取数据
Set<String> stringPropertyName():获取所有的key,返回一个Set集合
Properties p = new Properties();
String path = new File(".").getCanonicalPath();
String f = "src\\coollections\\pro.properties";
FileReader reader = new FileReader(new File(path, f));
p.load(reader);//数据的读取
reader.close();
System.out.println(p);
// {age=25, school=135, name=张三, sex=1}
p.setProperty("name", "李四");//如果key存在就覆盖原value值
p.setProperty("age", "24");
p.setProperty("sex", "3");
FileWriter fileWriter = new FileWriter(new File(path, f));
p.store(fileWriter, "text");
//数据的写入,输入的描述信息只能为英文,中文会自动转为unicode码
fileWriter.close();
//最后pro.properties文件内容:
//#text
//#Tue Feb 11 18:54:24 CST 2099
//age=24
//name=李四
//sex=3
try-with-resources释放资源
进行文件数据读写时,一般都会出现IO异常,需要我们主动捕获或抛出
同时对读写资源进行关闭,如果使用常见的try–catch-finally来关闭资源
1.就会使得finally中的代码十分臃肿
2.finally中如果抛出异常有时会覆盖try中抛出的异常
案例如下:
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("D:\\apache-tomcat-9.0.8-src.zip");
out = new FileOutputStream("E:\\a");
// 定义字节数组,缓冲
byte[] b = new byte[1024];
// 每次读取的数据长度
int len;
while ((len = in.read(b)) > 0) {// 读取文件
out.write(b, 0, len);// 写入文件
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件复制失败!");
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("资源关闭失败!");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("资源关闭失败!");
}
}
}
try–catch-finally方式将资源的关闭放在finally中,会使代码臃肿
try-with-resources将要释放的资源放在try(要释放的资源){}中
要释放的资源需要实现AutoCloseable 接口或Closeable接口
Closeable接口继承了AutoCloseable 接口
try-with-resources语句是一种声明了一种或多种资源的try语句
try-with-resources语句保证了每个声明了的资源在语句结束的时候都会被关闭。
任何实现了java.lang.AutoCloseable接口的对象,
和实现了java.io.Closeable接口的对象,都可以当做资源使用
所以无论try语句是正常结束还是异常结束,资源都会被被关闭
示例如下:
try(FileInputStream in = new FileInputStream("D:\\apache-tomcat-9.0.8-src.zip");
FileOutputStream out = new FileOutputStream("E:\\a");
) {
// 定义字节数组,缓冲
byte[] b = new byte[1024];
// 每次读取的数据长度
int len;
while ((len = in.read(b)) > 0) {// 读取文件
out.write(b, 0, len);// 写入文件
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件复制失败!");
}
抽象类InputStream实现了Closeable接口
Closeable接口继承了AutoCloseable接口
要释放自定义资源的话,只要实现 AutoCloseable 接口,并提供 close() 方法的实现即可
class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("关闭自定义的资源");
}
public void test() throws Exception{
throw new Exception("test()");
}
}
在处理必须关闭的资源时,考虑使用 try-with-resources,
而不是 try–catch-finally。
优点:
1.代码简洁
2.close方法中异常不会覆盖try语句块中抛出的异常