一、缓冲流

  1. 缓冲流
  2. * 之前的字节流字符流为了提升效率,我们搞了一个数组
  3. * byte[] arr =new byte[8];
  4. *
  5. * 为了进一步提升我们的程序的读写效率,我们使用缓冲流
  6. * 缓冲流是一个包装类,将我们需要的流包装一下,就可以提升效率
  7. * 缓冲流没有读写能力,只能是装饰我们需要读写的类
  8. * 字节流: FileInputStream FileOutputStream
  9. * 字符流: FileReader FileWriter

常见的缓冲流:
缓冲流内部提供了一个默认大小为8192(8kb)的缓冲区

  1. - BufferedInputStream : 缓冲字节输入流
  2. - BufferedOutputStream 缓冲字节输出流
  3. - BufferedReader 缓冲字符输入流
  4. - BufferedWriter 缓冲字符输出流

代码:

  1. package com.qfedu.day15;
  2. import java.io.BufferedInputStream;
  3. import java.io.FileInputStream;
  4. /**
  5. * @Author laoyan
  6. * @Description TODO
  7. * @Date 2022/3/21 9:15
  8. * @Version 1.0
  9. *
  10. * 缓冲流
  11. * 之前的字节流字符流为了提升效率,我们搞了一个数组
  12. * byte[] arr =new byte[8];
  13. *
  14. * 为了进一步提升我们的程序的读写效率,我们使用缓冲流
  15. * 缓冲流是一个包装类,将我们需要的流包装一下,就可以提升效率
  16. * 缓冲流没有读写能力,只能是装饰我们需要读写的类
  17. * 字节流: FileInputStream FileOutputStream
  18. * 字符流: FileReader FileWriter
  19. */
  20. public class Demo01 {
  21. public static void main(String[] args) {
  22. /**
  23. * 在之前的流的基础上套了一层
  24. */
  25. try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("C:\\Users\\User\\Desktop\\data.txt"))){
  26. /**
  27. * 之前我们每次读取一个数组,就写入到磁盘,这样会产生大量的IO操作,
  28. * 使用缓冲流之后,会将即将写入的内容先放起来,达到一定的时机在写入磁盘,减少了IO操作的频率,提升了效率。
  29. */
  30. byte[] bytes = new byte[8];
  31. int num;
  32. while((num= bufferedInputStream.read(bytes)) != -1){
  33. System.out.print(new String(bytes,0,num));
  34. }
  35. }catch (Exception e){
  36. }
  37. }
  38. }

BufferWriter 中有一个重要的API newLine()

  1. package com.qfedu.day15;
  2. import java.io.BufferedWriter;
  3. import java.io.FileWriter;
  4. /**
  5. * @Author laoyan
  6. * @Description TODO
  7. * @Date 2022/3/21 9:41
  8. * @Version 1.0
  9. */
  10. public class Demo03 {
  11. public static void main(String[] args) {
  12. try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("C:\\Users\\User\\Desktop\\write.txt"));){
  13. bufferedWriter.write("Hadoop Spark Flink");
  14. // 在windows 中 换行是\r\n \r 是回车 \n 是换行 在linux中,换行是 \n
  15. // 所以我们编写的代码还需要考虑运行的环境,很麻烦,直接封装成一个方法 newLine() 它可以根据运行的环境选择是哪种字符进行换行
  16. bufferedWriter.newLine();// 新的API
  17. bufferedWriter.write("java jvm mysql linux");
  18. }catch (Exception e){
  19. e.printStackTrace();
  20. }
  21. }
  22. }

BufferReader 中有一个重要的额API readLine()

  1. package com.qfedu.day15;
  2. import java.io.BufferedInputStream;
  3. import java.io.BufferedReader;
  4. import java.io.FileInputStream;
  5. import java.io.FileReader;
  6. /**
  7. * @Author laoyan
  8. * @Description TODO
  9. * @Date 2022/3/21 9:15
  10. * @Version 1.0
  11. *
  12. * readLine()
  13. */
  14. public class Demo02 {
  15. public static void main(String[] args) {
  16. /**
  17. * 在之前的流的基础上套了一层
  18. */
  19. try(BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\User\\Desktop\\data.txt"))){
  20. //bufferedReader.readLine(); // 每次读取一行,读不到内容就返回null
  21. String str ;
  22. // readLine() 相当的有用 只有在字符缓冲流才有这个API
  23. while((str =bufferedReader.readLine()) != null){
  24. System.out.println(str);
  25. }
  26. }catch (Exception e){
  27. e.printStackTrace();
  28. }
  29. }
  30. }

LineNumberReader 是BufferedReader 的子类,可以设置行号

  1. package com.qfedu.day15;
  2. import java.io.BufferedReader;
  3. import java.io.FileReader;
  4. import java.io.LineNumberReader;
  5. /**
  6. * @Author laoyan
  7. * @Description TODO
  8. * @Date 2022/3/21 9:52
  9. * @Version 1.0
  10. */
  11. public class Demo04 {
  12. // BufferedReader 下有一个子类 : LineNumberReader
  13. public static void main(String[] args) {
  14. /**
  15. * 在之前的流的基础上套了一层
  16. */
  17. try(LineNumberReader lineNumberReader = new LineNumberReader(new FileReader("C:\\Users\\User\\Desktop\\data.txt"))){
  18. String str ;
  19. // lineNumberReader.setLineNumber(5); // 设置行号,起始位置,设置为 5 ,打印的时候从 6 开始打印
  20. // 如果不设置,那么行号从0开始,打印的时候,先打印1
  21. while((str =lineNumberReader.readLine()) != null){
  22. int lineNumber = lineNumberReader.getLineNumber();
  23. System.out.print(lineNumber);
  24. System.out.print(str);
  25. System.out.println();
  26. }
  27. }catch (Exception e){
  28. e.printStackTrace();
  29. }
  30. }
  31. }

二、设计模式—装饰者

java 中有23种设计模式— 大话设计模式
设计模式, 前人总结出来的对一些常见问题的解决方案,后人直接拿来使用
所谓的设计—其实就是java代码中的编程技巧。只要代码足够的优化,都是殊途同归。
今天我们见到了装饰者设计模式的典型应用 — 缓冲流。
举个例子:
自己编写一个类,实现我们的缓冲流中的readLine方法。编写一个自己的MyBufferedReader 类,实现缓冲流中的readLine() 方法。
想对某个类进行增强,就是对某个类中的方法进行增强,所以我们需要将原来的类给引入进来,可以通过传参数。
穿着稻草的小人 —> 传递到了一个增强类中—> 将武器装备和衣服进行升级。

对某个类的功能进行增强:
1、继承
2、装饰者模式
3、代理模式

装饰者模式:
什么是装饰者模式: 对需要进行内容增强的类,进行方法上的增强(原有方法,新的方法)
实现步骤:
1、需要继承原有的类
2、需要有一个有参数的构造方法,将 基本的类传递进来
* 3、实现新的方法或者重写老的方法。

  1. package com.qfedu.day15;
  2. import java.io.FileReader;
  3. import java.io.IOException;
  4. import java.io.Reader;
  5. /**
  6. * @Author laoyan
  7. * @Description TODO
  8. * @Date 2022/3/21 10:15
  9. * @Version 1.0
  10. *
  11. * 增强的类,使用设计者模式
  12. *
  13. * 装饰者模式:
  14. * 什么是装饰者模式: 对需要进行内容增强的类,进行方法上的增强(原有方法,新的方法)
  15. * 实现步骤:
  16. * 1、需要继承原有的类
  17. * 2、需要有一个有参数的构造方法,将 基本的类传递进来
  18. * 3、实现新的方法或者重写老的方法。
  19. */
  20. public class MyBufferedReader extends Reader {
  21. /**
  22. * 对 FileReder 进行的增强
  23. */
  24. private Reader fileReader ;
  25. public MyBufferedReader(Reader fileReader){
  26. this.fileReader = fileReader;
  27. }
  28. /**
  29. * 增强原有的方法
  30. * @return
  31. * @throws IOException
  32. */
  33. public int read() throws IOException {
  34. /**
  35. * 增强的代码,此处可以随便写
  36. */
  37. System.out.println("我的增强代码");
  38. return fileReader.read();
  39. }
  40. @Override
  41. public int read(char[] cbuf, int off, int len) throws IOException {
  42. return fileReader.read(cbuf,off,len);
  43. }
  44. @Override
  45. public void close() throws IOException {
  46. }
  47. /**
  48. * 是对原来的FileReader 进行的增强方法
  49. * @return
  50. * @throws IOException
  51. */
  52. public String readLine() throws IOException {
  53. StringBuilder lineStr = new StringBuilder();
  54. /**
  55. * // 回车有两个字符 \r\n
  56. * hadoop version
  57. * flume java
  58. */
  59. while(true){
  60. int a = fileReader.read();
  61. if(a == '\r'){
  62. continue;
  63. }else if(a == '\n'){
  64. break;
  65. }else{
  66. lineStr.append((char)a);
  67. }
  68. }
  69. return lineStr.toString();
  70. }
  71. }

装饰器模式补充:

4.2.3.1. 介绍

  1. 我们对一个已经完成的类,如果需要进行功能的拓展,最容易让我们想到的就是继承!设计一个类的子类,子类继承到父类中所有的功能,同时也可以进行功能的添加。但是有时候我们要拓展的类无法继承,那么怎么办呢?
  2. 此时可以使用装饰模式,在不使用继承的情况下,对一个类的功能进行拓展。并且使用装饰模式进行功能拓展,比起使用继承来说要更加的简洁和灵活。
  3. IO流部分,对装饰模式的使用是比较多的。
  4. 例如: InputStream是一个字节输入流,如果我们需要进行效率的提升,可以使用BufferedInputStream。那么,在你实例化一个BufferedInputStream对象的时候,参数部分是不是一个InputStream对象呐?

image.png
4.2.3.2. 实现

  1. /**
  2. * @Author 千锋大数据教学团队
  3. * @Company 千锋好程序员大数据
  4. * @Description 修饰装饰
  5. */
  6. public interface Decorator {
  7. void show();
  8. }
  9. // 所有的装饰者的父类 基类
  10. abstract class AbstractDecorator implements Decorator {
  11. // 表示被装饰的对象
  12. protected Decorator decorator;
  13. //
  14. public AbstractDecorator(Decorator decorator) {
  15. this.decorator = decorator;
  16. }
  17. }
  18. class ShoesDecorator extends AbstractDecorator {
  19. public ShoesDecorator(Decorator decorator) {
  20. super(decorator);
  21. }
  22. @Override
  23. public void show() {
  24. this.decorator.show();
  25. System.out.println("穿了一双鞋");
  26. }
  27. }
  28. class TshirtDecorator extends AbstractDecorator {
  29. public TshirtDecorator(Decorator decorator) {
  30. super(decorator);
  31. }
  32. @Override
  33. public void show() {
  34. this.decorator.show();
  35. System.out.println("穿了一个T恤衫");
  36. }
  37. }
  38. class TrousersDecorator extends AbstractDecorator {
  39. public TrousersDecorator(Decorator decorator) {
  40. super(decorator);
  41. }
  42. @Override
  43. public void show() {
  44. this.decorator.show();
  45. System.out.println("穿了一条裤子");
  46. }
  47. }
  48. public class Demo08 {
  49. public static void main(String[] args) {
  50. // 穿衣服
  51. TshirtDecorator tshirtDecorator = new TshirtDecorator(new Decorator() {
  52. @Override
  53. public void show() {
  54. System.out.println("开始穿衣服");
  55. }
  56. });
  57. //tshirtDecorator.show();
  58. TrousersDecorator trousersDecorator = new TrousersDecorator(tshirtDecorator);
  59. ShoesDecorator shoesDecorator = new ShoesDecorator(trousersDecorator);
  60. shoesDecorator.show();
  61. /**
  62. * 比如: 点咖啡 加糖不加糖 加牛奶不加牛奶 加茶水不加茶水
  63. * 比如: 卖自行车 自行车是一个基类 很多的装饰类 加前灯,加后灯,加音响,加水杯
  64. */
  65. }
  66. }

image.png

三、序列化流(重点)—对象的序列化

1、概念

将jvm内存中的一个类的对象,存储到磁盘上,再从磁盘上加载到jvm内存中,称之为序列化。
序列化与反序列化的操作。
2、将一个对象序列化到磁盘上(文件中)
对象一定要实现序列化接口,否则报错

  1. package com.qfedu.day15;
  2. import java.io.Serializable;
  3. /**
  4. * @Author laoyan
  5. * @Description TODO
  6. * @Date 2022/3/21 10:46
  7. * @Version 1.0
  8. */
  9. public class Person implements Serializable {
  10. private String name;
  11. private int age;
  12. public Person(String name, int age) {
  13. this.name = name;
  14. this.age = age;
  15. }
  16. public String getName() {
  17. return name;
  18. }
  19. public void setName(String name) {
  20. this.name = name;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. @Override
  29. public String toString() {
  30. return "Person{" +
  31. "name='" + name + '\'' +
  32. ", age=" + age +
  33. '}';
  34. }
  35. }

image.png

  1. package com.qfedu.day15;
  2. import java.io.FileOutputStream;
  3. import java.io.ObjectOutputStream;
  4. /**
  5. * @Author laoyan
  6. * @Description TODO
  7. * @Date 2022/3/21 10:46
  8. * @Version 1.0
  9. */
  10. public class Demo06 {
  11. public static void main(String[] args) {
  12. // 对象实例化了,jvm中有内容了
  13. Person person = new Person("张三", 19);
  14. // 将该对象放入到一个txt文档里面 --> 对象序列化
  15. // ObjectInputStream、 ObjectOutputStream
  16. try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:\\Users\\User\\Desktop\\obj.txt"))){
  17. objectOutputStream.writeObject(person);
  18. objectOutputStream.flush();
  19. }catch (Exception e){
  20. e.printStackTrace();
  21. }
  22. }
  23. }

3、将文件中的序列化的对象,读取到jvm内存中— 反序列化

  1. package com.qfedu.day15;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.ObjectInputStream;
  5. import java.io.ObjectOutputStream;
  6. /**
  7. * @Author laoyan
  8. * @Description TODO
  9. * @Date 2022/3/21 10:55
  10. * @Version 1.0
  11. */
  12. public class Demo07 {
  13. public static void main(String[] args) {
  14. // 将一个文件中的对象读取到内存中,这个过程称之为对象的反序列化
  15. // ObjectInputStream、 ObjectOutputStream
  16. try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C:\\Users\\User\\Desktop\\obj.txt"))){
  17. Object o = objectInputStream.readObject();
  18. if(o instanceof Person){
  19. Person p = (Person)o;
  20. System.out.println(p);
  21. }
  22. }catch (Exception e){
  23. e.printStackTrace();
  24. }
  25. }
  26. }

对象的序列化技术:一般应用于网络间对象数据的传输。一是安全性得到保障另外对象序列化可以压缩空间便于传输。将对象序列化到本地一般不多见,常见于我们使用的一些软件—Redis.
4、序列化中要有序列号

可以通过IDEA帮我生成一个序列化号:
serialVersionUID
image.png
不进行上面的操作,是没有办法生成的。
在类上,alt + enter 点击生成 serialVersionUID 就可以了。
image.png

首先:
1、我们生成了一个序列化的编号,然后进行了序列化的操作,在磁盘上生成了一个文件
2、我们修改了序列化编号中的一个字,然后再反序列化,就出现了错误:

  1. java.io.InvalidClassException: com.qfedu.day15.Person; local class incompatible: stream classdesc serialVersionUID = 5163574055981245315, local class serialVersionUID = 5163574055981245316
  2. at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
  3. at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
  4. at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
  5. at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
  6. at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
  7. at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
  8. at com.qfedu.day15.Demo07.main(Demo07.java:21)

为什么呢?因为你修改了序列化的编号,导致错误。
将序列化编号修改正确即可。
推演:序列化的编号其实是一种安全策略,它可以保证序列化与反序列化是同一个类,没有发生变化。

package com.qfedu.day15;

import java.io.Serializable;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 10:46
 * @Version 1.0
 */
public class Person implements Serializable {

    // 让系统帮我们生成一个,而不是指定一个  1L
    private static final long serialVersionUID = 5163574055981245315L;

    //private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

四、Properties(重点)

1、介绍

Properties 不是IO流,是一个集合,是Hashtable的子类,是一个Map实现类。

2、用法

package com.qfedu.day15_02;

import java.util.Properties;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 14:50
 * @Version 1.0
     当集合用
 */
public class Demo01 {

    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.setProperty("first","java");
        properties.setProperty("second","python");
        properties.setProperty("third","php");
        System.out.println(properties);
        System.out.println(properties.getProperty("first"));
        properties.setProperty("first","scala");
        System.out.println(properties);
    }
}
package com.qfedu.day15_02;

import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 14:50
 * @Version 1.0
 */
public class Demo02 {

    // 获取系统属性
    public static void main(String[] args) {
        // 获取系统中的Properties 对象
        Properties properties = System.getProperties();
        // 获取一个Properties 中所有的key值
        Set<String> set = properties.stringPropertyNames();
        System.out.println(set);
        // 通过迭代器循环遍历
        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()){
            String key = iterator.next();
            System.out.println("key="+key+",value="+properties.getProperty(key));
        }


    }
}

可以加载配置文件,修改配置文件

package com.qfedu.day15_02;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Properties;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 15:01
 * @Version 1.0
 */
public class Demo03 {
    // 此处我们训练的是文件加载到Properties 中
    public static void main(String[] args) throws Exception {
        // 先搞一个空的集合 Properties
        Properties properties = new Properties();
        // 通过集合中的load 方法,加载一个流(可以是字符流也可以是字节流)
        properties.load(new FileReader("a.txt"));
        // 该方法只要执行完,集合中就有数据了。
        System.out.println(properties.getProperty("name"));

        /**
         *   以上内容是读取文件中的内容到Properties 中,以下内容是将properties 中的内容存入文件中
         */
        properties.setProperty("name","大家");
        // 通过store 可以将内容存储到文件中。
        properties.store(new FileWriter("a.txt"),"修改了name");

    }
}

五、Scannner类

回顾:常用语法:

package com.qfedu.day15_03;

import java.util.Scanner;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 15:16
 * @Version 1.0
 */
public class Demo01 {

    // Scanner

    /**
     *  常用方法
     *    next() 可以获取一个单词
     *    nextInt()  可以获取一个数字
     *    hasNext() 判断是否还有下一个单词
     *    nextLine()  读取一行数据,遇到换行符就不读取了
     *    hasNextLine()  判断是否还有下一行
     * @param args
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个单词:");
        String str = scanner.next();
        System.out.println(str);

        System.out.println("请输入一句话,使用空格隔开:");
        while(scanner.hasNext()){
            System.out.println(scanner.next());
        }
    }
}

流也可以从其他地方过来,比如File

package com.qfedu.day15_03;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 15:16
 * @Version 1.0
 */
public class Demo02 {

    // Scanner

    /**
     *  常用方法
     *    next() 可以获取一个单词
     *    nextInt()  可以获取一个数字
     *    hasNext() 判断是否还有下一个单词
     *    nextLine()  读取一行数据,遇到换行符就不读取了
     *    hasNextLine()  判断是否还有下一行
     * @param args
     */
    public static void main(String[] args)  {
        // Scanner 构造方法中可以传入其他的流,System.in 只是其中一个流而已
        try(Scanner scanner = new Scanner(new File("a.txt")))
        {
            while(scanner.hasNextLine()){
                System.out.println(scanner.nextLine());
            }

        }catch (Exception e){
            e.printStackTrace();
        }


    }
}

记录一个坑:

package com.qfedu.day15_03;

import java.util.Scanner;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 15:27
 * @Version 1.0
 */
public class Demo03 {

    /**
     *   补充内容:一个用法上的坑
     * @param args
     */
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        /*System.out.println("请输入字符串(nextLine):");//曹老板很有钱
        String str1 = input.nextLine();
        System.out.println(str1);*/

        System.out.println("请输入字符串(next):");
        String str = input.next(); // 你好(回车)
        System.out.println(str);//你好

        /**
         *  此后代码不让我填写文字,直接运行结束的原因是:
         *    nihao  然后回车了  \r\n
         *    next() 只读取了 nihao,并没有读取 \r\n
         *    所以后面的readLine 是 遇到 \n 就结束
         *    所以 \r\n 被我们的readLine() 读取到了,所以结束了,什么都没有打印
         */
        System.out.println("请输入字符串(nextLine):");//曹老板很有钱
        String str1 = input.nextLine();// 它读取了一个回车,刚好就结束了
        System.out.println(str1);


    }
}

六、打印流(了解)

常用类是:
字节打印流(PrintStream)
字符打印流(PrintWriter)

package com.qfedu.day15_03;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 16:05
 * @Version 1.0
 */
public class Demo04 {

    // 打印流,主要用于打印用的
    public static void main(String[] args) throws IOException {
        PrintWriter printWriter = new PrintWriter(new FileWriter("b.txt"));
        printWriter.write("你好,我是打印流!");
        printWriter.close();
    }
}

七、标准输入输出流(了解)

System.out   标准输出流  --默认输出到了控制台
System.in    标准输入流  --默认数据来源是键盘
package com.qfedu.day15_03;

import java.io.IOException;
import java.io.InputStream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 16:10
 * @Version 1.0
 */
public class Demo05 {

    /**
     *  实现从键盘上不断的接收字符的程序
     *  要求一行一行的接收
     * @param args
     */
    public static void main(String[] args) throws IOException {

        InputStream in = System.in;// 数据的来源是键盘
        myReadLine(in);

    }

    private static void myReadLine(InputStream in) throws IOException {
        StringBuffer stringBuffer = new StringBuffer();
        while(true){
            int read = in.read();
            if(read == '\r'){
                continue;
            }else if(read == '\n'){
                break;
            }else{
                stringBuffer.append((char)read);
            }

        }

        System.out.println(stringBuffer.toString());

    }
}
package com.qfedu.day15_03;

import java.io.FileOutputStream;
import java.io.PrintStream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 16:18
 * @Version 1.0
 */
public class Demo06 {

    public static void main(String[] args) {
        PrintStream out = System.out;// 竟然是一个打印流
        // 标准的输出流是输出到控制台的,我们也可以改变输出的方向
        try(PrintStream printStream = new PrintStream(new FileOutputStream("c.txt"))){

            // 可以修改输出流的方向
            System.setOut(printStream);
            System.out.println("我在哪里,我是谁,发生了什么事儿!!!");
        }catch (Exception e){

            e.printStackTrace();
        }

    }
}

面试题:System.out.println() 是线程安全的吗? 自行百度!!!!

八、转换流引出字符集的问题

拥有两个工具类:
转换流 —- 转换字符集的
当文本内容和我们的运行环境不一致的时候,我们需要使用转换流来操作
InputStreamReader
* OutputStreamWriter

1、演示采用某种字符集读取一个文件

package com.qfedu.day15_04;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 16:38
 * @Version 1.0
 */
public class Demo01 {

    /**
     *  转换流  --- 转换字符集的
     *  当文本内容和我们的运行环境不一致的时候,我们需要使用转换流来操作
     *  InputStreamReader
     *  OutputStreamWriter
     * @param args
     */

    public static void main(String[] args) {

        /**
         *  在之前的流的基础上套了一层
         *//*
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\User\\Desktop\\data.txt"))){

            //bufferedReader.readLine();   //  每次读取一行,读不到内容就返回null
            String str ;
            // readLine()  相当的有用    只有在字符缓冲流才有这个API
            while((str =bufferedReader.readLine()) != null){
                System.out.println(str);
            }
        }catch (Exception e){

            e.printStackTrace();
        }*/

        try(InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\User\\Desktop\\data.txt"),"GBK")){

            char[] arr =new char[100];
            int num;
            while((num =inputStreamReader.read(arr)) != -1){
                System.out.println(new String(arr,0,num));
            }
        }catch (Exception e){

            e.printStackTrace();
        }



    }
}

2、采用某个字符集写入一个文件

package com.qfedu.day15_04;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/21 16:38
 * @Version 1.0
 */
public class Demo02 {

    /**
     *  转换流  --- 转换字符集的
     *  当文本内容和我们的运行环境不一致的时候,我们需要使用转换流来操作
     *  InputStreamReader
     *  OutputStreamWriter
     * @param args
     */

    public static void main(String[] args) {



        try(OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("C:\\Users\\User\\Desktop\\data2.txt"),"GBK")){

           writer.write("Hello World");
           writer.write("你好,世界!");
           writer.flush();

        }catch (Exception e){

            e.printStackTrace();
        }



    }
}

九、字符集

- 常用字符集
  - 中国的字符集:GBK/GB2312
  - 欧洲的:ISO8859-1
  - 通用的:UTF-8
  - 美国的:ASCII
- 对中文的处理
  - 一个汉字:GBK:2个字节       ISO8859-1:1个字节       utf-8:3个字节    unicode:2个字节(内部编码)
  - 说明:GBK,UTF-8是支持中文的,ISO8859-1不支持中文

- 编码:将字符串转化成byte序列的过程
- 解码:是将byte序列转成字符串的过程
- 编码错误:乱码:在执行读与写的时候,由于使用的字符集不同,造成了编码的错误.

      GBK  中的中文     --> byte数组  -->  utf-8          utf-8 后就乱码

   怎么样才能不乱码    文本内容  GBK ,将来读取的时候也需要使用GBK

实战:

编码:

byte[] getBytes() //对于中文  默认的格式
使用平台的默认字符集将此 String 编码为 byte 序列,
并将结果存储到一个新的 byte 数组中。

byte[] getBytes(Charset charset)
使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

解码:

String(byte[] bytes) //对于中文  默认是格式
通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。

String(byte[] bytes, Charset charset)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
 // 编码,变为byte数组
        byte[] bytes = "你好".getBytes();// 并没有指定字符集,默认采用开发环境中的字符集 utf-8
        // 解码
        String str = new String(bytes, "UTF-8");
        System.out.println(str);

        byte[] bytes2 = "你好".getBytes("GBK");
        System.out.println(new String(bytes2,"GBK"));