声明:本笔记大部分来自观看该文章的心得:
原文链接
博主:宜春
本人文章仅用于查阅和本人记录知识点,如有侵权本人会自行删除。
IO流的含义:
Java的IO流是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。
相对路径:一个简化的路径,不以盘符开头,例如//123.txt//b.txt。
输入流创建
// 使用File对象创建流对象
File file = new File("xxx.txt");
FileReader reader= new FileReader(file);
// 使用文件名称创建流对象
FileReader reader = new FileReader("a.txt");
读取字符数据
// 使用文件名称创建流对象
FileReader reader = new FileReader("xxx.txt");
// 定义变量,保存数据
int len = -1;
// 循环读取
while ((len = reader .read())!=-1) {
System.out.println((char)len);
}
// 记得关闭资源
reader .close();
输出流类比一下
getAbsolutePath() :返回此File的绝对路径名字符串。
getPath() :将此File转换为路径名字符串。
getName() :返回由此File表示的文件或目录的名称。
length() :返回由此File表示的文件的长度(字节)
如果File是目录,则返回值未指定。
exists() :此File表示的文件或目录是否存在。
isDirectory() :此File表示的是否为 目录。
isFile() :此File表示的是否为 文件。
createNewFile() :文件不存在,创建一个新的空文件并返回true,文件存在,不创建文件并返回false。
delete() :删除由此File表示的文件或目录。
mkdir() :创建由此File表示的目录。如果父类不存在,则创建失败
mkdirs():创建由此File表示的目录,同时创建不存在的父级目录。
所以一般选用 mkdirs()
public static List<File> searchFiles(File directory, Predicate<File> predicate) {
//返回存储文件的容器
List<File> files = searchFiles(directory);
File dir = new File("G:\光标");
//造存放file的容器
File[] files = dir.listFiles();
//获取当前目录下的文件以及文件夹对象
for (File file : files) {
System.out.println(file);
}
}
listFiles指定目录必须存在,且必须指定目录,指定文件会发生空指针异常
File file=new File("D:\\XXX");
List<File> list = Arrays.asList(file.listFiles());
//判空
if (file != null) {
//遍历list
for (File f : list) {
if (f.isDirectory()) searchFiles(f);
else list.add(f);
}
}
那么什么时候用字节流什么时用字符流呢
在遍历文件时候,一般选择字符流
而对文件的内容进行读写操作时一般用字节流
无论使用什么样的流对象,底层传输的始终为二进制数据。但在用字符流读写文件内容时,规定的字符容器可能会大于需要读写的的内容(字节)
字节输、出流(OutputStream)
构造方法
public FileOutputStream(File file):以File对象为参数创建对象。
public FileOutputStream(String name): 以名称字符串为参数创建对象。
开发常用第二种,指定路径字符串
注意:创建输出流对象的时候,如果路径不存在,系统会自动去对应位置创建对应文件
输入流对象的时候,文件不存在则会报FileNotFoundException异常
写出字节、字节数组、指定长度字节数组:
// 使用文件名称创建流对象
FileOutputStream out = new FileOutputStream("XXX.系统自己创建的.txt");
// 向这个文件里写出数据
out .write(97); // 一次只写出一个字节
out .write(98); //输出的是ASCII字节码
out .write(99); //结果为abc
// 记得关闭资源
fos.close();
// 如果想要一些写出多个字节 可以使用字节数组
byte[] b = "麻麻我想吃烤山药".getBytes();
// 写出字节数组数据
out .write(b);
out.write(b,0,2);//从下标0开始 写出2个字节
// 关闭资源
fos.close();
new FileOutputStream(“xxx.txt”,boolean b);
当我向有数据的文件中写出字节时,可以传入第二个参数Boolean
boolean为ture时,表示追加数据,不会对元数据进行覆盖。
false 表示不追加 也就是清空原有数据。
——————————————————————————————————————————————————
字节输入流(InputStream)
构造方法类比输出流
同样推荐第二种狗构造方法
当你创建一个流对象时,传入的路径下,该文件,会抛出FileNotFoundException 。
// 使用文件名称创建流对象.
FileInputStream in = new FileInputStream("read.txt"); // 假定内容为abcde
// 定义变量,作为有效个数
int len = -1;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];//输出结果 不正常的问题所在
// 循环读取
while (( len= in .read(b))!=-1) {
// 每次读取后,把数组变成字符串打印
System.out.println(new String(b));
}
输出结果为 ab cd ed
为何:因为长度为2的数组,每次读取2个字符,下次读取时,另外两个字符将原字符覆盖
第一次:ab
第二次:c覆盖a,d覆盖b 结果为cd
第三次:e覆盖d,原来的d没有被覆盖,所以结果为 ed
close() :关闭流
flush() :强制缓冲的输出字节写出。
write(byte[] b):将Byte数组 .length()个字节数组写入输出流。
write(byte[] b, int off, int len) :从偏移量 off开始输出到此输出流,读取len个字符
write(int b) :将指定的字节输出流。
创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
构造方法:
public BufferedInputStream(InputStream in) :创建一个新的缓冲输入流,注意参数类型为InputStream。
字节输出流一样。
举例:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(“xxx.txt”));
字符缓冲流:
BufferedReader,BufferedWriter
字符缓冲流特有方法:
// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
// 定义字符串,保存读取的一行文字
String line = null;
// 循环读取,读取到最后返回null
while ((line = br.readLine())!=null) {//读一行数据
System.out.print(line);
System.out.println("------");
}
// 关流
br.close();
编码:字符(能看懂的) –> 字节(看不懂的)
解码:字节(看不懂的) –> 字符(能看懂的)
FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的UTF-8编码
但是,由于Windows系统的默认是GBK编码,读取Windows系统中创建的文本文件时,就会出现乱码。
FileReader reader = new FileReader("C:\\xxx.txt");//写点中文
int len = -1;
while ((len = reader .read()) != -1) {
System.out.print((char)len );
}
reader .close();
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息
1.该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException 。
2.该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
public class User implements Serializable {
private static final long serialVersionUID = -6180688742475305117L;
private Integer id ;
private String username ;
/**
* transient : 瞬时,短暂的
*
* 用来定义 不需要进行 序列化的 字段
*
*/
private transient String password ;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("User{");
sb.append("id=").append(id);
sb.append(", username='").append(username).append('\'');
sb.append(", password='").append(password).append('\'');
sb.append('}');
return sb.toString();
}
}
——————————
public class UserSerializer {
@Test
public void test() throws Exception {
/**
* 将 User 对象,存储到磁盘的过程,被称为 对象的序列化操作
*
* 如果想要将一个对象,进行序列化,则 必须让该对象 实现 Serializable 接口
*
*/
// 创建一个序列化对象的流
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:/user.txt"));
// 创建一个 User
User user = new User();
user.setId(1);
user.setUsername("张三");
user.setPassword("1234567");
// 将 user 对象,存储到磁盘中
out.writeObject(user);
// 关闭流
out.close();
}
@Test
public void test2() throws Exception{
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/user.txt"));
// 将 User 从磁盘读入到内存
Object o = in.readObject();
if (o instanceof User) {
User user = (User) o ;
System.out.println(user);
}
}
}
序列化练习题:
public class Test2 {
@Test
public void test(){
Goods goods = new Goods("001","奔驰",50.0,1);
//序列化
System.out.println(JSONArray.toJSON(goods));
//反序列化
String josn = "{\"Id\":001,\"Name\":奔驰,\"Num\":1,\"Price\":50.0";
Goods goods1 = JSONArray.parseObject(josn,Goods.class);
System.out.println(goods1);
}
}
@AllArgsConstructor
@NoArgsConstructor
class Goods{
private String Id;
private String Name;
private Double Price;
private Integer Num;
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Goods{");
sb.append("Id='").append(Id).append('\'');
sb.append(", Name='").append(Name).append('\'');
sb.append(", Price=").append(Price);
sb.append(", Num=").append(Num);
sb.append('}');
return sb.toString();
}
public String getId() {
return Id;
}
public void setId(String id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public Double getPrice() {
return Price;
}
public void setPrice(Double price) {
Price = price;
}
public Integer getNum() {
return Num;
}
public void setNum(Integer num) {
Num = num;
}
}
一种类与类交互的模式
甜品抽象类:
public abstract class Sweet {
/**
* 售卖甜点
*/
public abstract int sell() ;
/**
* 售卖的价格
* @return
*/
public abstract int sellPrice() ;
}
蛋糕类
public class CakeSweet extends Sweet{
@Override
public int sell() {
System.out.println("普通蛋糕");
return sellPrice();
}
@Override
public int sellPrice() {
return 50;
}
}
装饰(加工)类 这我自己取的名字哈 别乱用
public class CakeDecrator extends Sweet{
//维护一个 被装饰的 蛋糕对象
private Sweet sweet ;
private String fruit ;
public CakeDecrator(Sweet sweet, String fruit) {
this.sweet = sweet ;
this.fruit = fruit ;
}
public void sellFruit() {
System.out.println("本次出售的是" + fruit);
}
@Override
public int sell() {
int price = sellPrice();
// 调用普通蛋糕价钱
sweet.sell();
System.out.println("本次甜点添加了" + fruit);
return price ;
}
@Override
public int sellPrice() {
//加了啥 多多少钱
int price = switch (fruit) {
case "苹果" -> 3 ;
case "葡萄" -> 6 ;
default -> 1 ;
};
//买蛋糕的钱 + 装饰调料的钱
return sweet.sellPrice() + price;
}
}