一、数据流

输入输出的数据带有类型,文件中不旦保留了数据内容本身,还保留了所存储数据的类型,不是纯文本文件,使用很少,简单了解。

二、重定向标准输入输出流

Java的标准输入输出分别通过System.in和System.out来代表,在默认情况下它们分别指向键盘和显示器,当程序通过System.in来获取输入时,实际上是从键盘读取输入;当程序通过System.out执行输出时,程序总是输出到屏幕。
在System类里提供了如下三个重定向方法,用来改变标准输入输出的方向:

  • static void setErr(PrintStream err):重定向“标准”错误输出流。
  • static void setIn(InputStream in):重定向“标准”输入流。
  • static void setOut(PrintStream out):重定向“标准”输出流。

    1. class Test{
    2. public static void main(String[] args) throws IOException {
    3. // 默认输出到显示器
    4. System.out.println("hello");//合起来写
    5. PrintStream ps = System.out;//创建一个PrintStream对象
    6. ps.println("a");//分开写
    7. ps.println(1);
    8. // 标准输出流不需要手动close()
    9. // 创建PrintStream输出流,指向test.txt文件
    10. PrintStream printStream = new PrintStream(new FileOutputStream("test.txt"));
    11. // 将标准输出重定向到printStream输出
    12. System.setOut(printStream);
    13. // 输出
    14. printStream.println("hello");//向test.txt文件写hello
    15. }
    16. }

    三、File类

    1.理解File

    File类是java.io包下代表与平台无关的文件和目录,是文件和目录的抽象表现形式(即一个File对象对应一个文件或一条路径),在程序中操作文件和目录都可以通过File类来完成。要注意的是,不管是文件还是目录都是使用File类来操作的,File能新建、删除、重命名文件和目录,但File不能访问文件内容本身。如果需要访问文件内容本身,需要使用输入输出流。

    2.File常用方法

    1. class Test{
    2. public static void main(String[] args) throws IOException {
    3. File f1 = new File("D:\\file");//创建一个File对象
    4. System.out.println(f1.exists());//判断该文件是否存在
    5. if (!f1.exists()){
    6. f1.createNewFile();//不存在则以文件形式创建
    7. }
    8. if (!f1.exists()){
    9. f1.mkdir();//不存在则以目录形式创建
    10. }
    11. File f2 = new File("D:\\a\\b\\c\\d");
    12. if (!f2.exists()){
    13. f2.mkdirs();//以多重目录形式创建
    14. }
    15. System.out.println(f2.getParent());//获取父路径
    16. System.out.println(f2.getParentFile().getAbsolutePath());//获取父文件的绝对路径
    17. System.out.println("文件名:" + f1.getName());//获取文件名
    18. System.out.println(f1.isDirectory());//判断是否为一个目录
    19. System.out.println(f1.isFile());//判断是否为一个文件
    20. // 获取文件最后一次修改时间
    21. long count = f1.lastModified();//返回的是从1970年到现在的毫秒数
    22. // 将总毫秒数转化为日期(日期格式化)
    23. Date time = new Date(count);
    24. SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    25. String strTime = date.format(count);
    26. System.out.println(strTime);
    27. System.out.println(f1.length());//获取文件大小
    28. //获取当前目录下的所有子文件
    29. File f = new File("D:");
    30. File[] files = f.listFiles();//返回的是一个File数组
    31. for (File file : files) {
    32. System.out.println(file.getName());
    33. }
    34. }
    35. }

    四、对象序列化

    1.序列化的含义和意义

    对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而将对象保存到磁盘中,或允许在网络中直接传输对象,以备以后重新恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
    对象的序列化(Serialize)指将一个Java对象写入IO流中,与此对应的是,对象的反序列化(Deserialize)则指从IO流中恢复该Java对象。
    如果需要让某个对象支持序列化机制,则必须让它的类是可序列化的(serializable)。为了让某个类是可序列化的,该类必须实现如下两个接口之一:

  • Serializable

  • Externalizable

Java中的很多类已经实现了Serializable,该接口是一个标记接口,标记接口中无方法,它只是表明该类的实例是可序列化的,当Java虚拟机看到该类实现了Serializable接口后,会为该类自动生成一个序列化版本号。
image.png

2.序列化和反序列化的实现

  1. class Student implements Serializable{
  2. int age;
  3. String name;
  4. public Student(int age, String name) {
  5. this.age = age;
  6. this.name = name;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "age=" + age +
  12. ", name='" + name + '\'' +
  13. '}';
  14. }
  15. }
  16. public class Test{
  17. public static void main(String[] args) throws IOException, ClassNotFoundException {
  18. // 序列化
  19. // 创建Java对象
  20. Student s = new Student(23, "sundegan");
  21. // 创建序列化流
  22. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test"));
  23. // 序列化对象
  24. oos.writeObject(s);
  25. // 刷新
  26. oos.flush();
  27. // 关闭
  28. oos.close();
  29. //反序列化
  30. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test"));
  31. Object obj = ois.readObject();
  32. System.out.println(obj);
  33. ois.close();
  34. }
  35. }
  36. //Student{age=23, name='sundegan'}

3.序列化多个对象

将对象放到集合中,序列化集合对象。如果不使用集合,直接序列化多个对象会报错。

  1. class Student implements Serializable{
  2. int age;
  3. String name;
  4. public Student(int age, String name) {
  5. this.age = age;
  6. this.name = name;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "age=" + age +
  12. ", name='" + name + '\'' +
  13. '}';
  14. }
  15. }
  16. public class Test{
  17. public static void main(String[] args) throws IOException, ClassNotFoundException {
  18. // 序列化多个对象
  19. List<Student> list = new ArrayList<>();
  20. list.add(new Student(1,"a"));
  21. list.add(new Student(2,"b"));
  22. list.add(new Student(3,"c"));
  23. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test"));
  24. oos.writeObject(list);
  25. oos.flush();
  26. oos.close();
  27. //反序列化
  28. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test"));
  29. List<Student> stuList = (List<Student>) ois.readObject();
  30. for (Student s : stuList) {
  31. System.out.println(s);
  32. }
  33. ois.close();
  34. }
  35. }
  36. //Student{age=1, name='a'}
  37. //Student{age=2, name='b'}
  38. //Student{age=3, name='c'}

4.transient关键字

transient关键字表示游离的,用该关键字修饰的成员不参与序列化。

  1. class Student implements Serializable{
  2. int age;
  3. transient String name;//name属性不会被序列化
  4. public Student(int age, String name) {
  5. this.age = age;
  6. this.name = name;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "age=" + age +
  12. ", name='" + name + '\'' +
  13. '}';
  14. }
  15. }

5.序列化版本号

5.1 序列化版本号作用

序列化是Java中实现持久化存储的一种方法;为数据传输提供了线路级对象表示法。 Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
首先我们要知道java语言中采用什么机制来区分类,不同的人编写了同样的一个类,但是这两个类其实并不等于同一个类。
1.首先通过类名比较,如果类名一样则靠序列号区分。
java虚拟机看到Serilezable接口后,会默认的生成一个序列化版本号,序列化版本号也可以手动提供。
自动生成序列化版本号的好处:虚拟机可以根据序列化版本号区分开这两个类。
自动生成序列化版本号的缺陷:一旦代码确定之后,不能进行后续的修改。因为修改之后就会重新的编译,
此时会生成新的序列化版本号,java虚拟机会认为这是一个全新的类。
所有凡是一个类实现了Serializable接口,建议给这个类提供一个固定不变的序列化版本号
这样在后续如果修改了代码,Java虚拟机也会认为这是同一个类。

5.2 手动添加序列化版本号

  1. ublic class Users {
  2. //手动序列化版本号方法
  3. private static final long serialVersionUID = 1l;
  4. }

5.3 IDEA自动生成序列化版本号配置

  • 进入Setting

image.png

  • 在自定义类的时候,将光标放到类名上,按alt+enter快捷键提示自动添加序列化版本号

image.png

  • 最后效果如下
    1. public class Person implements java.io.Serializable{
    2. private static final long serialVersionUID = 6342241044575234092L;
    3. }

    五、IO和Properties联合使用

    IO:文件的读写。
    Properties:是一个Map集合,key和value都是String类型。

    1.作用

    可以将代码中一些经常发生变化的信息放到文件当中,Java程序动态地去读取文件信息,信息发生改变时只需去更改文件内容,而不需要修改代码,可以避免Java程序的重新编译和服务器的重新部署,就可以拿到动态的信息。

    2.例子

    将userinfo文件里的数据加载到Properties对象中。
    1. username=admin
    2. password=123
    1. public class Test{
    2. public static void main(String[] args) throws IOException {
    3. // 新建一个输入流对象
    4. FileReader reader = new FileReader("userinfo");
    5. // 新建一个Map集合
    6. Properties pro = new Properties();
    7. // 调用Properties对象的load方法将文件中的数据加载到Map集合
    8. pro.load(reader);//文件数据加载到map集合中,等号=左边做key,右边做value
    9. // 通过key获取value
    10. System.out.println(pro.getProperty("username"));
    11. System.out.println(pro.getProperty("password"));
    12. }
    13. }
    使用以上机制的文件称为配置文件,并且当文件中的内容格式是(不要有空格):
    key1=value1
    key2=value2
    的时候,我们把这种配置文件称为属性配置文件。Java规范中有要求:属性配置文件建议以.properties为后缀名,但这不是必须的。这种以.properties为后缀的文件在Java中被称为属性配置文件,其中Properties是专门存放属性配置文件内容的一个类。