java语言中的 BIO 流
一、流
(1)流的分类
1)根据数据传输的方向划分:
输入流:(得到数据)从文件中获取数据;
输出流:(输出数据)将数据保存到文件中。
2)根据读写数据的单位划分:
字节流:也称万能流,以字节为单位,可以读写所有数据;
字符流:以字符为单位,只能读写文本数据。
3)根据功能划分:
基础流:具有实际传输数据的读写功能;
包装流:在基础流的基础之上增强功能。
(2)File类:操作文件及目录(也叫文件夹)的类
1)File类一些方法的使用
public class Demo01 {
public static void main(String[] args) {
//相对路径:相对于项目底下的路径
//File file = new File("File01.txt");
//绝对路径:文件完整的路径(从盘符开始)
//File file = new File("E:/test/File02.txt");
File file = new File("File.txt");
System.out.println("是否为文件:"+file.isFile());
System.out.println("文件名:"+file.getName());
System.out.println("相对路径:"+file.getPath());
System.out.println("绝对路径:"+file.getAbsolutePath());
System.out.println("文件是否可读:"+file.canRead());
System.out.println("文件是否可写:"+file.canWrite());
System.out.println("文件是否隐藏:"+file.isHidden());
}
}
2)目录结构存在的情况下创建文件
public class Test {
public static void main(String[] args) {
try {
//前提是E盘中已经存在名为 “File” 的文件夹
File file = new File("E:\\File\\file.txt");
//判断文件夹中是否已经存在名为 “file.txt” 的文件
if(!file.exists()){
//如果不存在就创建名为 “file.txt” 的文件
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3)当前对象的父目录结构不存在的情况下创建文件
public class Test {
public static void main(String[] args) {
try {
File file = new File("E:\\File\\File\\file.txt");
//创建父目录结构
//获取父目录的对象 --- E:\\File\\File
File parentFile = file.getParentFile();
//判断父目录是否存在
if(!parentFile.exists()){
//创建单层父目录
parentFile.mkdir();
//创建多层父目录
//parentFile.mkdirs();
}
//判断文件是否存在
if(!file.exists()){
//创建文件
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4)输出所有文件及目录的绝对路径的方法
//输出所有文件及目录的绝对路径的方法
private static void aaa(File file) {
File[] files = file.listFiles();
//foreach遍历
for (File file01:files) {
//判断是否为文件
if (file01.isFile()) {
System.out.println(file01);
}else {
//使用递归一层一层的遍历文件夹
aaa(file01);
System.out.println(file01);
}
}
}
5)删除文件及目录
//因为File流的删除方法只能删除文件夹里面为空的文件夹
//所以下面写了一个方法,一层一层的遍历文件夹,直到找到文件或者文件夹里面为空的文件夹,然后一层一层的往回删除
private static int bbb(File file) {
int flag = 0;
File[] files = file.listFiles();
for (File file01:files) {
//如果是文件或者文件夹为空就删除
if (file01.isFile() || file01 == null) {
file01.delete();
flag = 1;
}else {
//使用递归一层一层的遍历文件夹
bbb(file01);
file01.delete();
flag = 1;
}
}
file.delete();
return flag;
}
6)使用过滤器输出指定目录下的所有以.txt结尾的文件
public class Test {
public static void main(String[] args) {
File file = new File("F:\\File");
//使用匿名内部类实现并重写FileFilter接口
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//判断是否是文件
if(pathname.isFile()){
//f.getName()输出的是所有的文件名
//endsWith()限制输出的内容
if(pathname.getName().endsWith(".txt")){
return true;
}
}
return false;
}
});
for (File f:files) {
System.out.println(f.getName());
}
}
}
二、字节流:针对任何文件
字节流 | 字节输出流 | 字节输入流 | 描述 |
---|---|---|---|
字节抽象类 | OutputStream | InputStream | 字节输出 / 输入流的基类 |
字节节点流 | FileOutputStream | FileInputStream | 文件字节输出 / 输入流 |
字节缓冲流 | BufferedOutputStream | BufferedInputStream | 带缓冲区的字节输出 / 输入流, 只有关流或者缓冲区满了,缓冲区的数据才会保存到文件中 |
对象流 | ObjectOutputStream | ObjectInputStream | 实体类需要实现序列化接口,拥有序列号, 输出是序列化,输入是反序列化 |
内存流 | ByteArrayOutputStream | ByteArrayInputStream | 针对临时文件,存放的数据小,效率高 |
打印流 | PrintStream | 没有输入,只有输出, 直接打印数据,是输出信息最方便的类 | |
随机访问流 | RandomAccessFile | RandomAccessFile | 拥有指针,原始数据会被覆盖不会被清空, 读取和写入使用的都是同一个类 |
(1)字节抽象类:InputStream、OutputStream
(2)字节节点流:FileOutputStream、FileInputStream
1)读取文件中的单个字节
public class Test {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("F:\\File\\File.txt");
//读取一个字节
int read = fis.read();
System.out.println((char)read);
} catch (IOException e) {
e.printStackTrace();
}
}
}
2)多个字节读取整个文件
public class Test {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("File.txt");
//定义一个byte类型的数组当缓冲区
byte[] b = new byte[100];
int len;
//fis.read(b)返回的是写入缓冲区的字节总数
while((len = fis.read(b)) != -1){
//第一个参数为存放数据的缓冲区
//第二个参数为偏移量
//第三个参数为缓冲区中的数据长度
System.out.println(new String(b,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//关流 -- 释放资源
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3)将数据写入到磁盘文件中
public class Test {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//如果没有这个文件,实例化对象的时候会自动创建文件,true表示在文件末尾追加数据
fos = new FileOutputStream("IO.txt",true);
//写入字符串“good”,getBytes()方法把String类型的数据转换成byte类型的数组
fos.write("good".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4)字节流的拷贝(可以拷贝任何文件)
//从一个txt文本拷贝到另一个txt文本
public class Demo01 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
int len;
byte[] b = new byte[100];
try {
fis = new FileInputStream("File01.txt");
fos = new FileOutputStream("File02.txt");
//用while循环从文件"File01.txt"中读取数据,暂时保存在byte数组中,再写入到"File02.txt"文件中
while ((len = fis.read(b)) != -1){
fos.write(b,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//自定义释放资源工具类
IOUtil.closeAll(fis,fos);
}
}
}
(3)字节缓冲流:BufferedOutputStream、BufferedInputStream
- 提高IO效率,减少访问磁盘的次数。
- 数据存储在缓冲区中,flush是将缓存区的内容写入文件中,也可以直接close。
1)带缓冲区的字节输出流
public void test01() throws IOException {
//带缓冲区的字节输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("IO1.txt"));
bos.write("good good study day day up!".getBytes());
bos.close();
}
2)带缓冲区的字节输入流
public void test02() throws IOException {
//带缓冲区的字节输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("IO1.txt"));
byte[] b = new byte[1024];
int len;
while( (len = bis.read(b)) != -1){
System.out.println(new String(b,0,len));
}
bis.close();
}
(4)对象流:ObjectInputStream、ObjectOutputStream
对象流写入对象信息:序列化 - - - 把对象转换成字节序列的成程;
对象流读取对象信息:反序列化 - - - 把字节序列恢复成对象的过程。
私有的transient属性不会被序列化,如:private transient int id;
私有的static属性不会被序列化,如:private static int id。
1)把对象存进集合中,再把集合写入文件中
public void aaa() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("File.txt"));
ArrayList<Student> arr = new ArrayList();
arr.add(new Student("aa",11));
arr.add(new Student("bb",22));
arr.add(new Student("cc",33));
oos.writeObject(arr);
oos.close();
}
2)读取集合,遍历并输出
public void bbb() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("File.txt"));
ArrayList<Student> arr = (ArrayList<Student>)ois.readObject();
for (Student student : arr){
System.out.println(student);
}
ois.close();
}
(5)内存流:ByteArrayOutputStream 、ByteArrayInputStream
针对临时文件,存放的数据小,效率高
内存流的读写操作:
@org.junit.Test
public void ddd() throws Exception {
//创建内存输出流对象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//写到内存中
bos.write("aaa".getBytes());
//toByteArray()获取存放在内存中的字节数组
byte[] bytes = bos.toByteArray();
//把内存中的数据写入到指定文本文件中
bos.writeTo(new FileOutputStream("File.txt"));
int c;
//创建内存输入流对象
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
while((c = bis.read()) != -1) {
System.out.println((char) c);
}
//内存流无法关闭
bos.close();
}
(6)打印流:PrintStream
public class Demo01 {
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream("File.txt"));
//打印流独有的方法 -- 直接打印数据(原样保存到文件中)
ps.println(97);
ps.println("a");
ps.println(true);
ps.close();
}
}
(7)随机访问流:RandomAccessFile
1)从文件指定位置处写入数据
//从文件指定位置处写入数据
@Test
public void test02() throws Exception {
RandomAccessFile rw = new RandomAccessFile("File.txt", "rw");
//设置偏移量,从文件中第6位开始写入
rw.seek(6);
//写入的位置后面的数据都会被覆盖
rw.write("Hello".getBytes());
rw.close();
}
//文本里原始的内容:RandomAccessFile
//写入后文本里的内容:RandomHellosFile
2)从文件指定位置处读取数据
//从文件指定位置处读取数据
@Test
public void test04() throws Exception {
RandomAccessFile r = new RandomAccessFile("File.txt", "r");
//设置偏移量,从文件中第6位开始读取
r.seek(6);
byte[] b = new byte[1024];
int len;
while( (len = r.read(b)) != -1){
System.out.println(new String(b,0,len));
}
r.close();
}
//文本里原始的内容:RandomAccessFile
//控制台显示的内容:AccessFile
3)利用随机访问流实现的断点续传功能
//断点续传
@Test
public void test05() throws Exception {
//源文件的对象
RandomAccessFile r = new RandomAccessFile("E:\\hello.mp4", "r");
//目标文件的对象
RandomAccessFile rw = new RandomAccessFile("F:\\hello.mp4", "rw");
//源文件和目标文件进行读写操作之前设置指针的位置(目标文件的长度)
r.seek(rw.length());
rw.seek(rw.length());
byte[] b = new byte[100];
int len;
while( (len = r.read(b)) != -1){
rw.write(b,0,len);
}
r.close();
rw.close();
}
三、常见的字符编码
编码 | 说明 |
---|---|
ISO-8859-1 | 收录除ASCII外,还包括西欧、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。 |
UTF-8 | 针对Unicode的可变长度字符编码。 |
GB2312 | 简体中文。 |
GBK | 简体中文、扩充。 |
BIG5 | 台湾,繁体中文。 |
注:当编码方式和解码方式不一致时,会出现乱码。
四、字符流:只针对文本数据
字符流 | 字符输出流 | 字符输入流 | 描述 |
---|---|---|---|
字符抽象类 | Writer | Reader | 字符输出 / 输入流的基类 |
字符节点流 | FileWriter | FileReader | 文件字符输出 / 输入流 |
字符缓冲流 | BufferedWriter | BufferedReader | 带缓冲区的字节输出 / 输入流, 只有关流或者缓冲区满了,缓冲区的数据才会保存到文件中 |
打印流 | PrintWriter | 没有输入,只有输出, 直接打印数据,是输出信息最方便的类 | |
转换流 | OutputStreamWriter | InputStreamReader | 前者是把程序中的字符转换成文件中的字节 , 后者是把文件中的字节转换成程序中的字符, 可以针对不同编码格式的文件进行读写操作 |
(1)字符抽象类:Reader、Writer
(2)字符节点流:FileReader、FileWriter
1)读取单个字符
public class Test {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("F:\\File\\File.txt");
int read = fr.read();
//因为读取到的数据是ASCALL码,所以要强转为字符型
System.out.println((char)read);
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtil.closeAll(fr);
}
}
}
2)读取多个字符
public class Test {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("F:\\File\\File.txt");
//因为字符流读取的是字符,所以使用char类型数组
char[] c = new char[100];
int len;
while( (len = fr.read(c)) != -1){
System.out.println(new String(c,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtil.closeAll(fr);
}
}
}
3)使用字符流写入数据
public class Test {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("F:\\File\\File.txt");
fw.write("hello");
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtil.closeAll(fw);
}
}
}
4)字符流的拷贝(只能拷贝文本文件)
//从一个txt文本拷贝到另一个txt文本
public class Demo01 {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
int len;
char[] ch = new char[100];
try {
fr = new FileReader("File.txt");
fw = new FileWriter("File2.txt",true);
while ((len = fr.read(ch)) != -1){
fw.write(ch,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//自定义释放资源工具类
IOUtil.closeAll(fr,fw);
}
}
}
(3)字符缓冲流:BufferedReader、BufferedWriter
1)字节缓冲流的读取
public class Test {
public static void main(String[] args) throws Exception {
//1、创建缓冲流
BufferedReader br=new BufferedReader(new FileReader("File.txt"));
//2、读取,一行一行的读取
String str=null;
while((str= br.readLine()) != null) {
System.out.println(str);
}
//3关流,释放资源
br.close();
}
}
2)字节缓冲流的写入
public class Test {
public static void main(String[] args) throws Exception {
//1创建BufferedWriter对象
BufferedWriter bw=new BufferedWriter(new FileWriter("File.txt"));
//2写入数据
bw.write("hello");
//3关流,释放资源
bw.close();
}
}
3)字节缓冲流的拷贝
public class Test {
public static void main(String[] args) throws Exception {
//带缓冲区的字符输入流
BufferedReader br = new BufferedReader(new FileReader("IO1.txt"));
//带缓冲区的字符输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("IO2.txt"));
String str;
//BufferedReader独有的方法 -- 读取一行
while( (str = br.readLine()) != null){
bw.write(str);
//BufferedWriter独有的方法 -- 换行
bw.newLine();
}
//关流,释放资源
br.close();
bw.close();
}
}
(4)打印流:PrintWriter
封装了print() / println()方法,支持写入后换行。
支持数据原样打印。
public void ccc() throws Exception{
//1创建打印流
PrintWriter pw=new PrintWriter("File.txt");
//2打印,将数据保存到文件中
pw.println(97);
pw.println(true);
pw.println(3.14);
pw.println('a');
//3关流,释放资源
pw.close();
}
(5)转换流:
1)InputStreamReader:把文件中的字节转换成程序中的字符
2)OutputStreamWriter:把程序中的字符转换成文件中的字节
可将字节流转换为字符流。
可设置字符的编码方式。
//指定编码格式读取文件
@org.junit.Test
public void test03() throws IOException{
InputStreamReader isr = new InputStreamReader(new FileInputStream("File.txt"),"GBK");
char[] c = new char[1024];
int len;
while((len = isr.read(c)) != -1){
System.out.println(new String(c,0,len));
}
isr.close();
}
//指定编码格式写出数据
@org.junit.Test
public void test04 ()throws IOException{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("File.txt"), "GBK");
osw.write("hello");
osw.close();
}
五、输入和输出的重定向
public class Test{
//输出重定向
//控制台输出 -- 重新定义方向 -- 输出到文件中
@Test
public void test01() throws Exception {
//标准输出流
System.out.println("hello");
//改变输出方向
System.setOut(new PrintStream(new FileOutputStream("File.txt")));
//标准输出流是输出到控制台的,因为使用setOut()方法改变了输出的方式,使内容保存到"File.txt"文件中
}
//输入重定向
// 控制台输入数据到程序中 -- 重新定义方向 -- 文本文件输入到程序中
@Test
public void test02() throws FileNotFoundException {
//重新定义方向
System.setIn(new FileInputStream("File.txt"));
//标准输入流
Scanner sc = new Scanner(System.in);
String str = sc.next();
System.out.println(str);
//标准输入流是通过控制台输入并在控制台输出的,因为使用setIn()方法改变了输入的方式,从而使输出流直接从文本文件获取数据并在控制台输出
}
}