一、File类
1.File类概述
File创建对象的方式
File创建对象支持绝对路径和相对路径,最常用的方法还是使用绝对路径创建文件对象,当你使用该路径创建完对象之后,就相当于拿到了该路径下文件的对象(定位文件和文件夹)。File对象封装的仅仅是路径名,他可以存在也可以不存在。
绝对路径:从盘符开始的路径
相对路径(模糊路径): 默认在当前工程下找文件
代码:
package com.itheima.d1_file;
import java.io.File;
/*
定位操作系统文件
*/
public class FileDemo {
public static void main(String[] args) {
//1. 创建File对象 指定文件路径
File f = new File("C:\\Users\\Justin 梁\\Desktop\\IMG_20220608_194305_881.jpg");
long size = f.length(); //文件的字节大小。
System.out.println(size);
//2. File创建对象 支持绝对路径 支持相对路径 不带盘符(重点)模糊路径 具体到工程中
File f1 = new File("C:\\Users\\Justin 梁\\Desktop\\zz.png");
System.out.println(f1.length());
//一般定位到模块中的文件的 相对到工程下
File f2 = new File("day09--oop-demo/src/data.txt");
System.out.println(f2.length());
//3. File 创建对象 可以是文件也可以是文件夹 但是当你计算大小的时候会出错
File f3 = new File("C:\\java");
System.out.println(f3.exists());
}
}
2.File类常用API
以下是File类常用的API以及API的使用方式
代码:
package com.itheima.d2_file_api;
import java.io.File;
import java.text.SimpleDateFormat;
public class ApiDemo {
public static void main(String[] args) {
//获取绝对路径字符串
File f = new File("C:\\Users\\Justin 梁\\Desktop\\zz.png");
String Abpath = f.getAbsolutePath();
System.out.println(Abpath);
//判断是否为文件夹
File f1 = new File("C:\\java");
System.out.println(f1.isDirectory()); //true
//判断是否为文件
File f2 = new File("day09--oop-demo/src/data.txt");
System.out.println(f2.isFile());
//判断是否存在该文件
File f3 = new File("C:\\asd");
System.out.println(f3.exists()); // false
//将路径名转为字符串
String path = f.getPath();
System.out.println(path);
//返回文件最后修改时间毫秒值
long time = f2.lastModified();
System.out.println(time);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println("最后修改时间:" + sdf.format(time));
//返回文件名称 带后缀
String name = f2.getName();
System.out.println(name);
}
}
File类创建文件的功能
其中要搞清楚 mkdir()和mkdirs()方法的区别,mkdir只能创建一级文件夹,也就是在该路径下只能创建一个文件夹,而mkdirs可以创建多级文件夹也就是文件夹的嵌套如:aaa/bbb/ccc 如果使用mkdir方法创建多级目录的话会报错。
delete()方法只能删除空文件夹,非空文件夹不可以删除,并且在程序占用的时候,也可以在程序中删除该文件
代码:
package com.itheima.d2_file_api;
import java.io.File;
import java.io.IOException;
/*
File 类的创建和删除的API
*/
public class ApiDemo1 {
public static void main(String[] args) throws IOException {
//创建类的
//创建新的空文件夹
File f= new File("day09--oop-demo/src/data02.txt");
System.out.println(f.createNewFile());
//mkdir 只能创建一级目录
File f2 = new File("C:/code/aaa");
System.out.println(f2.mkdir());
//创建多级文件夹
File f3 = new File("C:/code/aaa/bbb/ccc");
System.out.println(f3.mkdir());
System.out.println(f3.mkdirs());
//删除类的
//删除文件或空文件夹 只能删除空文件夹 delete不走回收站 直接在磁盘中删除
System.out.println(f.delete()); //在占用的时候一样可以删除
System.out.println(f2.delete());
}
}
File的遍历
LIST()获取一级文件名称到字符串数组去 listFile()返回一级文件对象到文件对象数组中去,后者用的更多,当你获取到的是对象的时候,这就意味着你有更多的操作可以使用。
一般方法:先获取File对象定位到一个目录下,再使用ListFiles方法将一级目录对象返回进行遍历操作,在foreach中使用获取绝对路径方法进行遍历。
注意:
package com.itheima.d2_file_api;
import java.io.File;
/*
File类的遍历功能
*/
public class ApiDemo2 {
public static void main(String[] args) {
//list方法 将当前目录下的所有一级文件名返回字符串数组
File f = new File("C:/code");
String[] arr = f.list();
for (String str: arr) {
System.out.println(str);
}
//listFiles(常用) 返回文件数组 将一级文件对象返回到数组中
File[] files = f.listFiles();
for (File f1 : files) {
System.out.println(f.getAbsolutePath());
}
File f1 = new File("C:/asd");
File[] files1 = f1.listFiles();
System.out.println(files1);
}
}
3.文件的递归查询
文件递归搜索:要想找到所需要的文件,思想为 创建绝对路径文件对象到盘符第一层,创建字符串接收所需要找的文件名字,传入方法中先判断盘符中是否为空或是否为私密文件夹,如果存在可访问文件夹进入查询,提取一级文件对象,判断是否存在一级文件对象,对对象进行判断和遍历,此时一级文件对象分为文件夹和文件,判断是文件的话与文件名进行模糊比对,如果是文件夹的话再次进行递归操作。
代码:
package com.itheima.d3_recusion;
import java.io.File;
public class Demo2 {
public static void main(String[] args) {
File f = new File("C:/");
FileRecusion(f,"itheima-data.txt");
}
public static void FileRecusion(File f,String filename)
{
//判断是否为目录
if (f != null && f.isDirectory())
{
File[] files = f.listFiles();// null []
//判断是否存在一级对象
if (files != null && files.length > 0)
{
for (File file: files) {
if (file.isFile())
{
if (file.getName().contains(filename));
{
System.out.println("绝对路径:" +file.getAbsolutePath());
}
}else {
FileRecusion(file , filename);
}
}
}
}else {
System.out.println("对不起,您当前搜索的不是文件夹");
}
}
}
二、字符集概述
什么是字符集?
因为计算机存储数据靠二进制0,1存储的,因此如果计算机想存储人类语言,这个时候就需要给人类语言进行编号,这就是字符集的概念。
最常见的字符集分为,ASCII字符集,GBK编码,Unicode编码,而Unicode又分为常见的UTF-8等等。
这是计算机编码与解码的过程,也就是计算机存储信息和读取信息的过程,编码与解码的字符集需一致,不然会出现乱码现象。
常见的编码和解码操作:编码:通过getBytes方法编码,参数中可以选择编码字符集。
而常见的解码操作就是转换成字符串,创建字符串对象在构造器中进行解码。
代码:
package com.itheima.d4_charset;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.util.Arrays;
/*
目标: 学会自己进行文字的编码和解码, 为以后可能用到的场景做准备。
*/
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
//编码 把文字转换成字节 (使用指定的编码表)
String s = "abc我爱你中国";
// byte[] bytes = s.getBytes(); //当前代码默认字符集进行编码 UTF-8
byte[] bytes = s.getBytes("GBK"); // 使用指定编码表进行编码 使用GBK编码 一个汉字数两个字节 ,一个英文是一个字节
System.out.println(bytes.length); //UTF-8存储中文是三个字节存储一个汉字,英文是一个字节,存储规律英文为正 中文为负 字符串为18字节
System.out.println(Arrays.toString(bytes));
//解码 把字节转换成对应的中文形式(编码前 和 解码后必须的字符集必须一致,否则乱码)
//String rs = new String(bytes);
String rs= new String(bytes,"GBK"); //指定GBK解码
System.out.println(rs);
}
}
三、IO流
IO流(INPUT OUTPUT) 也称为输入输出流,就是用来读写数据的。通过字节操作和字符操作来进行内存与硬盘中信息的交换。 IO流可以想象成水流,其中输入流和输出流就相当于一个管道来链接文件和硬盘。
IO流的分类
IO流体系
1.字节流概述
常用的是文件字节流,将数据以字节的形式进行输入和存储操作。
2.字节流输入常用API
创建字节输入流的构造器可以放文件对象,也可以放路径名称。方法分为读取一个字节和读取一个字节数组,当文件中数据读取完毕后返回-1.
1.读取字节,一个字节一个字节的返回
代码:
package com.itheima.d5_IOStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class IODemo {
public static void main(String[] args) throws Exception {
//创建一个文件字节输入流管道与源文件接通。
//InputStream is = new FileInputStream(new File("day09--oop-demo"));
//简化写法 java自己在内部帮你封装好
InputStream is = new FileInputStream("day09--oop-demo/src/data.txt");
// //读取一个字节返回 (一滴水一滴水的读)
// int b1 = is.read();
// System.out.println((char) b1);
//
// int b2 = is.read();
// System.out.println((char) b2);
//
// int b3 = is.read();
// System.out.println((char) b3);
//
// int b4 = is.read(); //读取完毕返回服役
// System.out.println(b4);
//使用循环改进
//定义一个变量记录每次读取的字节
int b;
while((b = is.read()) != -1)
{
System.out.println((char) b);
}
}
}
想将文件中的数据全部读取,如果是用这种方法的话可以使用循环进行读取,但是只能读取这种一个字节的字符,如果是中文的话,用这种方法读取就会乱码,因为在上面字符集中讲到,GBK和UTF-8对于中文的编码格式分别为两字节和三字节。因此只读取一个字节输出的话就会乱码。
2.每次读取一个字节数组 (相当于一次接一盆水)
通过创建字符数组,规定字符数组大小(一次接收多少个字符),再调用字节输出流的read方法对文件进行读取。
代码:
package com.itheima.d5_IOStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class IODemo {
public static void main(String[] args) throws Exception {
//创建一个文件字节输入流管道与源文件接通。
//InputStream is = new FileInputStream(new File("day09--oop-demo"));
//简化写法 java自己在内部帮你封装好
InputStream is = new FileInputStream("day09--oop-demo/src/data.txt");
// //读取一个字节返回 (一滴水一滴水的读)
// int b1 = is.read();
// System.out.println((char) b1);
//
// int b2 = is.read();
// System.out.println((char) b2);
//
// int b3 = is.read();
// System.out.println((char) b3);
//
// int b4 = is.read(); //读取完毕返回服役
// System.out.println(b4);
//使用循环改进
//定义一个变量记录每次读取的字节
int b;
while((b = is.read()) != -1)
{
System.out.println((char) b);
}
}
}
注意: 这是文件中存储的字符,当使用字符数组方法进行数据的读取的时,因为设置的每次读取三个字节,按逻辑来讲的话第三次应该输出的是cd, 但是输出结果为:cdc,这是什么原因呢?
因为再这个文件中为8个字节,但是在第三次读取的时候,只读了两个字节,因此不能完全覆盖上次数组存储的数据,所以在输出的时候就会出现这种问题,因此为了解决这个问题,可以用String构造器中的(buffer,int pos ,int length),这里面的参数为字符数组,起始位置,和输出长度。因此在用这个构造器进行读取的时候,就可以实现“读多少,倒多少”。
但是使用这种字符数组读取的方法还是无法避免中文乱码的问题,因为在文件中,中文字符的位置不确定,并且不同编码字符集对应中文的存储字节也不同,因此再用循环字符数组输出的时候,中文乱码的问题肯定是解决不了的。比如字符串: abcd啊,如果字符数组长度为3,那么每次读取三个字符,但是读取完d还可以在读取两个字节,如果是UTF-8的字符集,此时中文占三个字节,那么后面计算机解析到的两个字节就在编码表中找不到,因此就会产生乱码。
那么字节流读取用什么方法可以解决这样的问题呢?可以文件所有的字节一次性都存储到字符数组中,一次读取完文件的全部字节,这样虽然可以解决中文乱码问题,但是如果当读取的文件过大的时候可以能会发生内存溢出的现象。
两种方法:
package com.itheima.d5_IOStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class InPutStreamDemo3 {
/*
使用字节输入流一次性读取完全部字节,避免中文乱码问题
*/
public static void main(String[] args) throws Exception {
File f = new File("day09--oop-demo/src/data.txt");
InputStream is = new FileInputStream(f);
//定义一个字节数组与文件的大小一样大
// byte[] buffer = new byte[(int) f.length()];
// int len = is.read(buffer);
// System.out.println("读取了多少个字节" + len);
// System.out.println("文件大小" + f.length());
// System.out.println(new String(buffer));
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
}
}
3.字节流输出常用API
字节输出流,就是将内存中的数据以字节的大小写入到硬盘当中。并且在写完数据之后必须进行刷新数据,这样数据才可以写入到硬盘中。
字节输出流对象的两种形式:覆盖输出和追加输出。
覆盖输出:将文件中的信息全部覆盖掉,只留下输出信息。
追加输出:在文件的末尾加上所需要输出的信息。
字节流中的换行输出("\r\n".getbytes() )
,
代码:
package com.itheima.d5_IOStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class OutPutStreamDemo04 {
public static void main(String[] args) throws Exception {
//OutputStream os = new FileOutputStream("day09--oop-demo/src/data.txt"); //先清空之前的数据,写新数据进去 覆盖管道
OutputStream os = new FileOutputStream("day09--oop-demo/src/data.txt",true); // 追加管道
//写数据出去 写一个字节出去
os.write('a');
os.write(98);
os.write("\r\n".getBytes()); //写出去的数据实现换号
//写一个数组出去
byte[] buffer = {'a',97,98}; //a a b
os.write(buffer);
byte[] buffer2 = "我是中国人".getBytes(); //编码成字符数组
//byte[] buffer2 = "我是中国人".getBytes("GBK"); 如果编码格式不同的话 解码的时候会乱码
os.write(buffer2);
// 写数组的一部分出差去 write(buffer , int pos , int len)
byte[] buffer3 = {'a',97,98,99};
os.write(buffer,0,3);
// os.flush(); // 写数据必须刷新数据 可以继续使用流
os.close(); // 释放资源 包含刷新, 不释放输出流的话会占内存, 不能继续使用
}
}
4.字节流拷贝(常用于视频,音频的拷贝)
通过输入流和输出流的配合进行数据的拷贝,因为字节流拷贝的读取方式一般都是一次性全部读取,因此非常适合用于拷贝音频和视频。
代码:
package com.itheima.d5_IOStream;
import java.io.*;
/*
学会使用字节流完成文件的复制
*/
public class CopyDemo {
public static void main(String[] args) {
// 创建一个字节输入流管道与原文件接通
try {
InputStream is = new FileInputStream("day09--oop-demo/src/data.txt");
OutputStream os = new FileOutputStream("C:\\code\\data01.txt");
File f = new File("day09--oop-demo/src/data.txt");
byte[] buffer = is.readAllBytes();
os.write(buffer);
System.out.println("复制完成了");
//关闭流
os.close();
is.close();
} catch ( Exception e) {
e.printStackTrace();
}
}
}
5.资源
资源的释放:在刚刚的代码中,如果在没有释放资源之前就出现了异常,那么程序就不会再执行释放资源的代码,导致流对内存资源的占用,和对CPu线程的占用。因此需要进行优化。
1.Try-catch-finally
package com.itheima.d6_resource;
import java.io.*;
public class TryCatchFinallyDemo1 {
public static void main(String[] args) {
// 创建一个字节输入流管道与原文件接通
InputStream is = null; //在main方法中加载在哪个块里都能用
OutputStream os = null;
try {
int a = 10 / 0;
is = new FileInputStream("day09--oop-demo/src/data.txt");
os = new FileOutputStream("C:\\code\\data01.txt");
File f = new File("day09--oop-demo/src/data.txt");
byte[] buffer = is.readAllBytes();
os.write(buffer);
System.out.println("复制完成了");
//关闭流
os.close();
is.close();
} catch ( Exception e) {
e.printStackTrace();
}finally { //无论是正常结束,还是出现异常 都会执行这里 适合做收尾操作,释放资源
System.out.println("finally------");
//开发中不建议在finally中加return 不然不管其他块中返回什么值 都只会执行finally里的return
try {
if (os != null)os.close(); //加判断是因为 有可能还没赋值就异常了
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null)is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
根据这个格式的特点,就可以将资源释放的代码放入finally块中,因为不管是否程序异常退出还是正常执行,他都会执行finally块中的代码。注意:因为释放资源的代码要放在finally块中,所以try块中的字节流输入输出对象就用不了了,为了解决这个问题,可以将这两个对象再main方法中赋值,这样所有块里就都可以用了,但是在给输入输出流赋值之前出现异常的话就会直接到finnaly中去,可能会出现空指针异常,因此要在关闭之前还是需要进行判断,如果为空的话就不需要再进行资源的释放了。
弊端:如果再try catch中你想通过返回数return来进行操作,但是如果你的finally中也有return,那么你不管上面的两个块中返回的什么,他都只会返回finally中的值。
try-catch-resource
这个方法是简化了finally中的操作,可以直接再try(资源)中书写资源操作。
代码:
package com.itheima.d6_resource;
import java.io.*;
public class TryCatchResourceDemo {
/*
为了简化代码(手动释放资源) 引入一个新知识, 资源用完后会自动释放
*/
public static void main(String[] args) {
// 创建一个字节输入流管道与原文件接通
try (
//只有实现了Closeable接口的才算是资源
//这里面只能放置资源对象,用完会自动关闭: 自动调用资源对象的close方法关闭资源(即使出现异常也会执行操作)
InputStream is = new FileInputStream("day09--oop-demo/src/data.txt");
OutputStream os = new FileOutputStream("C:\\code\\data01.txt");
//int age = 23;只能放资源
MyConnection my = new MyConnection(); //只能放资源
) {
int a = 10 / 0;
File f = new File("day09--oop-demo/src/data.txt");
byte[] buffer = is.readAllBytes();
os.write(buffer);
System.out.println("复制完成了");
//关闭流
os.close();
is.close();
} catch ( Exception e) {
e.printStackTrace();
}
}
}
class MyConnection implements AutoCloseable{
@Override
public void close() throws IOException {
System.out.println("资源释放");
}
}
6.字符流概述
通过传输字符的方式对数据进行操作,很好的解决了字节流中读取中文输出的乱码问题,因此字符流很适合对于文本信息数据进行操作。
7.字符流输入常用API
代码:
package com.itheima.d7_char_Stream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class FileReaderDemo01 {
public static void main(String[] args) throws Exception {
//文件字符输入流 按字符读取文件 好处:读取中文的时候不会出现乱码: 代码和文件编码一致
Reader rd = new FileReader("day09--oop-demo/src/data.txt");
// 读取一个字符返回,没有可读的字符了返回-1
// int code = rd.read();
// System.out.println((char)code);
//使用循环读取字符
int code;
while ((code = rd.read()) != -1);
{
System.out.println((char) code);
}
}
}
package com.itheima.d7_char_Stream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
public class FileReaderDemo02 {
public static void main(String[] args) throws Exception {
Reader fr = new FileReader("day09--oop-demo/src/data.txt");
//使用字符数组输入
char[] buffer = new char[1024];
int len;
while (( len = fr.read(buffer)) != -1)
{
String rs = new String(buffer,0,len);
System.out.println(rs);
}
}
}
8.字符流输出常用API
代码:
package com.itheima.d7_char_Stream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
Writer fw = new FileWriter("day09--oop-demo/src/data.txt");
fw.write(98);
fw.write('a');
fw.write('帅');
fw.write("\r\n"); // 换行
fw.write("丹菲是猪");
fw.write("\r\n");
//写一个字符数组进去
char[] chars = "abc我是帅哥".toCharArray();
fw.write(chars);
fw.write("\r\n");
//写字符串的一部分
fw.write("我操丹菲",0,3);
fw.write("\r\n");
//写字符数组的一部分
fw.write(chars,0,2);
fw.write("\r\n");
fw.close();
}
}
高级流
上面介绍的流都是基础流,也成为低级管道,因为他的性能与这个流比起来差了很多。
一、缓冲流
1.缓冲流概述
缓冲流的主要作用就是提高低级管道的读写性能,在交换数据的时候,内存就相当于一块缓冲区,而上面讲到的低级流进行读写,对于每次读写的大小都有限制和弊端,而在缓冲流中,他自己就相当于一个缓冲区,底层有一个8kb的数组,当你使用缓冲流进行读写的时候,就相当于在内存中装数据就不需要再从磁盘中找数据这样操作就会非常的快,大大提升了性能。
四大缓冲流的体系和继承关系
package com.itheima.d1_byte_buffer;
import java.io.*;
public class ByteBufferDemo {
public static void main(String[] args) {
// 创建一个字节输入流管道与原文件接通
try (
//只有实现了Closeable接口的才算是资源
//这里面只能放置资源对象,用完会自动关闭: 自动调用资源对象的close方法关闭资源(即使出现异常也会执行操作)
InputStream is = new FileInputStream("day09--oop-demo/src/data.txt");
//把原始字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream("C:\\code\\data01.txt");
//把字节输出流管道包装成高级的缓冲字节输出流管道
OutputStream bos = new BufferedOutputStream(os);
//int age = 23;只能放资源
) {
int a = 10 / 0;
File f = new File("day09--oop-demo/src/data.txt");
byte[] buffer = bis.readAllBytes();
bos.write(buffer);
System.out.println("复制完成了");
//关闭流
os.close();
is.close();
} catch ( Exception e) {
e.printStackTrace();
}
}
}
2.字节缓冲流
缓冲流相当于一个包装类,再构造器的内部将低级的字节流包装成高级的字节流从而提高字节流的读写性能。
3.字节缓冲流的性能分析
在性能分析这里,缓冲流的操作会与字节流输入输出操作进行性能的比对,从而可以看出两个流之间性能的差异
代码:
package com.itheima.d2_byte_buffer_time;
import java.io.*;
public class ByteBufferTimeDemo {
public static void main(String[] args) throws Exception {
copy01();//使用低级的字节流按照一个一个字节的形式复制文件
copy02();
copy03();//慢 不建议使用
copy04();
}
private static void copy04() {
long startTime = System.currentTimeMillis();
InputStream is = null;
OutputStream os = null;
try{
is = new FileInputStream("day09--oop-demo/src/data.txt");
InputStream bis = new BufferedInputStream(is);
os = new FileOutputStream("C:\\code\\data04.txt");
OutputStream bos = new BufferedOutputStream(os);
int len;
byte[] buffer = new byte[1024];
while((len = bis.read(buffer)) != -1)
{
bos.write(buffer,0,len);
}
System.out.println("复制完成了");
}catch (Exception e)
{
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用高级的缓冲流按照一个字节数组一个字节数组的复制文件耗时: "+ (endTime - startTime) / 1000.0 + "S");
}
private static void copy03() {
long startTime = System.currentTimeMillis();
InputStream is = null;
OutputStream os = null;
try{
is = new FileInputStream("day09--oop-demo/src/data.txt");
InputStream bis = new BufferedInputStream(is);
os = new FileOutputStream("C:\\code\\data03.txt");
OutputStream bos = new BufferedOutputStream(os);
int len;
while((len = bis.read()) != -1)
{
bos.write(len);
}
}catch (Exception e)
{
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用高级的缓冲流按照一个一个字节的复制文件耗时: "+ (endTime - startTime) / 1000.0 + "S");
}
private static void copy02() {
long startTime = System.currentTimeMillis();
InputStream is = null;
OutputStream os = null;
try{
is = new FileInputStream("day09--oop-demo/src/data.txt");
os = new FileOutputStream("C:\\code\\data02.txt");
int len;
byte[] buffer = new byte[1024];
while((len = is.read(buffer)) != -1)
{
os.write(buffer,0,len);
}
System.out.println("复制完成了");
}catch (Exception e)
{
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用低级的字节流按照一个字节数组一个字节数组的复制文件耗时: "+ (endTime - startTime) / 1000.0 + "S");
}
private static void copy01() throws Exception {
long startTime = System.currentTimeMillis();
InputStream is = null;
OutputStream os = null;
try{
is = new FileInputStream("day09--oop-demo/src/data.txt");
os = new FileOutputStream("C:\\code\\data01.txt");
int len;
while((len = is.read()) != -1)
{
os.write(len);
}
}catch (Exception e)
{
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用低级的字节流按照一个一个字节的复制文件耗时: "+ (endTime - startTime) / 1000.0 + "S");
}
}
小细节:在低级字节流按数组方式输出中,可以将字节数组的大小调大些,这样性能相对会比较快。
结论: 用低级的字节流按字节拷贝性能最差,不要用这种方式进行拷贝。
缓冲字节流按照字节数组的方式复制文件>低级字节流按数组方式>缓冲字节流按字节方式拷贝>低级字节流按字节方式拷贝
4.字符缓冲流
字符缓冲流分为字符输出流和字符输入流 BufferReader和BufferWriter
代码:
package com.itheima.d3_char_buffer;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Reader;
public class BufferedReaderDemo1 {
public static void main(String[] args) throws Exception {
//文件字符输入流 按字符读取文件 好处:读取中文的时候不会出现乱码: 代码和文件编码一致
Reader rd = new FileReader("day09--oop-demo/src/data.txt");
BufferedReader bf = new BufferedReader(rd);
// 读取一个字符返回,没有可读的字符了返回-1
// int code = rd.read();
// System.out.println((char)code);
//使用循环读取字符
// int code;
// while ((code = rd.read()) != -1);
// {
// System.out.println((char) code);
// }
String line ;
while((line = bf.readLine()) != null)
{
System.out.println(line);
};
System.out.println("读取完成了");
}
}
应用: 在里面有新的排序方法。
代码:
package com.itheima.d3_char_buffer;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test {
public static void main(String[] args) {
try {
BufferedReader bf = new BufferedReader(new FileReader("day10--oop-demo/src/SC.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("day10--oop-demo/src/SC.txt"));
//数组存储信息
List<String> list = new ArrayList<>();
String str ;
while( (str = bf.readLine()) != null)
{
list.add(str);
};
List<String> list1 = new ArrayList<>();
Collections.addAll(list1,"一","二","三","四","五");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.indexOf(o1.substring(0,o1.indexOf("."))) - o2.indexOf(o2.substring(0,o2.indexOf(".")));
}
});
System.out.println(list);
for (String str1: list) {
bw.write(str1);
bw.newLine();
}
} catch ( Exception e) {
e.printStackTrace();
}
}
}
二、转换流
转换流的作用:解决代码编码和文件编码不一致,字符流乱码问题,如果使用字符流读取文本内容的话,如果文件为GBK,但是读取方式为UTF-8的话,在读中文字符的时候就会乱码,因为GBK为两个字节存储,UTF为三字节存储,为什么没有字节流(因为字节流中有getBytes方法)
解决思想:使用字符转换流,先用字节流获取他原始字节流中所有的字节,然后把字节流以指定编码形式转换成字符输入流,这样输入流中的字符就不会乱码了。
代码:
package com.itheima.d4_transfer_stream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
public class CharSetTest {
public static void main(String[] args) throws Exception {
//文件字符输入流 按字符读取文件 好处:读取中文的时候不会出现乱码: 代码和文件编码一致
Reader rd = new FileReader("C:\\code\\itheima1.txt");
BufferedReader bf = new BufferedReader(rd);
// 读取一个字符返回,没有可读的字符了返回-1
// int code = rd.read();
// System.out.println((char)code);
//使用循环读取字符
// int code;
// while ((code = rd.read()) != -1);
// {
// System.out.println((char) code);
// }
String line ;
while((line = bf.readLine()) != null)
{
System.out.println(line);
};
System.out.println("读取完成了");
}
}
如果要控制写出去的流编码
代码:
package com.itheima.d4_transfer_stream;
import java.io.*;
public class OutputStreamerDemo {
public static void main(String[] args) throws Exception {
OutputStream os = new FileOutputStream("day10--oop-demo/src/SC.txt") ;
//转换成字符输出流
Writer wr = new OutputStreamWriter(os,"GBK");
//缓冲流包装
BufferedWriter bw = new BufferedWriter(wr);
bw.write("我爱中国");
bw.write("我爱中国1");
bw.close();
}
}
三、序列化对象
1.序列化概述
对象的序列化就是将内存中的对象数据给存储到磁盘中去,并且对象可以序列化的前提是该类实现Serializable接口。只有实现了这个接口的类才可以被序列化,在类中如果属性值前有contrient关键字的话就表示该属性不会被序列化,也就是被隐藏起来了。
2.对象的序列化与反序列化
代码
package com.itheima.d5_serializable;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutPutStreamDemo1 {
public static void main(String[] args) throws IOException {
Student stu = new Student("帅哥","123456asd",2);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day10--oop-demo/src/ST.txt"));
oos.writeObject(stu);
oos.close();
}
}
代码:
package com.itheima.d5_serializable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
对象反序列化,将硬盘中存储的对象恢复成JAVA对象
transient修饰的属性不会被序列化
*/
public class ObjectInPutStreamDemo2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day10--oop-demo/src/ST.txt"));
Student student =(Student) ois.readObject();
System.out.println(student);
}
}
四、打印流
作用:打印流可以实现方便,高效的打印数据到文件中,打印一般指PrintStream与PrintWriter类
这两个类的区别就在于写上面,在打印上面的功能是一样的。
代码:
package com.itheima.d6_printStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
public class printDemo1 {
public static void main(String[] args) throws Exception {
/*
两种构造器
文件
低级管道
*/
// PrintStream ps = new PrintStream(new FileOutputStream("day10--oop-demo/src/ST.txt",true)); //封装低级管 追加操作需要在低级管道中操作
// PrintStream ps = new PrintStream ("day10--oop-demo/src/ST.txt"); //封装文件 字节打印流 写的话还是只能一个字节一个字节的写
PrintWriter ps = new PrintWriter("day10--oop-demo/src/ST.txt"); //字符打印流 打印功能上没区别
ps.println(97);
ps.println('a');
ps.println(true);
ps.println("我是打印流输出的");
ps.write("哈哈哈啊哈");
ps.close();
}
}
小应用:输出语句重定向,可以把输出语句的打印位置改到文件
代码:
package com.itheima.d6_printStream;
import java.io.FileNotFoundException;
import java.io.PrintStream;
public class printDemo2 {
/*
打印流实现重定向
*/
public static void main(String[] args) throws FileNotFoundException {
System.out.println("单飞");
System.out.println("双飞");
PrintStream ps = new PrintStream("day10--oop-demo/src/SC.txt");
System.setOut(ps); // 使打印流打印到路径文件,控制台中就没有了 实现重定向
System.out.println("三飞");
}
}
补充:properties
与IO流结合存储
package com.itheima.d7_properties;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class propertiesDemo {
public static void main(String[] args) throws Exception {
//使用Properties把键值对信息存入到属性中去
Properties properties = new Properties();
properties.setProperty("admin","12345600");
System.out.println(properties);
/*
参数一:保存管道 字符输出流管道
参数二: 内容心得
*/
properties.store(new FileWriter("day10--oop-demo/src/user.properties"),"this is users");
}
}
package com.itheima.d7_properties;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class Demo2 {
public static void main(String[] args) throws IOException {
//需求: Properties 读取属性文件中的键值对信息。(读取)
Properties pro = new Properties();
System.out.println(pro);
pro.load(new FileReader("day10--oop-demo/src/user.properties"));
System.out.println(pro);
//获取到值
String pwd = pro.getProperty("admin");
System.out.println(pwd);
}
}