IO流(四) --- 特殊操作流

IO流中特殊操作流


概述:

IO流中的特殊操作流,顾名思义就是有其特殊的作用;


标准输入输出流


概述:

标准输入输出流它们的作用是用于从键盘录入和打印输出;即我们通常使用的Scanner类System.out.println(),那我们为什么要去学习它呢?只是为了了解Scanner和输出语句的底层结构;


引入:

System类中有两个静态成员变量:

  1. public static final InputStream in; : 标准输入流。通常用于键盘录入;
  2. public static final PrintStream out; :标准输出流。通常用于打印输出;

标准输入流(InputStream)


概述:

其与Scanner类似,都是用来接收用户的输入;

代码示例一:

public class Demo01 {
    public static void main(String[] args) throws IOException {
        // 创建输入流
        InputStream in = System.in;
        // 具体操作
        int len;
        while ((len = in.read()) != -1) {
            System.out.print((char)len);
        }
    }
}
// 注意:这种方式,该流是关闭不了的,会让你一直输入
/* 
现在的功能只能输入简单的数字和英文,却不能输入中文,如果输入中文呢?那就需要用字符流,可是标准输入流是字节流,很容易想到,需要使用转换流将字节流转为字符流;
*/

代码示例二:

public class Demo02 {
    public static void main(String[] args) throws IOException {
        // 创建输入流对象
        Reader reader = new InputStreamReader(System.in);
        // 具体操作
        char[] chars = new char[1024];
        int len = -1;
        if ((len = reader.read(chars)) != -1) {
            System.out.println(new String(chars, 0, len));
        }
        // 释放资源
        reader.close();
    }
}
/*
那么可不可以直接实现读取一行,而不是通过字符数组的形式来读取呢?
答:可以,一次读取一行是字符缓冲流的特有功能,所以需要使用字符缓冲流;而字符缓冲流的构造方法需要传入字符流对象,而我们的System.in是字节对象,所以这里依旧需要使用我们的字符转换流;
*/

代码示例三:

public class Demo03 {
    public static void main(String[] args) throws IOException {
        // 创建输入流对象
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 具体操作
        System.out.println("请输入内容:");
        String line = br.readLine();
        System.out.println(line);
        System.out.println("请输入一个整数:");
        Integer num = Integer.parseInt(br.readLine());
        System.out.println(num);
        // 关闭资源
        br.close();
    }
}

总结:

我们通过IO流的方式完成了和Scanner类似的功能,但是却很繁琐,所以我们平时使用依旧是用Scanner类,这里的标准输入流只作为一种了解;


标准输出流(PrintStream)


概述:

我们的输出语句System.out.println()本质就是标准输出流,这里也是做一个了解;

代码示例:

public class Demo04 {
    public static void main(String[] args) {
        // 创建输出流对象
        PrintStream ps = System.out;
        // 可以打印各种数据
        ps.println(13);
        ps.println(true);
        ps.println(13.14);
        ps.println("你好,中国!");
        ps.println(13L);
        ps.println('a');
    }
}

总结:
在这里,我们的System.out本身就是一个对象,我们将其赋值给了ps变量,由ps去调用方法,那我们省去这一步就会变成:System.out.println(13);,所以标准输出语句的本质就是我们的标准输出流;


打印流


概述:

打印流顾名思义就是用来打印数据的,而打印流只负责打印数据,不负责数据的读取;这一点和我们的标准输出流类似;

分类:

字节打印流:PrintStream

字符打印流:PirntWriter

特点:

  1. 只负责打印输出数据,不负责读取数据;
  2. 永远不会抛出IOException异常;
  3. 有自己特有的功能;

字节打印流


概述:

字节打印流继承自FilterOutputStreamFilterOutputStream继承自OutputStream,也就是我们的标准输出流,所以拥有OutputStream中的所有输出方法;

使用继承自父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出;可以改变输出语句的目的地public static void setOut(PrintStream out)来重新分配“标准”输出流;

构造方法:

PrintStream(String fileName) // 使用指定的文件名创建新的字节打印流对象

PrintStream(OutputStream out) // 使用标准输出流来创建新的字节打印流对象

代码示例:

public class Demo05 {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建打印路流对象
        // 通过字节打印流直接将数据写入到文件中
        PrintStream ps = new PrintStream("IO\\a.txt");
        // 具体操作
        ps.write(97);
        ps.println("hello world!");

        // 通过传入标准输出流对象来创建字节打印流对象
        // 与标准输出流中的功能相同
        PrintStream ps1 = new PrintStream(System.out);
        ps1.println("hello world!");
    }
}

字符打印流


构造方法:

PrintWriter(String fileName) // 使用指定的文件名创建一个字符打印流对象,而不需要自动执行刷新

PrintWriter(Writer out, boolean autoFlush) // 创建一个字符打印流对象; 
/*
out:字符输出流
autoFlush: 一个布尔值,如果为真,则println,printf,或format方法将刷新输出缓冲区
*/

代码示例:

public class Demo06 {
    public static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter("IO\\c.txt");
        // 具体操作
        pw.write("你好,中国!");
        pw.write("abcdefg", 0, 3);
        pw.close();  // 这里不对流进行关闭,也是可以写进文件中的

        String path = "IO\\d.txt";
        PrintWriter pw1 = new PrintWriter(new FileWriter(path), true);
        pw1.write("你好,中国!");
        pw1.println("This is a string");
        pw1.close(); 
    }
}

利用字符打印流来复制文本文件:

public class Demo07 {
    public static void main(String[] args) throws IOException {
        // 创建源文件路径
        String source = "IO\\Demo01.java";
        // 创建目标文件路径
        String target = "IO\\copy.java";
        // 创建输入流对象
        BufferedReader br = new BufferedReader(new FileReader(source));
        // 创建输出流对象
        PrintWriter pw = new PrintWriter(new FileWriter(target), true);
        // 具体操作
        String line;
        while ((line = br.readLine()) != null) {
            pw.println(line); // 直写入一行后换行
            // 如果缓冲字符流或文件字符流,还需要调用换行方法和刷新方法
        }
        // 释放资源
        pw.close();
        br.close();
    }
}


总结:

利用字符打印流来进行复制文件,只是少了两步,性能上也并么有提高很多,开发中还是经常用FileWriterBufferedWriter来复制文本文件;打印流只是作为一个了解!



对象序列化流


什么叫做对象的序列化?

  1. 对象的序列化就是将对象保存到硬盘中或在网络中传输对象;

  2. 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息;

  3. 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息;

那我们简而言之为:将我们的对象进行一个持久化操作,让我们的对象数据存储到硬盘中或存取到数据库中去;


对象序列化流(ObjectOutputStream)


概述:

将Java对象的原始数据类型和图形写入OutputStream; 可以通过使用流的文件来实现对象的持久存储。可以使用ObjectInputStream读取(重构)对象;如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象 ;


构造方法:

ObjectOutputStream(OutputStream out) // 根据给定的字节输出流对象来创建对象序列化流对象

序列化方法:

void writeObject(Object obj) // 将指定的对象写入序列化流对象中

注意事项:

  1. 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口;
  2. Serializable是一个标记接口,实现该接口,不需要重写任何方法;

代码示例: 实现将集合对象写入到文件中

  1. 学生类:

    import java.io.Serializable;
    // 想要实现对象的序列化,那么该对象就需要实现Serializable接口
    
    public class Student implements Serializable {
    
        private String sid;
        private String name;
        private Character gender;
        private String address;
    
        public Student() {
        }
    
        public Student(String sid, String name, Character gender, String address) {
            this.sid = sid;
            this.name = name;
            this.gender = gender;
            this.address = address;
        }
    
        public String getSid() {
            return sid;
        }
    
        public void setSid(String sid) {
            this.sid = sid;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Character getGender() {
            return gender;
        }
    
        public void setGender(Character gender) {
            this.gender = gender;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "sid='" + sid + '\'' +
                    ", name='" + name + '\'' +
                    ", gender=" + gender +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    

  2. 将序列化对象写入到文件中:

    public class Demo04 {
        public static void main(String[] args) throws IOException {
            // 创建目标文件路径
            String path = "IO\\d.txt";
            // 创建序列化对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
            // 创建学生对象
            Student s1 = new Student("001","张三",'男',"江苏省南京市");
            Student s2 = new Student("002", "李四", '男', "安徽省合肥市");
            Student s3 = new Student("003", "王五", '男', "江苏省南京市");
    
            // 创建集合对象
            ArrayList<Student> list = new ArrayList<>();
            list.add(s1);
            list.add(s2);
            list.add(s3);
    
            oos.writeObject(list);
            
            // 释放资源
            oos.close();
        }
    }
    

对象反序列化流(ObjectInputStream)


概述:

将序列化的对象进行反序列化操作;

构造方法:

ObjectInputStream(InputStream in) // 根据给定的字节输入流对象来创建反序列化流对象

反序列化方法:

Object readObject() // 从反序列化对象中读取一个对象

代码示例: 从文件中读取被序列化的对象

public class Demo03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 创建源文件路径
        String path = "IO\\d.txt";
        // 创建流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
        // 具体操作
        ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
        for (Student s : list) {
            System.out.println(s);
        }
        // 释放资源
        ois.close();
    }
}


Properties


概述:

Properties类继承自HashTable类,HashTable中对于集合元素操作的方法,Properties中都有,除此之外还有对流操作的特殊方法;


Properties作为Map集合


作为map集合:

public class Demo04 {
    public static void main(String[] args) {
        // Properties中没有泛型约束,所以可以存放任意类型的数据,但是key保持唯一
        Properties p = new Properties();

        // 存储元素
        p.put("001", "张三");
        p.put("002", "李四");
        p.put("003", "王五");

        // 遍历集合
        Set<Object> keys = p.keySet();
        for (Object key : keys) {
            System.out.println(key + " : " + p.get(key));
        }

        Set<Map.Entry<Object, Object>> entries = p.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
    }
}

特有方法: 其特殊方法都是用来处理字符串的;

方法名 说明
Object setProperty(String key, String value) 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
String getProperty(String key) 使用此属性列表中指定的键搜索属性
Set stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串

public class Demo05 {
    public static void main(String[] args) {
        Properties p = new Properties();

        p.put("001", "张三");
        p.put("002", "李四");
        p.put("003", "王五");

        String property = p.getProperty("001");
        System.out.println(property);

        p.setProperty("001", "赵六");
        System.out.println(p);

        Set<String> strings = p.stringPropertyNames();
        for (String s : strings) {
            System.out.println(s + " : " + p.get(s));
        }
    }
}


Properties与IO流的结合


常用方法:

方法名 说明
void load(InputStream inStream) 从字节输入流中读取属性列表(键和元素对)
void load(Reader reader) 从字符输入流中读取属性列表(键和元素对)
void store(OutputStream out, String comments) 将此属性列表写入此Properties集合中,以适合于使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments) 将此属性列表写入此Properties集合中,以适合使用 load(Reader)方法的格式写入输出字符流

代码示例:

/**
 * 1.在项目根目录下创建Student.txt文件,文件内容如下,学生姓名和年龄是以”键值对”形式存在  	*   的.
 * 2.利用所学的Properties类的相关知识,将文件内容读取到项目中,判断”键值对”中是否有刘方的  	* 	数据,如果有,将其对应的年龄改为18岁.
 * 3.利用Properties类的相关知识把修改后的最新数据写入到newstu.txt文件中.
 * Student.txt文件内容:
 *	刘伊=18岁
 * 	王含=20岁
 * 	李风风=17岁
 * 	刘方=16岁
 * 	马红红=20岁
 * 	丁磊=18岁
 * 	方影=21岁
 * 	姚华华=20岁
 */
public class Demo06 {
    public static void main(String[] args) throws IOException {
        Properties p = new Properties();
        BufferedReader br = new BufferedReader(new FileReader("IO\\Student.txt"));
        p.load(br);
        Set<Object> keys = p.keySet();
        if (keys.contains("刘方")) {
            p.setProperty("刘方", "18岁");
        }

        BufferedWriter bw = new BufferedWriter(new FileWriter("IO\\newstu.txt"));
        p.store(bw, null);
        bw.close();
        br.close();
    }
}

发布了22 篇原创文章 · 获赞 0 · 访问量 348

猜你喜欢

转载自blog.csdn.net/Yi__Ying/article/details/104447134