(1)数据流
//注册用户: id-name-pass-age 每次拿,需要读一行,然后分割,再转类型
//如何直接拿,不需要转型 用数据流(也是字节流,但一般的字节流操作汉字会出现乱码)
@Data
class User {
private Integer id;
private String name;
private double balance;
private byte age;
}
public class DataDemo {
public static void main(String[] args) {
//注册用户: id-name-pass-age FileWriter.write(String str)
//读取一个用户信息: BufferedReader.readLine() split() Integer.parseInt()
try {
testRead();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//对于数据流 最常用的场景: writeUTF/readUTF (写一个汉字,读一个汉字)
//何时用它? 网络数据传输: 传输的都是字节内容 使用writeUTF/readUTF避免出现乱码的问题
private static void testWrite() throws FileNotFoundException {
//1.创建数据输出流对象 将数据从任意类型转为一系列字节,并写入二进制流 这是写给dataInputStream看的
DataOutputStream dataOutput = new DataOutputStream(new FileOutputStream("src/b.txt"));
try (dataOutput) {
dataOutput.writeInt(1001);
dataOutput.writeUTF("张三");
dataOutput.writeDouble(89999.567);
dataOutput.writeByte(20);
dataOutput.writeBoolean(true);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void testRead() throws FileNotFoundException {
//1.创建数据输入流对象
DataInputStream dataInputStream = new DataInputStream(new FileInputStream("src/b.txt"));
User user = new User();
//可以读,但前提要先写出来,才能读 人工写的它不认识 从二进制流里读数据
try (dataInputStream) {
user.setId(dataInputStream.readInt()); //每次读4个字节
user.setName(dataInputStream.readUTF());
user.setBalance(dataInputStream.readDouble());
user.setAge(dataInputStream.readByte());
System.out.println(dataInputStream.readBoolean());
System.out.println(user);
//未先写就读,报此异常 java.io.EOFException end of file 读到文件末尾也没发现这些东西
} catch (IOException e) {
e.printStackTrace();
}
}
private static void testRead1() throws FileNotFoundException {
//用以前的方法做
//弊端: 每读一个数据 都要进行类型的强制转换
BufferedReader reader = new BufferedReader(new FileReader("src/user.txt"));
User user = new User();
try (reader) {
String id = reader.readLine();
String name = reader.readLine();
String balance = reader.readLine();
String age = reader.readLine();
user.setId(Integer.parseInt(id));
user.setName(name);
user.setAge(Byte.parseByte(age));
user.setBalance(Double.parseDouble(balance));
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2)对象流
//Serializable是一个标记接口 jvm可以将这个类型的对象写入文件中
//修改java文件都需要重新编译新的class,产生新的class文件的版本号serialVersionUID,版本号唯一的,通过它可找到class文件
//在正常开发中: 记住一点 封装数据类(用户类,商品类等)上面 一定要实现 Serializable 并提供版本id
//类实现 Serializable接口在开发中 肯定是没有问题的(后期的Redis缓存/远程接口访问RMI)
//不实现Serializable 程序就有可能出现问题。
@Setter
@Getter
@ToString
public class Student implements Serializable {
private static final long serialVersionUID = -1775089732470255694L;
//版本号的id是自动生成的 编译工作是IDEA调javac做的,想生成id,告诉IDEA(让idea提醒我们自动生成id值)
//setting--Editor--inspections--java--serialization issues--serializable class without ‘serialVersionUID’--选中
private Integer id;
private String name;
private double balance;
private int age;
private int num;
public Student() {
System.out.println("无参构造.....");
}
}
//对象流也要求先写才能读
public class ObjectDemo {
public static void main(String[] args) {
demo2();
}
private static void demo2() {
//读取文件里面的对象
try {
ObjectInput objectInput = new ObjectInputStream(new FileInputStream("src/a.ini"));
Student student = (Student) objectInput.readObject();
System.out.println("读取到的文件对象:" + student);
//读取到的文件对象:com.lisa.io.Student@7d9d1a19
objectInput.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
//ClassNotFoundException 找不到字节码文件,也就是jvm里面找不到这个类型的class文件
//出现该异常有两种原因:
//1.1 java文件没有被编译(类没有被编译)
//1.2 可能没有引入第三方的类库(架包)
e.printStackTrace();
}
}
private static void demo1() {
//对于数据流 弊端: 读取多次 获得每个属性的信息 赋值多次
//对象流:直接读写一个完整的对象信息
//对象流也是在数据流基础之上丰富了一些功能。除了可以操作字面量类型 还可以操作对象类型。
try {
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("src/a.ini"));
//无参构造.....
//写入的对象:com.lisa.io.Student@38082d64
Student student = new Student();
System.out.println("写入的对象:"+student);
student.setId(1);
student.setName("张三");
//student.setAge((byte) 20);
student.setBalance(100000.123);
objectOutput.writeObject(student);
objectOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
//只写完运行会报 NotSerializableException 没有支持序列化异常
//这里做的事情:将对象写入磁盘文件中。 对于jvm,若操作对象 这个对象类型必须支持序列化(序列化跟writeObject有关)
//之前学的克隆: 克隆一个对象 也是jvm底层做的 要克隆的前提: 实现Cloneable接口
//类似的,想序列化student,必须让它支持序列化,告诉jvm,student可以被序列化,故student要实现Serializable接口
//序列化流就是ObjectOutputStream,将对象写入文件
//反序列化流就是ObjectinputStream,读取文件里的对象
}
}
(3)转换流
字节转字符----将网络上的小说章节内容,下载到本地
字符转字节----打印输出
/**
* @author: sunshine
* @description:
* @data: 2022/3/12 23:16
* @version: 0.1
* @since: jdk11
*/
public class NovelDemo {
public static void main(String[] args) {
try {
demo1();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private static void demo1() throws FileNotFoundException {
//需求:读取指定文件的数据,在控制台打印输出(将数据写在控制台)
//读取是read 输入流 字节输入流 字符输入流 比较合适的是字符输入流,从中选BufferedReader
//打印是write 输出流 对应的用Bufferedwriter
BufferedReader reader = new BufferedReader(new FileReader("src/novel.txt"));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); //写到控制台上去,之前是new FileWriter
//OutputStreamWriter字符转字节
//System.out.println("fasf") 为何调此命令可将内容打印到控制台,是字符串的println做的
//out是printStream,继承FilterOutputStream字节输出流 关键还是在out
//底层实现: 将数据打印在控制台---->System.out 字节输出流
try (reader;writer) {
String line;
while ((line = reader.readLine()) != null) { //循环去读
writer.write(line); //字符输出流写,但其实底层是转成字节输出流 一次写一行 因为想输出到控制台得结合打印流完成
writer.newLine(); //换行
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void downloadNovel() throws IOException {
//字节转字符场景:拿取网络资源,且和转换流相关的一般用BufferedReader和BufferedWriter 已成模板
String novelPath = "https://read.qidian.com/chapter/WS49qjrNDI7v7_WH3wfEdQ2/uNA4HnHXD0P6ItTi_ILQ7A2/"; //小说章节路径(肯定加密了)
novelPath = "https://read.qidian.com/chapter/OD1kAAaaLgMKgXB091LLaA2/eGiW7wfkoWTM5j8_3RRvhw2/";
//novelPath = "http://book.zongheng.com/chapter/1099697/64049078.html"; //全加密,这个就没法下载
//网络上的所有的数据都是字节流。
//想获得万维网的资源指针。条件:1.有网。2.获得万维网资源指针URL(统一资源定位符),拿到该指针就能拿到网络上的数据
//URL url = new URL(novelPath); //url也是个类,在java.net 读取网络资源,用InputStream
//InputStream inputStream = url.openStream(); //此时网络的数据都在inputStream
//在读取的功能里面: 程序本应该使用字节流进行读取 但是我们想使用字符流进行高效读取
//转换: 字节流读取转换成字符流读取
//高效字符流实现读写功能
//BufferedReader reader = new BufferedReader(new FileReader(novelPath) FileReader是找磁盘文件,故不能这样写
BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(novelPath).openStream(), StandardCharsets.UTF_8));
//new URL(novelPath).openStream()是字节流,需转字符流,同时指定编码格式(不能随便给,看人家编码格式给的什么)
BufferedWriter writer = new BufferedWriter(new FileWriter("src/novel1.txt"));
try (reader; writer) {
//循环读写
String line;
while ((line = reader.readLine()) != null) { //每次读一行
//System.out.println(line);
if (line.contains("class="read-content j_readContent"")) { //这是看着下载的模板进项修改,去除没用部分
reader.readLine();
reader.readLine();
line = reader.readLine();
writer.write(line.replaceAll("<p>", "
"));
break;
}
}
}
System.out.println("下载成功");
}
}
(4)Properties 一个能和IO结合使用的类,是hashTable的一个子类,称为属性集
一般存 属性名=属性值
和map方法一样,但一般不用
/**
* @author: sunshine
* @description:
* @data: 2022/3/13 0:06
* @version: 0.1
* @since: jdk11
*/
public class PropertiesDemo {
public static void main(String[] args) {
demo6();
}
private static void demo6() {
Properties properties = new Properties();
properties.setProperty("id", "1001");
properties.setProperty("score", "90");
properties.setProperty("studentName", "李雷");
//将properties里面的数据写入文件中
try {
properties.store(new FileOutputStream("src/student.properties"),"哈哈哈哈哈"); //第二个参数是注释
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo5() {
Properties properties = new Properties();
try {
//加载文件的数据
properties.load(new FileInputStream("src/user.properties"));
} catch (IOException e) {
e.printStackTrace();
}
//加载properties文件,出现汉字也会乱码,此时改idea配置,改后不再乱
//setting--搜encoding--File Encodings--全改成utf-8
System.out.println(properties);
}
//配置文件后缀: properties xml yml/yaml
private static void demo4() {
//用properties读取demo3()的文件数据
Properties properties = new Properties();
try {
//加载文件的数据
properties.load(new FileInputStream("src/user.txt"));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(properties);
String name = properties.getProperty("name"); //想要value,直接给key
System.out.println(name);
//将jim改成中文的李四,结果乱码了,此时要解码 手动解码,用String properties底层默认ISO_8859_1编码格式
String decodeName = new String(name.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); //转成UTF-8
System.out.println(decodeName);
}
private static void demo3() throws FileNotFoundException {
//Properties:体现在读取核心配置文件的数据
//要求文件里面数据格式是固定的:key=value,且一行中只有一组数据
//假设一个配置文件里的内容如下:
//id=1001
//name=jim
//age=20
//如何获得id name age的数据
//读: 字节/字符输入流 Reader FileReader(一次读一个字符或字符数组,不合适) BufferedReader(读一行)
BufferedReader reader = new BufferedReader(new FileReader("src/user.txt"));
try (reader) {
String line;
while ((line = reader.readLine()) != null) {
String[] array = line.split("=");
System.out.println(array[0] + "---" + array[1]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo2() {
Properties properties = new Properties();
//一般推荐使用以下的方法
properties.setProperty("id", "1001"); //一般参数是String类型
properties.setProperty("name", "张三");
properties.setProperty("age", "20");
System.out.println(properties.getProperty("id"));
System.out.println(properties.getProperty("name"));
System.out.println(properties.getProperty("age"));
//相比map集合方法: 类型转换的次数少一些
//但仍不是最重要的方法,要结合IO用
}
private static void demo1() {
//针对于Properties: 以下方法 都不用
//回顾以下map集合的方法
Properties properties = new Properties();
//存储数据
properties.put("id", 1001);
properties.put("name", "张三");
properties.put("age", 20);
//获得数据
/*int id = (int) properties.get("id");
String name = (String) properties.get("name");
int age = (int) properties.get("age");
System.out.println(id);
System.out.println(name);
System.out.println(age);*/
//遍历Properties里面所有的数据
//properties.forEach((k,v)-> System.out.println(k+"----"+v));
/*Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
for (Map.Entry<Object, Object> entry : entrySet) {
System.out.println(entry.getKey() + "---" + entry.getValue());
}*/
}
}
(5)单例模式问题
/**
* @author: sunshine
* @description:
* @data: 2022/3/13 0:32
* @version: 0.1
* @since: jdk11
*/
//单例模式是保证单实例的
public class Admin implements Cloneable, Serializable {
private static final long serialVersionUID = 1486187929375171608L;
private Admin() { //1.构造方法私有化
}
private static final Admin admin = new Admin(); //饿汉,一上来就new
public static Admin getInstance() { //提供对外访问的入口
return admin;
}
public Object readResolve() {
return admin;
}
@Override
public Admin clone() {
/*Admin admin = null;
try {
admin = (Admin) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return admin;*/
return this;
}
}
public class Demo {
public static void main(String[] args) {
//目的:保证admin的单例
Admin admin = Admin.getInstance(); //获得一个实例
System.out.println("原对象:" + admin);
/*Admin cloneAdmin = admin.clone(); //克隆一个对象,此时Admin要实现Cloneable接口,且重写clone方法
System.out.println("克隆的对象:" + cloneAdmin);*/ //产生了新的对象
//序列化----> ObjectOutputStream 写
String path = "src/aa.txt";
try {
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(path));
objectOutput.writeObject(admin);
objectOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
//反序列化: ObjectInputStream 读
try {
ObjectInputStream objectInput = new ObjectInputStream(new FileInputStream(path));
Admin readAdmin = (Admin) objectInput.readObject();
System.out.println("读取的对象:" + readAdmin); //又产生了新的对象
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
//维护单例的最佳实现方案: 枚举类
//获得对象方式:
//1. new
//2. 克隆: 可以打破单例 为避免打破单例,在重写的克隆方法里写: return this/Admin; 就这一句话
//3. 序列化: 可以打破单例 为避免打破单例,需在单例模式里提供readResolve()方法,这个只能返Admin,不能用this
//4. 反射,也能打破单例
}
}