IO
File类只能操作属性,无法操作文件里面的数据
IO
InputStream——OutputStream
从流向上划分:输入流/输出流
从操作内容上来划分:字节流 字符流
操作文件里面的数据:
- 读取文件里面的数据 --->read 输入流
- 将数据写入文件 –>write 输出流
1.字节流
计算机里面所有文件内存存储都是以字节的形式进行存储,也就是二进制文件。因此字节流就是万能流,可以操作计算机里面任意类型的文件
1.1 字节输入流InputStream
InputStream
是一个抽象类,常用子类:
FileInputStream();//文件字节输入流
BufferedInputStream();//高效字节输入流
DataInputStream();//数据输入流
ObjectInputStream();//对象输入流
InputStream
里面的方法:
abstract int read()
int read(byte[] bytes); 读取字节数组长度的字节内容
FileInputStream 效率低
FileInputStream(File file)
FileInputStream(String name)
read(); 读取一个字节内容
private static void demo1() {
//读取指定文件数据:文件必须存在
String filePath="1129\\1129.iml";
//创建字节输入流对象:
InputStream inputStream=null;
try {
inputStream=new FileInputStream(filePath);
//读取文件数据
int len;
while ((len=inputStream.read())!=-1){
System.out.print((char) len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//但是上面大部分代码都是用于处理异常,我们换下面的方式:
private static void demo2() {
//jdk1.7之后提供了一种更加优雅的方式释放资源(自动释放资源) try...with...resource
try (
//创建的对象(必须实现Closeable接口)
InputStream stream = new FileInputStream("1129\\1129.iml");
) {
//读取数据
int read;
while ((read = stream.read()) != -1) {
System.out.print((char) read);
}
} catch (IOException e) {
e.printStackTrace();
}
}
read(byte[] bytes); 读取字节数组长度的字节内容
//让他来读取给定长度(一般是1024的整数倍)的字节内容,读取的内容会放到该数组里面,想看数据的话得看读取的字节数组
private static void demo3() {
try(
InputStream stream=new FileInputStream("1129\\src\\exercise.java");
){
byte[] bytes = new byte[10];
//这里我给了10,如果文件里面的字节小于10,读取字节数也会小于10
// 文件长度超过10,也会只读10个
System.out.println(stream.read(bytes));
System.out.println(new String(bytes));
//将字节数组转字符串输出,如果是中文,有可能会出现乱码:import jav
System.out.println(stream.read(bytes));
//该字节数组已经装满了,如果再去读byte.length长度个,读取内容会覆盖原先的字节数组
}catch (IOException e){
e.printStackTrace();
}
}
read(byte[] bytes,int startIndex,int lenth) 从起始位置读取数组长度的内容,读取指定字节长度
private static void demo4() {
try(
InputStream stream=new FileInputStream("1129\\src\\exercise.java");
){
byte[] bytes=new byte[1024];
stream.read(bytes,0,1024);
System.out.println(new String(bytes));
}catch (IOException e){
e.printStackTrace();
}
}
1.2.字节输出流
OutputStream
:将数据内容写入文件中 也就是write,常用子类
:
FileOutputStream();//文件字节输出流
BufferedOutputStream();//高效字节输出流
DataOutputStream();//数据输出流
ObjectOutputStream();//对象输出流
outputstream
的构造:
FileOutputStream(File file)
FileOutputStream(File file, boolean append)//给定是拼接原文件还是覆盖源文件,默认覆盖
FileOutputStream(String name)
FileOutputStream(String name, boolean append)
FileOutputStream 效率低
private static void demo5() {
try (
//文件可以不存在,构造方法里面可以加一个布尔来判断是覆盖原文件还是拼接原文件
OutputStream stream = new FileOutputStream("1129\\src\\a.txt");
) {
stream.write(65);
stream.write(49);
stream.write(52);
stream.write("abdcfe".getBytes());
stream.write('\n');
stream.write("学习使我快乐!".getBytes());//给汉字的时候注意编码格式
} catch (IOException e) {
e.printStackTrace();
}
}
模拟用户上传头像
private static void demo6() {
File filePath = new File("E:/肖战.jpg");
File filePath1 = new File("D:/肖战.jpg");
String targetPath = "1129\\src\\image\\" + LocalDate.now().toString();
try (
InputStream inputStream = new FileInputStream(filePath1);
OutputStream outputStream =
new FileOutputStream(targetPath + UUID.randomUUID());
//由于图片名称可能会相同,下载路径一致的时候可能会被覆盖,这里我们用UUID的随机数字符来拼接
) {
int len;
while ((len = inputStream.read()) != -1) {
outputStream.write(len);
}
System.out.println(targetPath);
} catch (IOException e) {
e.printStackTrace();
}
}
//读取大文件的时候,这种读取速度是很慢的:
private static void demo7() {
String filePath="E:\\第一阶段课堂笔记(JavaSE).nyf";
String targetPath = "1129\\src" + LocalDate.now().toString();
try(
InputStream inputStream = new FileInputStream(filePath);
OutputStream outputStream = new FileOutputStream(targetPath + UUID.randomUUID());
) {
int len;
while ((len=inputStream.read())!=-1){
outputStream.write(len);
}
}catch (IOException e){
e.printStackTrace();
}
}
//读取速度很慢,我等了一分钟也没读完
但是上面的读写速度是比较慢的,如果需要传大文件的时候,一个读一个写,这样的速度是很慢的,因此我们可以用带参的write(byte[] bytes)
来加快写入文件的速度:
private static void upLoadFile(String sourceFilePath) {
Objects.requireNonNull(sourceFilePath);
File target = new File(TARGET_PATH, LocalDate.now().toString());
if (!target.exists()) {
target.mkdirs();//判断文件目录是否存在,不存在去创建
}
String filename=UUID.randomUUID().toString()+
sourceFilePath.substring(sourceFilePath.lastIndexOf(File.separator)+1);
File targetPath=new File(target,filename);//创建目标文件路径和目标文件名
try (
InputStream inputStream = new FileInputStream(sourceFilePath);
OutputStream outputStream = new FileOutputStream(targetPath);
) {
int len;
byte[] bytes = new byte[1024 * 100];//一次写入这样多的字节
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);//如果不加这一行最后会多读取1024个字节
}
System.out.println("文件上传成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
上面,file来操作文件夹,而IO来操作文件数据
高效字节流
BufferedInputStream:高效字节输入流 read
BufferedOutputStream:高效字节输出流 write
底层自动提供了字节数组来缓冲,默认长度为8192
private static void demo1() {
try{
BufferedOutputStream stream=new BufferedOutputStream(new FileOutputStream("day1202\\src\\a.txt"));
stream.write(78);
stream.write("hello".getBytes());
//写入的数据都放在缓存区,因为此时的文件不满8192的长度,需要释放资源,或者刷新
//stream.flush();
stream.close();//底层自动调用flush()
}catch (IOException e){
e.printStackTrace();
}
}
若用该方法进行io,并且在代码里面又加了一个临时缓冲区,效率会更快,两个缓存区一起使 ,因此推荐使用自己写一个缓冲区,然后再与BufferedOutputStream
来一起使用 ,效率会很快。
2.字符流
reader
:字符输入流
writer
:字符输入流
字符流不能操作其他的二进制文件,主要用于操作纯文本文件(txt、java、html.etc) ,对于有样式的文本文件,后期会用POI来进行操作。底层:字节流+字符集
,它可以写入一串
public void write(String str, int off, int len) throws IOException {
se.write(str, off, len);
}
reader常用子类:
BufferedReader
FileReader
InputStreamReader
writer常用子类:
BufferedWriter
FileWriter
OutputStreamWriter
用法跟字节流差不多,直接拿来用:
上传文件:
private static void demo2() {
String targetPath="day1202\\src";
File filePath=new File(targetPath,LocalDate.now().toString());
if (!filePath.exists()) {
System.out.println("目标路径目录不存在,已为您创建目录");
filePath.mkdirs();
}
String path= "day1202\\src\\a.txt";
File sourcePath=new File(path);
String name=path.substring(path.lastIndexOf('\\')+1);
File targetFile=new File(filePath,name);//得到文件(文件名+文件类型)
try (
Reader reader = new FileReader(sourcePath);
Writer writer=new FileWriter(targetFile)//参数只能是文件,不能是文件夹
) {
int read;
while ((read = reader.read())!=-1){
writer.write(read);
System.out.println("上传成功");
}
} catch (IOException e) {
e.printStackTrace();
}
}
但是它用来操作其他文件的话得到的是一个破损文件,是打不开的,只能操作文本文件
高效字符流
装饰了基本字符流的读写功能,底层自带缓冲(带一个长度为8192的字符数组) ,他里面有一个方法:String readLine() //一次读取一行返回值类型为string
private static void demo3() {
List<User> users=new ArrayList<>();
String sourPath = "day03\\src\\bean\\a.txt";
try (
BufferedReader reader=new BufferedReader(new FileReader(sourPath))
) {
byte[] bytes=new byte[1024*10];
String userInfor;
while ((userInfor=reader.readLine())!=null){
String[] split = userInfor.split("-");
users.add(new User(Integer.parseInt(split[0]),split[1],split[2],Integer.parseInt(split[3])));
}
users.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
序列化流:
也称为对象流,读写对象数据。被读写的对象类必须实现serializable接口,因此以后的大部分实体类都需要实现Serializable接口,表示可以在网络上进行数据传输
public class User implements Serializable {
private static final long serialVersionUID = -8154682815263956903L;
private Integer id;
private String name;
private transient String pwd;//防止序列化
private int age;
}
ObjectOutputStream对象输出流 (序列化操作)
private static void demo5() {
User user=new User(23,"liku","password",25);
try (
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("day03\\src\\bean\\a.txt"))
) {
stream.writeObject(user);//将对象写入
//如果该类没实现序列化接口,会报下面异常
//java.io.NotSerializableException: bean.User
System.out.println("序列化成功"+user.hashCode());
//我们将序列化成功之后的hashcode输出来看一下:序列化成功856419764
} catch (IOException e) {
e.printStackTrace();
}
}
ObjectInputStream对象输入流(反序列化操作)
对于java文件只要修改一次,都需要重新编译,也就是说源文件修改之后如果不重新编译,反序列化之后的版本号是不一致的,类似深克隆,class的版本号serialVersionUID就会发生改变
解决该方法的操作:
1、重新序列化,然后反序列化
2、固定版本ID:idea-->setting-->editor-->inspections-->搜索serial-->勾选已经实现序列化类但是没有生成serialVersionUID
//3、对于一些需要加密的数据(密码,身份证号等)变量可以用transient
来修饰,它可以保证变量不被序列化成功
private static void demo6() {
try(
ObjectInputStream stream=new ObjectInputStream(new FileInputStream("day03\\src\\bean\\a.abc"))
){
User user=(User) stream.readObject();
System.out.println("反序列化成功! "+user.hashCode());
//反序列化成功! 1989780873
System.out.println(user.toString());
//当我把对象以字符串来输出的时候,报了异常:java.io.InvalidClassException
//对于java文件只要修改一次,都需要重新编译,也就是说源文件修改之后如果不重新编译,反序列化之后的版本号是不一致的,类似深克隆,class的版本号serialVersionUID就会发生改变
//解决该方法的操作:
// 1、重新序列化,然后反序列化
//2、固定版本ID:
// idea-->setting-->editor-->inspections-->搜索serial-->勾选已经实现序列化类但是没有生成serialVersionUID
//3、对于一些需要加密的数据(密码,身份证号等)变量可以用transient来修饰,它可以保证变量不被序列化成功
}catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {//jvm找不到当前类的class文件
e.printStackTrace();
}
}
转换流
字节流与字符流之间的相互转换,常用:字节流转换成字符流:用于操作网络数据
InputStreamReader(InputStream in) 字节转字符
OutputStreamWriter(OutputStream out) 字符转字节
1、字节流转字符流:
抓取网站上的内容到本地文件
public static void main(String[] args) {
String path = "https://read.qidian.com/chapter/jE-Qgi-8d_j-JlVC31J8Aw2/BZmMwrZww9BMs5iq0oQwLQ2/";
demo7(path);
}
private static void demo7(String path) {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(path).openStream()));
BufferedWriter writer = new BufferedWriter(new FileWriter("day03\\src\\novel\\a.txt"))
) {
String s;
while ((s = reader.readLine()) != null) {
if(s.contains(" <p> “光之治愈……”<p>")){
writer.write(s.replaceAll("<p>","\n"));
}
}
System.out.println("下载成功");
} catch (IOException e) {
e.printStackTrace();
}
}
将读取的内容在控制台打印输出
private static void demo8() {
try (
BufferedReader reader = new BufferedReader(new FileReader("day03\\src\\novel\\a.txt"));
//读取文件内容,在控制台上打印输出
//System.out.println: PrintStream extends FilterOutputStream属于字节流,字节输出流,将内容写到控制台
//因此出现流的转换:字节流-->字符流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out))
) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
}
}
其他流:
数据流:
一般用于网络数据的传输
DataInputStream数据输入流 DataOutputStream 数据输出流
只有由数据流自己写入的数据,才可以读出数据,否则会出现乱码
private static void demo4() {
//1082
//liku
//李四
//adminwangwu
//true
try (
DataInputStream stream = new DataInputStream(new FileInputStream("day03\\src\\bean\\a.txt"));
) {
System.out.println(stream.readInt());
System.out.println(stream.readUTF());
System.out.println(stream.readUTF());
System.out.println(stream.readUTF());
System.out.println(stream.readChar());
//java.io.EOFException:end of file exception
//因为文件里面的内容不是由DataOutputStream写入的,因此会出现问题
} catch (IOException e) {
e.printStackTrace();
}
}
重要方法:readUTF() ,writeUTF() 读写汉字
Properties
Map接口的一个是实现类 继承了HashTable(里面也有map的方法:put,get,size,values,isEmpty,entryset,getOrDefault,foreach,remove,replace,clear等方法) 该类表示一组持久的属性
Properties() //创建一个没有默认值的空属性列表
Properties(Properties defaults)
//一般不利用map的get,put来操作元素,用它自带的
Object setProperty(String key, String value) //赋值
String getProperty(String key) //取值
String getProperty(String key, String defaultValue) //可以防止空指针
void load(InputStream inStream)//读取数据
void load(Reader reader)
读取核心配置文件(文件名.properties)里面的数据
private static void demo9() {
//我创建了配置文件路径为:day03\src\bean\b.txt 里面给了一组数据:
// username=张三
//password=password
Properties properties=new Properties();
try {
//读取直接用load
properties.load(new FileInputStream("day03\\src\\bean\\b.txt"));
} catch (IOException e) {
e.printStackTrace();
}
//取值
System.out.println(properties.getProperty("username"));//出现乱码:å¼ ä¸
System.out.println(properties.getProperty("password"));//password
}
注意配置文件里面的数据必须是key=value的格式,否则会读不到值,在配置文件里面一般都是用英文来写的,注释用#号 ,上面用户名出现乱码,因为properties的编码不一定是utf-8,因此需要在idea里面去修改一下配置:(改完之后重启)
setting---》search File Encoding---》将Default Properties的编码格式:utf-8 并且将后面的transparent native to ascII conversion ✔
获取配置文件的方式:【b.properties在bean下面,当前位置为Test2.class】,因此需要通过指定路径才能找到文件
//jvm加载的是class文件 也就是out文件里面的class文件
//加载核心配置文件,需要加载编译路径下的核心配置文件 也就是:某类.class(该类必须是跟配置文件在同一个项目下)
Properties p = new Properties();
try {
//指定编译文件的路径 ../表示回退到上一级目录
p.load(Test2.class.getResourceAsStream("../bean/b.properties"));
System.out.println(p);
} catch (IOException e) {
e.printStackTrace();
}
但是上面的方式需要回退,至于回退几级还得看文件路径,过于麻烦,当我们知道配置文件相对当前文件(自己写的,不是jdk的文件)的路径,我们换下面的方式:
当前文件.class:获取当前文件的编译文件
getClassLoader():得到类加载器
getResourceAsStream("文件相对路径"):文件相对当前文件的路径
当前文件.class.getClassLoader().getResourceAsStream("bean/b.properties"));
private static void demo3() {
Properties p = new Properties();
try { p.load(Test2.class.getClassLoader().getResourceAsStream("bean/b.properties"));
System.out.println(p);
} catch (IOException e) {
e.printStackTrace();
}
}
一般加载文件都是放在工具类里面,并且只加载一次