Java-进阶篇【日志、File、递归、IO】---07

1:日志 概叙

希望系统能记住某些数据是被谁操作的,比如被谁删除了?
想分析用户浏览系统的具体情况,以便挖掘用户的具体喜好?
当系统在开发或者上线后出现了bug,崩溃了,该通过什么去分析、定位bug?

日志:用来记录程序运行过程中的信息,并可以进行永久存储。好比生活中的日记,可以记录你生活的点点滴滴。

日志技术应该具备哪些特点和优势
  可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中)。
  可以随时以开关的形式控制是日志的记录和取消,无需侵入到源代码中去进行修改。

什么是日志?
  用来记录程序运行过程中的信息,并可以进行永久存储
日志:业务的单独跑,日志系统自己开个多线程记录【程序性能更优秀】

2:日志技术体系、Logback 概述

Logback日志框架
  官方网站:https://logback.qos.ch/index.html 
  Logback是由log4j创始人设计的另外一个开源日志组件,性能比log4j要好
Logback日志框架分为以下模块:   logback-core: 该模块为其他两个模块提供基础代码。 (必须有)   logback-classic:完整实现了slf4j API的模块。(必须有)   logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能(可选模块,以后接触) 想使用Logback日志框架,至少需要在项目中整合如下三个模块:   slf4j-api:日志接口   logback-core:基础模块   logback-classic:功能模块,它完整实现了slf4j API
日志接口是什么,常见的有几种形式。
  日志接口大多是一些规范,用来约束日志实现框架的设计。
  Commons Logging、Simple Logging Facade for Java(slf4j)
常见的日志实现框架有哪些?
  Log4J、Logback(我们重点学习的,其他的都大同小异)。
  Logback是基于slf4j日志接口实现的日志框架。
使用Logback至少需要使用哪几个模块,各自的作用是什么?
  slf4j-api:日志接口【日志规范】
  logback-core:基础模块
  logback-classic:功能模块,它完整实现了slf4j API

3:Logback 快速入门

目的:使用Logback日志框架,纪录系统的运行信息。
实现步骤:
  ①:导入Logback框架到项目中去。在项目下新建文件夹lib,导入Logback的jar包到该文件夹下
  ②:将存放jar文件的lib文件夹添加到项目依赖库中去。
    1:打开lib选中三个jar包——>2:右击鼠标 Add as library【加到依赖库】   ③:将Logback的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)。   ④:创建Logback框架提供的Logger日志对象,后续使用其方法记录系统的日志信息。     
public static final Logger LOGGER = LoggerFactory.getLogger(“类名"); 使用Logback的开发步骤是怎么样的?   ①:在项目下新建文件夹lib,导入Logback的相关jar包到该文件夹下,并添加到项目库中去。   ②:必须将Logback的核心配置文件logback.xml直接拷贝到src目录下。   ③:在代码中获取日志的对象   ④:调用日志对象的方法记录日志信息

 4:Logback 配置详解

对Logback日志框架的控制,都是通过核心配置文件logback.xml来实现的。

Logback日志输出位置、格式设置:
  通过logback.xml 中的<append>标签可以设置输出位置。
  通常可以设置2个日志输出位置:一个是控制台、一个是系统文件中

输出到控制台的配置标志
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

输出到系统文件的配置标志
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
package com.itheima.logback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Test {
    // 左边 Logger slf4j 接口类,日志规范            右边:logback实现类对象      多态写法
    public static final Logger LOGGER = LoggerFactory.getLogger("Test.class");
    public static void main(String[] args) {
        try {
            LOGGER.debug("debug message");
            LOGGER.info("info message");
            int a = 10;
            int b = 0;
            LOGGER.trace("a"+ a);
            LOGGER.trace("b"+ b);
            System.out.println(a/b);
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("功能异常" + e);
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--
        CONSOLE :表示当前的日志信息是可以输出到控制台的。
    -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--输出流对象 默认 System.out 改为 System.err-->
        <target>System.out</target>
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
                %msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level]  %c [%thread] : %msg%n</pattern>
        </encoder>
    </appender>

    <!-- File是输出的方向通向文件的 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <!--日志输出路径-->
        <file>E:/code/ywt-data.log</file>
        <!--指定日志文件拆分和压缩规则  日志过大进行拆分-->
        <rollingPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--通过指定压缩文件名称,来确定分割文件方式-->
            <fileNamePattern>E:/code/ywt-data2-%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
            <!--文件拆分大小-->
            <maxFileSize>1MB</maxFileSize>
        </rollingPolicy>
    </appender>

    <!--
    level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR    |    ALL 和 OFF
   , 默认debug
    <root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
    -->
    <root level="ALL">
        <appender-ref ref="CONSOLE" />
        <!-- 注意:如果这里不配置关联打印位置,该位置将不会记录日志-->
        <appender-ref ref="FILE" />
    </root>
</configuration>

 5:Logback 日志级别

1、如果系统上线后想关闭日志,或者只想记录一些错误的日志信息,怎么办?
  可以通过设置日志的输出级别来控制哪些日志信息输出或者不输出。

日志级别
  ALL  和 OFF分别是打开、及关闭全部日志信息。
  除此之外,日志级别还有: TRACE <  DEBUG <  INFO < WARN < ERROR  ; 默认级别是DEBUG,对应其方法
  作用:当在logback.xml文件中设置了某种日志级别后,系统将只输出当前级别,以及高于当前级别的日志。

具体在<root level=“INFO”>标签的level属性中设置指定系统的日志级别
<root level=“INFO">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="FILE" />
</root>

6:项目简单实战  --电影上架系统

日志框架搭建、系统对象设计
  集成日志框架、用于后期记录日志信息。
  定义一个 电影 movie 类,【片名,主演,评分,时长,票价,余票等】
  系统包含两个用户角色,客户,商家存在大量相同属性信息
  User父类:登录名称,密码,真实名称,性别,电话,账户金额
  Business商家类:店铺名称,地址
  Customer客户类:
  集合List 存在系统注册用户对象信息
  map集合:商家 和 影片信息【多个电影数据】
package com.itheima.bean;

import com.itheima.run.MovieSystem;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;

public class Movie {
    private String name;
    private String actor;
    private double time;
    private double price;
    private int number; // 余票
    private Date startTime; // 放映时间

    public Movie() {
    }

    public Movie(String name, String actor, double time, double price, int number, Date startTime) {
        this.name = name;
        this.actor = actor;
        this.time = time;
        this.price = price;
        this.number = number;
        this.startTime = startTime;
    }

    public String getName() {
        return name;
    }

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

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }

    public double getScore() {
        List<Double> scores = MovieSystem.MOVIES_SCORE.get(name);
        if(scores!=null && scores.size() > 0){
            double sum = 0;
            for (Double score : scores) {
                sum += score;
            }
            return BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(scores.size()), 2 , RoundingMode.UP).doubleValue();
        }else {
            return 0;
        }
    }

    public double getTime() {
        return time;
    }

    public void setTime(double time) {
        this.time = time;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }
}
package com.itheima.bean;

/**
   用户类(客户和商家的父类 )
 */
public class User {
    private String loginName;  // 假名  不能重复
    private String userName; // 真名
    private String passWord;
    private char sex;
    private String phone;
    private double money;

    public User(){
    }

    public User(String loginName, String userName, String passWord, char sex, String phone, double money) {
        this.loginName = loginName;
        this.userName = userName;
        this.passWord = passWord;
        this.sex = sex;
        this.phone = phone;
        this.money = money;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
package com.itheima.bean;

public class Business extends User{
    // 店铺名称
    private String shopName;
    // 店铺地址
    private String address;

    public String getShopName() {
        return shopName;
    }

    public void setShopName(String shopName) {
        this.shopName = shopName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
package com.itheima.bean;

import java.util.HashMap;
import java.util.Map;

/**
   客户角色
 */
public class Customer extends User{
    // 定义一个属性存储购买记录。
    private Map<String, Boolean> buyMovies = new HashMap<>();

    public Map<String, Boolean> getBuyMovies() {
        return buyMovies;
    }

    public void setBuyMovies(Map<String, Boolean> buyMovies) {
        this.buyMovies = buyMovies;
    }
}
首页,登录,商家界面,用户界面设计
  1:首页需要包含 登录,商家入驻,客户注册功能
  2:商家 和 客户公用一个登录功能
  3:判断登录成功的用户真实类型,根据用户类型完成对应的操作界面设计
商家功能:
  展示详情,影片上架,退出
  展示本商家的信息和排片情况
  提供影片上架功能,创建一个影片对象,存入到商家集合中去
  退出,回到登录首页
  提供影片下架功能,商家集合中删除影片对象
  影片修改功能,拿到需要修改的影片对象,修改里面数据
用户功能:
  展示全部影片,遍历全部商家和其拍片信息并展示【遍历 map】
  购票-
package com.itheima.run;

import com.itheima.bean.Business;
import com.itheima.bean.Customer;
import com.itheima.bean.Movie;
import com.itheima.bean.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class MovieSystem {
    /**
        定义系统的数据容器用户存储数据
        1、存储很多用户(客户对象,商家对象)
     */
    public static final List<User> ALL_USERS = new ArrayList<>();   // 集合存储用户对象
    /**
       2、存储系统全部商家和其排片信息 。
           商家1 = [p1,p2,p3,...]
           商家2 = [p2,p3,...]
           ...
     */
    public static final Map<Business, List<Movie>> ALL_MOVIES = new HashMap<>();

    public static final Scanner SYS_SC = new Scanner(System.in);
    public static User loginUser;           // 定义一个静态的User类型的变量记住当前登录成功的用户对象
    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    public static final Logger LOGGER = LoggerFactory.getLogger("MovieSystem.class");

    /**
       3、准备一些测试数据   静态代码块初始化时候存储测试数据
     */
    static {
        Customer c = new Customer();
        c.setLoginName("zyf888");
        c.setPassWord("123456");
        c.setUserName("黑马刘德华");
        c.setSex('男');
        c.setMoney(10000);
        c.setPhone("110110");
        ALL_USERS.add(c);

        Customer c1 = new Customer();
        c1.setLoginName("gzl888");
        c1.setPassWord("123456");
        c1.setUserName("黑马关之琳");
        c1.setSex('女');
        c1.setMoney(2000);
        c1.setPhone("111111");
        ALL_USERS.add(c1);

        Business b = new Business();
        b.setLoginName("baozugong888");
        b.setPassWord("123456");
        b.setUserName("黑马包租公");
        b.setMoney(0);
        b.setSex('男');
        b.setPhone("110110");
        b.setAddress("火星6号2B二层");
        b.setShopName("甜甜圈国际影城");
        ALL_USERS.add(b);
        // 注意,商家一定需要加入到店铺排片信息中去
        List<Movie> movies = new ArrayList<>();
        ALL_MOVIES.put(b , movies); // b = []

        Business b2 = new Business();
        b2.setLoginName("baozupo888");
        b2.setPassWord("123456");
        b2.setUserName("黑马包租婆");
        b2.setMoney(0);
        b2.setSex('女');
        b2.setPhone("110110");
        b2.setAddress("火星8号8B八层");
        b2.setShopName("巧克力国际影城");
        ALL_USERS.add(b2);
        // 注意,商家一定需要加入到店铺排片信息中去
        List<Movie> movies3 = new ArrayList<>();
        ALL_MOVIES.put(b2 , movies3); // b2 = []
    }


    public static void main(String[] args) {
        showMain();
    }

    /**
       首页展示
     */
    private static void showMain() {
        while (true) {
            System.out.println("===============黑马电影首页=================");
            System.out.println("1、登录");
            System.out.println("2、用户注册");
            System.out.println("3、商家注册");
            System.out.println("请输入操作命令:");
            String command = SYS_SC.nextLine();     // 定义一个静态扫描器,这里调用 【nextLine 每次接收一行,接收的是字符串类型】
            switch (command) {
                case "1":       // 登录了
                    login();
                    break;
                case "2":       //用户注册
                    break;
                case "3":       //商家注册
                    break;
                default:
                    System.out.println("命令有误,请确认!");
            }
        }
    }

    /**
       登录功能
     */
    private static void login() {
        while (true) {
            System.out.println("请您输入登录名称:");
            String loginName = SYS_SC.nextLine();
            System.out.println("请您输入登录密码:");
            String passWord = SYS_SC.nextLine();

            User u = getUserByLoginName(loginName);     // 1、根据登录名称查询用户对象【独立成一个方法】

            if(u != null){                              // 2、判断用户对象是否存在,存在说明登录名称正确了
                if(u.getPassWord().equals(passWord)){   // 3、比对密码是否正确   // 登录成功了:...
                    loginUser = u;                      // 记住登录成功的用户
                    LOGGER.info(u.getUserName() +"登录了系统~~~");
                    if(u instanceof Customer) {     // 判断是用户登录的,还是商家登录的 【判断用户对象类型】
                        showCustomerMain();         // 当前登录的是普通用户   【展示普通用户相关界面方法】
                    }else {
                        showBusinessMain();         // 当前登录的肯定是商家用户
                    }
                    return;             // 方法执行完了,需要return返回到  showMain 死循环
                }else {
                    System.out.println("密码有毛病~~");
                }
            }else {
                System.out.println("登录名称错误,请确认");
            }
        }
    }

    /**
      商家的后台操作界面
     */
    private static void showBusinessMain() {
        while (true) {
            System.out.println("============黑马电影商家界面===================");
            System.out.println(loginUser.getUserName() + (loginUser.getSex()=='男'? "先生":"女士" + "欢迎您进入系统"));
            System.out.println("1、展示详情:");
            System.out.println("2、上架电影:");
            System.out.println("3、下架电影:");
            System.out.println("4、修改电影:");
            System.out.println("5、退出:");

            System.out.println("请输入您要操作的命令:");
            String command = SYS_SC.nextLine();
            switch (command){
                case "1":                   // 展示全部排片信息
                    showBusinessInfos();
                    break;
                case "2":                   // 上架电影信息
                    addMovie();
                    break;
                case "3":                   // 下架电影信息
                    deleteMovie();
                    break;
                case "4":
                    updateMovie();          // 修改电影信息
                    break;
                case "5":
                    System.out.println(loginUser.getUserName() +"请您下次再来啊~~~");
                    return; // 干掉方法
                default:
                    System.out.println("不存在该命令!!");
                    break;
            }
        }
    }

    /**
     影片修改功能
     */
    private static void updateMovie() {
        System.out.println("================修改电影====================");
        Business business = (Business) loginUser;
        List<Movie> movies = ALL_MOVIES.get(business);

        if(movies.size() == 0) {
            System.out.println("当期无片可以修改~~");
            return;
        }

        // 2、让用户选择需要下架的电影名称
        while (true) {
            System.out.println("请您输入需要修改的电影名称:");
            String movieName = SYS_SC.nextLine();

            // 3、去查询有没有这个影片对象。
            Movie movie = getMovieByName(movieName);
            if(movie != null){
                // 修改它
                System.out.println("请您输入修改后的片名:");
                String name  = SYS_SC.nextLine();
                System.out.println("请您输入修改后主演:");
                String actor  = SYS_SC.nextLine();
                System.out.println("请您输入修改后时长:");
                String time  = SYS_SC.nextLine();
                System.out.println("请您输入修改后票价:");
                String price  = SYS_SC.nextLine();
                System.out.println("请您输入修改后票数:");
                String totalNumber  = SYS_SC.nextLine(); // 200\n
                while (true) {
                    try {
                        System.out.println("请您输入修改后的影片放映时间:");
                        String stime  = SYS_SC.nextLine();

                        movie.setName(name);
                        movie.setActor(actor);
                        movie.setPrice(Double.valueOf(price));
                        movie.setTime(Double.valueOf(time));
                        movie.setNumber(Integer.valueOf(totalNumber));
                        movie.setStartTime(sdf.parse(stime));

                        System.out.println("恭喜您,您成功修改了该影片了!!!");
                        showBusinessInfos();
                        return; // 直接退出去
                    } catch (Exception e) {
                        e.printStackTrace();
                        LOGGER.error("时间解析出了毛病");
                    }
                }
            }else {
                System.out.println("您的店铺没有上架该影片!");
                System.out.println("请问继续修改吗?y/n");
                String command = SYS_SC.nextLine();
                switch (command) {
                    case "y":
                        break;      // 终止本次 switch
                    default:
                        System.out.println("好的!");
                        return;
                }
            }
        }
    }

    /**
      影片下架功能
     */
    private static void deleteMovie() {
        System.out.println("================下架电影====================");
        Business business = (Business) loginUser;
        List<Movie> movies = ALL_MOVIES.get(business);
        if(movies.size() == 0) {
            System.out.println("当期无片可以下架~~");
            return;
        }

        // 2、让用户选择需要下架的电影名称
        while (true) {
            System.out.println("请您输入需要下架的电影名称:");
            String movieName = SYS_SC.nextLine();

            // 3、去查询有没有这个影片对象。
            Movie movie = getMovieByName(movieName);
            if(movie != null){      // 下架它
                movies.remove(movie);
                System.out.println("您当前店铺已经成功下架了:" + movie.getName());
                showBusinessInfos();
                return;
            }else {
                System.out.println("您的店铺没有上架该影片!");
                System.out.println("请问继续下架吗?y/n");
                String command = SYS_SC.nextLine();
                switch (command) {
                    case "y":
                        break;
                    default:
                        System.out.println("好的!");
                        return;
                }
            }
        }
    }

    /**
       去查询当前商家下的排片
     */
    public static Movie getMovieByName(String movieName){
        Business business = (Business) loginUser;
        List<Movie> movies = ALL_MOVIES.get(business);
        for (Movie movie : movies) {
            if(movie.getName().contains(movieName)) {
                return movie;
            }
        }
        return null;
    }

    /**
      商家进行电影上架
     Map<Business , List<Movie>> ALL_MOVIES
     u1 = [p1,p2,p3]
     u2 = [p1,p2,p3]
     */
    private static void addMovie() {
        System.out.println("================上架电影====================");
        // 根据商家对象(就是登录的用户loginUser),作为Map集合的键 提取对应的值就是其排片信息 :Map<Business , List<Movie>> ALL_MOVIES
        Business business = (Business) loginUser;
        List<Movie> movies = ALL_MOVIES.get(business);

        System.out.println("请您输入新片名:");
        String name  = SYS_SC.nextLine();
        System.out.println("请您输入主演:");
        String actor  = SYS_SC.nextLine();
        System.out.println("请您输入时长:");
        String time  = SYS_SC.nextLine();
        System.out.println("请您输入票价:");
        String price  = SYS_SC.nextLine();
        System.out.println("请您输入票数:");
        String totalNumber  = SYS_SC.nextLine(); // 200\n
        while (true) {
            try {
                System.out.println("请您输入影片放映时间:");
                String stime  = SYS_SC.nextLine();
            // public Movie(String name, String actor, double time, double price, int number, Date startTime)        // 封装成电影对象 ,加入集合movices中去
                Movie movie = new Movie(name, actor ,Double.valueOf(time) , Double.valueOf(price)
                        , Integer.valueOf(totalNumber) ,  sdf.parse(stime));
                movies.add(movie);
                System.out.println("您已经成功上架了:《" + movie.getName() + "》");
                return; // 直接退出去
            } catch (ParseException e) {
                e.printStackTrace();
                LOGGER.error("时间解析出了毛病");
            }
        }
    }

    /**
        定义一个静态的Map集合存储电影的评分
     */
    public static final Map<String , List<Double>> MOVIES_SCORE = new HashMap<>();

    /**
        展示商家的详细:展示当前商家的信息。
     */
    private static void showBusinessInfos() {
        System.out.println("================商家详情界面=================");
        LOGGER.info(loginUser.getUserName() +"商家,正在看自己的详情~~~");
        // 根据商家对象(就是登录的用户loginUser),作为Map集合的键 提取对应的值就是其排片信息 :Map<Business , List<Movie>> ALL_MOVIES
        Business business = (Business) loginUser;
        System.out.println(business.getShopName() + "\t\t电话:" + business.getPhone()
                + "\t\t地址:" + business.getAddress() + "\t\t余额:" + business.getMoney());
        List<Movie> movies = ALL_MOVIES.get(business);      // 拿到商家影片信息
        if(movies.size() > 0) {
            System.out.println("片名\t\t\t主演\t\t时长\t\t评分\t\t票价\t\t余票数量\t\t放映时间");
            for (Movie movie : movies) {
                System.out.println(movie.getName()+"\t\t\t" + movie.getActor()+ "\t\t" + movie.getTime()
                        + "\t\t" + movie.getScore() + "\t\t" + movie.getPrice() + "\t\t" + movie.getNumber() + "\t\t"
                        +   sdf.format(movie.getStartTime()));
            }
        }else {
            System.out.println("您的店铺当前无片在放映~~~~");
        }
    }

    /**
      客户操作界面
     */
    private static void showCustomerMain() {
        while (true) {
            System.out.println("============黑马电影客户界面===================");
            System.out.println(loginUser.getUserName() + (loginUser.getSex()=='男'? "先生":"女士" + "欢迎您进入系统" +
                    "\t余额:" + loginUser.getMoney()));
            System.out.println("请您选择要操作的功能:");
            System.out.println("1、展示全部影片信息功能:");
            System.out.println("2、根据电影名称查询电影信息:");
            System.out.println("3、评分功能:");
            System.out.println("4、购票功能:");
            System.out.println("5、退出系统:");
            System.out.println("请输入您要操作的命令:");
            String command = SYS_SC.nextLine();
            switch (command){
                case "1":
                    showAllMovies();            // 展示全部排片信息
                    break;
                case "2":
                    break;
                case "3":
                    scoreMovie();               // 评分功能
                    showAllMovies();
                    break;
                case "4":
                    buyMovie();                 // 购票功能
                    break;
                case "5":
                    return; // 干掉方法
                default:
                    System.out.println("不存在该命令!!");
                    break;
            }
        }
    }

    private static void scoreMovie() {
        // 1、查询当前登录成功的用户历史购买记录,看哪些电影是它可以评分的。
        Customer c = (Customer) loginUser;
        Map<String, Boolean> movies = c.getBuyMovies();
        if(movies.size() == 0 ){
            System.out.println("当前您没有看过电影,不能评价!");
            return;
        }

        // 买过了 ,看哪些电影是它可以评分的。
        movies.forEach((name, flag) -> {
            if(flag){
                System.out.println(name +"此电影已评价");
            }else {
                System.out.println("请您对:" + name +"进行打分(0-10):");
                double score = Double.valueOf(SYS_SC.nextLine());

                // 先根据电影名称拿到评分数据
                List<Double> scores = MOVIES_SCORE.get(name); // MOVIES_SCORE = [ 名称=[10] , ... ]
                if(scores == null){
                    // 说明此电影是第一次评价
                    scores = new ArrayList<>();
                    scores.add(score);
                    MOVIES_SCORE.put(name , scores);
                }else {
                    scores.add(score);
                }

                movies.put(name, true);
            }
        });
    }

    /**
      用户购票功能  ALL_MOVIES = {b1=[p1,p2,p3,..] , b2=[p2,p3,...]}
     */
    private static void buyMovie() {
        showAllMovies();    // 展示所有影片信息
        System.out.println("=============用户购票功能=================");
        while (true) {
            System.out.println("请您输入需要买票的门店:");
            String shopName = SYS_SC.nextLine();
            Business business = getBusinessByShopName(shopName);    // 1、查询是否存在该商家。
            if(business == null){
                System.out.println("对不起,没有该店铺!请确认");
            }else {
                List<Movie> movies = ALL_MOVIES.get(business);      // 2、此商家全部的排片
                if(movies.size() > 0) {             // 3、判断是否存在上映的电影
                    while (true) {                  // 4、开始进行选片购买
                        System.out.println("请您输入需要购买电影名称:");
                        String movieName = SYS_SC.nextLine();
                        Movie movie = getMovieByShopAndName(business, movieName);       // 去当前商家下,查询该电影对象。
                        if(movie != null){
                            // 开始购买
                            while (true) {
                                System.out.println("请您输入要购买的电影票数:");
                                String number = SYS_SC.nextLine();
                                int buyNumber = Integer.valueOf(number);
                                // 判断电影是否购票
                                if(movie.getNumber() >= buyNumber){
                                    // 可以购买了
                                    // 当前需要花费的金额
                                    double money = BigDecimal.valueOf(movie.getPrice()).multiply(BigDecimal.valueOf(buyNumber))
                                            .doubleValue();
                                    if(loginUser.getMoney() >= money){
                                        // 终于可以买票了
                                        System.out.println("您成功购买了"+ movie.getName() + buyNumber +
                                                 "张票!总金额是:" + money);
                                        // 更新自己的金额 更新商家的金额
                                        loginUser.setMoney(loginUser.getMoney() - money);
                                        business.setMoney(business.getMoney() + money);
                                        movie.setNumber(movie.getNumber() -  buyNumber);

                                        Customer c = (Customer) loginUser;
                                        // 记录购买电影的信息
                                        // 第一个参数是购买的电影,第二个参数是没有评价的标记!
                                        c.getBuyMovies().put(movie.getName(), false);

                                        return;// 结束方法
                                    }else {
                                        // 钱不够!
                                        System.out.println("是否继续~~");
                                        System.out.println("是否继续买票?y/n");
                                        String command = SYS_SC.nextLine();
                                        switch (command) {
                                            case "y":
                                                break;
                                            default:
                                                System.out.println("好的!");
                                                return;
                                        }
                                    }
                                }else {
                                    // 票数不够
                                    System.out.println("您当前最多可以购买:" + movie.getNumber());
                                    System.out.println("是否继续买票?y/n");
                                    String command = SYS_SC.nextLine();
                                    switch (command) {
                                        case "y":
                                            break;
                                        default:
                                            System.out.println("好的!");
                                            return;
                                    }
                                }
                            }

                        }else {
                            System.out.println("电影名称有毛病~~");
                        }
                    }

                }else {
                    System.out.println("该电影院关门了~~~");
                    System.out.println("是否继续买票?y/n");
                    String command = SYS_SC.nextLine();
                    switch (command) {
                        case "y":
                            break;
                        default:
                            System.out.println("好的!");
                            return;
                    }
                }
            }
        }
    }

    public static Movie getMovieByShopAndName(Business business , String name){
        List<Movie> movies = ALL_MOVIES.get(business);
        for (Movie movie : movies) {
            if(movie.getName().contains(name)){
                return movie;
            }
        }
        return null;
    }

    /**
       根据商家店铺名称查询商家对象
     * @return
     */
    public static Business getBusinessByShopName(String shopName){
        Set<Business> businesses = ALL_MOVIES.keySet();
        for (Business business : businesses) {
            if(business.getShopName().equals(shopName)){
                return  business;
            }
        }
        return null;
    }

    /**
      用户功能:展示全部商家和其排片信息
     */
    private static void showAllMovies() {
        System.out.println("=============展示全部商家排片信息=================");
        ALL_MOVIES.forEach((business, movies) -> {
            System.out.println(business.getShopName() + "\t\t电话:" + business.getPhone() + "\t\t地址:" + business.getAddress());
            System.out.println("\t\t\t片名\t\t\t主演\t\t时长\t\t评分\t\t票价\t\t余票数量\t\t放映时间");
            for (Movie movie : movies) {
                System.out.println("\t\t\t" + movie.getName()+"\t\t\t" + movie.getActor()+ "\t\t" + movie.getTime()
                        + "\t\t" + movie.getScore() + "\t\t" + movie.getPrice() + "\t\t" + movie.getNumber() + "\t\t"
                        +   sdf.format(movie.getStartTime()));
            }
        });
    }

    public static User getUserByLoginName(String loginName){
        for (User user : ALL_USERS) {
            // 判断这个用户的登录名称是否是我们想要的
            if(user.getLoginName().equals(loginName)){
                return user;
            }
        }
        return null; // 查询此用户登录名称
    }
}

7:File 类

File类的对象代表操作系统的文件(文件、文件夹),File类在java.io.File包下。
File类提供了诸如:创建文件对象代表文件,获取文件信息(大小、修改时间)、删除文件、创建文件(文件夹)等功能
File类不能读写文件内容

读写文件内容:
  IO流技术,对硬盘中的文件进行读写
File类创建对象:java.io.file下【代表操作系统的文件对象(文件,文件夹)】
  public File​(String pathname)            根据文件路径创建文件对象
  public File​(String parent, String child)    根据父路径名字符串和子路径名字符串创建文件对象
  public File​(File  parent, String child)     根据父路径对应文件对象和子路径名字符串创建文件对象
注意
  File对象可以定位文件和文件夹
  File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。
绝对路径和相对路径
  绝对路径:从盘符开始
    File file1 = new File(“D:\\itheima\\a.txt”); 
  相对路径:不带盘符,默认直接到当前工程下的目录寻找文件。
    File file3 = new File(“模块名\\a.txt”); 
package com.itheima.d1_file;

import java.io.File;

/**
    目标:学会创建File对象定位操作系统的文件(文件 文件夹的)
 */
public class FileDemo {
    public static void main(String[] args) {
        // 1、创建File对象(指定了文件的路径)
        // 路径写法: D:\resources\xueshan.jpeg
        //          D:/resources/xueshan.jpeg
        //          File.separator   系统拼接符【可以跨平台使用】
//        File f = new File("D:\\resources\\xueshan.jpeg");
//        File f = new File("D:/resources/xueshan.jpeg");
        File f = new File("D:" + File.separator+"resources"+ File.separator +"xueshan.jpeg");
        long size = f.length(); // 是文件的字节大小
        System.out.println(size);

        // 2、File创建对象,支持绝对路径 支持相对路径(重点)
        File f1 = new File("D:\\resources\\beauty.jpeg"); // 绝对路径
        System.out.println(f1.length());

        // 相对路径:一般定位模块中的文件的。 相对到工程下!!
        File f2 = new File("file-io-app/src/data.txt");
        System.out.println(f2.length());

        // 3、File创建对象 ,可以是文件也可以是文件夹
        File f3 = new File("D:\\resources");
        System.out.println(f3.exists()); // 判断这个路径是否存在,这个文件夹存在否
    }
}
File类的判断文件类型、获取文件信息功能
  public boolean isDirectory()          判断此路径名表示的File是否为文件夹
  public boolean isFile()            判断此路径名表示的File是否为文件
  public boolean exists()            判断此路径名表示的File是否存在
  public long length()              返回文件的大小(字节数量)  
  public String getAbsolutePath()        返回文件的绝对路径
  public String getPath()            返回定义文件时使用的路径
  public String getName()            返回文件的名称,带后缀
  public long lastModified()          返回文件的最后修改时间(时间毫秒值)
package com.itheima.d1_file;
import java.io.File;
import java.text.SimpleDateFormat;

/**
     目标:File类的获取功能的API
     - public String getAbsolutePath()  :返回此File的绝对路径名字符串。
     - public String getPath()  : 获取创建文件对象的时候用的路径
     - public String getName()  : 返回由此File表示的文件或目录的名称。
     - public long length()  :    返回由此File表示的文件的长度。
 */
public class FileDemo02 {
    public static void main(String[] args) {
        // 1.绝对路径创建一个文件对象
        File f1 = new File("D:/resources/xueshan.jpeg");
        // a.获取它的绝对路径。
        System.out.println(f1.getAbsolutePath());
        // b.获取文件定义的时候使用的路径
        System.out.println(f1.getPath());
        // c.获取文件的名称:带后缀。
        System.out.println(f1.getName());
        // d.获取文件的大小:字节个数。
        System.out.println(f1.length()); // 字节大小
        // e.获取文件的最后修改时间
        long time = f1.lastModified();
        System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time));
        // f、判断文件是文件还是文件夹
        System.out.println(f1.isFile()); // true
        System.out.println(f1.isDirectory()); // false

        System.out.println("-------------------------");

        File f2 = new File("file-io-app\\src\\data.txt");
        // a.获取它的绝对路径。
        System.out.println(f2.getAbsolutePath());
        // b.获取文件定义的时候使用的路径。
        System.out.println(f2.getPath());
        // c.获取文件的名称:带后缀。
        System.out.println(f2.getName());
        // d.获取文件的大小:字节个数。
        System.out.println(f2.length()); // 字节大小
        // e.获取文件的最后修改时间
        long time1 = f2.lastModified();
        System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time1));
        // f、判断文件是文件还是文件夹
        System.out.println(f2.isFile()); // true
        System.out.println(f2.isDirectory()); // false
        System.out.println(f2.exists()); // true

        File file = new File("D:/");
        System.out.println(file.isFile()); // false
        System.out.println(file.isDirectory()); // true
        System.out.println(file.exists()); // true

        File file1 = new File("D:/aaa");
        System.out.println(file1.isFile()); // false
        System.out.println(file1.isDirectory()); // false
        System.out.println(file1.exists()); // false
    }
}
File类创建文件的功能
  public boolean createNewFile()    创建一个新的空的文件
  public boolean mkdir()    只能创建一级文件夹
  public boolean mkdirs()    可以创建多级文件夹
File类删除文件的功能   
public boolean delete​() 删除由此抽象路径名表示的文件或空文件夹 delete方法默认只能删除文件和空文件夹,delete方法直接删除不走回收站
package com.itheima.d1_file;

import java.io.File;
import java.io.IOException;

/**
     目标:File类的创建和删除的方法
     - public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,
              创建一个新的空文件。 (几乎不用的,因为以后文件都是自动创建的!)
     - public boolean delete() :删除由此File表示的文件或目录。 (只能删除空目录)
     - public boolean mkdir() :创建由此File表示的目录。(只能创建一级目录)
     - public boolean mkdirs() :可以创建多级目录(建议使用的)
 */
public class FileDemo03 {
    public static void main(String[] args) throws IOException {
        File f = new File("file-io-app\\src\\data.txt");
        // a.创建新文件,创建成功返回true ,反之 ,不需要这个,以后文件写出去的时候都会自动创建
        System.out.println(f.createNewFile());
        File f1 = new File("file-io-app\\src\\data02.txt");
        System.out.println(f1.createNewFile()); // (几乎不用的,因为以后文件都是自动创建的!)

        // b.mkdir创建一级目录
        File f2 = new File("D:/resources/aaa");
        System.out.println(f2.mkdir());

        // c.mkdirs创建多级目录(重点)
        File f3 = new File("D:/resources/ccc/ddd/eee/ffff");
//        System.out.println(f3.mkdir());
        System.out.println(f3.mkdirs()); // 支持多级创建

        // d.删除文件或者空文件夹
        System.out.println(f1.delete());
        File f4 = new File("D:/resources/xueshan.jpeg");
        System.out.println(f4.delete()); // 占用一样可以删除

        // 只能删除空文件夹,不能删除非空文件夹.
        File f5 = new File("D:/resources/aaa");
        System.out.println(f5.delete());
    }
}
File类的遍历功能
  public String[] list()          获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
  public File[] listFiles()(常用)     获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)

listFiles方法注意事项:
  当文件不存在时或者调用者是一个文件时,返回null
  当文件对象代表一个空文件夹时,返回一个长度为0的数组
  当文件对象是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
  当文件对象是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
  当没有权限访问该文件夹时,返回null
package com.itheima.d1_file;

import java.io.File;
import java.util.Arrays;

/**

    目标:File针对目录的遍历
    - public String[] list():
         获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
    - public File[] listFiles()(常用):
         获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
 */
public class FileDemo04 {
    public static void main(String[] args) {
        // 1、定位一个目录
        File f1 = new File("D:/resources");
        String[] names = f1.list();
        for (String name : names) {
            System.out.println(name);
        }

        // 2.一级文件对象
        // 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
        File[] files = f1.listFiles();
        for (File f : files) {
            System.out.println(f.getAbsolutePath());
        }
        // 注意事项
        File dir = new File("D:/resources/ddd");
        File[] files1 = dir.listFiles();
        System.out.println(Arrays.toString(files1));
    }
}

 8:方法的递归

什么是方法递归?
  递归做为一种算法在程序设计语言中广泛应用。
  方法调用自身的形式称为方法递归( recursion)。
递归的形式
  直接递归:方法自己调用自己。
  间接递归:方法调用其他方法,其他方法又回调方法自己。
方法递归存在的问题?
  递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出现象。
package com.itheima.d2_recusion;
/**
         递归的形式
 */
public class RecursionDemo01 {
    public static void main(String[] args) {
        test2();
    }
    public static void test(){
        System.out.println("=======test被执行========");
        test(); // 方法递归 直接递归形式
    }
    public static void test2(){
        System.out.println("=======test2被执行========");
        test3(); // 方法递归 间接递归
    }
    private static void test3() {
        System.out.println("=======test3被执行========");
        test2();
    }
}

9:方法递归的应用、执行流程、递归算法的三个核心要素

package com.itheima.d2_recusion;

/**
      目标:递归的算法和执行流程
 */
public class RecursionDemo02 {
    public static void main(String[] args) {
        System.out.println(f(5));
    }

    public static int f(int n){
        if(n == 1){
            return 1;
        }else {
            return f(n - 1) * n;
        }
    }
}
递归解决问题思路:
    把一个复杂的问题层层转换成一个与原问题相似的较小规模问题来求解
递归算法三要素大体可以总结为:
    递归的公式: f(n) =  f(n-1) * n;
    递归的终结点:f(1) 
    递归的方向必须走向终结点:
    f(5) =  f(4) * 5
    f(4) =  f(3) * 4
    f(3) =  f(2) * 3
    f(2) =  f(1) * 2
    f(1) =  1

package com.itheima.d2_recusion;

/**
      目标:1 - n求和
 */
public class RecursionDemo03 {
    public static void main(String[] args) {
        System.out.println(f(5));
    }
    public static int f(int n){
        if(n == 1){
            return 1;
        }else {
            return f(n - 1)  + n;
        }
    }
}

10:递归经典问题

公式:
  f(n) - f(x)/2 -1 = f(x+1)
  f(n) = 2f(x+1)+ 2
f(10) = 1  终结点
f(1)
package com.itheima.d2_recusion;

/**
      目标 猴子吃桃。

     公式(合理的): f(x) - f(x)/2 - 1 = f(x+1)
                   2f(x) - f(x) - 2 = 2f(x + 1)
                   f(x) = 2f(x + 1) + 2

    求f(1) = ?
    终结点: f(10) = 1
    递归的方向:合理的
 */
public class RecursionDemo04 {
    public static void main(String[] args) {
        System.out.println(f(1));
        System.out.println(f(2));
        System.out.println(f(3));
    }
    public static int f(int n){
        if(n == 10){
            return 1;
        }else {
            return 2 * f(n + 1) + 2;
        }
    }
}

11:递归 解决 文件搜索

在上述的案例中递归算法都是针对存在规律化的递归问题。
有很多问题是非规律化的递归问题,比如文件搜索。如何解决?


package com.itheima.d2_recusion;

import java.io.File;
import java.io.IOException;

/**
    目标:去D判断搜索 eDiary.exe文件
 */
public class RecursionDemo05 {
    public static void main(String[] args) {
        // 2、传入目录 和  文件名称
        searchFile(new File("D:/") , "eDiary.exe");
    }

    /**
     * 1、搜索某个目录下的全部文件,找到我们想要的文件。
     * @param dir  被搜索的源目录
     * @param fileName 被搜索的文件名称
     */
    public static void searchFile(File dir,String fileName){
        // 3、判断dir是否是目录
        if(dir != null && dir.isDirectory()){
            // 可以找了
            // 4、提取当前目录下的一级文件对象
            File[] files = dir.listFiles(); // null  []
            // 5、判断是否存在一级文件对象,存在才可以遍历
            if(files != null && files.length > 0) {
                for (File file : files) {
                    // 6、判断当前遍历的一级文件对象是文件 还是 目录
                    if(file.isFile()){
                        // 7、是不是咱们要找的,是把其路径输出即可
                        if(file.getName().contains(fileName)){
                            System.out.println("找到了:" + file.getAbsolutePath());
                            // 启动它。
                            try {
                                Runtime r = Runtime.getRuntime();
                                r.exec(file.getAbsolutePath());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }else {
                        // 8、是文件夹,需要继续递归寻找
                        searchFile(file, fileName);
                    }
                }
            }
        }else {
            System.out.println("对不起,当前搜索的位置不是文件夹!");
        }
    }
}

package com.itheima.d2_recusion;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

/**
    目标:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,
        请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
        答案:15瓶 3盖子 1瓶子

 */
public class RecursionDemo06 {

    // 定义一个静态的成员变量用于存储可以买的酒数量
    public static int totalNumber; // 总数量
    public static int lastBottleNumber; // 记录每次剩余的瓶子个数
    public static int lastCoverNumber; // 记录每次剩余的盖子个数


    public static void main(String[] args) {
        // 1、拿钱买酒
        buy(10);
        System.out.println("总数:" + totalNumber);
        System.out.println("剩余盖子数:" + lastCoverNumber);
        System.out.println("剩余瓶子数:" + lastBottleNumber);
    }

    public static void buy(int money){
        // 2、看可以立马买多少瓶
        int buyNumber = money / 2; // 5
        totalNumber += buyNumber;

        // 3、把盖子 和瓶子换算成钱
        // 统计本轮总的盖子数  和 瓶子数
        int coverNumber = lastCoverNumber + buyNumber;
        int bottleNumber = lastBottleNumber + buyNumber;

        // 统计可以换算的钱
        int allMoney = 0;
        if(coverNumber >= 4){
            allMoney += (coverNumber / 4) * 2;
        }
        lastCoverNumber = coverNumber % 4;

        if(bottleNumber >= 2){
            allMoney += (bottleNumber / 2) * 2;
        }
        lastBottleNumber = bottleNumber % 2;
        if(allMoney >= 2){
            buy(allMoney);
        }
        Integer[] arr2 = new Integer[]{11, 22, 33};
        Arrays.sort(arr2);
    }
}

12:递归 删除非空文件夹

 

package com.itheima.d2_recusion;

import java.io.File;

/**
    目标:删除非空文件夹
 */
public class RecursionDemo07 {
    public static void main(String[] args) {
        deleteDir(new File("D:/new"));
    }

    /**
       删除文件夹,无所谓里面是否有内容,都可以删除
     * @param dir
     */
    public static void deleteDir(File dir){
        // 1、判断dir存在且是文件夹
        if(dir != null && dir.exists() && dir.isDirectory()){
            // 2、提取一级文件对象。
            File[] files = dir.listFiles();
            // 3、判断是否存在一级文件对象,存在则遍历全部的一级文件对象去删除
            if(files != null && files.length > 0){
                // 里面有内容
                for (File file : files) {
                    // 4、判断file是文件还是文件夹,文件直接删除
                    if(file.isFile()){
                        file.delete();
                    }else {
                        // 递归删除
                        deleteDir(file);
                    }
                }
            }
            // 删除自己
            dir.delete();
        }
    }
}

 

13:字符集

字符集基础知识:
  字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有
  ASCII字符集
  GBK字符集
  Unicode(UTF-8)字符集等。
ASCII字符集:  1个字节
  ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。
  ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于表示英文、数字来说是够用的

01100001‬ = 97  => a
‭01100010‬ = 98  => b
GBK:  2个字节【英文还是1个字节存储】   GBK 是中国的码表,包含了几万个汉字等字符,同时也要兼容ASCII编码,   GBK 编码中一个中文字符一般以两个字节的形式存储。 Unicode字符集:   统一码,也叫万国码。是计算机科学领域里的一项业界标准。   UTF
-8是Unicode的一种常见编码方式。 注意   UTF-8 编码后一个中文一般以三个字节的形式存储,同时也要兼容ASCII编码表。   技术人员都应该使用UTF-8的字符集编码
unicode是万国码,以utf-8编码后一个中文一般是三个字节的形式存储
utf-8也需要兼容ASCLL编码
技术人员应该使用utf-8的字符集编码
编码钱和编码后的字符集需要一致,否则会出现中文乱码

常见字符集底层字符的编码是什么样的?
  英文和数字等在任何国家的字符集中都占1个字节
  GBK字符中一个中文字符占2个字节
  UTF-8编码中一个中文1般占3个字节
编码前的字符集和解码时的字符集有什么要求?
  必须一致,否则会出现字符乱码
  英文和数字不会乱码

 14:字符集的编码,解码操作

String编码
  byte[] getBytes​()    使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
  byte[] getBytes​(String charsetName)    使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 
String解码
  String​(byte[] bytes)    通过使用平台的默认字符集解码指定的字节数组来构造新的 String
  String​(byte[] bytes, String charsetName)    通过指定的字符集解码指定的字节数组来构造新的 String

如何使用程序对字符进行编码?
  String类下的方法:
    byte[] getBytes​():默认编码
    byte[] getBytes​(String charsetName):指定编码
如何使用程序进行解码?
  String类的构造器:
    String​(byte[] bytes):使用默认编码解码
    String​(byte[] bytes, String charsetName)):指定编码解码
英文字节一般都是正数,中文字节一般是负数,看到是负数计算机就会把3/2个字节拼接成一个字符
package com.itheima.d3_charset;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

/**
     目标:学会自己进行文字的编码和解码,为以后可能用到的场景做准备。
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、编码:把文字转换成字节(使用指定的编码)
        String name = "abc我爱你中国";
        // byte[] bytes = name.getBytes(); // 以当前代码默认字符集进行编码 (UTF-8)
        byte[] bytes = name.getBytes("GBK"); // 指定编码    返回成字节数组
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));

        // 2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码 )
        // String rs = new String(bytes); // 默认的UTF-8
        String rs = new String(bytes, "GBK"); // 指定GBK解码
        System.out.println(rs);
    }
}

 15:IO流概叙

IO流概述
  I表示intput,把硬盘文件中的数据读入到内存的过程,称之输入,负责读。
  O表示output,把内存中的数据写出到硬盘文件的过程,称之输出,负责写


IO流的作用?
  读写文件数据的
IO流是怎么划分的,大体分为几类,各自的作用?
  字节输入流 InputStream(读字节数据的)
  字节输出流 OutoutStream(写字节数据出去的)
  字符输入流 Reader(读字符数据的)
  字符输出流 Writer(写字符数据出去的)

 16:字节输入流    InputStream【抽象类】  FileInputStream【实现类】

文件字节输入流:FileInputStream
  作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

构造器
  public FileInputStream​(File file)        创建字节输入流管道与源文件对象接通  【参数:对象】
  public FileInputStream​(String pathname)    创建字节输入流管道与源文件路径接通  【参数:文件路径】
常用方法
  public int read()    每次读取一个字节返回,如果字节已经没有可读的返回-1
  public int read(byte[] buffer)    每次读取一个字节数组返回,如果字节已经没有可读的返回-1
package com.itheima.d4_byte_stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
    目标:字节输入流的使用。

    IO流的体系:
                 字节流                                   字符流
        字节输入流            字节输出流               字符输入流        字符输出流
        InputStream          OutputStream           Reader           Writer  (抽象类)
        FileInputStream      FileOutputStream       FileReader       FileWriter(实现类,可以使用的)

    文件字节输入流:FileInputStream
        -- 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。
                按照字节读文件数据到内存中。
        -- 构造器:
            public FileInputStream(File file):创建字节输入流管道与源文件对象接通
            public FileInputStream(String pathname):创建字节输入流管道与源文件路径接通。
        -- 方法:
            public int read(): 每次读取一个字节返回,读取完毕返回-1。

    小结:
        一个一个字节读取中文数据输出其实是被淘汰的,性能极差!
         一个一个字节读取中文数据输出,会出现截断中文字节的情况,无法避免读取中文输出乱码的问题。

 */
public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通。
        // InputStream is = new FileInputStream(new File("file-io-app\\src\\data.txt"));
        // 简化写法
        InputStream is = new FileInputStream("file-io-app\\src\\data.txt");

        // 2、读取一个字节返回 (每次读取一滴水)  【1个个字节读取返回整数,int类型范围 4个字节,返回字节的整数编号】
//        int b1 = is.read();
//        System.out.println((char)b1);
//
//        int b2 = is.read();
//        System.out.println((char)b2);
//
//        int b3 = is.read();
//        System.out.println((char)b3);
//
//        int b4 = is.read(); // 读取完毕返回-1
//        System.out.println(b4);

        // 3、使用循环改进
        // 定义一个变量记录每次读取的字节    a  b  3   爱
        //                              o o  o   [ooo]
        int b;
        while (( b = is.read() ) != -1){
            System.out.print((char) b);
        }
    }
}

17:字节输入流  每次读取一个字节数组

文件字节输入流:FileInputStream
  作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。
    public int read()              每次读取一个字节返回,如果字节已经没有可读的返回-1
    public int read(byte[] buffer)        每次使用字节数组来读取数据,返回读取的字节个数,如果没有可读返回-1
读取性能得到了优化
还是无法避免读取字符的时候中文乱码问题【不确定读取几个前面中英文间隔】
package com.itheima.d4_byte_stream;
import java.io.FileInputStream;
import java.io.InputStream;
/**
   目标:使用文件字节输入流每次读取一个字节数组的数据。
 */
public class FileInputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app/src/data02.txt");

        // 2、定义一个字节数组,用于读取字节数组  读取的数据就会往buffer这个桶里面流,返回流进去的个数3,或者2,因为最后一桶水可能装不满
//        byte[] buffer = new byte[3]; // 3B 
//        int len = is.read(buffer);
//        System.out.println("读取了几个字节:" + len);
//        String rs = new String(buffer);    // 读取的字节数组内容,使用String 解码
//        System.out.println(rs);
//
//        int len1 = is.read(buffer);
//        System.out.println("读取了几个字节:" + len1);
//        String rs1 = new String(buffer);
//        System.out.println(rs1);
//        // buffer = [a b c]
//
//        // buffer = [a b c]  ==>  [c d c]
//        int len2 = is.read(buffer);
//        System.out.println("读取了几个字节:" + len2);
//        // 读取多少倒出多少      使用字节数组,最后一桶水只读了两滴,第三滴被上一次读取的内容覆盖,全部读出来有问题,那么读多少字节那么就导出来多少字节解决这个问题
//        String rs2 = new String(buffer,0 ,len2);
//        System.out.println(rs2);
//
//        int len3 = is.read(buffer);
//        System.out.println(len3); // 读取完毕返回-1

        // 3、改进使用循环,每次读取一个字节数组
        byte[] buffer = new byte[3];
        int len; // 记录每次读取的字节数。
        while ((len = is.read(buffer)) != -1) {
            // 读取多少倒出多少
            System.out.print(new String(buffer, 0 , len));
        }
    }
}

18:字节输入流  一次性读取完全部字节

1、使用字节流读取中文输出乱码,如何使用字节输入流读取中文输出不乱码呢?
  定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。
2、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
  如果文件过大,字节数组可能引起内存溢出
方式一
  自己定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成
    public int read(byte[] buffer)    每次读取一个字节数组返回,如果字节已经没有可读的返回-1
方式二
  官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中
    public byte[] readAllBytes() throws IOException    直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回
    不支持读大文件,
1、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?   如果文件过大,字节数组可能引起内存溢出。 如何使用字节输入流读取中文内容输出不乱码呢?   一次性读取完全部字节。 可以定义与文件一样大的字节数组读取,也可以使用官方API.   直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?   如果文件过大,定义的字节数组可能引起内存溢出。
package com.itheima.d4_byte_stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
   目标:使用文件字节输入流一次读完文件的全部字节。可以解决乱码问题。
 */
public class FileInputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        File f = new File("file-io-app/src/data03.txt");
        InputStream is = new FileInputStream(f);

        // 2、定义一个字节数组与文件的大小刚刚一样大。
//        byte[] buffer = new byte[(int) f.length()];
//        int len = is.read(buffer);
//        System.out.println("读取了多少个字节:" + len);
//        System.out.println("文件大小:" + f.length());
//        System.out.println(new String(buffer));

        // 读取全部字节数组
        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));

    }
}

19:字节输出流  写数据  OutputStream

文件字节输出流:FileOutputStream
作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流。
构造器:
  public FileOutputStream​(File file)                  创建字节输出流管道与源文件对象接通
  public FileOutputStream​(File file,boolean append)        创建字节输出流管道与源文件对象接通,可追加数据
  public FileOutputStream​(String filepath)              创建字节输出流管道与源文件路径接通
  public FileOutputStream​(String filepath,boolean append)    创建字节输出流管道与源文件路径接通,可追加数据
文件字节输出流(FileOutputStream)写数据出去的API
  public void write(int a)                          写一个字节出去
  public void write(byte[] buffer)                      写一个字节数组出去
  public void write(byte[] buffer , int pos , int len)          写一个字节数组的一部分出去。
流的关闭与刷新 
  flush()        刷新流,还可以继续写数据
  close()       用完之后关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
package com.itheima.d4_byte_stream;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
    目标:字节输出流的使用。

    IO流的体系:
            字节流                                   字符流
    字节输入流           字节输出流               字符输入流       字符输出流
    InputStream         OutputStream           Reader         Writer     (抽象类)
    FileInputStream     FileOutputStream       FileReader     FileWriter (实现类)

    a.FileOutputStream文件字节输出流。
        -- 作用:以内存为基准,把内存中的数据,按照字节的形式写出到磁盘文件中去。
                 简单来说,把内存数据按照字节写出到磁盘文件中去。
        -- 构造器:
            public FileOutputStream(File file):创建一个字节输出流管道通向目标文件对象。
            public FileOutputStream(String file):创建一个字节输出流管道通向目标文件路径。
            public FileOutputStream(File file , boolean append):创建一个追加数据的字节输出流管道通向目标文件对象。
            public FileOutputStream(String file , boolean append):创建一个追加数据的字节输出流管道通向目标文件路径。
        -- 方法:
           public void write(int a):写一个字节出去 。
           public void write(byte[] buffer):写一个字节数组出去。
           public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
                           参数一,字节数组;参数二:起始字节索引位置,参数三:写多少个字节数出去。
    小结:
        记住。
        换行:  os.write("\r\n".getBytes()); // 换行
        追加数据管道: OutputStream os = new FileOutputStream("day10_demo/out01.txt" , true); // 追加管道!!
 */
public class OutputStreamDemo04 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/out04.txt" , true); // 追加数据管道
//        OutputStream os = new FileOutputStream("file-io-app/src/out04.txt"); // 先清空之前的数据,写新数据进入【覆盖管道】

        // 2、写数据出去
        // a.public void write(int a):写一个字节出去
        os.write('a');
        os.write(98);
        os.write("\r\n".getBytes()); // 换行  \r\n  换行,不能直接写字符串,需要转成字节数组
        // os.write('徐'); // [ooo]  默认只能写一个字节,但是 中文占用 3 个字节,写进乱码

        // b.public void write(byte[] buffer):写一个字节数组出去。
        byte[] buffer = {'a' , 97, 98, 99};
        os.write(buffer);    // 写数据一定要刷新数据,不然可能在缓存中
        os.write("\r\n".getBytes()); // 换行

        byte[] buffer2 = "我是中国人".getBytes();    // 中文编码成字节数组,然后再写字节数组
//        byte[] buffer2 = "我是中国人".getBytes("GBK");
        os.write(buffer2);
        os.write("\r\n".getBytes()); // 换行

        // c. public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
        byte[] buffer3 = {'a',97, 98, 99};
        os.write(buffer3, 0 , 3);
        os.write("\r\n".getBytes()); // 换行

        // os.flush(); // 写数据必须,刷新数据 可以继续使用流
        os.close(); // 释放资源,包含了刷新的!关闭后流不可以使用了
    }
}

20:文件拷贝

package com.itheima.d4_byte_stream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
 *   目标:学会使用字节流完成文件的复制(支持一切文件类型的复制)
 */
public class CopyDemo05 {
    public static void main(String[] args) {
        try {
            // 1、创建一个字节输入流管道与原视频接通
            InputStream is = new FileInputStream("file-io-app/src/out04.txt");
            // 2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

            // 4、关闭流。
            os.close();
            is.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

字节流适合做一切文件数据的拷贝吗?
  任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式、编码一致没有任何问题。

21:IO流:资源释放的方式

try-catch-finally
  finally:放在try-catch后面的,无论是正常执行还是异常执行代码,最后一定要执行,除非JVM退出。【就算try 里有 return 也会先执行 finally】
  作用:一般用于进行最后的资源释放操作(专业级做法)

package com.itheima.d5_resource;

import java.io.*;

/**
 *   目标:学会使用finally释放资源。
 */
public class TryCatchFinallyDemo1 {
    public static void main(String[] args) {
        InputStream is = null;    // 需要这里定义 finally 才能访问 这个变量
        OutputStream os = null;
        try {
            // System.out.println(10/ 0);
            // 1、创建一个字节输入流管道与原视频接通
             is = new FileInputStream("file-io-app/src/out04.txt");
            // 2、创建一个字节输出流管道与目标文件接通
             os = new FileOutputStream("file-io-app/src/out05.txt");
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

         //   System.out.println( 10 / 0);

        } catch (Exception e){
            e.printStackTrace();
        } finally {
            // 无论代码是正常结束,还是出现异常都要最后执行这里
            System.out.println("========finally=========");
            try {
                // 4、关闭流。
                if(os!=null)os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.out.println(test(10, 2));
    }

    public static int test(int a , int b){
        try {
            int c = a / b;
            return c;
        }catch (Exception e){
            e.printStackTrace();
            return -111111; // 计算出现bug.
        }finally {
            System.out.println("--finally--");
            // 哪怕上面有return语句执行,也必须先执行完这里才可以!
            // 开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
            return 100;
        }
    }
}
1. finally虽然可以用于释放资源,但是释放资源的代码过于繁琐?
2. 有没有办法简化?
  try-catch-resource

JDK 7和JDK9中都简化了资源释放操作

JDK 7 以及 JDK 9的()中只能放置资源对象,否则报错
什么是资源呢?
资源都是实现了Closeable/AutoCloseable接口的类对象
  public abstract class InputStream implements Closeable {}
  public abstract class OutputStream implements Closeable, Flushable{} 
jdk 7 资源释放改进方案
package com.itheima.d5_resource;

import java.io.*;

/**
 *   目标:学会使用JDK 7的新方式释放资源
 */
public class TryCatchResouceDemo2 {
    public static void main(String[] args) {

        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
                // 1、创建一个字节输入流管道与原视频接通
               InputStream is = new FileInputStream("file-io-app/src/out04.txt");
                // 2、创建一个字节输出流管道与目标文件接通
               OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");

               // int age = 23; // 这里只能放资源,放变量报错【实现了 autoCloseable 接口才能认为是资源,可以放这里】
                MyConnection connection = new MyConnection(); // 最终会自动调用资源的close方法
                ) {

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

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

class MyConnection implements AutoCloseable{
    @Override
    public void close() throws IOException {
        System.out.println("连接资源被成功释放了!");
    }
}
jdk 9 资源释放改进方案:和jdk7类似,但是括号内不需要定义流对象,外部定义就行,只需要把对象名称写到括号里
  但是两个管道也需要try catch,try catch之后变量又访问不了,所以方法把异常跑出去就行,如下    
  好处:is 和 os 后续还可以使用这两个变量,但是try catch 结束这两个流变量也是有不了,已经调用了 close 方法
  坏处:用的少,不建议使用,使用jdk 7 方式
package com.itheima.d5_resource;

import java.io.*;

/**
 *   目标:JDK 9释放资源的方式:可以了解下。
 */
public class TryCatchResouceDemo3 {
    public static void main(String[] args) throws Exception {

        // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
        // 1、创建一个字节输入流管道与原视频接通
        InputStream is = new FileInputStream("file-io-app/src/out04.txt");
        // 2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");
        try ( is ; os ) {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

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

 21:字符流的使用  Reader【FileReader】  Writer【FileWriter】

字符流读取中文
    读少了乱码,全部读取内存溢出
读取中文输出,使用字符流
  最小的单位按照单个字符读取的【读取1个10个字符】
文件字符输入流:Reader
作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去
构造器:
  public FileReader​(File file)    创建字符输入流管道与源文件对象接通
  public FileReader​(String pathname)    创建字符输入流管道与源文件路径接通
方法:    
  public int read()    每次读取一个字符返回,如果字符已经没有可读的返回-1
  public int read(char[] buffer)    每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1
字符流的好处。每次读取一个字符存在什么问题?
  读取中文字符不会出现乱码(如果代码文件编码一致)
  性能较慢
package com.itheima.d6_char_stream;  // 每次读取一个

import java.io.File;
import java.io.FileReader;
import java.io.Reader;
/**
     目标:字符输入流的使用。

     IO流的体系:
            字节流                                   字符流
     字节输入流           字节输出流               字符输入流       字符输出流
     InputStream         OutputStream            Reader         Writer     (抽象类)
     FileInputStream     FileOutputStream        FileReader     FileWriter (实现类)

     c.FileReader:文件字符输入流。
         -- 作用:以内存为基准,把磁盘文件的数据以字符的形式读入到内存。
            简单来说,读取文本文件内容到内存中去。

         -- 构造器:
            public FileReader(File file):创建一个字符输入流与源文件对象接通。
            public FileReader(String filePath):创建一个字符输入流与源文件路径接通。

         -- 方法:
            public int read(): 读取一个字符的编号返回! 读取完毕返回-1
            public int read(char[] buffer):读取一个字符数组,读取多少个字符就返回多少个数量,读取完毕返回-1
     小结:
        字符流一个一个字符的读取文本内容输出,可以解决中文读取输出乱码的问题。
        字符流很适合操作文本文件内容。
        但是:一个一个字符的读取文本内容性能较差!!
 */
public class FileReaderDemo01 {
    public static void main(String[] args) throws Exception {
        // 目标:每次读取一个字符。
        // 1、创建一个字符输入流管道与源文件接通
        Reader fr = new FileReader("file-io-app\\src\\data06.txt");

        // 2、读取一个字符返回,没有可读的字符了返回-1
//        int code = fr.read();
//        System.out.print((char)code);
//
//        int code1 = fr.read();
//        System.out.print((char)code1);

        // 3、使用循环读取字符
        int code;
        while ((code = fr.read()) != -1){
            System.out.print((char) code);
        }
    }
}
字符输入流- 一次读取一个字符数组
文件字符输入流:FileReader
作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。
  public int read()    每次读取一个字符返回,如果字符已经没有可读的返回-1
  public int read(char[] buffer)    每次读取一个字符数组,返回读取的字符数,如果字符已经没有可读的返回-1

每次读取一个字符数组的优势?
  读取的性能得到了提升
  读取中文字符输出不会乱码
package com.itheima.d6_char_stream;

import java.io.FileReader;
import java.io.Reader;

/**
     目标:字符输入流的使用-按照字符数组读取。

     IO流的体系:
            字节流                                       字符流
     字节输入流           字节输出流               字符输入流       字符输出流
     InputStream         OutputStream           Reader         Writer     (抽象类)
     FileInputStream     FileOutputStream       FileReader     FileWriter (实现类)

     c.FileReader:文件字符输入流。
         -- 作用:以内存为基准,把磁盘文件的数据以字符的形式读入到内存。
            简单来说,读取文本文件内容到内存中去。
         -- 构造器:
            public FileReader(File file):创建一个字符输入流与源文件对象接通。
            public FileReader(String filePath):创建一个字符输入流与源文件路径接通。
         -- 方法:
            public int read(): 读取一个字符的编号返回! 读取完毕返回-1
            public int read(char[] buffer):读取一个字符数组,
                    读取多少个字符就返回多少个数量,读取完毕返回-1
     小结:
         字符流按照字符数组循环读取数据,可以解决中文读取输出乱码的问题,而且性能也较好!!
 */
public class FileReaderDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字符输入流与源文件接通
        Reader fr = new FileReader("file-io-app/src/data07.txt");

        // 2、用循环,每次读取一个字符数组的数据。  1024 + 1024 + 8
        char[] buffer = new char[1024]; // 1K字符
        int len;
        while ((len = fr.read(buffer)) != -1) {
            String rs = new String(buffer, 0, len);
            System.out.print(rs);
        }
    }
}
文件字符输出流:FileWriter
作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流。
构造器:
  public FileWriter(File file)    创建字符输出流管道与源文件对象接通
  public FileWriter​(File file,boolean append)    创建字符输出流管道与源文件对象接通,可追加数据
  public FileWriter​(File file,boolean append)    创建字符输出流管道与源文件路径接通
  public FileWriter​(String filepath,boolean append)    创建字符输出流管道与源文件路径接通,可追加数据

文件字符输出流(FileWriter)写数据出去的API
  方法:
  void write​(int c)    写一个字符
  void write​(char[] cbuf)    写入一个字符数组
  void write​(char[] cbuf, int off, int len)    写入字符数组的一部分
  void write​(String str)    写一个字符串
  void write​(String str, int off, int len)    写一个字符串的一部分

流的关闭与刷新 
  flush()    刷新流,还可以继续写数据
  close()    关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
package com.itheima.d6_char_stream;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;

/**
     目标:字符输出流的使用。

     IO流的体系:
            字节流                                   字符流
     字节输入流           字节输出流               字符输入流       字符输出流
     InputStream         OutputStream           Reader         Writer     (抽象类)
     FileInputStream     FileOutputStream       FileReader     FileWriter (实现类)

     d.FileWriter文件字符输出流的使用。
        -- 作用:以内存为基准,把内存中的数据按照字符的形式写出到磁盘文件中去。
            简单来说,就是把内存的数据以字符写出到文件中去。
        -- 构造器:
           public FileWriter(File file):创建一个字符输出流管道通向目标文件对象。
           public FileWriter(String filePath):创建一个字符输出流管道通向目标文件路径。
           public FileWriter(File file,boolean append):创建一个追加数据的字符输出流管道通向目标文件对象。
           public FileWriter(String filePath,boolean append):创建一个追加数据的字符输出流管道通向目标文件路径。
        -- 方法:
             a.public void write(int c):写一个字符出去
             b.public void write(String c)写一个字符串出去:
             c.public void write(char[] buffer):写一个字符数组出去
             d.public void write(String c ,int pos ,int len):写字符串的一部分出去
             e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
     小结:
        字符输出流可以写字符数据出去,总共有5个方法写字符。
        覆盖管道:
             Writer fw = new FileWriter("Day10Demo/src/dlei03.txt"); // 覆盖数据管道
        追加数据管道:
             Writer fw = new FileWriter("Day10Demo/src/dlei03.txt",true); // 追加数据管道
        换行:
             fw.write("\r\n"); // 换行
        结论:读写字符文件数据建议使用字符流。复制文件建议使用字节流。
 */
public class FileWriterDemo03 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字符输出流管道与目标文件接通
        // Writer fw = new FileWriter("file-io-app/src/out08.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
        Writer fw = new FileWriter("file-io-app/src/out08.txt", true); // 追加管道

//      a.public void write(int c):写一个字符出去
        fw.write(98);
        fw.write('a');
        fw.write('徐'); // 不会出问题了
        fw.write("\r\n"); // 换行

//       b.public void write(String c)写一个字符串出去
        fw.write("abc我是中国人");
        fw.write("\r\n"); // 换行

//       c.public void write(char[] buffer):写一个字符数组出去
        char[] chars = "abc我是中国人".toCharArray();
        fw.write(chars);
        fw.write("\r\n"); // 换行

//       d.public void write(String c ,int pos ,int len):写字符串的一部分出去
        fw.write("abc我是中国人", 0, 5);
        fw.write("\r\n"); // 换行

//       e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
        fw.write(chars, 3, 5);
        fw.write("\r\n"); // 换行

        // fw.flush();// 刷新后流可以继续使用
        fw.close(); // 关闭包含刷线,关闭后流不能使用
    }
}

 22:缓存流

缓冲流概述
  缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。
  作用:缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能

23:字节缓存流  BufferedInputStream        BufferedOutputStream

字节缓冲流:
  字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能
  字节缓冲输出流:BufferedOutputStream,提高字节输出流读取数据的性能。
构造器:包装原始管道
  public BufferedInputStream​(InputStream is)    可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能
  public BufferedOutputStream​(OutputStream os)    可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能
package com.itheima.d1_byte_buffer;

import java.io.*;

/**
    目标:使用字节缓冲流完成数据的读写操作。
 */
public class ByteBufferDemo {
    public static void main(String[] args) {
        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
                // 1、创建一个字节输入流管道与原视频接通
                InputStream is = new FileInputStream("D:\\resources\\newmeinv.jpeg");
                // a.把原始的字节输入流包装成高级的缓冲字节输入流
                InputStream bis = new BufferedInputStream(is);
                // 2、创建一个字节输出流管道与目标文件接通
                OutputStream os = new FileOutputStream("D:\\resources\\newmeinv222.jpeg");
                // b.把字节输出流管道包装成高级的缓冲字节输出流管道
                OutputStream bos = new BufferedOutputStream(os);
        ) {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

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

缓存管道和普通管道读写性能对比

package com.itheima.d2_byte_buffer_time;

import java.io.*;
/**
    目标:利用字节流的复制统计各种写法形式下缓冲流的性能执行情况。

    复制流:
        (1)使用低级的字节流按照一个一个字节的形式复制文件。
        (2)使用低级的字节流按照一个一个字节数组的形式复制文件。
        (3)使用高级的缓冲字节流按照一个一个字节的形式复制文件。
        (4)使用高级的缓冲字节流按照一个一个字节数组的形式复制文件。

    源文件:C:\course\3-视频\18、IO流-文件字节输出流FileOutputStream写字节数据出去.avi
    目标文件:C:\course\
    小结:
        使用高级的缓冲字节流按照一个一个字节数组的形式复制文件,性能好,建议开发使用!
 */
public class ByteBufferTimeDemo {
    private static final String SRC_FILE = "D:\\course\\基础加强\\day08-日志框架、阶段项目\\视频\\14、用户购票功能.avi";
    private static final String DEST_FILE = "D:\\course\\";

    public static void main(String[] args) {
        // copy01(); // 使用低级的字节流按照一个一个字节的形式复制文件:慢的让人简直无法忍受。直接被淘汰。
        copy02(); // 使用低级的字节流按照一个一个字节数组的形式复制文件: 比较慢,但是还是可以忍受的!
        // copy03(); // 缓冲流一个一个字节复制:很慢,不建议使用。
        copy04(); // 缓冲流一个一个字节数组复制:飞快,简直太完美了(推荐使用)
    }
    private static void copy04() {
        long startTime = System.currentTimeMillis();
        try (
                // 1、创建低级的字节输入流与源文件接通
                InputStream is = new FileInputStream(SRC_FILE);
                // a.把原始的字节输入流包装成高级的缓冲字节输入流
                InputStream bis = new BufferedInputStream(is);
                // 2、创建低级的字节输出流与目标文件接通
                OutputStream os = new FileOutputStream(DEST_FILE + "video4.avi");
                // b.把字节输出流管道包装成高级的缓冲字节输出流管道
                OutputStream bos = new BufferedOutputStream(os);
        ) {

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0 , len);
            }

        } catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("使用缓冲的字节流按照一个一个字节数组的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s");
    }

    private static void copy03() {
        long startTime = System.currentTimeMillis();
        try (
                // 1、创建低级的字节输入流与源文件接通
                InputStream is = new FileInputStream(SRC_FILE);
                // a.把原始的字节输入流包装成高级的缓冲字节输入流
                InputStream bis = new BufferedInputStream(is);
                // 2、创建低级的字节输出流与目标文件接通
                OutputStream os = new FileOutputStream(DEST_FILE + "video3.avi");
                // b.把字节输出流管道包装成高级的缓冲字节输出流管道
                OutputStream bos = new BufferedOutputStream(os);
        ){

            // 3、定义一个变量记录每次读取的字节(一个一个字节的复制)
            int b;
            while ((b = bis.read()) != -1){
                bos.write(b);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("使用缓冲的字节流按照一个一个字节的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s");
    }

    private static void copy02() {
        long startTime = System.currentTimeMillis();
        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
                // 1、创建一个字节输入流管道与原视频接通
                InputStream is = new FileInputStream(SRC_FILE);
                // 2、创建一个字节输出流管道与目标文件接通
                OutputStream os = new FileOutputStream(DEST_FILE + "video2.avi")
        ) {

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("使用低级的字节流按照一个一个字节数组的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s");
    }

    /**
      使用低级的字节流按照一个一个字节的形式复制文件
     */
    private static void copy01() {
        long startTime = System.currentTimeMillis();
        try (
                // 1、创建低级的字节输入流与源文件接通
                InputStream is = new FileInputStream(SRC_FILE);
                // 2、创建低级的字节输出流与目标文件接通
                OutputStream os = new FileOutputStream(DEST_FILE + "video1.avi")
                ){

            // 3、定义一个变量记录每次读取的字节(一个一个字节的复制)
            int b;
            while ((b = is.read()) != -1){
                os.write(b);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("使用低级的字节流按照一个一个字节的形式复制文件耗时:" + (endTime - startTime)/1000.0 + "s");
    }
}
推荐使用哪种方式提高字节流读写数据的性能?
  建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前来看是性能最优的组合。

23:字符缓存流  BufferedReader       BufferedWriter

字符缓冲输入流
  字符缓冲输入流:BufferedReader。
  作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能。
构造器:
  public BufferedReader​(Reader r)    可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能
字符缓冲输入流新增功能
  public String readLine()    读取一行数据返回,如果读取没有完毕,无行可读返回null

字符缓冲输出流
  字符缓冲输出流:BufferedWriter。
  作用:提高字符输出流写取数据的性能,除此之外多了换行功能
构造器:
  public BufferedWriter​(Writer w)    可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能
字符缓冲输出流新增功能
  public void newLine()    换行操作

字符缓冲流为什么提高了操作数据的性能?
  字符缓冲流自带8K缓冲区【往8k的缓存内存里写,缓存内存对象自己往磁盘里写】
  可以提高原始字符流读写数据的性能
字符缓冲流的功能如何使用?【包装低级的流】
  public BufferedReader​(Reader r)
    性能提升了,多了readLine()按照行读取的功能
  public BufferedWriter​(Writer w)
    性能提升了,多了newLine()换行的功能
package com.itheima.d3_char_buffer;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

/**
    目标:学会使用缓冲字符输入流提高字符输入流的性能,新增了按照行读取的方法(经典代码)
 */
public class BufferedReaderDemo1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个文件字符输入流与源文件接通。
                Reader fr = new FileReader("io-app2/src/data01.txt");
                // a、把低级的字符输入流包装成高级的缓冲字符输入流。
                BufferedReader br = new BufferedReader(fr);
                ){

            // 2、用循环,每次读取一个字符数组的数据。  1024 + 1024 + 8
//            char[] buffer = new char[1024]; // 1K字符
//            int len;
//            while ((len = br.read(buffer)) != -1) {
//                String rs = new String(buffer, 0, len);
//                System.out.print(rs);
//            }

              String line;
              while ((line = br.readLine()) != null){
                  System.out.println(line);
              }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.itheima.d3_char_buffer;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;

/**
     目标:缓冲字符输出流的使用,学会它多出来的一个功能:newLine();
 */
public class BufferedWriterDemo2 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字符输出流管道与目标文件接通
        Writer fw = new FileWriter("io-app2/src/out02.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
       //Writer fw = new FileWriter("io-app2/src/out02.txt", true); // 追加数据
        BufferedWriter bw = new BufferedWriter(fw);

//      a.public void write(int c):写一个字符出去
        bw.write(98);
        bw.write('a');
        bw.write('徐'); // 不会出问题了
        bw.newLine(); // bw.write("\r\n"); // 换行

//       b.public void write(String c)写一个字符串出去
        bw.write("abc我是中国人");
        bw.newLine(); // bw.write("\r\n"); // 换行

//       c.public void write(char[] buffer):写一个字符数组出去
        char[] chars = "abc我是中国人".toCharArray();
        bw.write(chars);
        bw.newLine(); // bw.write("\r\n"); // 换行

//       d.public void write(String c ,int pos ,int len):写字符串的一部分出去
        bw.write("abc我是中国人", 0, 5);
        bw.newLine(); // bw.write("\r\n"); // 换行

//       e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
        bw.write(chars, 3, 5);
        bw.newLine(); // bw.write("\r\n"); // 换行

        // fw.flush();// 刷新后流可以继续使用
        bw.close(); // 关闭包含刷线,关闭后流不能使用

    }
}

24:案例演示

package com.itheima.d3_char_buffer;

import java.io.*;
import java.util.*;

/**
   目标:完成出师表顺序的恢复,并存入到另一个新文件中去。
 */
public class BufferedCharTest3 {
    public static void main(String[] args) {
        try(
                // 1、创建缓冲字符输入流管道与源文件接通
                BufferedReader br = new BufferedReader(new FileReader("io-app2/src/csb.txt"));
                // 5、定义缓冲字符输出管道与目标文件接通
                BufferedWriter bw = new BufferedWriter(new FileWriter("io-app2/src/new.txt"));
                ) {

            // 2、定义一个List集合存储每行内容
            List<String> data = new ArrayList<>();
            // 3、定义循环,按照行读取文章
            String line;
            while ((line = br.readLine()) != null){
                data.add(line);
            }
            System.out.println(data);

            // 4、排序
            // 自定义排序规则
            List<String> sizes = new ArrayList<>();
            Collections.addAll(sizes, "一","二","三","四","五","陆","柒","八","九","十","十一");

            Collections.sort(data, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    // o1   八.,....
                    // o2   柒.,....
                    return sizes.indexOf(o1.substring(0, o1.indexOf(".")))
                            - sizes.indexOf(o2.substring(0, o2.indexOf(".")));
                }
            });
            System.out.println(data);

            // 6、遍历集合中的每行文章写出去,且要换行
            for (String datum : data) {
                bw.write(datum);
                bw.newLine(); // 换行
            }

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

 25:转换流

1、之前我们使用字符流读取中文是否有乱码?
  没有的,因为代码编码和文件编码都是UTF-82、如果代码编码和文件编码不一致,使用字符流直接读取还能不乱码吗?
  会乱码。
  文件编码和读取的编码必须一致才不会乱码

package com.itheima.d4_transfer_stream;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

/**
   演示一下代码编码与文件编码相同和不同的情况    【会出现乱码】
 */
public class CharSetTest00 {
    public static void main(String[] args) {
        try (
                // 代码:UTF-8 文件 UTF-8 不会乱码
                // 1、创建一个文件字符输入流与源文件接通。
                // Reader fr = new FileReader("io-app2/src/data01.txt");


                // 代码:UTF-8 文件 GBK  乱码.     abc 我   爱 你中国    【操作系统创建 一个 gbk文件】
                //                                  oo  oo
                Reader fr = new FileReader("D:\\resources\\data.txt");
                // a、把低级的字符输入流包装成高级的缓冲字符输入流。
                BufferedReader br = new BufferedReader(fr);
        ){

            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 26:字符输入转换流      InputStreamReader

1、如果代码编码和文件编码不一致,使用字符流直接读取还能不乱码吗?
  会乱码。
2、如果如何解决呢?
  使用字符输入转换流
  可以提取文件(GBK)的原始字节流,原始字节不会存在问题。
  然后把字节流以指定编码转换成字符输入流,这样字符输入流中的字符就不乱码


字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流
构造器:
  public InputStreamReader(InputStream is)              可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样。
  public InputStreamReader(InputStream is ,String charset)    可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不乱码了(重点)
package com.itheima.d4_transfer_stream;

import java.io.*;

/**
     目标:字符输入转换流InputStreamReader的使用。

             字节流                                     字符流
     字节输入流               字节输出流              字符输入流            字符输出流
     InputStream             OutputStream          Reader               Writer   (抽象类)
     FileInputStream         FileOutputStream      FileReader           FileWriter(实现类)
     BufferedInputStream     BufferedOutputStream  BufferedReader       BufferedWriter(实现类,缓冲流)
                                                   InputStreamReader    OutputStreamWriter
     字符输入转换流InputStreamReader:
          -- 作用:可以解决字符流读取不同编码乱码的问题。
                  也可以把原始的字节流按照指定编码转换成字符输入流

          -- 构造器:
                public InputStreamReader(InputStream is):可以使用当前代码默认编码转换成字符流,几乎不用!
                public InputStreamReader(InputStream is,String charset):可以指定编码把字节流转换成字符流(核心)

     小结:
        字符输入转换流InputStreamReader:作用:可以解决字符流读取不同编码乱码的问题。
        public InputStreamReader(InputStream is,String charset):可以指定编码把字节流转换成字符流(核心)
 */
public class InputStreamReaderDemo01 {
    public static void main(String[] args) throws Exception {
        // 代码UTF-8   文件 GBK  "D:\\resources\\data.txt"
        // 1、提取GBK文件的原始字节流。   abc 我
        //                            ooo oo
        InputStream is = new FileInputStream("D:\\resources\\data.txt");
        // 2、把原始字节流转换成字符输入流
        // Reader isr = new InputStreamReader(is); // 默认以UTF-8的方式转换成字符流。 还是会乱码的  跟直接使用FileReader是一样的
        Reader isr = new InputStreamReader(is , "GBK"); // 以指定的GBK编码转换成字符输入流  完美的解决了乱码问题

        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
    }
}

 27:字符输出转换流      OutputStreamWriter

1、如果需要控制写出去的字符使用的编码,怎么办?
  可以把字符以指定编码获取字节后再使用字节输出流写出去:
    “我爱你中国”.getBytes(编码)
  也可以使用字符输出转换流实现。
字符输入转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流

构造器
  public OutputStreamWriter(OutputStream os)              可以把原始的字节输出流按照代码默认编码转换成字符输出流。几乎不用。
  public OutputStreamWriter(OutputStream os,String charset)    可以把原始的字节输出流按照指定编码转换成字符输出流(重点)

package com.itheima.d4_transfer_stream;

import java.io.*;
import java.nio.Buffer;

/**
     目标:字符输出转换OutputStreamWriter流的使用。

                字节流                                         字符流
     字节输入流               字节输出流              字符输入流            字符输出流
     InputStream             OutputStream          Reader               Writer   (抽象类)
     FileInputStream         FileOutputStream      FileReader           FileWriter(实现类)
     BufferedInputStream     BufferedOutputStream  BufferedReader       BufferedWriter(实现类,缓冲流)
                                                   InputStreamReader    OutputStreamWriter

     字符输出转换流:OutputStreamWriter
           -- 作用:可以指定编码把字节输出流转换成字符输出流。
                   可以指定写出去的字符的编码。
           -- 构造器:
                public OutputStreamWriter(OutputStream os) :   用当前默认编码UTF-8把字节输出流转换成字符输出流
                public OutputStreamWriter(OutputStream os , String charset):指定编码把字节输出流转换成字符输出流
     小结:
        字符输出转换流OutputStreamWriter可以指定编码把字节输出流转换成字符输出流。
        从而实现指定写出去的字符编码!
 */
public class OutputStreamWriterDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、定义一个字节输出流
        OutputStream os = new FileOutputStream("io-app2/src/out03.txt");

        // 2、把原始的字节输出流转换成字符输出流
        // Writer osw = new OutputStreamWriter(os); // 以默认的UTF-8写字符出去 跟直接写FileWriter一样
        Writer osw = new OutputStreamWriter(os , "GBK"); // 指定GBK的方式写字符出去

        // 3、把低级的字符输出流包装成高级的缓冲字符输出流。
        BufferedWriter bw = new BufferedWriter(osw);

        bw.write("我爱中国1~~");
        bw.write("我爱中国2~~");
        bw.write("我爱中国3~~");

        bw.close();
    }
}

28:对象序列化      ObjectOutputStream

对象序列化:
作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化。
使用到的流是对象字节输出流ObjectOutputStream

使用到的流是对象字节输出流:ObjectOutputStream

构造器:
  public ObjectOutputStream(OutputStream out)      把低级字节输出流包装成高级的对象字节输出流
ObjectOutputStream序列化方法
  public final void writeObject(Object obj)        把对象写出去到对象序列化流的文件中去

对象序列化的含义是什么?
  把对象数据存入到文件中去。
  对象序列化用到了哪个流?
  对象字节输出流ObjectOutputStram
  public void writeObject(Object obj)
序列化对象的要求是怎么样的?
  对象必须实现序列化接口 

package com.itheima.d5_serializable;

import java.io.Serializable;

/**
  对象如果要序列化,必须实现Serializable序列化接口。
 */
public class Student implements Serializable {
    // 申明 序列化的版本号码  【常量】
    // 序列化的版本号与反序列化的版本号必须一致才不会出错!
    private static final long serialVersionUID = 1;
    private String name;
    private String loginName;
    // transient修饰的成员变量不参与序列化了
    private transient String passWord;
    private int age ;

    public Student(){
    }

    public Student(String name, String loginName, String passWord, int age) {
        this.name = name;
        this.loginName = loginName;
        this.passWord = passWord;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", loginName='" + loginName + '\'' +
                ", passWord='" + passWord + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.itheima.d5_serializable;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;

/**
     目标:学会对象序列化,使用 ObjectOutputStream 把内存中的对象存入到磁盘文件中。
     transient修饰的成员变量不参与序列化了
     对象如果要序列化,必须实现Serializable序列化接口。

     申明序列化的版本号码
     序列化的版本号与反序列化的版本号必须一致才不会出错!
     private static final long serialVersionUID = 1;
 */
public class ObjectOutputStreamDemo1 {
    public static void main(String[] args) throws Exception {
        // 1、创建学生对象
        Student s = new Student("陈磊", "chenlei","1314520", 21);
        // 2、对象序列化:使用对象字节输出流包装字节输出流管道
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("io-app2/src/obj.txt"));
        // 3、直接调用序列化方法
        oos.writeObject(s);
        // 4、释放资源
        oos.close();
        System.out.println("序列化完成了~~");
    }
}

29:对象反序列化  ObjectInputStream

对象反序列化:
  使用到的流是对象字节输入流:ObjectInputStream
  作用:以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化。

使用到的流是对象字节输入流:ObjectInputStream
构造器:
  public ObjectInputStream(InputStream out)    把低级字节输如流包装成高级的对象字节输入流
序列化方法:
  public Object readObject()              把存储到磁盘文件中去的对象数据恢复成内存中的对象返回
package com.itheima.d5_serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.ObjectInputStream;

/**
    目标:学会进行对象反序列化:使用对象字节输入流把文件中的对象数据恢复成内存中的Java对象。
 */
public class ObjectInputStreamDemo2 {
    public static void main(String[] args) throws Exception {
        // 1、创建对象字节输入流管道包装低级的字节输入流管道
        ObjectInputStream is = new ObjectInputStream(new FileInputStream("io-app2/src/obj.txt"));
        // 2、调用对象字节输入流的反序列化方法  默认读取 返回的是 object对象,但是本质上还是一个 student 对象,直接强转
        Student s = (Student) is.readObject();
        System.out.println(s);
    }
}

30:打印流    PrintStream  PrintWriter

打印流
  作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。
  可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。

PrintStream
构造器:
  public PrintStream(OutputStream os)          打印流直接通向字节输出流管道
  public PrintStream(File  f)                打印流直接通向文件对象
  public PrintStream(String filepath)          打印流直接通向文件路径
方法:
  public void print(Xxx xx)                打印任意类型的数据出去

PrintWriter
构造器:
  public PrintWriter(OutputStream os)          打印流直接通向字节输出流管道
  public PrintWriter (Writer w)              打印流直接通向字符输出流管道
  public PrintWriter (File  f)              打印流直接通向文件对象
  public PrintWriter (String filepath)          打印流直接通向文件路径
方法:
  public void print(Xxx xx)                打印任意类型的数据出去

PrintStream和PrintWriter的区别
  打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)
  PrintStream继承自字节输出流OutputStream,支持写字节数据的方法。write 方法能写字节,但是打印流一般使用print打印功能,不使用write【所以除了这个两者打印上没有区别】
  PrintWriter继承自字符输出流Writer,支持写字符数据出去。write 方法能写字符

打印流有几种?各有什么特点?
  打印流一般是指:PrintStreamPrintWriter两个类。
  打印功能2者是一样的使用方式
  PrintStream继承自字节输出流OutputStream,支持写字节
  PrintWrite继承自字符输出流Writer,支持写字符
打印流的优势是什么?
  两者在打印功能上都是使用方便,性能高效(核心优势)
package com.itheima.d6_printStream;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;

/**
    目标:学会使用打印流 高效  方便写数据到文件。
 */
public class PrintDemo1 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个打印流对象
//        PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt"));
//        PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt" , true)); // 追加数据,在低级管道后面加True
//        PrintStream ps = new PrintStream("io-app2/src/ps.txt" );
        PrintWriter ps = new PrintWriter("io-app2/src/ps.txt"); // 打印功能上与PrintStream的使用没有区别

        ps.println(97);
        ps.println('a');
        ps.println(23.3);
        ps.println(true);
        ps.println("我是打印流输出的,我是啥就打印啥");

        ps.close();
    }
}
输出语句重定向
属于打印流的一种应用,可以把输出语句的打印位置改到文件。
PrintStream ps = new PrintStream("文件地址")
System.setOut(ps);
package com.itheima.d6_printStream;

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

/**
    目标:了解改变输出语句的位置到文件
 */
public class PrintDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("锦瑟无端五十弦");
        System.out.println("一弦一柱思华年");

        // 改变输出语句的位置(重定向)
        PrintStream ps = new PrintStream("io-app2/src/log.txt");
        System.setOut(ps); // 把系统打印流改成我们自己的打印流

        System.out.println("庄生晓梦迷蝴蝶");
        System.out.println("望帝春心托杜鹃");
    }
}

30:properties  【配置文件】

以后我们系统中,可能会有一个.properties结尾的属性文件
怎么用程序生成这个属性文件?
怎么读取这个属性文件里面的内容    

Properties属性集对象:其实就是一个Map集合,但是我们一般不会当集合使用【也可以当集合使用,不过一般不这样用而已】,因为 HashMap 更好用。

Properties核心作用:
  Properties代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去。
  属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value,后续做系统配置信息的。

Properties的API:
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)方法的格式写入输出字符流
  public Object setProperty(String key, String value)     保存键值对(put)
  public String getProperty(String key)             使用此属性列表中指定的键搜索属性值 (get)
  public Set<String> stringPropertyNames()           所有键的名称的集合  (keySet())


Properties的作用?
  可以存储Properties属性集的键值对数据到属性文件中去:
    void store​(Writer writer, String comments)
  可以加载属性文件中的数据到Properties对象中来:
    void load​(Reader reader)
package com.itheima.d7_properties;

import java.io.FileWriter;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;

/**
    目标:Properties的概述和使用(框架底层使用,了解这个技术即可)(保存数据到属性文件)

    Properties: 属性集对象。
         其实就是一个Map集合。也就是一个键值对集合,但是我们一般不会当集合使用,
         因为有HashMap。

    Properties核心作用:
         Properties代表的是一个属性文件,可以把键值对的数据存入到一个属性文件中去。
         属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value。

    大家在后期学的很多大型框架技术中,属性文件都是很重要的系统配置文件。
        users.properties
                admin=123456
                dlei=dlei

     需求:使用Properties对象生成一个属性文件,里面存入用户名和密码信息。

     Properties的方法:
         -- public Object setProperty(String key, String value) : 保存一对属性。  (put)
         -- public String getProperty(String key) : 使用此属性列表中指定的键搜索属性值 (get)
         -- public Set<String> stringPropertyNames() : 所有键的名称的集合  (keySet())
         -- public void store(OutputStream out, String comments): 保存数据到属性文件中去
         -- public void store(Writer fw, String comments): 保存数据到属性文件中去

     小结:
            Properties可以保存键值对数据到属性文件

 */
public class PropertiesDemo01 {
    public static void main(String[] args) throws Exception {
        // 需求:使用Properties把键值对信息存入到属性文件中去。
        Properties properties = new Properties();
        properties.setProperty("admin", "123456");    // put 也可以,父类的统一方法,但是一般还是使用 setProperty【底层还是 put 方法】
        properties.setProperty("dlei", "003197");
        properties.setProperty("heima", "itcast");
        System.out.println(properties);

        /**
           参数一:保存管道 字符输出流管道
           参数二:保存心得【随便怎么写,注释】
         */
        properties.store(new FileWriter("io-app2/src/users.properties")
                , "this is users!! i am very happy! give me 100!");

    }
}
package com.itheima.d7_properties;

import java.io.FileReader;
import java.util.Properties;
import java.util.Set;

/**
    目标:Properties读取属性文件中的键值对信息。(读取)
    Properties的方法:
        -- public Object setProperty(String key, String value) : 保存一对属性。
        -- public String getProperty(String key) :使用此属性列表中指定的键搜索属性值
        -- public Set<String> stringPropertyNames() :所有键的名称的集合
        -- public void store(OutputStream out, String comments):保存数据到属性文件中去
        -- public synchronized void load(InputStream inStream):加载属性文件的数据到属性集对象中去
        -- public synchronized void load(Reader fr):加载属性文件的数据到属性集对象中去
    小结:
        属性集对象可以加载读取属性文件中的数据!!
 */
public class PropertiesDemo02 {
    public static void main(String[] args) throws Exception {
        // 需求:Properties读取属性文件中的键值对信息。(读取)
        Properties properties = new Properties();
        System.out.println(properties);

        // 加载属性文件中的键值对数据到属性对象properties中去
        properties.load(new FileReader("io-app2/src/users.properties"));

        System.out.println(properties);
        String rs = properties.getProperty("dlei");
        System.out.println(rs);
        String rs1 = properties.getProperty("admin");
        System.out.println(rs1);
    }
}

31:IO框架      commons-io

commons-io概述
  commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
  commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils

FileUtils主要有如下方法:
  String readFileToString(File file, String encoding)              读取文件中的数据, 返回字符串
  void copyFile(File srcFile, File destFile)                    复制文件。
  void copyDirectoryToDirectory(File srcDir, File destDir)            复制文件夹。

package com.itheima.d8_commons_io;
import org.apache.commons.io.FileUtils;
import java.io.File;

/**
    目标:Commons-io包的使用介绍。

    什么是Commons-io包?
            commons-io是apache开源基金组织提供的一组有关IO操作的类库,
            可以挺提高IO功能开发的效率。commons-io工具包提供了很多有关io操作的类,

    见下表:
         | 包                                  | 功能描述                                     |
         | ----------------------------------- | :------------------------------------------- |
         | org.apache.commons.io               | 有关Streams、Readers、Writers、Files的工具类 |
         | org.apache.commons.io.input         | 输入流相关的实现类,包含Reader和InputStream  |
         | org.apache.commons.io.output        | 输出流相关的实现类,包含Writer和OutputStream |
         | org.apache.commons.io.serialization | 序列化相关的类

    步骤:
         1. 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
         2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中
         3. 将commons-io-2.6.jar加入到classpath中

    小结:
         IOUtils和FileUtils可以方便的复制文件和文件夹!!
 */
public class CommonsIODemo01 {
    public static void main(String[] args) throws Exception {

////        // 1.完成文件复制!
//        IOUtils.copy(new FileInputStream("D:\\resources\\hushui.jpeg"),
//                new FileOutputStream("D:\\resources\\hushui2.jpeg"));


////        // 2.完成文件复制到某个文件夹下!
//        FileUtils.copyFileToDirectory(new File("D:\\resources\\hushui.jpeg"), new File("D:/"));


          // 3.完成文件夹复制到某个文件夹下!
//          FileUtils.copyDirectoryToDirectory(new File("D:\\resources") , new File("D:\\new"));
//           FileUtils.deleteDirectory(new File("D:\\new"));

         // JDK1.7 自己也做了一些一行代码完成复制的操作:New IO的技术
         // Files.copy(Path.of("D:\\resources\\hushui.jpeg"), Path.of("D:\\resources\\hushui3.jpeg"));

        FileUtils.deleteDirectory(new File("D:\\new"));
    }
}

 

posted @ 2024-05-16 15:25  至高无上10086  阅读(1)  评论(0编辑  收藏  举报