Spring四

Spring四

关于dao层中实现类的一个问题

  • dao层中的实现类需要一个JdbcTemplate进行增删改查时

    1. 方式一:可以增加一个成员变量,然后在配置文件中进行配置,适用于所有配置方式(xml 和注解都可以),建议与注解配置bean相搭配

    2. 方式二:可以让实现类继承JdbcDaoSupport,这样就不再需要增加成员变量JdbcTemplate及其set方法,可以简化代码,但是此方法只能在配置文件配置bean时搭配使用。


Spring中的事务控制

  • 第一:JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解决方案。

  • 第二:spring 框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。

  • 第三:spring 的事务控制都是基于 AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式实现。

    Spring中事务控制的API介绍

  • PlatformTransactionManager:此接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

    1. 获取事务状态信息:TransactionStatus getTransaction(TransactionDefinition definition)
    2. 提交事务:void commit(TransactionStatus status)
    3. 回滚事务:void rollback(TransactionStatus status)
    • 在开发中使用它的实现类:
      1. DataSourceTransactionManager:使用 Spring JDBC 或 iBatis 进行持久化数据时使用
      2. HibernateTransactionManager:使用Hibernate 版本进行持久化数据时使用
  • TransactionDefinition:事务的定义信息对象,里面有如下方法

    1. 获取事务对象名称:String getName()
    2. 获取事务隔离级别:int getIsolationLevel()
    3. 获取事务传播行为:int getPropagationBehavior()
      • 事务的传播行为:
        • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
        • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
        • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
        • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
        • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
        • NEVER:以非事务方式运行,如果当前存在事务,抛出异常
        • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作。
    4. 获取事务超时时间:int getTimeout()
    5. 获取事务是否只读:boolean isReadOnly()
  • TransactionStatus:是事务具体的运行状态,方法如下

    1. 刷新事务:void flush()
    2. 获取是否存在存储点:boolean hasSavepoint()
    3. 获取事务是否完成:boolean isCompleted()
    4. 获取事务是否为新的事务:boolean isNewTransaction()
    5. 获取事务是否回滚:boolean isRollbackOnly()
    6. 设置事务回滚:void setRollbackOnly()

基于XML的声明式事务控制

  • 步骤:
    1. 配置事务管理器
    2. 配置事务的通知
      • 此时我们需要导入事务的约束tx名称空间和约束,同时也需要aop的使用tx:advice标签配置事务通知
        • 属性:
          • id:给事务通知起一个唯一标识
          • transaction-manager:给事务通知提供一个事务管理器引用
    3. 配置AOP中的通用切入点表达式
    4. 建立事务通知和切入点表达式的对应关系
    5. 配置事务的属性
      • 是在事务的通知tx:advice标签的内部
      • 事务的属性有:
        1. isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
        2. propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
        3. read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
        4. timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
        5. rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
        6. no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。

基于注解的配置方式

  • 步骤:
    1. 配置事务管理器
    2. 开启spring对注解事务的支持
    3. 在需要事务支持的地方使用@Transactional注解,对应的想要配置属性可以在此注解中配置

Spring三

Spring三

AOP

什么是AOP?

  • AOP:全称是 Aspect Oriented Programming 即:面向切面编程
  • 简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。就是不再需要创建一个实体类去给某些类的方法进行增强,只需要将这些增强的代码抽取出来为一个类,然后配置Spring,在使用需要增强方法的时候由Spring自己动态代理的给你增强。

    AOP 的作用及优势

  • 作用:
    • 在程序运行期间,不修改源码对已有方法进行增强。
  • 优势:
    1. 减少重复代码
    2. 提高开发效率
    3. 维护方便

      AOP 的实现方式

      使用动态代理技术。

动态代理

动态代理:

  • 特点:字节码随用随创建,随用随加载

  • 作用:不修改源码的基础上对方法增强

  • 类:

    1. 基于接口的动态代理
    2. 基于子类的动态代理
  • 基于接口的动态代理:

    • 涉及的类:Proxy
    • 提供者:JDK官方
    • 如何创建代理对象:
      • 使用Proxy类中的newProxyInstance方法
      • 创建代理对象的要求:
        • 被代理类最少实现一个接口,如果没有则不能使用
        • newProxyInstance方法的参数:
          1. ClassLoader:类加载器
            • 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
          2. Class[]:字节码数组
            • 它是用于让代理对象和被代理对象有相同方法。固定写法。
          3. InvocationHandler:用于提供增强的代码
            • 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
            • 此接口的实现类都是谁用谁写。
            • 实现此接口时会重写invoke方法,其中的参数proxy代表代理对象的引用,method代表当前执行的方法,args当前执行方法所需的参数,return返回值为和被代理对象方法有相同的返回值.
  • 基于子类的动态代理

    • 涉及的类:Enhancer
    • 提供者:第三方cglib库
    • 如何创建代理对象:
      • 使用Enhancer类中的create方法
      • 创建代理对象的要求:
        • 被代理类不能是最终类
      • create方法的参数:
        1. Class:字节码
          • 它是用于指定被代理对象的字节码。
        2. Callback:用于提供增强的代码
          • 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
          • 此接口的实现类都是谁用谁写。
          • 我们一般写的都是该接口的子接口实现类:MethodInterceptor

AOP的相关术语

  1. Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。就是所有方法。
  2. Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。就是需要动态代理增强的方法。
  3. Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。就是对要增强的方法干什么。
    • 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
      tzjbTK.md.jpg
  4. Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
  5. Target(目标对象):代理的目标对象。
  6. Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。就是动态代理增强的这个过程。
  7. Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
  8. Aspect(切面):是切入点和通知(引介)的结合。

Spring中基于XML的AOP配置

  • 配置步骤

    1. 把通知Bean也交给spring来管理
    2. 使用aop:config标签表明开始AOP的配置
    3. 使用aop:aspect标签表明配置切面
      • id属性:是给切面提供一个唯一标识
      • ref属性:是指定通知类bean的Id。
    4. 在aop:aspect标签的内部使用对应标签来配置通知的类型
      • aop:before:表示配置前置通知
        • method属性:用于指定Logger类中哪个方法是前置通知
        • pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
  • 切入点表达式的写法:

    • 关键字:execution(表达式)
    • 表达式:
      • 访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
      • 标准的表达式写法:
        public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
      • 访问修饰符可以省略
        void com.itheima.service.impl.AccountServiceImpl.saveAccount()
      • 返回值可以使用通配符,表示任意返回值
        * com.itheima.service.impl.AccountServiceImpl.saveAccount()
      • 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个.
        `
        ....AccountServiceImpl.saveAccount())`
      • 包名可以使用..表示当前包及其子包
        *..AccountServiceImpl.saveAccount()
      • 类名和方法名都可以使用来实现通配
        `
        ...*()`
      • 参数列表:
        • 可以直接写数据类型:
          • 基本类型直接写名称 int
          • 引用类型写包名.类名的方式 java.lang.String
          • 可以使用通配符表示任意类型,但是必须有参数
          • 可以使用..表示有无参数均可,有参数可以是任意类型
      • 全通配写法:
        * *..*.*(..)
      • 实际开发中切入点表达式的通常写法:
        • 切到业务层实现类下的所有方法
          * com.itheima.service.impl.*.*(..)
  • Spring中基于注解配置环绕通知

  • 问题:

    • 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
  • 分析:

    • 通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
  • 解决:

    • Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
    • 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
  • spring中的环绕通知:

    • 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。

Spring中基于注解的AOP配置

  • 首先需要导入关于注解及aop的约束。

  • 步骤:

    1. 在通知类上使用@Aspect注解声明为切面。

    2. 在增强的方法上使用注解配置通知

      1. @Before
        • 作用:把当前方法看成是前置通知。
        • 属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
      2. @AfterReturning
        • 作用:把当前方法看成是后置通知。
        • 属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
      3. @AfterThrowing
        • 作用:把当前方法看成是异常通知。
        • 属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
      4. @After
        • 作用:把当前方法看成是最终通知。
        • 属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
      5. @Around
        • 作用:把当前方法看成是环绕通知。
        • 属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
    3. 在配置文件中开启对注解AOP的支持

      <aop:aspectj-autoproxy/>
  • 切入点表达式注解,使用@Pointcut

    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt1() {}
    应用时
    @Before("pt1()")//注意:千万别忘了写括号

Spring一

Spring一

什么是Spring?

  • Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以IoC(Inverse Of Control:反转控制AOP(Aspect Oriented Programming:面向切面编程为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

什么是控制反转-Inversion Of Control?

  • 原本创建对象这一过程对我们来说是主动的,想用什么方式就用什么方式,可以自己new,也可以用工厂创建,而现在我们将创建对象这一过程交给了Spring,我们只是被动接收,这种被动接收的方式获取对象的思想就是控制反转。
  • 作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。

Spring中配置bean

<bean id="  " class="  "></bean>
  • 作用:

    • 用于配置对象让 spring 来创建的。
    • 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
  • 属性:

    1. id:给对象在容器中提供一个唯一标识。用于获取对象。
    2. class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
    3. scope:指定对象的作用范围。
      • singleton :默认值,单例的.
      • prototype :多例的.
      • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
      • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
      • global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
    4. init-method:指定类中的初始化方法名称。
    5. destroy-method:指定类中销毁方法名称。

作用范围和生命周期

  1. 单例对象:scope=”singleton”

    • 一个应用只有一个对象的实例。它的作用范围就是整个引用。
    • 生命周期:
      • 对象出生:当应用加载,创建容器时,对象就被创建了。
      • 对象活着:只要容器在,对象一直活着。
      • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
  2. 多例对象:scope=”prototype”

    • 每次访问对象时,都会重新创建对象实例。
    • 生命周期:
      • 对象出生:当使用对象时,创建新的对象实例。
      • 对象活着:只要对象在使用中,就一直活着。
      • 对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

实例化 Bean 的三种方式

  1. 第一种方式:使用默认无参构造函数

    • 在默认情况下:它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。

      <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>
  2. 第二种方式:spring 管理静态工厂-使用静态工厂的方法创建对象

    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="createAccountService"></bean>
  3. 第三种方式:spring 管理实例工厂-使用实例工厂的方法创建对象

    <bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean> 
    <bean id="accountService" factory-bean="instancFactory" factory-method="createAccountService"></bean>

Spring中的容器:用于读取配置文件

  1. ApplicationContext:是BeanFactory接口的子类,只要一读取配置文件,默认情况下就会创建对象。

    • 它的实现类:
      • ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件 推荐使用这种
      • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
      • AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
  2. BeanFactory:是Spring容器中的顶层接口。什么时候使用什么时候创建对象。


spring 的依赖注入

  • 概念:
    • 依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。
    • 我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。
    • ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。
    • 那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。
    • 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

构造函数注入

  • 就是将需要注入的属性加入到构造函数中。

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg> 
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
  • constructor-arg

    • 属性:
      1. index:指定参数在构造函数参数列表的索引位置
      2. type:指定参数在构造函数中的数据类型
      3. name:指定参数在构造函数中的名称
      4. value:它能赋的值是基本数据类型和 String 类型
      5. ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
  • 优势:

    • 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
  • 弊端:

    • 改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。

set方法注入

  • 就是将需要注入的属性设置set方法,然后在配置文件中注入。

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> 
        <property name="name" value="test"></property>
        <property name="age" value="21"></property>
        <property name="birthday" ref="now"></property>
    </bean>
  • 涉及的标签:property

    • 属性:
      • name:找的是类中 set 方法后面的部分
      • ref:给属性赋值是其他 bean 类型的
      • value:给属性赋值是基本数据类型和 string 类型的
  • 优势:

    • 创建对象时没有明确的限制,可以直接使用默认构造函数
  • 弊端:

    • 如果有某个成员必须有值,则获取对象是有可能set方法没有执行。

集合注入

  • 使用的方法还是构造方法注入与set方法注入的一种,只不过注入的对象是集合类型。

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
        <!-- 给数组注入数据 --> 
        <property name="myStrs"> 
            <set>
                <value>AAA</value> 
                <value>BBB</value> 
                <value>CCC</value>
            </set>
        </property>
        <!-- 注入 list 集合数据 --> 
        <property name="myList"> 
            <array> 
                <value>AAA</value>
                <value>BBB</value> 
                <value>CCC</value>
            </array>
        </property>
        <!-- 注入 set 集合数据 --> 
        <property name="mySet"> 
            <list>
                <value>AAA</value> 
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
        <!-- 注入 Map 数据 --> 
        <property name="myMap"> 
            <props> 
                <prop key="testA">aaa</prop> 
                <prop key="testB">bbb</prop>
            </props>
        </property>
        <!-- 注入 properties 数据 -->
        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry> 
                <entry key="testB"> <value>bbb</value>
                </entry>
            </map>
        </property>
    </bean>
  • 用于给List结构集合注入的标签:

    • list array set
  • 用于个Map结构集合注入的标签:

    • map props
  • 结构相同,标签可以互换*

Spring二

Spring二

Spring中基于注解进行配置

  • 曾经XML的配置:

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
        scope=""  init-method="" destroy-method="">
        <property name=""  value="" | ref=""></property>
    </bean>
    • 用于创建对象的注解

      • 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
      1. Component:
        • 作用:用于把当前类对象存入spring容器中
        • 属性:
          • value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
      2. Controller:一般用在表现层
      3. Service:一般用在业务层
      4. Repository:一般用在持久层
        • 以上三个注解他们的作用和属性与Component是一模一样。
        • 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
    • 用于注入数据的注解

      • 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
      1. Autowired:
        • 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
        • 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
        • 如果Ioc容器中有多个类型匹配时:则需要和Qualifier注解搭配使用。
          • 出现位置:
            • 可以是变量上,也可以是方法上
          • 细节:
            • 在使用注解注入时,set方法就不是必须的了。
      2. Qualifier:
        • 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以。
        • 属性:
          • value:用于指定注入bean的id。
      3. Resource
        • 作用:直接按照bean的id注入。它可以独立使用
        • 属性:
          • name:用于指定bean的id。
      • 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
      • 另外,集合类型的注入只能通过XML来实现。
      1. Value
        • 作用:用于注入基本类型和String类型的数据
        • 属性:
          • value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
          • SpEL的写法:${表达式}
    • 用于改变作用范围的注解

      • 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
      1. Scope
        • 作用:用于指定bean的作用范围
        • 属性:
          • value:指定范围的取值。常用取值:singleton prototype
    • 和生命周期相关的注解

      • 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
      1. PreDestroy
        • 作用:用于指定销毁方法
      2. PostConstruct
        • 作用:用于指定初始化方法
    • 在注解完成后还需要去XML配置,告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束中

      <context:component-scan base-package="你的包"></context:component-scan>

完全使用注解进行配置不再需要XML

  • 有一个配置类,它的作用和bean.xml是一样的

    spring中的新注解

  1. Configuration

    • 作用:指定当前类是一个配置类
    • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
  2. ComponentScan

    • 作用:用于通过注解指定spring在创建容器时要扫描的包
    • 属性:
      • value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
    • 我们使用此注解就等同于在xml中配置了<context:component-scan base-package="com.itheima"></context:component-scan>
  3. Bean

    • 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
    • 属性:
      • name:用于指定bean的id。当不写时,默认值是当前方法的名称
    • 细节:
      • 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
      • 查找的方式和Autowired注解的作用是一样的
  4. Import

    • 作用:用于导入其他的配置类
    • 属性:
      • value:用于指定其他配置类的字节码。
    • 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
  5. PropertySource

    • 作用:用于指定properties文件的位置
    • 属性:
    • value:指定文件的名称和路径。
    • 关键字:classpath,表示类路径下

Spring整合Junit

  • Spring整合junit的配置
    1. 导入spring整合junit的jar(坐标)
    2. 使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的@Runwith
    3. 告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
      • @ContextConfiguration
        • locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
        • classes:指定注解类所在地位置
          • 当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上

Stream流

Stream流

Stream流中的一些方法

1. 获取Stream流

  • java.util.stream.Stream< T >是Java 8新加入的最常用的流接口。(这并不是一个函数式接口)
  • 获取一个流非常简单,有以下几种常用方式:
    • 所有的Collection集合都可以通过stream默认方法获取流:
      • default Stream<E> stream​();
    • Stream接口的静态方法of可以获取数组对应的流:
      • static <T> Stream<T> of​(T... values);
      • 参数是一个可变参数,可以传递一个数组

2. Stream流中计数

  • Stream流中的常用方法count:用于统计Stream流中元素的个数。
    • long count();
  • 是一个终结方法,返回值是long类型,所以不能调用Stream类型中其他方法。

3. Stream流中过滤

  • Stream中的常用方法filter:用于对Stream流中的数据进行过滤。
  • Stream<T> filter​(Predicate<? super T> predicate) ;
  • 参数是一个Predicate接口,是一个函数式接口,可以传递lambda表达式,对数据进行过滤。

4. Stream流中遍历

  • Stream中两种方法:

    • 延迟方法:方法的返回值仍然是Stream类型,可以继续调用Stream中的方法。
    • 终结方法:方法的返回值不再是Stream类型,不能再调用Stream中的方法。
  • Stream中的常用方法forEach

  • void forEach​(Consumer<? super T> action) :该方法接收一个Consumer接口函数,会将每一个流元素交给函数进行处理。

  • Consumer接口是一个消费型的函数式接口,可以传递lambda表达式消费数据。

  • 简单记:

    • 用来遍历流中的数据,是一个终结方法,b遍历之后就不能再继续调用Stream流中的其他方法。

5. Stream流中截取

  • Stream流中的常用方法limit:用于截取流中的元素。
  • limit方法可以对流进行截取,只取用前n个。
  • Stream<T> limit​(long maxSize);
    • 参数是一个long类型,如果集合当前长度大于截取长度则进行截取(一个截取完以后的新的流),否则不进行操作(一个元素和之前一模一样的新的流)。

6. Stream流中的映射转化

  • 如果需要将流中的元素映射到另一个流中,可以使用map方法。
  • <R> Stream<R> map​(Function<? super T,? extends R> mapper) ;
  • 该接口需要一个Function函数式接口参数,可以将当前l流中的T类型数据转换为另一种类型的R类型数据。

7. Stream流中跳过元素

  • Stream流中的常用方法skip:用于跳过元素。
  • 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流。
  • Stream<T> skip​(long n) ;
    • 如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流。

8. Stream流将流组合

  • Stream流中的常用方法concat:用于把流组合到一起。
  • 如果有两个流,希望合并为一个流,那么可以使用Stream接口的静态方法concat。
  • static <T> Stream<T> concat​(Stream<? extends T> a, Stream<? extends T> b)

Socket

Socket

TCPServer

  • TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据

  • 表示服务器的类:

    • java.net.ServerSocket:此类实现服务器套接字。
  • 构造方法:
    ServerSocket(int port);创建绑定到特定端口的服务器套接字。

  • 服务器端必须明确一件事,必须得知道哪个客户端请求的服务器

  • 所以可以使用accept方法获取到请求的客户端Socket

  • 成员方法:

    • Socket accept();侦听并接受到此套接字的连接。
  • 服务器实现步骤:

    1. 创建一个服务器ServerSocket对象,和系统要指定的端口号
    2. 使用ServerSocket对象中的方法accept获取到请求的客户端对象Socket
    3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    4. 使用网络字节输入流InputStream对象中的方法read读取客户端发送的数据
    5. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    6. 使用网络字节输出流OutputStream对象中的方法write给客户端发送数据
    7. 释放资源

TCPClient

  • TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器写回的数据

  • 表示客户端的类:

    • java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
    • 套接字:包含了IP地址和端口号的网络单位
  • 构造方法:

    • Socket(InetAddress address, int port);创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
    • 参数:
      • InetAddress address:服务器主机的名称/服务器的IP地址
      • int port:服务器的端口号
  • 成员方法:

    • OutputStream getOutputStream();返回此套接字的输出流。
    • InputStream getInputStream();返回此套接字的输入流。
    • void close();关闭此套接字。
  • 实现步骤:

    1. 创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
    2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    3. 使用网络字节输出流OutputStream对象中的方法write给服务器发送数据
    4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    5. 使用网络字节输入流InputStream对象中的方法read读取服务器回写的数据
    6. 释放资源
  • 注意:

    1. 客户端和服务器进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
    2. 当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
      • 这时如果服务器没有启动,那么就会抛出异常
      • 如果服务器已经启动那么可以进行交互

客户端服务器阻塞解决

tqILo8.jpg


模拟BS(浏览器与服务器)

tqoPe0.jpg

Thread

Thread线程

创建多线程的方式

第一种:创建Thread类的子类

  • java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类

  • 实现步骤:

    1. 创建一个Thread类的子类
    2. 在Thread的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)
    3. 创建一个Thread类的子类对象
    4. 调用Thread类中的方法start方法,开启新的线程,执行run方法
      • void start();使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
  • 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

第二种:实现Runnable接口

  • Java.lang.Runnable

    • Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
  • Java.lang.Thread的构造方法:

    • Thread(Runnable target);分配新的 Thread 对象。
    • Thread(Runnable target, String name);分配新的 Thread 对象。
  • 实现步骤:

    1. 创建一个Runnable接口的实现类
    2. 在实现类中重写Runnable接口的run方法,设置线程任务
    3. 创建一个Runnable接口的实现对象
    4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
    5. 调用Thread类中的start方法,开启新的线程,执行run方法
  • 实现Runnable接口创建多线程的好处:

    1. 避免了单线程的局限性
      • 一个类只能继承一个类,类继承了Thread类就不能继承其他类
      • 实现了Runnable接口还可以继承其他类实现其他接口
    2. 增强了程序的扩展性,降低了程序的耦合性
      • 实现Runnable接口的方式,把设置线程任务和开启新的线程进行了分离
      • 实现类中,重写了run方法,用来设置线程任务
      • 创建Thread类对象,调用start方法,用来开启新的线程
  • 匿名内部类实现线程创建

    • 可以简化代码,把子类继承父类,重写父类的方法,创建子类对象合成一步完成,或者把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成

    • 格式:

      new 父类/接口(){
          重写方法
      }
      
      //线程的父类是Thread
      new Thread() {
          @Override
          public void run() {
              for (int i = 0; i < 20; i++) {
                  System.out.println(Thread.currentThread().getName() + "  " + i);
              }
          }
      }.start();
      
      //线程的接口Runnable
      new Thread(new Runnable() {
          @Override
          public void run() {
              for (int i = 0; i < 20; i++) {
                  System.out.println(Thread.currentThread().getName() + "  " + i);
              }
          }
      }).start();

获取线程的名称

  • 获取线程的名称:

    1. 使用Thread类中的方法getName();
      • String getName();返回该线程的名称。
    2. 可以先获取到当前正在执行的线程,再使用线程中的方法getName()获取线程名称
      • static Thread currentThread();返回对当前正在执行的线程对象的引用。
  • 线程的名称:

    • 主线程:main
    • 子线程:Thread-0,Thread-1,Thread-2….

暂停线程

public static void sleep(long millis, int nanos); 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)

  • 此操作受到系统计时器和调度程序精度和准确性的影响。
  • 注意:毫秒数结束后,线程继续执行

多线程内存图解

tq0zH1.jpg


多线程原理

tqBNEq.jpg


线程不安全的解决方案

第一种:使用同步代码块

  • 格式:

    synchronized (锁对象){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    }
  • 注意:

    1. 同步代码块中的锁对象可以是任意的对象
    2. 但是必须保证多个线程使用的锁对象是同一个
    3. 锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行

第二种:使用同步方法

  • 使用步骤:

    1. 把访问了共享数据的方法抽取出来,放到一个方法中
    2. 在方法上添加一个修饰符synchronized
  • 格式:

    修饰符 synchronized 返回值类型 方法名(参数列表){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    }

第三种:Lock锁

  • java.util.concurrent.locks.lock接口

  • Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

  • Lock接口中的常用方法:

    • void lock();获取锁。
    • void unlock();释放锁。
  • java.util.concurrent.locks.ReentrantLock implements Lock接口

  • 使用步骤:

    1. 在成员位置创建一个ReentrantLock对象
    2. 在可能会出现线程安全问题的代码前调用Lock接口中的方法lock()获取锁
    3. 在可能会出现线程安全问题的代码后调用Lock接口中的方法unlock()释放锁

线程池

  • 线程池:JDK1.5之后提供的

  • java.util.concurrent.Executors:线程池的工厂类,用来生产线程池

  • Executors类中的静态方法:

    • static ExecutorService newFixedThreadPool(int nThreads);创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
      • 参数:
        • int nThreads:创建线程池中包含的线程数量
      • 返回值:
        • ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收
  • java.util.concurrent.ExecutorService:线程池接口

  • 用来从线程池中获取线程,调用start方法执行线程任务:

    • Future<?> submit(Runnable task);提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
  • 关闭销毁线程池的方法:

    • void shutdown();启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
  • 线程池的使用步骤:

    1. 使用线程池工厂类Executors里边的静态方法newFixedThreadPool生产一个指定线程数量的线程池
    2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
    3. 调用ExecutorService中的方法submit传递线程任务,开启线程,执行run方法

线程池类似原理

tqr9SO.jpg

Expection

Expection

相关的概念与结构

  • java.lang.Throwable类是是java语言中所有错误或异常的祖类
    • Expection:编译的异常,进行编译java程序出现的问题
      • RuntimeExpection:运行期异常,java程序运行过程中出现的问题
    • Error:错误
      • 错误就相当于程序得了一个无法治愈的毛病,必须修改源代码,程序才能运行

tqYrGj.png


异常产生过程解析

tqtPyt.png


异常的处理

1. throw

  • throw关键字

  • 作用:

    • 可以适用于throw关键字在指定的位置抛出指定的异常
  • 使用格式:

    • throw new xxxExpection(“异常产生的原因”);
  • 注意:

    1. throw关键字必须写在方法的内部
    2. throw关键字后边new的对象必须是Expection或者是Expection的子类对象
    3. throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
      • throw关键字后面创建的是RuntimeExpection或者是RuntimeExpection的子类对象,我们可以不处理,默认交给JVM处理
      • throw关键字后面创建的是编译异常,我们就必须处理这个异常,要么throws要么trycatch

2. throws

  • throws关键字:异常处理的第一种方式,交给别人处理

  • 作用:

    • 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
    • 可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理)
      最终交给JVM处理
  • 使用格式:

    修饰符 返回值类型 方法名(参数列表) throws AAAExpection,BBBExpection....{
        throw new AAAExpection("产生原因");
        throw new BBBExpection("产生原因");
    }
  • 注意:

    1. throws关键字必须写在方法声明处
    2. throws关键字后边声明的异常必须是Expection或者是Expection的子类
    3. 方法内部如果抛出了多个异常对象,那么throws后边也必须声明多个异常
      • 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可
    4. 调用了一个声明抛出异常的方法,我们就必须处理声明的异常
      • 要么继续使用throws把异常交给方法的调用者处理,最终交割jvm处理
      • 要么trycatch自己处理异常

3. try catch

  • try…catch:异常处理的第二种方式,自己处理异常

  • 格式:

    try{
        可能产生异常的代码
    }catch(定义一个异常变量,用来接收try中抛出的异常对象){
        异常的处理逻辑,产生异常对象之后怎么处理异常对象
        一般在工作中会把异常的信息记录在日志中
    }
    ...
    catch(异常类目 变量名){
    
    }
    catch可以有多个
  • 注意:

    1. try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
    2. 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后代码
    3. 如果try中没有产生异常,就不会执行catch中的异常处理逻辑,继续执行try…catch之后代码
  • try catch 可以和finally一起使用

    1. finally不能单独使用,必须和try一起使用
    2. finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放
    3. 无论如何finally中的代码都会执行

自定义异常类

  • 自定义异常类:Java提供的异常类不够我们使用,需要自己定义一些异常类

  • 格式:

    public class xxxExpection extends Expection | RuntimeExpection{
            添加一个空参数的构造方法
            添加一个带异常信息的构造方法
    }
  • 注意:

    1. 自定义异常类一般都是以Expection结尾,说明该类是一个异常类
    2. 自定义异常类必须继承Expection或者是RuntimeExpection
      • 继承Expection:那么自定义类异常就是一个编译器异常,如果方法内部抛出了编译器异常就必须处理这个异常,要么throws要么trycatch
      • 继承RuntimeExpection:那么自定义异常类就是一个运行期异常,无需处理,交给虚拟机处理

异常继承之间的一些关系

  • 子父类的异常:
    1. 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常
    2. 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生异常,只能通过捕获处理,不能声明抛出。
  • 注意:父类异常是什么样子类异常就是什么样

File

File

Lambda表达式

new Thread(()->{方法体}).start();
    ()->{方法体}
        ()代表参数
        {}代表方法的代码
        ->代表把参数传给后面的代码
  • 可以使代买更简洁,冗余更少

File类

  • java.io.File类

    • 文件和目录路径名的抽象表示形式
  • java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作

  • 我们可以使用File类的方法

    创建一个文件/文件夹
    删除文件/文件夹
    获取一个文件/文件夹
    判断文件/文件夹是否存在
    对文件夹进行遍历
    获取文件的大小
  • File类是一个与系统无关的类,任何的操作系统都可以使用类中的方法

  • 重点:记住三个单词

    1. file:文件
    2. directory:文件夹/目录
    3. path:路径
  • 系统中的分隔符:
    static String pathSeparator;与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
    static char pathSeparatorChar:与系统有关的路径分隔符。

static String separator;与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
static char separatorChar;与系统有关的默认名称分隔符。


File类的构造方法

  1. File(String pathname);通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

    • 参数:
      • String pathname 字符串的路径名称
      • 路径可以是真实存在,也可以不存在
      • 因为创建File对象,只是把字符串封装为File对象,不考虑路径的真假情况
  2. File(String parent, String child);根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

    • 参数:
      • String parent 父路径
      • String child 子路径
    • 好处:
      • 父路径和子路径可以单独的书写,使用起来灵活,父路径和子路径都可以变化
  3. File(File parent, String child);根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

    • 参数:
      • String parent 父路径
      • String child 子路径
    • 好处:
      • 父路径和子路径可以单独的书写,使用起来灵活,父路径和子路径都可以变化
      • 父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象

创建删除文件/文件夹相关的一些方法:

  1. public boolean createNewFile();当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
    • 文件不存在才会创建,文件存在则不创建
    • 创建文件的路径必须存在,否则会抛出异常
  2. public boolean delete();删除此抽象路径名表示的文件或目录。
  3. public boolean mkdir();创建此抽象路径名指定的目录。
    • 文件夹不存在才会创建,存在则不会创建。
    • 如果方法中的路径不存在,则返回false
  4. public boolean mkdirs();创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
    • 文件夹不存在才会创建,存在则不会创建。
    • 如果方法中的路径不存在,则返回false

File类中与获取相关的一些方法

  1. public String getAbsolutePath();返回此抽象路径名的绝对路径名字符串。
    • 无论构造方法中是相对路径还是绝对路径,此方法返回的都是绝对路径
  2. public String getPath();将此抽象路径名转换为一个路径名字符串。
    • 构造方法中是相对路径返回的就是相对路径,绝对路径返回的就是绝对路径。
  3. public String getName();返回由此抽象路径名表示的文件或目录的名称。
    • 获取的就是构造方法中传递路径的结尾部分,要么是文件,要么就是文件夹
  4. public long length();返回由此抽象路径名表示的文件的长度。
    • 获取的是构造方法指向的文件的大小,以字节为单位
    • 注意:
      1. 文件夹没有大小概念,不能获取文件夹大小
      2. 如果构造方法中给出的路径不存在,那么length方法返回0

File类中与判断相关的一些方法:

  1. public boolean exists();测试此抽象路径名表示的文件或目录是否存在。
  2. public boolean isDirectory();测试此抽象路径名表示的文件是否是一个目录。
    • 注意:使用前提是路径必须是存在的,否则都返回false
  3. public boolean isFile();测试此抽象路径名表示的文件是否是一个标准文件。
      • 注意:使用前提是路径必须是存在的,否则都返回false

File类遍历目录(文件夹)

  1. public String[] list();返回一个String数组,表示该File类目录中的所有子文件或目录
  2. public File[] listFiles();返回一个File数组,表示该File目录中的所有子文件或目录
  • 注意:
    1. list方法和listFiles方法遍历的是构造方法中给出的目录
    2. 如果构造方法中给出的目录的路径不存在,会抛出空指针异常
    3. 如果构造方法中给出的路径不是一个目录,也会抛出空指针异常

文件过滤器

  • 在File类中有两个和listFiles重载的方法,方法的参数传递的就是过滤器:

    • File[] listFiles(FileFilter filter)

      • java.io.FileFilter接口:用于抽象路径名的过滤器
        • 作用:用来过滤文件
        • 抽象方法:用来过滤文件的方法:
          • boolean accept(File pathname);测试指定抽象路径名是否应该包含在某个路径名列表中。
          • 参数:
            • File pathname:使用listFiles遍历目录得到的每一个文件对象
      • File[] listFiles(FileFilter filter)
      • listFiles做了三件事:
        1. 遍历了目录
        2. 把遍历的File对象传递给FileFilter实现类对象的accept方法
        3. 如果accept返回值为true则保存到File数组中
    • File[] listFiles(FilenameFilter filter)

      • java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名
        • 作用:用于过滤文件名称
        • 抽象方法:用来过滤文件的方法:
          • boolean accept(File dir, String name);测试指定文件是否应该包含在某一文件列表中。
          • 参数:
            • File dir:构造方法中传递的被遍历的目录
            • String name:使用listFiles方法遍历目录获取的每一个文件/文件夹的名称
      • 原理与上面差不多,只是将File dir与String name封装称一个对象
  • 注意:两个过滤器接口,没有实现类,需要自己写实现类,重写accept方法,在方法中自己定义过滤的规则

输入输出流

输入输出流

InputStream

  • java.io.InputStream:字节输入流。此抽象类是表示字节输入流的所有类的超类。

  • 定义了所有子类共性的方法:

    • abstract int read();从输入流中读取数据的下一个字节。读取到末尾返回-1。
    • int read(byte[] b);从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。返回值为为-1则读到结束标记。
    • int read(byte[] b, int off, int len);将输入流中最多 len 个数据字节读入 byte 数组。
    • void close();关闭此输入流并释放与该流关联的所有系统资源。
  • java.io.FileInputStream extends InputStream:文件字节输入流

  • 作用:把硬盘文件中的数据读取到内存中使用

  • 构造方法:

    • FileInputStream(File file);通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
    • FileInputStream(String name);通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
    • 参数:
      • File file,String name:读取文件的数据源
    • 作用:
      1. 创建一个FileInputStream对象
      2. 会把FileInputStream对象指向要读取的文件

OutputStream

  • java.io.OutputStream:此抽象类是表示输出字节流的所有类的超类。是一个抽象类,不能直接创建对象,需要用到它的子类。

  • 定义了一些子类共性的成员方法:

    • void close();关闭此输出流并释放与此流有关的所有系统资源。
    • void flush();刷新此输出流并强制写出所有缓冲的输出字节。
    • void write(byte[] b);将 b.length 个字节从指定的 byte 数组写入此输出流。
    • void write(byte[] b, int off, int len);将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
    • abstract void write(int b);`将指定的字节写入此输出流。
  • java.io.FileOutputStream extends OutputStream:文件字节输出流

  • 作用:

    • 把内存中的数据写入到硬盘的文件中
  • 构造方法:

    • FileOutputStream(File file);创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    • FileOutputStream(String name);创建一个向具有指定名称的文件中写入数据的输出文件流。
    • 参数:写入数据的目的地
      • File file:写入数据的文件
      • String name:写入数据的文件的路径
  • 构造方法作用:

    1. 创建一个FileOutputStream对象
    2. 根据构造方法中传递的文件或者文件的路径创建一个空的文件
    3. 会把FileOutputStream对象指向创建好的文件
  • 追加写:

  • 文件的追加写/续写:使用两个参数的构造方法

    • FileOutputStream(File file, boolean append);创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    • FileOutputStream(String name, boolean append);创建一个向具有指定 name 的文件中写入数据的输出文件流。
    • 参数:
      • File file,String name:写入数据的目的地
      • boolean append:追加写的开关,true为在文件末尾追加写数据,false则创建新文件覆盖原文件
  • 写换行:写换行符号

    1. windows:\r\n
    2. Linux:\n
    3. mac:\r

Reader

  • 使用字符流与字节流的区别:编码表不同,ascll中中文为2字节,UTF-8中为3字节,字节流单个读取中文时,再将其打印可能出现乱码

  • java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类。

  • 共性的成员方法:

    • int read();读取单个字符。
    • int read(char[] cbuf);将字符读入数组。
    • abstract void close();关闭该流并释放与之关联的所有资源。
  • java.io.FileReader extends InputStreamReader extends Reader:文件字符输入流

  • 作用:把硬盘文件中的数据以字符的方式读取到文件中

  • 构造方法:

    • FileReader(File file);在给定从中读取数据的 File 的情况下创建一个新 FileReader。
    • FileReader(String fileName);在给定从中读取数据的文件名的情况下创建一个新 FileReader。
    • 参数:
      • File file,String fileName:读取文件的数据源
  • 作用:

    1. 创建一个FileReader对象
    2. 会把FileReader对象指向要读取的文件

Writer

  • java.io.Writer:字符输出流。所有字符输出流的最顶层的父类,是一个抽象类,定义了共性的成员方法。

    • void write(char[] cbuf);写入字符数组。
    • abstract void write(char[] cbuf, int off, int len);写入字符数组的某一部分。
    • void write(int c);写入单个字符。
    • void write(String str);写入字符串。
    • void write(String str, int off, int len);写入字符串的某一部分。
    • abstract void close();关闭此流,但要先刷新它。
    • abstract void flush();刷新该流的缓冲。
  • java.io.FileWriter extends OutputStreamWriter extends Writer:文件字符输出流

  • 作用:把内存中的字符数据写入到内存缓冲区中,调用flush或者close方法后才把转换的字节刷新到硬盘中的文件中

  • 构造方法:

    • FileWriter(File file);根据给定的 File 对象构造一个 FileWriter 对象。
    • FileWriter(String fileName);根据给定的文件名构造一个 FileWriter 对象。
    • 参数:
      • File file,String fileName:写入数据的目的地
  • 作用:

    1. 会创建一个FileWriter对象
    2. 根据构造方法中传递的参数创建一个文件
    3. 把FileWriter对象指向创建好的文件
  • 追加写:

    • 与OutpputStream类似,续写用两个参数的构造方法,换行用换行符号

Properties

  • java.util.Properties extends HashTable< K,V > implements Map< K,V >

  • Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。

  • Properties集合是唯一一个和IO流相结合的集合

    • 可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。
    • 可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用。
  • 属性列表中每个键及其对应值都是一个字符串。

    • Properties集合是一个双列集合,key和value默认都是字符串。
  • Object setProperty(String key, String value);调用 Hashtable 的方法 put。

  • String getProperty(String key);用指定的键在此属性列表中搜索属性。

  • Set<String> stringPropertyNames();返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。

store

  • 使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储

  • void store(OutputStream out, String comments);以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。

  • void store(Writer writer, String comments);以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

  • 参数:

    • OutputStream out:字节输出流,不能写入中文
    • Writer writer:字符输入流,可以写入中文
    • String comments:注释,用来解释说明保存的文件是做什么的,不能使用中文,因为会产生乱码,默认是unicode编码,一般使用“”空字符串
  • 使用步骤:

    1. 创建Properties集合对象,添加数据
    2. 创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
    3. 使用Properties集合中的方法store
    4. 释放资源

load

  • 使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用

  • void load(InputStream inStream);从输入流中读取属性列表(键和元素对)。

  • void load(Reader reader);按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

  • 参数:

    • InputStream inStream:字节输入流,不能读取含有中文的键值对
    • Reader reader:字符输入流,能读取还含有中文的键值对
  • 使用步骤:

    1. 创建Properties集合对象
    2. 使用Properties集合中的方法load读取保存键值对的文件
    3. 遍历Properties集合
  • 注意:

    1. 存储键值对的文件中,键与值默认的连接符号可以使用等号,空格,其他符号
    2. 存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
    3. 存储键值对的文件中,键与值,默认都是字符串不用再加引号

BufferedInputStream

  • java.io.BufferedInputStream extends FilterInputStream extends InputStream:字节缓冲输入流

  • 构造方法:

    • BufferedInputStream(InputStream in);创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
    • BufferedInputStream(InputStream in, int size);创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
  • 参数:

    • InputStream in:字节输入流,给其增加一个缓冲区,提高读取效率
    • int size:指定的缓冲区大小,不写则是默认的
  • 使用步骤:

    1. 创建一个字节输入流对象,构造方法中绑定读取的数据源
    2. 创建一个BufferedInputStream对象,构造方法中传递字节输入流对象,提高字节输入流读取效率
    3. 使用BufferedInputStream对象中的方法read读取文件
    4. 释放资源

BufferedOutputStream

  • java.io.BufferedOutputStream extends FilterOutputStream extends OutputStream:字节缓冲输出流

  • 构造方法:

    • BufferedOutputStream(OutputStream out);创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
    • BufferedOutputStream(OutputStream out, int size);创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
  • 参数:

    • OutputStream out:字节输出流,给传递的参数添加一个缓冲区,提高它的写入效率
    • int size:指定缓冲流内部缓冲区的大小,不指定则为默认大小
  • 使用步骤:

    1. 创建一个字节输出流对象
    2. 创建BufferedOutputStream对象,构造方法中传递字节输出流对象,提高它的效率
    3. 使用BufferedOutputStream对象中的方法write把数据写入到内部的缓冲区中
    4. 使用BufferedOutputStream中的方法flush把内部缓冲区中的数据刷新到文件中
    5. 释放资源

缓冲流原理

tqfXvt.jpg


BufferedReader

  • java.io.BufferedReader extends Reader:字符缓冲输入流

  • 构造方法:

    • BufferedReader(Reader in);创建一个使用默认大小输入缓冲区的缓冲字符输入流。
    • BufferedReader(Reader in, int sz);创建一个使用指定大小输入缓冲区的缓冲字符输入流。
  • 参数:

    • Reader in:字符输入流,缓冲流为其增加一个缓冲区,提高读取的效率
    • int sz;指定缓冲流的缓冲区大小,不写则为默认
  • 特有的成员方法:

    • String readLine();读取一个文本行。 读取一行数据
      • 行的终止符号:通过下列字符之一即可认为某行已终止:换行 (‘\n’)、回车 (‘\r’) 或回车后直接跟着换行(\r\n)。
      • 返回值:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
  • 使用步骤:

    1. 创建字符缓冲输入流对象,构造方法中传递字符输入流
    2. 使用字符缓冲输入流对象中的方法read/readLine读取文本
    3. 释放资源

BufferedWriter

  • java.io.BufferedWriter extends Writer:字符缓冲输出流

  • 构造方法:

    • BufferedWriter(Writer out);创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    • BufferedWriter(Writer out, int sz);创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
  • 参数:

    • Writer out:字符输出流,缓冲流会给其一个缓冲区,提高写入的效率
    • int sz:指定缓冲区的大小,不写则默认大小
  • 特有的成员方法:

    • void newLine();写入一个行分隔符。会根据不同的操作系统获取不同的行分隔符
  • 使用步骤:

    1. 创建一个字符缓冲输出流对象,构造方法中传递字符输出流
    2. 调用字符缓冲输出流的方法write,把数据写入到内存缓冲区中
    3. 调用flush方法
    4. 释放资源

InputStreamReader

  • java.io.InputStreamReader extends Reader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。(解码)

  • 构造方法:

    • InputStreamReader(InputStream in);创建一个使用默认字符集的 InputStreamReader。
    • InputStreamReader(InputStream in, String charsetName);创建使用指定字符集的 InputStreamReader。
    • 参数:
      • InputStream in:字节输入流,用来读取文件中保存的字节
      • String charsetName:指定的编码表名称
  • 使用步骤:

    1. 创建一个InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    2. 使用InputStreamReader对象中的方法读取文件
    3. 释放资源
  • 注意:构造方法中指定的编码表要和文件编码相同,否则会发生乱码


OutputStreamWriter

  • java.io.OutputStreamWriter extends Writer:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。(编码)

  • 构造方法:

    • OutputStreamWriter(OutputStream out);创建使用默认字符编码的 OutputStreamWriter。
    • OutputStreamWriter(OutputStream out, String charsetName);创建使用指定字符集的 OutputStreamWriter。
    • 参数:
      • OutputStream out:字节输出流,可以用来写转换之后的文件到文件中
      • String charsetName:指定的编码表名称,不区分大小写
  • 使用步骤:

    1. 创建一个OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
    2. 使用OutputStreamWriter对象中的方法write把字符转换为字节,存储到缓冲区中
    3. 使用OutputStreamWriter中的方法flush把缓冲区的字节刷新到文件中(使用字节流写字节的过程)
    4. 释放资源

ObjectInputStream

  • java.io.ObjectInputStream extends InputStream:对象的反序列化流,把文件中保存的对象以流的方式读取出来使用

  • 构造方法:

    • ObjectInputStream(InputStream in);创建从指定 InputStream 读取的 ObjectInputStream。
    • 参数:
      • InputStream in:字节输入流
  • 特有的成员方法:

    • Object readObject();从 ObjectInputStream 读取对象。
  • 使用步骤:

    1. 创建一个ObjectInputStream对象,构造方法中传递字节输入流
    2. 使用ObjectInputStream对象中的方法readObject读取保存对象的文件
    3. 释放资源

ObjectOutputStream

  • java.io.ObjectOutputStream extends OutputStream:对象的序列化流,把对象以流的方式写入到文件中保存

  • 构造方法:

    • ObjectOutputStream(OutputStream out);创建写入指定 OutputStream 的 ObjectOutputStream。
    • 参数:
      • OutputStream out:字节输出流
  • 特有的成员方法:

    • void writeObject(Object obj);将指定的对象写入ObjectOutputStream。
  • 使用步骤:

    1. 创建一个ObjectOutputStream对象,构造方法中传递字节输出流
    2. 使用ObjectOutputStream对象中的方法writeObject把对象写入到文件中
    3. 释放资源

对象的序列化与反序列化

  • 当这个类创建的对象需要序列化时,必须实现Serializable接口,不然会抛出NotSerializableException异常
  • Serializable也叫标记型接口:要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
  • 当我们进行序列化和反序列化的时候,就会检测类上是否有标记,如果有,就可以序列化和反序列化如果没有,就会抛出异常

tq4PeK.jpg

  • 序列化时可能会遇到的序列号问题InvalidClassException的解决方案:手动添加一个序列号
    tq4Wm6.jpg

请我喝杯咖啡吧~

支付宝
微信