JavaImprove--Lesson11--IO流-序列化流
一.序列化流
对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来
ObjectOutputStream对象字节输出流
可以将Java对象序列化,然后向写到文件中去
构造器:
public ObjectOutputStream(OutputStream os);创建对象字节输出流,包装基础的字节输出流
方法:
public final void writeObject(object o) throws IOException ;把对象写出去
public static void main(String[] args) { try ( //创建序列化流 FileOutputStream os = new FileOutputStream("static/text/t4.txt"); ObjectOutputStream oos = new ObjectOutputStream(os); ){ //创建一个学生类对象 Student s = new Student("msf","boy",18); oos.writeObject(s); } catch (Exception e) { throw new RuntimeException(e); } }
注意点:
序列化对象必须要实现Serializable接口,虽然这个接口什么内容都没有,但是也必须要实现它,因为它不是给我们看的,而是打上标记,由JVM虚拟机来执行
当JVM看到对象实现这个接口后,就会调用相关的序列化程序加工,如果不实现此接口就无法完成序列化,如下:
实现此接口后,再次运行代码如下:
由于序列化也存入对象类型,所以又看不清楚,这是正常的,只有反序列化后可以得到对象内容
重点:如果一个对象的属性不用参与序列化,可以使用transient关键字修饰,表示此成员变量不参与序列化
private transient int grade;
ObjectInputStream对象字节输入流
可以将Java对象反序列化,把存储到文件中的对象读取到内存
构造器:
public ObjectInputStream(InputStream is)创建对象字节输入流,包装基础的字节输入流
方法:
public final Object readObject()把存储在文件中的Java对象读取出来
public static void main(String[] args) { try ( //创建反序列化流 FileInputStream is = new FileInputStream("static/text/t4.txt"); ObjectInputStream ois = new ObjectInputStream(is); ){ Student s =(Student) ois.readObject(); System.out.println(s); } catch (Exception e) { throw new RuntimeException(e); } }
输出如下:
一次序列化多个对象
如果要求一次序列化多个对象,一个一个写肯定不高效,所以可以把它放在一个list集合中,序列化这个集合,因为集合也实现了Serializable接口,所以是可以序列化的
当反序列化时,拿到的也是一个集合,可以方便的拿取这多个对象
如上就是ArrayList类,在官方文档中可以看到,的确实现了Serializable接口
序列化多个对象代码如下:
public static void main(String[] args) { try ( //申请一个序列化流 FileOutputStream os = new FileOutputStream("static/text/t5.txt"); ObjectOutputStream oos = new ObjectOutputStream(os); ){ //申请集合装多个需要序列化的对象 ArrayList<Student> list = new ArrayList<>(); Collections.addAll(list,new Student("msf","boy",15), new Student("mam","girl",25), new Student("faw","boy",19)); oos.writeObject(list); } catch (Exception e) { throw new RuntimeException(e); } }
反序列化多个对象代码如下:
public static void main(String[] args) { try ( //申请反序列化流 FileInputStream is = new FileInputStream("static/text/t5.txt"); ObjectInputStream ois = new ObjectInputStream(is); ){ List<Student> list = (List<Student>)ois.readObject(); System.out.println(list); } catch (Exception e) { throw new RuntimeException(e); }
输出结果如下:
二.IO框架
框架指的是在解决某一类问题时,编写的类或接口,可以理解为一个半成品,大多框架都是第三方研制的
好处:可以在已有的半成品基础上继续研发,可以基于优秀的软件框架开发,并提高开发效率
框架的形式:一般时把类,接口等编译成class形式,在压缩成.jar包供开发人员使用
什么是IO框架?
封装了Java对文件数据操作的代码,对外提供更简单的文件方式操作文件,对数据进行读写
Commons-io是apache开源基金组织提供的一组有关操作IO的小框架,目的是提高IO流开发的效率
FileUtils部分方法展示
public static void copyFile(File srcFile,File destFile);复制文件
public static void copyDirectory(File srcDir,File destFile);复制文件夹
public static void deleteDirectory(File directory);删除文件夹,可以删除非空文件夹,慎用
public static String readFileToString(File file,String encoding);读数据
public static void writeStringToFile(File file,String data,String charsert,boolean append);写数据
public static void main(String[] args) throws IOException { //简单复制文件 FileUtils.copyFile(new File("static/text/t2.txt"),new File("static/text/t3.txt")); //简单读取文件 String rs = FileUtils.readFileToString(new File("static/text/t3.txt"), "UTF-8"); System.out.println(rs); /* 我爱中国 我爱中国 */ //简单写入文件 FileUtils.write(new File("static/text/t3.txt"),"我很爱中国","UTF-8"); }
IOUtils提供的方法
public static int copy(InputStream is ,OutputStream os);复制文件
public static int copy(Reader r,Writer,w);复制文件
public static void writer(String data,OutputStream os ,String charset);写数据
public static void main(String[] args) throws Exception { //简单复制文件 int copy = IOUtils.copy(new FileInputStream("static/text/t2.txt"), new FileOutputStream("static/text/t3.txt")); System.out.println(copy); //简单写数据 IOUtils.write("世界和平",new FileOutputStream("static/text/t2.txt"),"UTF-8"); }
Java官方自带的Files也做了优化:
//Java官方自己提供的优化代码 //目的文件名要为空 Path copy1 = Files.copy(Path.of("D:\\其它\\Markdown\\Project\\JavaSeImprove\\static\\text\\t2.txt"), Path.of("D:\\其它\\Markdown\\Project\\JavaSeImprove\\static\\text\\t6.txt")); System.out.println(copy1);
三.特殊文件类型
目前Java中常用的文件类型一共有三种,分别是:txt文本文件,properties属性文件,xml文件
其中properties文件是单行有效,也就是一行即代表一个属性,读取的时候需要注意这个特点
还有xml文件有人称之为双标签文件,因为其总是以成对的文件标识包括数据
Properties文件
properties文件在Java中也是一个类对象,它相当于一个Map集合,也就是以键值对的形式存在在,文件中可以看出来,键和值由“=”分割,它也是Map接口下的一个实现类
但是呢,一般我们不把properties文件当作集合使用,集合用hashmap就够了,更多的是使用它的文件形式,也就是持久化保存数据
核心作用:
Properties用来表达文件属性,通过Properties可以读写文件中的数据
读取键值对
构造器:
public Properties();用于构建一个Properties文件的空容器
public static void main(String[] args) { //申请一个空的Properties容器 Properties pro = new Properties(); }
相关方法:
public void load(InputStream is);通过字节输入流,读取属性文件中的键值对数据
public void load(Reader reader);通过字符输入流,读取属性文件中的键值对数据
public String getProperties(String key);根据键获取值(其实就是get方法的效果)
public set<String> stringPropertyNmaes();获取全部的键集合(其实就是keySet方法)
public static void main(String[] args) throws Exception { //申请一个空的Properties容器 Properties pro = new Properties(); //加载一个properties文件 pro.load(new FileReader("static/pro/user.properties")); //根据键拿取值 String age = pro.getProperty("age"); System.out.println(age);//18 //获取全部集合 Set<String> rs = pro.stringPropertyNames(); rs.forEach((a) -> System.out.println(a+":"+pro.getProperty(a))); }
写入键值对
方法:
public Object setProperties(String key,String value);保存键值对到properties键值对对象中
public void store(OutputStream os,String comments);把键值对通过字节流写入到文件中,可以附加上注释
public void store(Writer w,String comments);把键值对数据通过字符写入到文件中去,并附加上注释
public static void main(String[] args) throws Exception { //申请一个properties空容器 Properties pro = new Properties(); //先往容器中加数据,最后一次写入 pro.setProperty("username","msf"); pro.setProperty("age","18"); pro.setProperty("sex","boy"); //最后一起刷入到文件 pro.store(new FileWriter("static/pro/user1.properties"),"I add a user"); }
案例
判断如下文件中,是否包含张三,如果有将张三的值改为100
如上,虽然是一个txt文件,但是只要满足一行是一个键值对,且以“=”分割,都是properties文件
代码实现:
public static void main(String[] args) throws Exception{ //申请一个Properties文件容器 Properties p = new Properties(); //装入文件字节流 p.load(new FileReader("static/text/t2.txt")); //判断是否包含条件 if (p.containsKey("张三")){ p.setProperty("张三","100"); } //保存文件 p.store(new FileWriter("static/text/t2.txt"),"this a change"); }
结果如下:
XML文件
XML:本质上是一种数据格式,可以用来存储复杂的数据结构和数据关系
特点:
- XML中的“标签名”称为一个标签或一个元素,一般是成对出现
- XML的标签名可以自定义,但必须以嵌套的形式存在
- XML只能有一个根标签
- XML的标签可以有属性
- 如果文件XML,则后缀名就一定要为xml
在XML文件中可能存在一些符号是不能直接写入的,如:>,<等,则需要一些符号代替
< = <
> = >
& = &
' = '
" = "
另外,XML的一个CDATA区域是特殊区域,写在这个区域可以不用符号标识,直接写
<user><![CDATA[ > < & : ' " ]]></user>
读取XML文件内容
读取XML文件可以使用IO流来做,但是由于XML文件的标签没有特定的约束,所以自己写IO流来写就很麻烦
于是,我们就想到使用第三方库,如Dom4j,将其下载后装载idea的额lib中
如上:是Dom4j对XML文件的解析,会将整个XML文件解析成为doucment文档,然后是元素及属性
构造器:
public SAXReader();构建Dom4j的解析器对象
public Document read(FileInputStream is);通过字节流解读XML
SAXReader read = new SAXReader();
public Document read(String url);把xml解读成doucment文件
SAXReader saxReader = new SAXReader("static/pro/users.xml");
方法:
Elment getRootElement()获得根元素对象
Element element = document.getRootElement();
public String getName()得到元素名字
Element element = document.getRootElement();//获得根节点 System.out.println(element.getName());//得到根节点名字
public List<Element> elements()获得当前元素下所有子元素
Element element = document.getRootElement();//获得根节点 List<Element> list = element.elements();//获得所有下一级子元素 for (Element e : list) { System.out.println(e.getName()); }
public List<Element> elemnets(String name);获得指定名字的元素集合
Element element = document.getRootElement();//获得根节点 List<Element> list = element.elements("user");//获得指定名字的子元素节点 for (Element e : list) { System.out.println(e.element("name").getText()); }
public Element element(String name)得到当前元素下指定名字的子元素,如果有很多,返回第一个
Element element = document.getRootElement();//获得根节点 Element user = element.element("user");//获得指定名字的子元素节点(单个) String sex = user.element("sex").getText(); System.out.println(sex);
public String attributeValue(String name)通过属性名直接得到属性值
Element element = document.getRootElement();//获得根节点 Element user = element.element("user");//获得指定名字的子元素节点(单个) String s = user.attributeValue("id");//获得当前元素的属性值 System.out.println(s);
public String elementText(子元素名)得到指定名称的子元素文本
Element element = document.getRootElement();//获得根节点 Element user = element.element("user");//获得指定名字的子元素节点(单个) String s = user.elementText("age");//获得指定子元素的子元素属性值 System.out.println(s);
public String getText()得到文本
Element element = document.getRootElement();//获得根节点 Element user = element.element("user");//获得指定名字的子元素节点(单个) Element name = user.element("name"); String text = name.getText();//获得当前元素的文本内容 System.out.println(text);
同意义:
String textTrim = name.getTextTrim();//取出文本,去掉前后空格
使用程序写入XML
在开发中,很少使用程序去写入XML的情况,如果一定要写入,推荐使用字符流,直接拼接字符串写入
public static void main(String[] args) throws Exception { StringBuilder st = new StringBuilder(); //拼接约束 st.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n"); //拼接根标签 st.append("<users>\r\n"); //拼接元素 st.append("<user1>\r\n"); //拼接子元素节点 st.append("<username>").append("msf").append("</username>\r\n"); st.append("<age>").append("20").append("</age>\r\n"); st.append("<sex>").append("boy").append("</sex>\r\n"); //补充完标签 st.append("</user1>\r\n"); st.append("</users>\r\n"); //创建字节写出池 BufferedWriter bw = new BufferedWriter(new FileWriter("static/pro/user1.xml")); //写出 bw.write(st.toString()); //关闭流 bw.close(); }
XML约束:限制XML文件只能按照某种格式书写
利用DTD约束文档,约束一个XML文件的编写
- 编写的DTD文档,必须是.dtd结束
- 在需要约束的XML文件中导入该DTD文件
- 然后,XML文件就只能按照约定的格式书写,否则会报错
编写DTD文件:
导入dtd文件,并按照约束写XML文件
XXXX