当前位置:   article > 正文

java 8 教程(基础篇)_java8

java8

java 8 教程(基础篇)

注意:Java 教程是为 JDK 8编写的。本页中描述的示例和实践没有利用后期版本中引入的改进,并且可能使用不再可用的技术。有关 Java SE 9和后续版本中更新的语言特性的摘要,请参阅 Java 语言更改。有关所有 JDK 版本的新特性、增强功能以及已删除或已弃用选项的信息,请参阅 JDK 发布说明。
本文是对官方教程的简单总结,省略了许多示例,阅读中如有疑惑请参阅文章末尾的参考链接查看官方教程。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

入门指南

Java技术现象

  • 关于 Java

    • 既是一种编程语言,也是一种平台。

    • 编程语言

      • 标签:简单,面向对象,分布式,多线程,动态,体系结构中立,可移植,高性能,健壮,安全
      • .java -> .class
      • 一次编写,到处运行
    • java 平台

      • Java 虚拟机
      • Java 应用程序编程接口
  • Java 可以做什么

    • 开发工具

      • javac,java,javadoc
    • 应用程序接口

    • 部署技术

      • Java Web Start,Java Plug-In
    • 用户界面工具箱

      • JavaFX,Swing,2D toolkits
    • 集成库

      • IDL,JDBC,JNDI,RMI,RMI-IIOP

“Hello World!”应用程序

  • 用于NetBeans IDE
  • 用于微软操作系统
  • 适用于Solaris OS、Linux和Mac OS X

深入“Hello World!”

  • 代码注释

    • // text
    • /* text */
    • /** documentation */
  • 类定义

    • class name { }
  • main 方法

    • public static void main(String[] args)

常见问题

基本概念与特性

面向对象编程概念

  • 对象

    • 包含相关状态和行为的软件包。软件对象通常用于对日常生活中发现的真实世界对象建模。
    • 好处:模块性,信息隐藏,代码重用,可插性,调试简单
    • 创建对象的蓝图或原型。
  • 继承

    • 一种机制
  • 接口

    • 类与外部世界之间的契约。当一个类实现一个接口时,它承诺提供该接口发布的行为。
    • 以逻辑方式组织类和接口的名称空间。将代码放入包中可以使大型软件项目更容易管理。

语言基础

  • 变量

    • 实例变量(非静态字段)

    • 类变量(静态字段)

    • 局部变量

    • 参数

    • 命名规则

      • 变量名区分大小写。变量名可以是任何合法的标识符——一个无限长的 Unicode 字母和数字序列,以字母、美元符号“$”或下划线“_”开头。但是,按照惯例,变量名总是以字母开头,而不是“$”或“_”。此外,按照惯例,美元符号根本就不用。您可能会发现在某些情况下,自动生成的名称将包含美元符号,但您的变量名称应始终避免使用它。对于下划线字符也存在类似的约定;虽然从技术上讲,变量名以“_”开头是合法的,但这种做法是不鼓励的。不允许留白。
      • 后面的字符可以是字母、数字、美元符号或下划线。惯例(和常识)也适用于这条规则。在为变量选择名称时,使用完整的单词而不是隐晦的缩写。这样做将使您的代码更容易阅读和理解。在许多情况下,它还会使你的代码自文档化;例如,命名为 cadence、speed 和 gear 的字段比缩写版本(如s、c和g)要直观得多。还要记住,您选择的名称不能是关键字或保留字。
      • 如果您选择的名称只包含一个单词,请将该单词全部拼写为小写字母。如果包含多个单词,则后面每个单词的首字母大写。名称 gearRatio 和 currentGear 是这种约定的主要例子。如果变量存储的是一个常量值,比如 static final int NUM_GEARS = 6,则惯例略有变化,每个字母都大写,后面的单词用下划线分隔。按照惯例,下划线字符永远不会在其他地方使用。
    • 基本数据类型

      • byte

        • 字节数据类型是一个8位有符号二补整数。最小值为-128,最大值为127(包括)。在大型数组中,字节数据类型对于节省内存非常有用,因为节省内存实际上很重要。它们也可以用来代替 int,它们的限制有助于澄清你的代码;变量的范围是有限的这一事实可以作为一种文档形式。
      • short

        • 短数据类型是一个16位有符号二补整数。最小值为-32,768,最大值为32,767(含)。与使用byte一样,同样的原则也适用:在大型数组中,在节省内存非常重要的情况下,可以使用 short 来节省内存。
      • int

        • 默认情况下,int 数据类型是32位有符号的二补型整数,最小值为-231,最大值为231-1。在 Java SE 8及更高版本中,可以使用 int 数据类型表示无符号32位整数,该整数的最小值为0,最大值为232-1。使用 Integer 类将int 数据类型用作无符号整数。有关更多信息,请参阅数字类一节。像 compareUnsigned, divideUnsigned 等静态方法已经添加到Integer类中,以支持无符号整数的算术操作。
      • long

        • 长数据类型是一个64位的二进制补码整数。signed long的最小值为-263,最大值为263-1。在Java SE 8及更高版本中,可以使用 long 数据类型表示无符号64位 long,其最小值为0,最大值为264-1。当需要的值范围大于 int提供的值范围时,请使用此数据类型。Long 类还包含类似 compareUnsigned, divideUnsigned 等方法来支持unsigned Long 的算术运算。
      • float

        • 浮点数据类型是一个单精度32位 IEEE 754浮点数。它的值范围超出了本文的讨论范围,但在 Java 语言规范的浮点类型、格式和值一节中指定。与字节和 short 的建议一样,如果需要在大型浮点数数组中节省内存,则使用float(而不是double)。这种数据类型永远不应该用于精确的值,比如货币。为此,您将需要使用java.math.BigDecimal 类。数字和字符串涵盖BigDecimal 和其他有用的类
      • double

        • 双数据类型是双精度64位 IEEE 754浮点数。它的值范围超出了本文的讨论范围,但在 Java 语言规范的浮点类型、格式和值一节中指定。对于十进制值,此数据类型通常是默认选择。如上所述,这种数据类型永远不应该用于精确的值,比如货币。
      • boolean

        • 布尔数据类型只有两个可能的值: true 和 false。将此数据类型用于跟踪 true/false 条件的简单标记。这种数据类型表示一个比特信息,但它的“大小”并不是精确定义的。
      • char

        • char 数据类型是一个16位 Unicode 字符。它的最小值为’\u0000’(或0),最大值为’\uffff’(或65,535包括在内)。
      • 除了上面列出的8种基本数据类型之外,Java 编程语言还通过 Java .lang. String 类提供了对字符串的特殊支持。用双引号括住你的字符串会自动创建一个新的 String 对象;例如,String s = "this is a String ";字符串对象是不可变的,这意味着一旦创建,它们的值就不能更改。String类在技术上不是基本数据类型,但考虑到语言给予它的特殊支持,您可能倾向于认为它是基本数据类型。

      • 默认值

        • 声明字段时并不总是需要赋值。已声明但未初始化的字段将被编译器设置为合理的默认值。一般来说,这个默认值将为零或 null,这取决于数据类型。然而,依赖这些默认值通常被认为是糟糕的编程风格。
        • byte:0,short:0,int:0,long:0L,float:0.0f,double:0.0d,char:‘\u0000’,String(或任何对象):null,boolean:false
        • 局部变量略有不同;编译器从不为未初始化的局部变量赋值。如果不能在声明局部变量的地方初始化它,请确保在尝试使用它之前为它赋值。访问未初始化的局部变量将导致编译时错误。
      • 字面量

        • 基本类型是内置在语言中的特殊数据类型;它们不是从类中创建的对象。字面量是一个固定值的源代码表示;字面量直接在代码中表示,不需要计算。

        • 整数字面值

          • 如果一个整型字面值以字母L或L结尾,则它的类型为long;否则为 int 类型。建议使用大写字母“L”,因为小写字母“L”和数字“1”很难区分。
          • 整型 byte、short、int 和 long 的值可以从 int 字面值中创建。可以从长字面值创建超过 int 范围的 long 类型值。整数字面量可以用以下数字系统表示:
          • 十进制:以10为基数,由数字0到9组成;这是你每天使用的数字系统
            十六进制:以16为进制,由数字0 ~ 9和字母 A ~ F 组成
            二进制:基数2,其数字由数字0和1组成(可以在 Java SE 7及更高版本中创建二进制文字)
          • 对于通用编程,十进制可能是您将使用的唯一数字系统。但是,如果需要使用另一种数字系统,前缀0x表示十六进制,0b 表示二进制:
        • 浮点字面值

          • 浮点字面值如果以字母F或f结尾,则为 float 类型;否则它的类型是 double,并且可以选择以字母 D 或 d 结尾。
          • 浮点类型(浮点和双精度)也可以用 E 或 e (用于科学计数法)、F 或 f (32位浮点字面值)和 D 或 d (64位双精度字面值;这是默认值,按惯例省略)。
        • 字符与字符串字符值

          • char 和 String 类型的文字可以包含任何 Unicode (UTF-16)字符。如果您的编辑器和文件系统允许,您可以直接在代码中使用这些字符。如果不是,你可以使用“Unicode转义”,如“\u0108”(大写C带回旋),或“S\u00ED Se\ u00f1”(西班牙语为Sí Señor)。对于字符字面值总是使用“单引号”,对于字符串字面值总是使用“双引号”。Unicode 转义序列可以在程序的其他地方使用(例如在字段名中),而不仅仅是在 char 或 String 字面量中使用。
          • Java 编程语言还支持一些针对 char 和 String 字面值的特殊转义序列:\b(退格)、\t(制表符)、\n(换行)、\f(换行)、\r(回车)、"(双引号)、'(单引号)和\(反斜杠)。
          • 还有一个特殊的空文字,可以用作任何引用类型的值。null 可以赋给任何变量,但基本类型的变量除外。对于空值,除了测试它是否存在之外,几乎没有什么可以做的。因此,在程序中经常使用null作为标记,表示某些对象不可用。
        • 类字面量

          • 由取类型名并附加".class"构成;例如,String.class。这引用了表示类型本身的对象(类型为Class)。
      • 在数字字符中使用下划线字符

        • 在 Java SE 7及更高版本中,任何数量的下划线字符(_)都可以出现在数值文字中数字之间的任何位置。例如,该特性使您能够将数字字面值中的数字组分开,这可以提高代码的可读性。
        • 您只能在数字之间放置下划线
    • 数组

      • 声明一个变量引用一个数组

        • int[] anArray;
      • 创建、初始化和访问数组

        • anArray = new int[10];
        • anArray = { }
      • 复制数组

        • System 类的 arraycopy 方法
      • 数组操作

        • java.util.Arrays 类
  • 运算符

    • 优先级

    • 赋值

      • =
    • 算数

      • +, -, *, /, %
    • 一元运算符

      • +, -, ++, --, !
    • 相等

      • ==
    • 关系

      • !=, >, >=, <, <=
    • 条件

      • &&,||(短路)
      • 三元运算 ?:
    • 类型比较

      • instanceof
    • 位与位移

      • <<, >>
      • >>>
      • &, |, ~, ^
  • 表达式,语句和块

    • 表达式是由变量、操作符和方法调用组成的构造,这些变量、操作符和方法调用是根据语言的语法构造的,计算结果为单个值。
    • 语句大致相当于自然语言中的句子。一条语句形成了一个完整的执行单元。通过用分号(;)结束表达式。
    • 块是一组介于平衡花括号之间的零条或多条语句,可以在任何允许使用一条语句的地方使用。
  • 控制流程语句

    • if-then 和 if-then-else 语句

    • switch 语句

    • while 和 do-while 语句

    • for 语句

    • 分支语句

      • break 语句

        • 有标记
        • 无标记
      • continue 语句

      • return 语句

类与对象

    • 声明类

      • 修饰符,类名,父类,实现接口列表,类体
    • 声明成员变量

      • 分类

        • 类中的成员变量:字段
        • 方法或代码块中的变量:局部变量
        • 方法声明中的变量:参数
      • 访问修饰符

        • public,private
      • 类型

      • 变量名

    • 定义方法

      • 修饰符,返回类型,方法名,参数列表,异常列表,方法体

      • 方法签名:方法名和参数类型

      • 方法命名

      • 重载方法

      • 构造方法

      • 给方法或构造方法传递信息

        • 参数类型
        • 任意数量的参数
        • 参数名
        • 传递基本数据类型参数(值传递)
        • 传递引用数据类型参数(值传递)
  • 对象

    • 创建对象

      • 声明,实例化,初始化

      • 声明一个变量来引用一个对象

      • 实例化一个类

        • new 运算符
        • 构造函数
    • 使用对象

      • 引用一个对象的字段
      • 调用对象的方法
      • 垃圾收集器
  • 深入类

    • 方法的返回值

      • 返回时机

        • 完成方法中所有的语句
        • 执行到了 return 语句
        • 抛出一个异常
      • 返回一个类或接口

        • 返回对象的类型必须是返回类型或该类型的子类
        • 协变返回类型:返回类型允许与子类沿同一方向变化
        • 接口可用作返回类型,那么返回的对象必须实现该接口
    • this 关键字

      • 实例方法或构造函数中
      • this 与字段的使用
      • this 与构造函数的使用
    • 访问控制

      • 访问层级

        • 顶级

          • public, 包私有(没有指定修饰符)
        • 成员级

          • public,private,protected,包私有
    • 类成员

      • 类变量

        • static
        • 通过类名引用
      • 类方法

      • 常量

        • static final
        • 编译时常量
    • 初始化字段

      • 静态初始化块

      • 初始化块

        • 构造函数间共享
      • 使用 final 方法初始化实例变量(用于子类希望重用初始化方法)

  • 嵌套类

    • 使用嵌套类的原因

      • 是一种对只在一个地方使用的类进行逻辑分组的方法
      • 增加了封闭性
      • 可以产生更可读和更易于维护的代码
    • 内部类(非静态)

      • 不可以定义静态成员,可以定义常量

      • 内部类的实例对象存在于外部类的实例中

        • OuterClass.InnerClass innerObject = outObject.new InnerClass();
      • 举例

        • 使用内部类来实现 helper 类
        • 要处理用户界面事件,您必须知道如何使用内部类,因为事件处理机制广泛使用了他们。
      • 修饰符

        • 与外部类成员使用相同的修饰符
      • 本地类

        • 在块中定义,一般是在方法中

        • 声明

        • 访问外围类的成员

          • 只可以访问声明为 final 的本地变量
          • 从 java8 开始,本地类可以访问封闭块中为 final 或有效 final 的局部变量和参数(一个变量或参数的值在初始化后从未改变过称为有效 final)
      • 匿名类

        • 和本地类类似,除了没有名字

        • 声明

          • 一个表达式
        • 语法

          • 类似于构造函数的调用,只不过代码块中包含一个类的定义
          • new 运算符,要实现的接口名或要继承的类,包含构造函数参数的括号(如果是接口的话,是空参),类定义体
        • 访问外围作用域中的本地变量

          • 访问外围类中的成员
          • 不能访问外围作用域中没有声明为 final 或有效 final 的本地变量
          • 与本地类的约束一样不可以声明静态初始化器或成员接口
          • 不可以声明构造函数
        • 声明与访问匿名类的成员

      • Lambda 表达式

        • 函数作为方法参数或者说代码作为数据

        • 理想用例

          • 创建搜索匹配某一特性成员的方法

          • 创建更通用的搜索方法

          • 在本地类中指定搜索条件

          • 在匿名类中中指定搜索条件

          • 在 Lambda 表达式中指定搜索条件

          • 使用 Lambda 表达式的标准函数接口

            • 函数接口:只包含一个抽象方法
            • java.util.function
          • 在应用中使用 Lambda 表达式

          • 更广泛的使用泛型

          • 使用接受 Lambda 表达式作为参数的聚合操作

            • stream(),filter(),map(),forEach()
        • GUI 程序中的 Lambda 表达式

        • 语法

          • 用逗号分隔的形式参数列表,用括号括起来(参数数据类型可省略,如果只有一个参数,可以省略括号)

          • 箭头符号:->

          • 主体

            • 单个表达式或语句块
            • 返回计算的值或使用 return 语句
        • 访问外围作用域中本地变量

          • 词法范围:不会从超类型继承任何名称,也不会引入新的作用域级别
          • 只能访问包含块的 final 或 有效 final 类型的局部变量和参数
        • 目标类型

          • 方法所期望的数据类型

          • 只能在 Java 编译器可以确定目标类型的情况下使用lambda 表达式

          • 方法参数

            • Java编译器用另外两个语言特性确定目标类型
            • 重载解析
            • 类型参数推断
        • 序列化

          • 不推荐
        • 方法引用

          • 分类

            • 引用静态方法

              • ContainingClass::staticMethodName
            • 引用特定对象的实例方法

              • containingObject::instanceMethodName
            • 引用特定类型的任意对象的实例方法

              • ContainingType::methodName
            • 引用构造函数

              • ClassName::new
    • 静态嵌套类

      • 在行为上是一个顶级类
    • 影子

      • 如果特定作用域中的类型声明与封闭作用域中的另一个声明同名,则该声明将隐藏封闭作用域的声明
      • x, this.x, Shadow.this.x
    • 序列化

      • 不同编译器对内部类的序列化结果可能不同
    • 使用时机

      • 本地类

        • 如果需要创建一个类的多个实例、访问其构造函数或引入一个新的命名类型(例如,因为稍后需要调用其他方法),请使用它。
      • 匿名类

        • 如果需要声明字段或其他方法,可以使用它。
      • Lambda 表达式

        • 如果您正在封装想要传递给其他代码的单个行为单元,请使用它。例如,如果您希望在流程完成或流程遇到错误时对集合的每个元素执行某个操作,则可以使用lambda表达式。
        • 如果您需要一个功能接口的简单实例,并且前面的条件都不适用(例如,您不需要构造函数、命名类型、字段或其他方法),请使用它。
      • 嵌套类

        • 如果您的需求与局部类类似,您希望使该类型更广泛地可用,并且您不需要访问局部变量或方法参数,请使用它。
        • 如果需要访问封闭实例的非公共字段和方法,请使用非静态嵌套类(或内部类)。如果不需要这种访问,请使用静态嵌套类。
  • 枚举类型

    • 所有枚举都隐式地扩展 java.lang.Enum,因此枚举不能扩展其他任何东西。
    • 枚举类型的构造函数必须是包私有或私有访问。它自动创建在枚举体开头定义的常量。您不能自己调用枚举构造函数。
    • 枚举常量列表必须以分号结束。

注解

  • 元数据的一种形式,提供关于程序的数据,而这些数据不是程序本身的一部分。注释对它们所注释的代码的操作没有直接影响。

  • 应用

    • 编译器信息
    • 编译时和部署时处理
    • 运行时处理
  • 基础

    • 注释的格式

    • java8 支持重复注释

    • 注释可以使用的地方

      • 声明:类、字段、方法和其他程序元素的声明。在声明上使用时,根据约定,每个注释通常出现在自己的行上。

      • 从 java8 开始,注释也可以应用于类型的使用

        • new @Interned MyObject();
        • myString = (@NonNull String) str;
        • class UnmodifiableList implements
          @Readonly List<@Readonly T> { … }
        • void monitorTemperature() throws
          @Critical TemperatureException { … }
  • 声明一个注解类型

    • @interface
    • 可以定义可选的默认值
  • 预定义注解类型

    • @Deprecated

    • @Override

    • @SuppressWarnings

    • @SafeVarargs

    • @FunctionalInterface

    • 适用于其他注解的注解

      • @Retention
      • @Documented
      • @Target
      • @Inherited
      • @Repeatable
  • 类型注解可插拔类型系统

    • 注解可以应用于任何类型的使用。这意味着注解可以在使用类型的任何地方使用。
    • 例:类实例创建表达式(new)、类型转换、实现子句和抛出子句。
    • 为了支持改进的Java程序分析,以确保更强的类型检查。Java SE 8发行版没有提供类型检查框架,但是它允许您编写(或下载)类型检查框架,该框架实现为一个或多个与Java编译器一起使用的可插拔模块。
  • 重复注解

    • 出于兼容性考虑,重复注释存储在Java编译器自动生成的容器注释中。为了让编译器做到这一点,在代码中需要两个声明。
    • 声明一个可重复注解类型
    • 声明包含注解类型,包含注解类型的值元素必须具有数组类型。数组类型的组件类型必须是可重复的注解类型。
  • 检索注解

    • Reflection API
  • 设计考虑

    • 基数性

      • 现在可以使用注解0次(一次),或者(如果注解的类型被标记为@Repeatable)多次。
    • 位置

      • 还可以通过使用 @Target 元注释来限制注释类型的使用位置。

接口

  • 一种引用类型,类似于类,只能包含常量、方法签名、默认方法、静态方法和嵌套类型。方法体只存在于默认方法和静态方法中。接口不能被实例化——它们只能由类实现或由其他接口扩展

  • 接口作为 api

  • 定义接口

    • public,interface,extends

    • 接口体

      • 抽象方法,默认方法,静态方法,常量声明
      • 接口中的所有抽象方法、默认方法和静态方法都是隐式公共方法,因此可以省略 public 修饰符。常量声明同理。
  • 实现接口

    • 在类声明中包含 implements 子句。
    • implements 关键字后面跟着一个由该类实现的接口列表,以逗号分隔。
    • 按照惯例,implements 子句跟在 extends 子句后面(如果有的话)。
  • 以类型的方式使用接口

    • 定义一个新接口时,就是在定义一个新的引用数据类型。可以在任何可以使用任何其他数据类型名称的地方使用接口名称。
  • 默认方法

    • 默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

    • 扩展包含默认方法的接口

      • 完全不要提及默认方法,这将使您的扩展接口继承默认方法。
      • 重新声明默认方法,使其抽象。
      • 重新定义默认方法,该方法将覆盖它。
    • 静态方法

      • 这让你更容易在你的库中组织帮助方法;您可以在同一个接口中保留特定于某个接口的静态方法,而不是在一个单独的类中。
    • 将默认方法集成到现有库中

      • 以 Comparator 接口为例

继承

  • 定义

    • 从另一个类派生的类称为子类(也称为派生类、扩展类或子类)。派生子类的类称为超类(也称为基类或父类)。
  • 除了 Object,它没有超类,每个类都有且只有一个直接超类(单继承)。在没有任何其他显式超类的情况下,每个类都隐式地是Object 的子类。

  • 子类从父类继承所有成员(字段、方法和嵌套类)。构造函数不是成员,因此不能由子类继承,但是父类的构造函数可以从子类调用。

  • Java 平台类层次结构

  • 在子类中可以做什么

    • 子类继承父类的所有 public 和 protected 成员,无论子类在哪个包中。如果子类与其父类在同一个包中,它还继承父类的包私有成员。你可以按原样使用继承的成员,替换它们,隐藏它们,或者用新成员补充它们:
    • 可以编写调用父类构造函数的子类构造函数,可以隐式调用,也可以使用关键字 super。
  • 超类的私有成员

  • 转换对象

  • 状态、实现和类型的多重继承

    • Java 编程语言不允许扩展多个类的一个原因是为了避免状态的多重继承问题,即从多个类继承字段的能力。因为接口不包含字段,所以不必担心状态的多重继承所导致的问题。
    • Java 编程语言支持类型的多重继承,即类实现多个接口的能力。一个对象可以有多种类型:它自己的类的类型和类实现的所有接口的类型。
  • 覆盖和隐藏方法

    • 实例方法

    • 静态方法

    • 被调用的被重写实例方法的版本是子类中的版本。

    • 被调用的隐藏静态方法的版本取决于它是从父类还是子类调用的。

    • 接口方法

      • 实例方法优先于接口默认方法。
      • 已经被其他候选对象覆盖的方法将被忽略。
      • 接口中的静态方法不会被继承。
      • 可以使用 super 关键字调用任何默认实现
    • 覆盖方法的访问说明符可以允许比被覆盖方法更多的访问,但不能更少。

    • 重载

  • 多态性

    • 类的子类可以定义它们自己独特的行为,同时还可以共享父类的一些相同功能。
    • Java 虚拟机(JVM)为每个变量中引用的对象调用适当的方法。它不调用由变量类型定义的方法。这种行为被称为虚拟方法调用,它展示了 Java 语言中重要的多态性特性的一个方面。
  • 隐藏字段

    • 在类中,与超类中的字段具有相同名称的字段将隐藏超类的字段,即使它们的类型不同。
    • 在子类中,父类中的字段不能通过其简单名称引用。相反,必须通过 super 访问该字段
  • super 关键字

    • 访问超类成员

    • 子类的构造函数

      • 如果构造函数没有显式调用超类构造函数,Java编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参数构造函数,则会得到编译时错误。
      • 构造函数链接
  • Object 类

    • protected Object clone() throws CloneNotSupportedException

      • Cloneable 接口
      • 如果一个对象包含一个外部对象的引用,你可能需要重写clone() 来获得正确的行为。
    • public boolean equals(Object obj)

      • Object 中测试的是引用是否相等
      • 如果重写 equals(),也必须重写 hashCode()。
    • protected void finalize() throws Throwable

      • Object 的实现为空
      • 可以由系统自动调用,但是何时调用,甚至是否调用都是不确定的。因此,不要依赖这种方法来为你做清理工作。
    • public final Class getClass()

      • 不可以能重写 getClass()。
    • public int hashCode()

    • public String toString()

    • 线程同步相关

      • public final void notify()
        public final void notifyAll()
        public final void wait()
        public final void wait(long timeout)
        public final void wait(long timeout, int nanos)
  • 编写 Final 类和方法

    • 从构造函数调用的方法通常应该声明为final。如果构造函数调用非final方法,子类可能会以令人惊讶或不希望的结果重新定义该方法。
    • 声明为 final 的类不能被子类化。
  • 抽象类和方法

    • 声明为抽象的类,它可以包含也可以不包含抽象方法。抽象类不能被实例化,但可以被子类化。
    • 当抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。然而,如果不是这样,那么子类也必须声明为抽象的。
    • 与接口相比
    • 静态成员

数字与字符串

  • 数字

    • Number 类

      • 所有数字包装类都是 Number 类的子类

      • 使用 Number 对象的情况

        • 作为期望对象的方法的参数
        • 使用类定义的常量,如 MIN_VALUE 和 MAX_VALUE,它们提供数据类型的上界和下界。
        • 使用类方法将值与其他基本类型进行转换,将值与字符串进行转换,并在数字系统(十进制、八进制、十六进制、二进制)之间进行转换。
      • Number 子类需要实现的方法

        • 类型转换:xxxValue()
        • 比较:compareTo()
        • 判等:equals()
    • 数字打印输出格式化

      • printf 和 format 方法

        • PrintStream 类
        • java.util.Formatter
      • DecimalFormat 类

        • 控制前导和后导零、前缀和后缀、分组(千位)分隔符和小数分隔符的显示。
    • Math 类

      • 静态导入

        • import static java.lang.Math.*;
      • 常量

        • PI,E
      • 基本方法

        • 绝对值

        • 四舍五入

        • 上界,下界

        • 最小,最大

        • 指数,对数

        • 三角函数

        • 随机数

          • 当你需要生成单个随机数时,Math.random很有效。如果需要生成一系列随机数,则应该创建java.util.Random的实例,并调用该对象上的方法来生成数字。
  • Character 类

    • 不可变

    • 方法

      • 判断字母,数字,空格,大写,小写
      • 转大写,小写
    • 转义字符

  • 字符串

    • 创建 String

      • “ ”,字符数组
      • 不可变
    • 字符串长度

    • 字符串连接

      • concat(),+
      • Java编程语言不允许字面值字符串在源文件中跨行
    • 创建格式字符串

      • String.format()
    • 数字和字符串之间的转换

      • 字符串转数字

        • valueOf()
        • parseXXXX()
      • 数字转字符串

        • toString()
    • 操作字符串中的字符

      • 获取

        • charAt()

        • substring()

        • 其他方法

          • split(),subSequence(),trim(),toLowerCase()
      • 搜索

        • indexOf(),contains()
      • 替换

        • replace()
    • 比较与部分比较

      • endsWith,startsWith,compareTo,compareToIgnoreCase,equals,equalsIgnoreCase,regionMatches,matches
    • StringBuilder 类

      • 与String对象类似,只是它们可以被修改。在内部,这些对象被视为包含字符序列的变长数组。在任何时候,序列的长度和内容都可以通过方法调用来更改。

      • 长度和容量

        • setLength,ensureCapacity
      • 操作

        • append,insert,delete,replace,reverse
  • 自动装箱与拆箱

泛型

  • 为什么使用泛型

    • 在编译时更强的类型检查。
    • 类型转换的消除。
    • 使程序员能够实现泛型算法。
  • 泛型类型

    • 是通过类型参数化的泛型类或接口。

    • 类型参数命名约定

      • E - Element (used extensively by the Java Collections Framework)
      • K - Key
      • V - Value
      • N - Number
      • T - Type
      • S,U,V etc. - 2nd, 3rd, 4th types
    • 调用和实例化泛型类型

    • 菱形

      • 在Java SE 7及更高版本中,只要编译器能够从上下文确定或推断类型参数,就可以将调用泛型类的构造函数所需的类型参数替换为类型参数的空集(<>)。这对尖括号<>,被非正式地称为菱形
    • 多种类型参数

    • 参数化类型

    • 粗类型

      • 是没有任何类型参数的泛型类或接口的名称,非泛型类或接口类型不是粗类型。

      • 为了向后兼容,允许将参数化类型赋值给粗类型

      • 未检查错误消息

        • Note: Example.java uses unchecked or unsafe operations.
          Note: Recompile with -Xlint:unchecked for details.
        • 当使用操作粗类型的旧API时,可能会发生这种情况
        • 术语“未检查”意味着编译器没有足够的类型信息来执行确保类型安全所需的所有类型检查。
  • 泛型方法

    • 泛型方法是引入自己的类型参数的方法。这类似于声明泛型类型,但类型参数的作用域仅限于声明它的方法。允许使用静态和非静态泛型方法,以及泛型类构造函数。

    • 语法

      • 包括一个类型参数列表,位于尖括号内,出现在方法的返回类型之前。对于静态泛型方法,类型参数段必须出现在方法的返回类型之前。
    • 类型推断

      • 允许您将泛型方法作为普通方法调用,而不需要在尖括号之间指定类型。
  • 受限类型参数

    • 上界:extends

    • 多个边界:&

      • 具有多个边界的类型变量是边界中列出的所有类型的子类型。如果其中一个边界是类,则必须首先指定它。
    • 泛型方法和有界类型参数

  • 泛型,继承与子类型

    • Integer 是 Number 的子类型,并不意味着 Box<Integer> 是 Box<Number> 的子类型
    • 通过扩展或实现泛型类或接口,可以对其进行子类型划分。一个类或接口的类型参数与另一个类或接口的类型参数之间的关系由 extends 和 implements 子句决定。
  • 类型推断

    • 类型推断是 Java 编译器查看每个方法调用和相应声明的能力,以确定使调用适用的类型参数(或多个参数)。推理算法确定参数的类型,如果可用,还确定结果被赋值或返回的类型。最后,推理算法试图找到适用于所有参数的最特定类型。
    • 类型推导和泛型方法
    • 泛型类的类型推断和实例化
    • 泛型类和非泛型类的类型推断和泛型构造函数
    • 目标类型
  • 通配符

    • 上界通配符

      • ?extends
    • 无界通配符

      • 如果您正在编写一个可以使用 Object 类中提供的功能实现的方法。

      • 当代码在泛型类中使用不依赖类型参数的方法时。

        • 例如,List.size 或 List.clear。事实上,Class<?> 之所以如此常用,是因为 Class<T> 中的大多数方法都不依赖于T。
      • 需要注意的是,List<Object> 和 List<?> 是不一样的。您可以将 Object 或 Object 的任何子类型插入到 List\

  • 包是提供访问保护和名称空间管理的相关类型的分组。

  • 创建

    • 为包选择一个名称,并在每个包含希望包含在包中的类型(类、接口、枚举和注解类型)的源文件的顶部放置带有该名称的包语句。
    • 包语句必须是源文件中的第一行。每个源文件中只能有一个包语句,它适用于文件中的所有类型。
  • 命名

    • 包名全部用小写字母书写,以避免与类名或接口名冲突。
    • 公司使用颠倒的 Internet 域名作为包名的开头
    • 在单个公司内发生的名称冲突需要在该公司内按照约定进行处理,可能是在公司名称之后包含地区或项目名称
    • Java语言中的包以 Java 开头或 javax
    • 在某些情况下,internet 域名可能不是有效的包名。如果域名包含一个连字符或其他特殊字符,如果包名以一个数字或其他字符作为Java名的开头是非法的,或者如果包名包含一个保留的 Java 关键字,如“int”,就会发生这种情况。在这种情况下,建议的约定是添加下划线。
  • 使用包成员

    • 组成包的类型称为包成员。

    • 若要从其包外部使用公共包成员,必须执行以下操作之一

      • 通过成员的完全限定名引用该成员
      • 导入包成员
      • 导入整个包的成员
    • 另一种不太常见的导入形式允许导入外围类的公共嵌套类。

    • 为了方便起见,Java编译器会自动为每个源文件导入两个完整的包:(1) Java.Lang 包和 (2) 当前包 (当前文件所在的包)

    • 包的层次结构

      • 起初,包看起来是分层的,但实际上并非如此。
      • import java.awt.* 导入在 java.awt 包中的所有类。但它不导入 java.awt.color, java.awt.font 或任何其他 java.awt.xxxx包。
    • 名称冲突

      • 使用完全限定名
    • 静态导入语句

      • 谨慎使用静态导入。过度使用静态导入会导致代码难以阅读和维护,因为代码的读者不知道哪个类定义了特定的静态对象。如果使用得当,静态导入可以消除类名重复,从而使代码更具可读性。
  • 管理源码与类文件

    • Java平台的许多实现依赖于分层文件系统来管理源文件和类文件,尽管Java语言规范并不要求这样做。
    • 包成员的限定名和文件的路径名是并行的
    • 与 .java 源文件一样,编译后的 .class 文件应该位于反映包名的一系列目录中。但是 .class 文件的路径不必与 .java 源文件的路径相同。你可以分别安排你的源目录和类目录
    • 类目录的完整路径 <path_two>\classes 称为类路径,它由 CLASSPATH 系统变量设置。编译器和 JVM 都通过向类路径添加包名来构造.class文件的路径
    • 一个类路径可以包括多条路径,用分号(Windows)或冒号(UNIX)分隔。默认情况下,编译器和 JVM 搜索当前目录和包含 Java 平台类的 JAR 文件,以便这些目录自动位于类路径中。
    • 设置 CLASSPATH 系统变量

基本类

异常

  • 什么是异常

    • 术语异常是短语“异常事件”的简称。
    • 异常是在程序执行期间发生的一种事件,它会中断程序指令的正常流程。
    • 当方法中发生错误时,该方法创建一个对象并将其移交给运行时系统。该对象称为异常对象,包含有关错误的信息,包括错误类型和错误发生时程序的状态。创建异常对象并将其交给运行时系统称为抛出异常。
    • 方法抛出异常后,运行时系统会尝试寻找处理异常的方法。处理异常的可能的“某些东西”的集合是被调用的方法的有序列表,这些方法被调用以获得发生错误的方法。方法列表称为调用堆栈
    • 运行时系统在调用堆栈中搜索包含可以处理异常的代码块的方法。这段代码称为异常处理程序。搜索从发生错误的方法开始,然后按照调用方法的相反顺序在调用堆栈中进行。当找到适当的处理程序时,运行时系统将异常传递给处理程序。如果抛出的异常对象的类型与处理程序可以处理的类型匹配,则认为异常处理程序是合适的。
    • 所选择的异常处理程序被称为捕获异常。如果运行时系统穷尽地搜索调用堆栈上的所有方法,而没有找到适当的异常处理程序,则运行时系统(以及程序)将终止。
  • Catch 或指定要求

    • 可能引发某些异常的代码必须包含以下任何一种:

      • 捕获异常的 try 语句。try 必须为异常提供一个处理程序,如捕获和处理异常中所述。
      • 一个方法,指定它可以抛出异常。该方法必须提供一个throws 子句,其中列出异常,如指定方法抛出的异常中所述。
    • 三种异常类型

      • 检查异常

        • 这些都是编写良好的应用程序应该预料到并从中恢复的异常情况
        • 检查的异常服从于 Catch 或指定需求。
      • 错误

        • 这些是应用程序外部的异常条件,应用程序通常无法预测或从中恢复。
        • 错误不受 Catch 或指定要求的约束。错误是由 Error 及其子类指示的异常。
      • 运行时异常

        • 这些是应用程序内部的异常条件,应用程序通常无法预测或从中恢复。这些通常表示编程错误,例如逻辑错误或 API 使用不当。
        • 运行时异常不受 Catch 或指定需求的约束。运行时异常是由 RuntimeException 及其子类指示的异常。
      • 错误和运行时异常统称为未检查异常。

    • 绕过 Catch 或指定

      • 一些程序员认为 Catch 或指定需求是异常机制中的一个严重缺陷,并通过使用未检查异常来代替已检查异常来绕过它。一般来说,不建议这样做。
  • 捕获和处理异常

    • try 语句块

    • catch 语句块

      • 通过在 try 块之后直接提供一个或多个 catch 块,可以将异常处理程序与 try 块关联起来。

      • 每个 catch 块都是一个异常处理程序,用于处理由其参数指示的异常类型。参数类型声明了处理程序可以处理的异常类型,并且必须是继承自 Throwable 类的类名。

      • 异常处理程序可以做的不仅仅是打印错误消息或停止程序。它们可以执行错误恢复,提示用户做出决定,或者使用链式异常将错误传播到更高级别的处理程序

      • 用一个异常处理程序捕获多种类型的异常

        • 在 catch 子句中,指定语句块可以处理的异常类型,并用竖条(|)分隔每种异常类型。
        • 如果一个 catch 块处理多个异常类型,则catch形参隐式为final。
    • finally 语句块

      • finally 块总是在 try 块退出时执行。这确保即使发生意外异常,finally 块也会被执行。但是 finally 的用处不仅仅是异常处理——它允许程序员避免清理代码被 return、continue 或 break 意外地绕过。将清理代码放在 finally块中始终是一个很好的实践,即使在没有预期异常的情况下也是如此。
    • try-with-resources 语句

      • 是一个 try 语句,声明一个或多个资源。资源是程序使用完后必须关闭的对象。try-with-resources 语句确保在语句末尾关闭每个资源。任何实现 java.lang.AutoCloseable 的对象,包括所有实现 java.io.Closeable 的对象。可作为资源使用。

      • try-with-resources 语句可以像普通的 try 语句一样有catch 和 finally 块。在 try-with-resources 语句中,任何 catch 或 finally 块都在声明的资源被关闭后运行

      • 抑制异常

        • 实现 AutoCloseable 或 Closeable 接口的类
  • 指定方法抛出的异常

    • throws 关键字
  • 抛出异常

    • Java 平台提供了大量异常类。所有的类都是 Throwable类的后代,并且都允许程序区分在程序执行期间可能发生的各种类型的异常。

    • throw 语句

      • throw 语句只需要一个参数:一个可抛出对象。可抛出对象是 Throwable 类的任何子类的实例。
    • Throwable 类及其子类

      • Error

      • Exception

        • RuntimeException
    • 异常链

      • Throwable 中的方法以及构造函数

        • Throwable getCause()
        • Throwable initCause(Throwable)
        • Throwable(String, Throwable)
        • Throwable(Throwable)
      • 访问堆栈跟踪信息

        • 堆栈跟踪提供有关当前线程执行历史的信息,并列出异常发生时所调用的类和方法的名称。堆栈跟踪是一种有用的调试工具,通常在抛出异常时可以利用它。
      • 日志 API

        • java.util.logging
    • 创建异常类

      • 何时

        • 您是否需要 Java 平台中无法表示的异常类型?
        • 如果用户能够将您的异常与其他供应商编写的类抛出的异常区分开来,是否会对他们有所帮助?
        • 您的代码是否抛出多个相关异常?
        • 如果您使用其他人的异常,用户是否可以访问这些异常?一个类似的问题是,您的包是否应该是独立且自包含的?
      • 选择超类

      • 对于可读的代码,将字符串 Exception 附加到(直接或间接)从 Exception 类继承的所有类的名称后是一个很好的实践。

  • 未检查异常

    • 为什么设计人员决定强制一个方法指定所有可在其作用域内抛出的未捕获的已检查异常?方法可以抛出的任何异常都是方法的公共编程接口的一部分。调用方法的人必须知道方法可能抛出的异常,这样他们才能决定如何处理这些异常。这些异常与方法的参数和返回值一样,都是该方法编程接口的一部分。
    • “如果记录方法的API(包括它可能抛出的异常)这么好,为什么不也指定运行时异常呢?”运行时异常表示由编程问题导致的问题,因此,API 客户端代码不能合理地期望从中恢复或以任何方式处理它们。
    • 准则:如果客户端可以合理地从异常中恢复,那么将其设置为检查异常。如果客户端无法从异常中恢复,则将其设置为未检查异常。
  • 异常的优点

    • 将错误处理代码与“常规”代码分离
    • 在调用堆栈中向上传播错误
    • 对错误类型进行分组和区分

基本 I/O

  • I/O 流

    • 字节流

      • 程序使用字节流来执行8位字节的输入和输出。

      • InputStream,OutputStream

      • 使用

        • FileInputStream,FileOutputStream
      • 关闭

      • 使用时机

        • 低级 I/O 避免使用
    • 字符流

      • Java 平台使用 Unicode 约定存储字符值。字符流 I/O 自动将这种内部格式转换为本地字符集。

      • 使用字符流代替字节流的程序可以自动适应本地字符集,并为国际化做好准备——所有这些都不需要程序员付出额外的努力。

      • Reader, Writer

      • 使用

        • FileReader,FileWriter
      • 使用字节流的字符流

        • 字符流通常是字节流的“包装器”。字符流使用字节流执行物理I/O,而字符流处理字符和字节之间的转换
        • 有两种通用的字节到字符的“桥接”流: InputStreamReader 和 OutputStreamWriter
      • 面向行的 I/O

        • 支持所有可能的行终止符允许程序读取在任何广泛使用的操作系统上创建的文本文件。
        • BufferedReader.readLine,PrintWriter.println
    • 缓冲流

      • BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter

      • 刷新

        • 自动
        • 手动
    • 扫描与格式化

      • 编程 I/O 通常涉及到将数据与人们喜欢使用的格式之间转换。

      • 扫描

        • Scanner 类

          • 将格式化的输入分解为标记,并根据它们的数据类型转换单个标记。
        • 将输入分解为标记

          • 默认情况下,扫描器使用空白来分隔标记。(空格字符包括空格、制表符和行结束符。有关完整列表,请参阅Character.isWhitespace 的文档。)
          • 要使用不同的分隔符,请调用 useDelimiter(),并指定正则表达式
          • 即使扫描器不是流,您也需要关闭它来表明您已经完成了它的底层流。
        • 翻译单个标记

      • 格式化

        • PrintWriter,PrintStream

        • 惟一可能需要的 PrintStream 对象是 System.out 和 System.err。当需要创建格式化输出流时,实例化PrintWriter,而不是 PrintStream。

        • print 和println 方法

        • format 方法

          • d 将整数值格式化为十进制值。
          • f 将浮点值格式化为十进制值。
          • n 输出一个特定于平台的行结束符。
          • x 将整数格式化为十六进制值。
          • s 将任何值格式化为字符串。
          • tB 将整数格式化为特定于区域设置的月份名称。
    • 命令行 I/O

      • 标准流

        • 默认情况下,它们从键盘读取输入并将输出写入显示器。它们还支持文件上和程序之间的 I/O,但该功能由命令行解释器控制,而不是程序。
        • System.in,System.out,System.err
        • 自动定义,无需打开
        • 您可能期望标准流是字符流,但是由于历史原因,它们是字节流。System.out 和 System.err 被定义为PrintStream 对象。尽管 PrintStream 在技术上是一个字节流,但它利用一个内部字符流对象来模拟字符流的许多特性。
        • 相比之下,System.in 是一个没有字符流特征的字节流。
      • 控制台

        • 标准流的一个更高级的替代品是控制台。这是一个单一的、预定义的 Console 类型对象,它具有标准流提供的大多数特性,以及其他特性。
        • 控制台对于安全的密码输入特别有用。Console 对象还通过其 reader 和 writer 方法提供了真正的字符流的输入和输出流。
        • 在程序可以使用控制台之前,它必须尝试通过调用System.console() 来检索控制台对象。如果返回失败,可能是因为操作系统不支持这些操作,也可能是因为程序是在非交互环境中启动的。
        • Console 对象通过其 readPassword 方法支持安全的密码输入。此方法从两方面帮助保护密码输入。首先,它抑制了回显,因此密码在用户的屏幕上不可见。其次,readPassword 返回一个字符数组,而不是一个 String,因此可以覆盖密码,一旦不再需要它就从内存中删除它。
    • 数据流

      • 支持基本数据类型值以及 String 值的二进制I/O。

      • DataInput ,DataOutput

      • DataInputStream,DataOutputStream

        • DataStreams通过捕获EOFException来检测文件结束条件,而不是测试无效的返回值。
        • DataStreams中的每个专门化写入都与相应的专门化读取完全匹配。由程序员来确保输出类型和输入类型以这种方式匹配:输入流由简单的二进制数据组成,没有任何东西表明单个值的类型,或者它们在流中的起始位置。
      • 用于货币值的正确类型是 java.math.BigDecimal。不幸的是,BigDecimal 是一种对象类型,因此它不能用于数据流。但是,BigDecimal 将使用对象流。

    • 对象流

      • 大多数(但不是全部)标准类支持其对象的序列化。那些实现标记接口 Serializable 的。

      • ObjectInput,ObjectOutput

        • 也是 DataInput,DataOutput 的子接口
      • ObjectInputStream,ObjectOutputStream

      • 复杂对象的输出和输入

  • 文件 I/O(NIO.2 特性)

    • 路径

      • 用于分隔目录名的字符(也称为分隔符)是特定于文件系统的: Solaris OS 使用正斜杠(/),Microsoft Windows 使用反斜杠()。

      • 相对与绝对

      • 符号链接(软链接)

        • 符号链接通常对用户是透明的。
        • 粗心创建的符号链接会导致循环引用。
    • Path 类

      • 是 java.nio.file 包的一个主要入口点

      • Path 对象包含用于构造路径的文件名和目录列表,并用于检查、定位和操作文件。

      • 路径与系统无关。您不能比较来自 Solaris 文件系统的Path,并期望它与来自 Windows 文件系统的 Path 相匹配,即使目录结构相同且两个实例定位相同的相对文件。

      • 创建

        • Paths.get() 等价于 FileSystems.getDefault().getPath()
      • 检索

        • 您可以将 Path 视为将这些 name 元素存储为一个序列。目录结构中最高的元素位于索引0。目录结构中最低的元素位于索引[n-1],其中n是 Path 中 name 元素的数量。方法可用于使用这些索引检索 Path 的单个元素或子序列。
        • toString,getFileName,getName,subpath,getParent,getRoot
      • 移除路径中的冗余

        • normalize,toRealPath
      • 路径转换

        • toUri

        • toAbsolutePath

          • 如果将 true 传递给此方法,且文件系统支持符号链接,则此方法将解析路径中的任何符号链接。
          • 如果 Path 是相对路径,则返回绝对路径。
          • 如果 Path 包含任何冗余元素,则返回删除了这些元素的路径。
      • 组合路径

        • resolve
      • 在两条路径之间创建路径

        • relativize
        • 如果只有一条路径包含根元素,则不能构造相对路径。如果两条路径都包含根元素,构造相对路径的能力取决于系统。
      • 比较两种路径

        • 支持 equals()
        • startsWith,endsWith
        • 实现了 Iterable,Comparable 接口
        • isSameFile
    • 文件操作

      • Files 类是 java.nio.file 包的另一个主要入口点。

      • 释放系统资源

      • 捕获异常

        • 对于文件 I/O,意外条件是不可避免的:文件在预期的情况下存在(或不存在),程序不能访问文件系统,默认文件系统实现不支持特定功能,等等。可能会遇到许多错误。
        • 所有访问文件系统的方法都可以抛出 IOException。
        • 除了 IOException 之外,许多特定的异常扩展了FileSystemException。该类有一些有用的方法,它们返回涉及的文件(getFile)、详细的消息字符串(getMessage)、文件系统操作失败的原因(getReason)以及涉及的“其他”文件(如果有的话)。
      • 可变参数

        • CopyOption
      • 原子操作

      • 方法链

      • glob

        • glob 模式被指定为字符串,并与其他字符串(如目录或文件名)进行匹配

        • getPathMatcher

        • 星号 * 匹配任意数量的字符(包括 none)。

        • 两个星号 ** 类似于 *,但跨越了目录边界。此语法通常用于匹配完整路径。

        • 问号 ? 只能匹配一个字符。

        • 花括号指定子模式的集合。

        • 方括号表示一组单个字符,如果使用连字符(-),则表示一组字符。

          • 在方括号内,*、?和\匹配它们自己。
        • 所有其他字符都与自己匹配。

        • 要匹配*、?或其他特殊字符,可以使用反斜杠字符\进行转义

      • 链接感知

        • 每个 Files 方法要么检测遇到符号链接时该做什么,要么提供一个选项,使您能够在遇到符号链接时配置行为。
    • 检查文件或目录

      • 验证文件或目录是否存在

        • exists(Path, LinkOption…),notExists(Path, LinkOption…)

        • 验证结果

          • 存在,不存在,未知(当程序无法访问该文件时)
      • 检查文件可访问性

        • isReadable,isWritable,isExecutable
        • 由于检查与使用存在时差,一旦这些方法中的任何一个完成,也不能保证文件可以被访问。
      • 检查两条路径是否指向同一个文件

        • isSameFile
    • 删除文件或目录

      • 可以删除文件、目录或链接。
      • 对于符号链接,删除的是该链接,而不是该链接的目标。
      • 对于目录,目录必须为空,否则删除失败。
      • delete,deleteIfExists
    • 复制文件或目录

      • copy

      • 如果目标文件存在,复制将失败,除非指定了 REPLACE_EXISTING 选项。

      • 目录可以复制。但是,目录内的文件不会被复制,因此即使原始目录中有文件,新目录也是空的。

      • 复制符号链接时,将复制该链接的目标。如果您想复制链接本身,而不是链接的内容,请指定 NOFOLLOW_LINKS或 REPLACE_EXISTING 选项。

      • REPLACE_EXISTING,COPY_ATTRIBUTES,NOFOLLOW_LINKS

      • Files 类还定义了可用于在文件和流之间进行复制的方法。

      • 递归复制

        • Files.walkFileTree
    • 移动文件或目录

      • move
      • REPLACE_EXISTING,ATOMIC_MOVE
      • 尽管可以在单个目录上实现 move 方法,但该方法最常与文件树递归机制一起使用。
    • 管理元数据(文件和文件存储属性)

      • size,isDirectory,isRegularFile,isSymbolicLink,isHidden,LastModifiedTime,Owner,PosixFilePermissions,Attribute
      • readAttributes
      • 不同的文件系统对于应该跟踪哪些属性有不同的概念。出于这个原因,相关的文件属性被分组到视图中。
      • BasicFile,DosFile,PosixFile,FileOwner,AclFile,UserDefinedFile
      • getFileAttributeView
      • 文件基本属性
      • 设置时间戳
      • DOS文件属性
      • POSIX文件权限
      • 设置文件或组所有者
      • 自定义文件属性
      • 文件存储属性
    • 读,写,创建文件

      • OpenOptions 参数

        • StandardOpenOptions
        • WRITE,APPEND,TRUNCATE_EXISTING,CREATE_NEW,CREATE,DELETE_ON_CLOSE,SPARSE,SYNC,DSYNC
      • 小文件常用方法

        • 从文件中读取所有字节或行

          • readAllBytes,readAllLines
        • 将所有字节或行写入文件

          • write
      • 文本文件的缓冲I/O方法

        • file 包支持通道 I/O,它可以在缓冲区中移动数据,绕过一些可能会导致流 I/O 瓶颈的层。

        • 使用缓冲流I/O读取文件

          • newBufferedReader
        • 使用缓冲流I/O写入文件

          • newBufferedWriter
      • 可与 java.io APIs 互操作的无缓冲流方法

        • 使用流I/O读取文件

          • newInputStream
        • 使用流I/O创建和写入文件

          • newOutputStream
      • Channels 与 ByteBuffers 的方法

        • 使用通道I/O读写文件

          • newByteChannel
          • newByteChannel 方法返回 SeekableByteChannel 的实例。使用默认文件系统,您可以将这个可查找的字节通道转换为 FileChannel,以便访问更高级的特性,例如将文件的区域直接映射到内存中以实现更快的访问,锁定文件的区域以使其他进程无法访问它,或者从绝对位置读取和写入字节而不影响通道的当前位置。
      • 创建常规文件和临时文件的方法

        • 创建文件

          • createFile
          • 如果打开一个新的输出流并立即关闭它,则会创建一个空文件。
        • 创建临时文件

          • createTempFile
          • 临时文件名的具体格式与平台有关。
    • 随机访问文件

      • SeekableByteChannel

        • position
        • read
        • write
        • truncate
    • 创建,读取目录

      • 列出文件系统的根目录

        • FileSystem.getRootDirectories
      • 创建目录

        • createDirectory
      • 创建临时目录

        • createTempDirectory
      • 列出目录的内容

        • newDirectoryStream
      • 使用 Globbing 过滤目录列表

        • newDirectoryStream
      • 编写自己的目录过滤器

        • DirectoryStream.Filter 接口
    • 链接,符号和其他

      • 硬链接

        • 链接的目标必须已经存在。
        • 目录上通常不允许硬链接。
        • 硬链接不允许跨分区或卷。因此,它们不能跨文件系统存在。
        • 硬链接的外观和行为与普通文件类似,因此很难找到它们。
        • 硬链接,无论出于什么目的,都是与原始文件相同的实体。它们具有相同的文件权限、时间戳等。所有属性都是相同的。
      • 创建符号链接

        • createSymbolicLink
      • 创建硬链接

        • createLink
      • 检测符号链接

        • isSymbolicLink
      • 发现链路的目标

        • readSymbolicLink
    • 遍历文件树

      • FileVisitor 接口

        • 指定遍历过程中关键点上所需的行为:访问文件时、访问目录之前、访问目录之后或发生故障时。
        • preVisitDirectory,postVisitDirectory ,visitFile,visitFileFailed
        • SimpleFileVisitor 类
      • 启动文件遍历

        • walkFileTree
      • 创建 FileVisitor 时的注意事项

        • 对文件树进行深度遍历,但是不能对访问子目录的迭代顺序做任何假设。
        • 如果您的程序将更改文件系统,则需要仔细考虑如何实现 FileVisitor。
        • 如果正在编写递归拷贝,则在尝试将文件复制之前,在 preVisitDirectory 中创建新目录。如果希望保留源目录的属性(类似于UNIX cp -p命令),则需要在复制文件后在 postVisitDirectory 中执行此操作。
        • 如果正在编写文件搜索,则在 visitFile 方法中执行比较。此方法查找符合条件的所有文件,但不查找目录。如果希望同时找到文件和目录,还必须在 preVisitDirectory 或postVisitDirectory 方法中执行比较。
        • 您需要决定是否要遵循符号链接。例如,如果您正在删除文件,遵循符号链接可能是不可取的。如果您正在复制文件树,您可能希望允许这样做。默认情况下, walkFileTree 不遵循符号链接。
        • visitFile 方法用于文件调用。如果您已经指定了FOLLOW_LINKS 选项,并且您的文件树具有到父目录的循环链接,那么循环目录将在 visitFileFailed 方法中使用 FileSystemLoopException 报告。
      • 控制流

        • FileVisitor 方法返回一个 FileVisitResult 值。您可以中止文件遍历过程或控制是否通过 FileVisitor 方法中返回的值访问目录:
        • CONTINUE,TERMINATE,SKIP_SUBTREE,SKIP_SIBLINGS
    • 查找文件

      • PathMatcher
      • 递归模式匹配
    • 查看目录的更改

      • Watch Service API

        • 允许您向监视服务注册一个目录(或多个目录)。注册时,您告诉服务您感兴趣的事件类型:文件创建、文件删除或文件修改。当服务检测到感兴趣的事件时,将其转发到已注册的流程。已注册的进程有一个线程(或线程池)专门用于监视它已注册的任何事件。当事件传入时,将根据需要处理它。
      • Watch Service 简介

        • 低级,允许自定义。

        • 实现步骤

          • 为文件系统创建一个 WatchService “watcher”
          • 对于想要监视的每个目录,将其注册到监视程序。注册目录时,可以指定要通知的事件类型。对于注册的每个目录,都会收到一个 WatchKey 实例。
          • 实现一个无限循环来等待传入事件。当事件发生时,该键被发出信号并放入监视者的队列中。
          • 从监视者的队列中检索键。您可以从该密钥获取文件名。
          • 检索键的每个挂起事件(可能有多个事件)并根据需要处理。
          • 重置键,并恢复等待事件。
          • 关闭服务:当线程退出或关闭时(通过调用其 closed 方法),watch 服务退出。
        • watchkey 是线程安全的,可以与 java.nio.concurrent 包一起使用。您可以专门为这项工作提供一个线程池。

      • 尝试

      • 创建监视服务并注册事件

        • WatchService watcher = FileSystems.getDefault().newWatchService();

        • Watchable 接口

        • StandardWatchEventKinds

          • ENTRY_CREATE
          • ENTRY_DELETE
          • ENTRY_MODIFY
          • OVERFLOW
      • 处理事件

        • 获取一个 watch key

          • poll
          • take
        • 处理顺序

          • 处理键的挂起事件。从 pollEvents 方法获取watcheventslist。
          • 使用 kind 方法检索事件的类型。无论该键为什么事件 ,都有可能接收 OVERFLOW 事件。您可以选择处理或忽略它,但是您应该对它进行测试。
          • 检索与事件关联的文件名。文件名作为事件的上下文存储,因此使用 context 方法检索它。
          • 在处理了键的事件之后,您需要通过调用 reset 将密钥放回到就绪状态。如果此方法返回 false,则密钥不再有效,循环可以退出。这一步非常重要。如果调用 reset 失败,此键将不会接收任何进一步的事件。
        • watch key 的状态

          • Ready
          • Signaled
          • Invalid
      • 检索文件名

        • 从事件上下文检索文件名。
      • API 使用时机

        • Watch Service AP I是为需要收到有关文件更改事件通知的应用程序而设计的。
        • 它非常适合任何应用程序,如编辑器或 IDE,这些应用程序可能有许多打开的文件,并且需要确保文件与文件系统同步。它还非常适合用于监视目录的应用服务器,例如等待 .jsp 或 .jar 文件删除,以便部署它们。
        • 此 API 不是为索引硬盘驱动器而设计的。大多数文件系统实现都具有对文件更改通知的本机支持。Watch Service API 在可用的情况下利用了这种支持。但是,当文件系统不支持此机制时,Watch Service 将轮询文件系统,等待事件发生。
    • 其他有用的方法

      • 确定MIME类型

        • Files.probeContentType
        • 这种方法的实现是高度特定于平台的,并不是绝对正确的。内容类型由平台的默认文件类型检测器决定。
        • FileTypeDetector
      • 默认文件系统

        • FileSystems.getDefault()
      • 路径字符串分隔符

        • File.separator
        • FileSystems.getDefault().getSeparator();
      • 文件系统的文件存储

        • 一个文件系统有一个或多个文件存储来保存它的文件和目录。文件存储表示底层存储设备。
        • FileSystems.getDefault().getFileStores()
        • Files.getFileStore(file)
    • 遗留 File I/O 代码

      • 与遗留代码的互操作性

        • toPath,toFile
      • 将java.io.File 功能映射到 java.nio.file

并发

  • 进程与线程

    • Java虚拟机的大多数实现都是作为单个进程运行的。Java应用程序可以使用 ProcessBuilder 对象创建其他进程。
    • 多线程执行是 Java 平台的一个基本特性。每个应用程序至少有一个线程——如果算上做内存管理和信号处理等工作的“系统”线程,就会有几个。但是从应用程序程序员的角度来看,开始时只有一个线程,称为主线程。这个线程具有创建其他线程的能力。
  • 线程对象

    • 每个线程都与 thread 类的一个实例相关联。

    • 基本策略

      • 要直接控制线程的创建和管理,只需在应用程序每次需要初始化异步任务时实例化 thread 即可。
      • 要从应用程序的其余部分抽象线程管理,请将应用程序的任务传递给执行器。
    • 定义和启动一个线程

      • 线程中运行的代码

        • 实现 Runnable 接口
        • Thread类本身实现了 Runnable,尽管它的 run 方法什么也不做。应用程序可以子类化 Thread,提供自己的run 实现
      • 调用 Thread.start 启动一个新线程

    • 使用 Sleep 暂停执行

      • Thread.sleep
    • 中断

      • 中断是对线程的一个指示,它应该停止正在做的事情并做其他事情。由程序员决定线程如何响应中断,但是线程终止是很常见的。

      • 线程通过调用将要中断的线程的 thread 对象的 interrupt 来发送中断。为了使中断机制正确工作,被中断的线程必须支持自己的中断。

      • 支持中断

        • 取决于它当前正在做什么。

        • 许多抛出 InterruptedException 的方法(如 sleep)被设计为取消当前操作,并在接收到中断时立即返回。

        • 如果线程运行很长时间而没有调用抛出InterruptedException 的方法,该怎么办?然后它必须周期性地调用 Thread.interrupted,如果接收到中断则返回 true。

        • 中断状态标志

          • 中断机制是使用一个称为中断状态的内部标志来实现的。调用 Thread.interrupt 设置此标志。
          • 当线程通过调用静态方法 thread .interrupted 来检查中断时,中断状态将被清除。
          • 非静态的 isInterrupted 方法被一个线程用来查询另一个线程的中断状态,它不改变中断状态标志。
          • 按照惯例,任何通过抛出 InterruptedException 退出的方法都会在退出时清除中断状态。然而,中断状态总是可能立即被另一个调用中断的线程再次设置。
    • 加入

      • join()

        • 允许一个线程等待另一个线程完成。
    • 示例

  • 同步

    • 线程干扰

      • 当在不同线程中运行但作用于相同数据的两个操作交叉时,就会发生干扰。
      • 由于线程干扰错误不可预测,因此很难发现和修复。
    • 内存一致性错误

      • 当不同线程对应该是相同数据出现视图不一致时,就会发生内存一致性错误。

      • 程序员不需要详细了解这些原因。我们所需要的只是一种避免它们的策略。

      • happens-before 关系

        • 保证一个特定语句写入的内存对另一个特定语句可见。
      • 具有 happens-before 关系的行为

        • 当语句调用 Thread.start 时,每个与该语句具有happens-before 关系的语句也与新线程执行的每个语句具有 happens-before 关系。导致创建新线程的代码的效果对新线程是可见的。
        • 当一个线程终止并导致一个Thread.join 到另一个线程返回,那么终止线程执行的所有语句与成功连接之后的所有语句都具有 happens-before 关系。执行连接的线程现在可以看到线程中代码的效果。
    • 同步方法

      • synchronized 关键字
      • 对同一对象的同步方法的两次调用不可能交织。当一个线程正在为一个对象执行同步方法时,所有为同一对象调用同步方法的其他线程将暂停执行,直到第一个线程处理完该对象。
      • 当同步方法退出时,它自动与同一对象的同步方法的任何后续调用建立 happens-before 关系。这保证了对对象状态的更改对于所有线程都是可见的。
      • 构造函数不能同步——在构造函数中使用 synchronized关键字是一个语法错误。同步构造函数没有意义,因为只有创建对象的线程才能在构造对象时访问它。
      • 在构造一个将在线程之间共享的对象时,要非常小心,不要过早地“泄漏”对象的引用。
    • 内在锁与同步

      • 同步是围绕一个称为内在锁或监视器锁的内部实体构建的。(API规范通常将此实体简单地称为“监视器”)

      • 内在锁在同步方面都发挥作用

        • 强制对对象状态的独占访问
        • 建立对可见性至关重要的 happens-before 关系。
      • 每个对象都有一个与之相关的内在锁。

      • 按照惯例,需要独占和一致访问对象字段的线程必须在访问对象字段之前获得对象的内在锁,然后在使用完对象字段时释放内在锁。线程在获得锁和释放锁之间拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在试图获取锁时将阻塞。

      • 当线程释放一个内在锁时,在该操作和后续获得的任何相同锁之间建立 happens-before 关系。

      • 同步方法中的锁

        • 当线程调用同步方法时,它会自动获取该方法对象的内在锁,并在该方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁释放。
        • 静态方法与类而不是对象关联。在这种情况下,线程获得与类关联的 Class 对象的内在锁。因此,对类的静态字段的访问由一个不同于类的任何实例的锁控制。
      • 同步语句

        • 与同步方法不同,同步语句必须指定提供内在锁的对象
        • 同步语句对于通过细粒度同步提高并发性也很有用。
        • 可以不使用同步方法或以其他方式使用与之关联的锁,而是单独创建两个对象来提供锁。
      • 可重入同步

        • 允许一个线程多次获得相同的锁可以实现重入同步。
        • 同步代码直接或间接地调用包含同步代码的方法,并且两组代码使用相同的锁。如果没有可重入同步,同步代码将不得不采取许多额外的预防措施,以避免线程导致自身阻塞。
    • 原子访问

      • 一个有效地同时发生的事件

      • 原子的动作不可能中途停止:它要么完全发生,要么根本不发生。在操作完成之前,原子操作的副作用是不可见的。

      • 可以指定为原子的行为

        • 对于引用变量和大多数基本变量(除了 long 和 double 之外的所有类型),读和写都是原子的。
        • 对于所有声明为 volatile 的变量(包括 long 和 double ),读和写都是原子的。
        • 对 volatile 变量的任何写入都会与该变量的后续读取建立happens-before 关系。这意味着对 volatile 变量的更改总是对其他线程可见。更重要的是,这也意味着当线程读取一个 volatile 变量时,它不仅看到了对 volatile 的最新更改,还看到了导致更改的代码的副作用。
  • 活跃度

    • 并发应用程序及时执行的能力被称为它的活跃度

    • 死锁

      • 死锁描述了两个或多个线程永远被阻塞,彼此等待的情况。
    • 饥饿与活锁

      • 饥饿描述了一种情况,即线程无法获得对共享资源的常规访问,并且无法取得进展。当共享资源被“贪婪的”线程长时间占用时,就会发生这种情况。
      • 与死锁一样,活锁线程无法取得进一步进展。然而,线程并没有被阻塞——它们只是忙于相互响应而无法恢复工作。
  • 保护块

    • 线程通常必须协调它们的动作。最常见的协调习语是受保护的块。
    • 这样的块首先轮询一个必须为真的条件,然后才能继续执行。要正确地做到这一点,需要遵循许多步骤。
    • 始终在循环中调用 wait,以测试正在等待的条件。不要假设中断是为了您正在等待的特定条件,或者该条件仍然为真。
    • notify,用于唤醒单个线程。因为 notify 不允许您指定被唤醒的线程,所以它只在大型并行应用程序中有用——也就是说,具有大量线程的程序,它们都在做类似的工作。在这样的应用程序中,您不关心哪个线程被唤醒。
  • 不可变对象

    • 如果一个对象的状态在被构造后不能改变,它被认为是不可变的。

    • 不可变对象在并发应用程序中特别有用。由于它们不能改变状态,因此不会被线程干扰破坏或处于不一致的状态。

    • 同步类示例

    • 定义不可变对象的策略

      • 下面的规则定义了一个创建不可变对象的简单策略。并不是所有记录为“不可变”的类都遵循这些规则。这并不一定意味着这些类的创建者很草率——他们可能有很好的理由相信他们类的实例在构造之后永远不会改变。然而,这种策略需要复杂的分析,不适合初学者。

      • 不要提供“setter”方法——修改字段或字段引用的对象的方法。

      • 将所有字段设置为 final 和 private。

      • 不允许子类重写方法。最简单的方法是将类声明为 final。更复杂的方法是将构造函数设为私有,并在工厂方法中构造实例。

      • 如果实例字段包含对可变对象的引用,则不允许更改这些对象

        • 不要提供修改可变对象的方法。
        • 不要共享对可变对象的引用。永远不要存储传递给构造函数的外部可变对象的引用;如有必要,创建副本,并存储对副本的引用。类似地,在必要时创建内部可变对象的副本,以避免在方法中返回原始对象。
  • 高级并发对象

    • java.util.concurrent

    • 锁对象

      • java.util.concurrent.locks
      • Lock 接口
      • 与隐式锁一样,一次只能有一个线程拥有一个 Lock 对象。锁对象还通过其关联的 Condition 对象支持等待/通知机制。
      • 与隐式锁相比,Lock 对象的最大优点是它们能够从获取锁的尝试中退出。如果锁不能立即或在超时到期(如果指定)之前可用,tryLock 方法将退出。如果另一个线程在获得锁之前发送中断,lockInterruptibly 方法将退出。
    • 执行器

      • 在大型应用程序中,将线程管理和创建与应用程序的其余部分分离是有意义的。封装这些函数的对象称为执行器。

      • 执行器接口

        • Executor

          • 提供了一个单独的方法 execute,该方法被设计为通用线程创建习惯用法(.start)的直接替换。
        • ExecutorService

          • 补充使用类似但更通用的 submit 方法执行。
          • 像 execute 一样,submit 接受 Runnable 对象,但也接受 Callable 对象,这允许任务返回一个值。
          • submit 方法返回一个 Future 对象,该对象用于检索Callable返回值并管理 Callable 和 Runnable 任务的状态。
          • 提供了用于提交大型 Callable 对象集合的方法。
          • 提供了许多方法来管理执行器的关闭。为了支持立即关机,任务应该正确处理中断。
        • ScheduledExecutorService

          • 用 schedule 补充 ExecutorService 的方法,它在指定的延迟后执行可运行或可调用任务。
          • 定义了 scheduleAtFixedRate 和 scheduleWithFixedDelay,以指定的时间间隔重复执行指定的任务。
      • 线程池

        • 大多数执行器实现使用线程池,线程池由工作线程组成。

        • 这种线程与它执行的 Runnable 和 Callable 任务分开存在,通常用于执行多个任务。

        • 使用工作线程可以最小化由于线程创建造成的开销。

        • 固定线程池

          • 总是有指定数量的线程在运行

          • 如果一个线程在仍在使用时以某种方式终止,则会自动用一个新线程替换它。

          • 任务通过内部队列提交到池中,当活动任务多于线程时,内部队列就会保存额外的任务。

          • 优点

            • 使用它的应用程序可以优雅地降级
        • Executors

          • newFixedThreadPool
          • newCachedThreadPool
          • newSingleThreadExecutor
          • 有几个工厂方法是上述执行器的ScheduledExecutorService 版本。
        • 额外的选项

          • ThreadPoolExecutor
          • ScheduledThreadPoolExecutor
      • Fork/Join

        • fork/join 框架是 ExecutorService 接口的实现,它可以帮助您利用多个处理器

        • 它是为可以递归分解为更小块的工作而设计的。目标是使用所有可用的处理能力来增强应用程序的性能。

        • 与任何 ExecutorService 实现一样,fork/join 框架将任务分配给线程池中的工作线程。fork/join 框架是独特的,因为它使用了工作窃取算法。当工作线程工作完时,可以从其他线程那里窃取任务。

        • ForkJoinPool 类

          • fork/join 框架的中心
          • 是 AbstractExecutorService 类的扩展。
          • 实现了核心的工作窃取算法,可以执行 ForkJoinTask 进程。
        • 基本使用

          • 第一步是编写执行部分工作的代码。
          • 将这段代码包装在 ForkJoinTask 子类中,通常使用它的一种更专门的类型,RecursiveTask (可以返回结果)或RecursiveAction。
          • 创建一个对象来表示所有要完成的工作,并将它传递给ForkJoinPool 实例的 invoke() 方法。
        • 帮助理解

        • 标准实现

          • Java .util. Arrays 类的 parallelSort 方法
          • Java .util.streams 包中的方法使用
    • 并发集合

      • BlockingQueue

      • ConcurrentMap

        • 标准通用实现是 ConcurrentHashMap,它是 HashMap的并发模拟。
      • ConcurrentNavigableMap

        • ConcurrentMap 的子接口,支持近似匹配。ConcurrentNavigableMap 的标准通用实现是ConcurrentSkipListMap,它是 TreeMap 的并发模拟。
    • 原子变量

      • java.util.concurrent.atomic 包定义了支持对单个变量进行原子操作的类。
      • 所有类都有 get 和 set 方法,其工作方式类似于对volatile 变量的读写。也就是说,一个 set 与同一个变量上的任何后续 get 具有 happens-before 关系。
      • 原子 compareAndSet 方法也具有这些内存一致性特性,应用于整数原子变量的简单原子算术方法也是如此。
    • 并发随机数

      • ThreadLocalRandom
      • 用于希望使用来自多线程或 ForkJoinTasks 的随机数
      • 对于并发访问,使用 ThreadLocalRandom 而不是 Math.random() 可以减少争用,并最终获得更好的性能。
      • 您所需要做的就是调用 ThreadLocalRandom.current(),然后调用它的一个方法来检索一个随机数。
  • 参考资料

    • Java并发编程:设计原则和模式(第二版)。这是由一位领先的专家撰写的综合性著作,他也是 Java 平台并发框架的架构师。
    • Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowbeer、David Holmes 和 Doug Lea 所著的《Java 并发实践》。为新手设计的实用指南。
    • 有效的 Java 编程语言指南(第二版),作者Joshua Bloch。虽然这是一个通用的编程指南,但它关于线程的章节包含了并发编程的基本“最佳实践”。
    • 并发性:状态模型和Java程序(第二版),Jeff Magee和Jeff Kramer著。通过建模和实际示例的结合介绍并发编程。
    • Java并发动画:https://sourceforge.net/projects/javaconcurrenta/

平台环境

  • 应用程序在平台环境中运行,平台环境由底层操作系统、Java虚拟机、类库和应用程序启动时提供的各种配置数据定义。

  • 配置工具

    • 帮助应用程序访问其启动上下文的配置实用程序。

    • 属性

      • 属性是作为键/值对管理的配置值。在每对中,键和值都是 String 值。键标识并用于检索值,就像变量名用于检索变量的值一样。

      • java.util.Properties 类

        • 管理属性

        • 方法

          • 从流中将键/值对加载到 Properties 对象中
          • 从键中检索值
          • 列出键及其值
          • 枚举键
          • 将属性保存到流中
        • 扩展了 java.util.Hashtable

          • 测试 Properties 对象中是否有特定的键或值
          • 获取当前键/值对的数量
          • 删除键及其值
          • 添加一个键/值对到属性列表
          • 枚举值或键
          • 通过键检索值
          • 找出 Properties 对象是否为空
      • System 类维护一个 Properties 对象,该对象定义当前工作环境的配置。

      • 应用程序生命周期中的属性

        • Starting Up

          • 应用程序将默认属性从已知位置加载到 properties 对象中。通常,默认属性与 .class 和应用程序的其他资源文件一起存储在磁盘上的文件中。
          • 应用程序将创建另一个 Properties 对象并加载上次运行应用程序时保存的属性。许多应用程序在每个用户的基础上存储属性,因此在此步骤中加载的属性通常位于该应用程序在用户的主目录中维护的特定目录中的特定文件中。
          • 最后,应用程序使用默认的和记住的属性来初始化自身。
        • Running

          • 在应用程序执行期间,用户可能会更改一些设置(可能是在 Preferences 窗口中),Properties 对象将被更新以反映这些更改。如果要在以后的会话中记住用户的更改,则必须保存这些更改。
        • Exiting

          • 退出时,应用程序将属性保存到其已知位置,以便在下次启动应用程序时再次加载。
      • 设置属性对象

        • .load()
      • 保存属性

        • .store
      • 获取属性信息

        • contains,containsKey,getProperty,list,elements,keys,propertyNames,stringPropertyNames,size
      • 设置属性

        • setProperty,remove
      • 上面描述的一些方法是在 Hashtable 中定义的,因此接受 String 以外的键和值参数类型。始终使用字符串作为键和值,即使该方法允许其他类型。也不要在属性对象上调用 Hashtable.set 或 Hastable.setAll,总是使用Properties.setProperty。

    • 命令行参数

      • Java应用程序可以从命令行接受任意数量的参数。这允许用户在启动应用程序时指定配置信息。

      • 用户在调用应用程序时输入命令行参数,并在要运行的类名之后指定它们。

      • 响应命令行参数

        • public static void main (String[] args)
        • 空格字符分隔命令行参数。要将他们解释为单个参数,用户可以通过将它们括在引号中来连接它们。
      • 解析数值型命令行参数

    • 环境变量

      • 许多操作系统使用环境变量将配置信息传递给应用程序。与Java平台中的属性一样,环境变量也是键/值对,其中键和值都是字符串。设置和使用环境变量的约定因操作系统和命令行解释器的不同而不同。

      • 查询环境变量

        • System.getenv
      • 向新进程传递环境变量

        • 使用 ProcessBuilder 对象创建新进程
        • 传递给新进程的默认环境变量集与提供给应用程序虚拟机进程的环境变量集相同。应用程序可以使用 ProcessBuilder.environment 更改此集。
      • 平台依赖问题

        • 在不同系统上实现环境变量的方式之间存在许多微妙的差异

          • Windows 会忽略环境变量名中的大小写,而 UNIX 则不会。
        • 环境变量的使用方式也各不相同。

          • Windows 在名为 USERNAME 的环境变量中提供用户名,而 UNIX 实现可能在 user、LOGNAME 或两者中提供用户名。
    • 其他

      • Preferences API

        • 允许应用程序在依赖于实现的备份存储中存储和检索配置数据。
        • 支持异步更新,同一组首选项可以由多个线程甚至多个应用程序安全地更新。
      • 部署在 JAR 存档中的应用程序使用 manifest 来描述存档的内容。

      • Java Web Start 应用程序的配置包含在 JNLP 文件中。

      • Java Plug-in applet 的配置部分由用于将 applet 嵌入web 页面的 HTML 标记决定。

        • 根据 applet 和浏览器的不同,这些标记可以包括<applet>、<object>、<embed>和<param>。
      • Serviceloader 类提供了一个简单的服务提供者工具。

        • 服务提供者是服务的实现——一组众所周知的接口和(通常是抽象的)类。
        • 服务提供者中的类通常实现接口,并继承服务中定义的类。
        • 服务提供者可以作为扩展安装。
        • 提供程序也可以通过将它们添加到类路径或其他特定于平台的方法来提供。
  • 系统工具

    • 命令行I/O对象

      • System 提供了几个预定义的 I/O 对象,这些对象在从命令行启动的 Java 应用程序中非常有用。
      • 它们实现了大多数操作系统提供的标准I/O流,以及一个用于输入密码的控制台对象。
    • 系统属性

      • System 类维护一个 Properties 对象,该对象描述当前工作环境的配置。

      • 系统属性包括有关当前用户、Java 运行时的当前版本以及用于分隔文件路径名称的组件的字符的信息。

      • “file.separator”,“java.class.path”,“java.home”,“java.vendor”,“java.vendor.url” ,“java.version”,“line.separator”,“os.arch”,“os.name”,“os.version”,“path.separator”,“user.dir”,“user.home”,“user.name”

      • 读取系统属性

        • getProperty,getProperties
      • 写入系统属性

        • System.setProperties
    • 安全管理器

      • 为应用程序定义安全策略的对象。

      • 此策略指定不安全或敏感的操作。

      • 安全策略不允许的任何操作都会引发SecurityException。

      • 应用程序还可以查询其安全管理器,以发现允许哪些操作。

      • 通常,web applet 运行时带有浏览器或 Java web Start插件提供的安全管理器。其他类型的应用程序通常在没有安全管理器的情况下运行,除非应用程序本身定义了安全管理器。如果没有安全管理器,则应用程序没有安全策略,操作不受限制。

      • 与安全管理器交互

        • System.getSecurityManager

        • 一旦应用程序拥有对安全管理器对象的引用,它就可以请求执行特定操作的权限。

          • 标准库中的许多类都这样做。例如,System.exit 以退出状态终止Java虚拟机,调用 SecurityManager.checkExit以确保当前线程具有关闭应用程序的权限。
        • SecurityManager 类定义了许多用于验证其他类型操作的其他方法

          • SecurityManager.checkAccess
          • SecurityManager.checkPropertyAccess
          • checkXXX() 方法集表示已经受到安全管理器保护的操作集。通常,应用程序不需要直接调用任何 checkXXX() 方法。
      • 识别安全违规

        • 许多没有安全管理器的常规操作在使用安全管理器运行时会抛出 SecurityException。即使在调用没有记录为抛出SecurityException 的方法时也是如此。
    • 其他

      • arrayCopy

      • currentTimeMillis,nanoTime

        • currentTimeMillis 和 nanoTime 的准确性受到操作系统提供的时间服务的限制。
        • 使用高级方法,例如 java.util.Calendar.getInstance。
      • exit

  • PATH 和 CLASSPATH

    • 更新 PATH 环境变量(Microsoft Windows)
    • 更新 PATH 变量(Solaris 和 Linux)
    • 检查 CLASSPATH 变量(所有平台)

正则表达

  • java.util.regex API 与正则表达式进行模式匹配

  • 介绍

    • 什么是正则表达式

      • 正则表达式是一种基于集合中每个字符串共享的公共特征来描述一组字符串的方法
      • 它们可用于搜索、编辑或操作文本和数据。
      • 在正则表达式的世界中,有许多不同的风格可供选择,例如 grep、Perl、Tcl、Python、PHP 和 awk。
    • 正则表达式如何在这个包中表示

      • java.util.regex 包的主要组成

        • Pattern

          • Pattern 对象是正则表达式的编译表示。
          • Pattern 类不提供公共构造函数。要创建一个模式,必须首先调用它的一个公共静态编译方法,该方法将返回一个pattern 对象。这些方法接受正则表达式作为第一个参数
        • Matcher

          • 是解释模式并对输入字符串执行匹配操作的引擎。
          • Matcher 没有定义公共构造函数。
          • 可以通过在 Pattern 对象上调用 Matcher 方法来获得Matcher对象。
        • PatternSyntaxException

          • 是一个未检查的异常,它指示正则表达式模式中的语法错误。
  • 测试工具

  • 字符串字面量

    • 此 API 支持的模式匹配的最基本形式是字符串文字的匹配。

    • 元字符

      • 一个由匹配器解释的具有特殊含义的字符。

      • 支持的元字符 : <([{^-=$!|]})?*+.>

      • 强制元字符被视为普通字符

        • 在元字符前加一个反斜杠
        • 将它包含在 \Q (开始引用)和 \E (结束引用)中
  • 字符类

    • “字符类”中的“类”一词并不是指 .class 文件。在正则表达式的上下文中,字符类是用方括号括起来的一组字符。它指定将成功匹配给定输入字符串中的单个字符的字符。
    • [abc]:a、b或c(简单类)
    • [^abc]:除a、b、c以外的任何字符(否定)
    • [a-zA-Z]:a到z,或者A到Z(范围)
    • [a-d[m-p]]:a到d,或m到p(并集)
    • [a-z&&[def]]:d e或f(交集)
    • a-z&&[^bc]]:a到z,除了b和c(差集)
    • [a-z&&[^m-p]]:a到z,不包括不是m到p(差集)
  • 预定义字符类

    • .:任意字符
    • \d:[0-9]
    • \D:[^0-9]
    • \s:[ \t\n\x0B\f\r]
    • \S:[^\s]
    • \w:[a-zA-Z_0-9]
    • \W:[^\w]
    • 以反斜杠开头的构造称为转义构造。
    • 如果在字符串字面量中使用转义构造,则必须在反斜杠之前加上另一个反斜杠,以便编译字符串。
  • 量词

    • 允许指定要匹配的出现次数

    • ?* + {n} {n, } {n,m}

    • 零长度匹配

      • 出现情况

        • 在空输入字符串中
        • 在输入字符串的开头
        • 在输入字符串的最后一个字符之后
        • 在输入字符串的任意两个字符之间。
    • 用量词捕获组和字符类

      • 量词一次只能连接一个字符
      • 量词可以附加到字符类和捕获组
    • 三种量词的区别

      • 贪婪

        • 默认形式
        • 贪婪量词被认为是“贪婪的”,因为它们迫使匹配器在尝试第一次匹配之前读入或吃掉整个输入字符串。
        • 如果第一次匹配尝试失败,匹配器将后退输入字符串一个字符,然后再次尝试,重复这个过程,直到找到匹配或没有更多的字符可供后退。
        • 根据表达式中使用的量词,它最后尝试匹配的对象是1或0个字符。
      • 不情愿

        • 量词后面加(?)
        • 它们从输入字符串的开头开始,然后不情愿地一次吃掉一个字符,寻找匹配的字符。
        • 他们尝试的最后一件事是整个输入字符串。
      • 所有格

        • 量词后面加(+)
        • 总是吃掉整个输入字符串,为匹配尝试一次(而且只有一次)。
        • 与贪婪量词不同,所有格量词从不后退,即使这样做会让整体匹配成功。
  • 捕获组

    • 是一种将多个字符视为单个单元的方法。

    • 是通过将要分组的字符放置在一组括号中来创建的。

    • 编号

      • 通过从左到右计算它们的开括号来对捕获组进行编号。
      • 要找出表达式中有多少组,可以调用匹配器对象上的groupCount 方法。
      • 还有一个特殊的组,组0,它总是表示整个表达式。这个组不包括在 groupCount 报告的总数中。
      • 以(? 开头的是不捕获文本且不计入组总数的纯非捕获组。
      • 理解组是如何编号的是很重要的,因为一些 Matcher 方法接受一个 int,指定一个特定的组号作为参数:
    • 反向引用

      • 在正则表达式中指定为反斜杠(),后面跟着一个数字,表示要收回的组的编号。
      • 输入字符串中与捕获组匹配的部分保存在内存中,以便以后通过反向引用检索。
      • 对于嵌套的捕获组,反向引用的工作方式完全相同
  • 边界匹配

    • 可以使模式匹配更加精确。
    • ^:一行的开头
    • $:一行的末尾
    • \b:字边界
    • \B:非字边界
    • \A:输入的开始
    • \G:前一个匹配的结束
    • \Z:输入的结束符,如果有,则为最终结束符
    • \z:输入的结束
  • Pattern 类的方法

    • 创建带有标志的 Pattern

      • Pattern 类定义了一个可选的编译方法,该方法接受一组影响模式匹配方式的标志。flags 参数是位掩码,可以包括以下任何一个公共静态字段

      • Pattern.CANON_EQ

        • 启用规范等价。
      • Pattern.CASE_INSENSITIVE

        • 启用不区分大小写的匹配。
        • 也可以通过嵌入的标志表达式(?i)来启用。
      • Pattern.COMMENTS

        • 允许模式中有空格和注释。
        • 可以通过嵌入的标志表达式(?x)来启用。
      • Pattern.DOTALL

        • 启用 dotall 模式
        • 在 dotall 模式下,表达式 . 匹配任何字符,包括行结束符。默认情况下,该表达式不匹配行结束符。
      • Pattern.LITERAL

        • 启用模式的文字解析。
        • 当指定此标志时,指定模式的输入字符串将被视为文字字符序列。输入序列中的元字符或转义序列将没有特殊含义。
      • Pattern.MULTILINE

        • 启用多行模式。
        • 在多行模式下,表达式^和$分别匹配行结束符之后或之前或输入序列的结束。
        • 默认情况下,这些表达式只匹配整个输入序列的开头和结尾。
      • Pattern.UNICODE_CASE

        • 启用 unicode 大小写折叠。
        • 当指定此标志时,当 CASE_INSENSITIVE 标志启用时,不区分大小写的匹配将以与 Unicode 标准一致的方式进行。
        • 默认情况下,不区分大小写匹配假设只匹配 US-ASCII 字符集中的字符。
      • Pattern.UNIX_LINES

        • 启用 UNIX 行模式。
        • 在这种模式下,在. ^和$的行为中,只有’\n’行结束符被识别。
      • 要编译具有多个标志的模式,请使用按位或运算符“|”分隔要包含的标志。

    • 嵌套标志的表达式

      • 双参数编译版本的替代品,并在正则表达式本身中指定。
    • 使用 matches(String,CharSequence) 方法

    • 使用 split(String) 方法

    • 其他实用方法

      • public static String quote(String s)
      • public String toString()
    • java.lang.String 中等价 Pattern 的方法

      • public boolean matches(String regex)
      • public String[] split(String regex, int limit)
      • public String[] split(String regex)
      • public String replace(CharSequence target,CharSequence replacement)
  • Matcher 类的方法

    • 索引方法

      • 提供了有用的索引值,可以精确地显示在输入字符串中找到匹配项的位置
      • start,end
    • 搜索方法

      • 检查输入字符串并返回一个布尔值,指示是否找到模式。
      • lookingAt,find,matches
    • 替换方法

      • 替换输入字符串中的文本
      • appendReplacement,appendTail,replaceAll,replaceFirst,quoteReplacement
    • start 和 end 方法

    • matches 和 lookingAt 方法

    • replaceFirst(String) 和 replaceAll(String)

    • appendReplacement(StringBuffer,String) 和appendTail(StringBuffer)

    • 在 java.lang.String 中等价的方法

      • replaceFirst
      • replaceAll
  • PatternSyntaxException 类的方法

    • getDescription,getIndex,getPattern,getMessage
  • Unicode 支持

    • 在JDK 7发行版中,正则表达式模式匹配扩展了功能,支持 Unicode 6.0。

    • 匹配特定的代码点

    • Unicode 字符属性

      • 每个 Unicode 字符除了值之外,还具有某些属性。
      • 可以将属于特定类别的单个字符与表达式\p{prop}匹配。可以用表达式\P{prop}匹配不属于特定类别的单个字符。
      • Scripts
      • Blocks
      • General Category
  • 其他资源

    • Jeffrey E. F. Friedl的《Mastering regular Expressions》

集合

简介

  • 集合——有时称为容器——是一个对象,它将多个元素组合成一个单元。

  • 集合用于存储、检索、操作和通信聚合数据。

  • 集合框架

    • 表示和操作集合的统一体系结构。

    • 组成

      • 接口

        • 表示集合的抽象数据类型。接口允许对集合进行独立于其表示细节的操作。在面向对象语言中,接口通常形成层次结构。
      • 实现

        • 是集合接口的具体实现。本质上,它们是可重用的数据结构。
      • 算法

        • 这些方法在实现集合接口的对象上执行有用的计算,比如搜索和排序。
        • 这些算法被认为是多态的:也就是说,相同的方法可以用于适当的集合接口的许多不同实现。
        • 本质上,算法是可重用的功能。
  • Java 集合框架的好处

    • 减少编程工作
    • 提高程序的速度和质量
    • 允许不相关 api 之间的互操作性
    • 减少学习和使用新 api 的工作量
    • 减少设计新 api 的努力
    • 促进软件重用

接口

  • 核心集合接口

    • Collection

      • Set

        • SortedSet
      • List

      • Queue

      • Deque

    • Map

      • SortedMap
  • 所有核心集合接口都是泛型的

  • 为了保持核心集合接口的数量易于管理,Java 平台没有为每种集合类型的每种变体提供单独的接口。(这些变体可能包括不可变的、固定大小的和仅追加的。)

  • 相反,每个接口中的修改操作都是可选的——一个给定的实现可以选择不支持所有操作。如果调用了不受支持的操作,集合将抛出 UnsupportedOperationException 异常。

  • 实现负责记录它们支持哪些可选操作。

  • 所有 Java 平台的通用实现都支持所有可选操作。

  • Collection

    • 集合层次结构的根。

    • 表示一组被称为元素的对象。

    • Collection 接口是所有集合实现的最小公约数,用于传递集合,并在需要最大通用性时对它们进行操作。

    • Java 平台不提供该接口的任何直接实现,但提供了更具体的子接口的实现,如 Set 和 List。

    • 按照惯例,所有通用目的的集合实现都有一个接受collection 参数的构造函数。这个构造函数称为转换构造函数,它初始化新集合以包含指定集合中的所有元素,而不管给定集合的子接口或实现类型是什么。换句话说,它允许您转换集合的类型。

    • 基本方法

      • size,isEmpty,contains,add,remove,iterator
      • remove 操作始终从列表中删除指定元素的第一个出现项。add 和 addAll 操作总是将新元素附加到列表的末尾。
    • 操作整个集合的方法

      • containsAll,addAll,removeAll,retainAll,clear
    • 数组操作

      • toArray
    • 获取顺序流或并行流

      • 在 JDK 8及以后版本中,Collection 接口还公开了方法Stream Stream() 和 Stream parallelStream(),用于从底层集合获取顺序流或并行流。
    • 遍历集合

      • 使用聚合操作

        • 在 JDK 8及更高版本中,迭代集合的首选方法是获取流并对其执行聚合操作。
        • 聚合操作通常与 lambda 表达式一起使用,以使编程更具表现力,使用更少的代码行。
        • 新的聚合操作和现有的批量操作(容器、addAll 等)之间的关键区别是,旧版本都是一成不变的,这意味着它们都修改了底层集合。相反,新的聚合操作不修改底层集合。在使用新的聚合操作和 lambda 表达式时,您必须注意避免突变,以免将来在并行流中运行您的代码时引入问题。
      • 使用 for-each 构造

        • 允许您使用 for 循环简洁地遍历一个集合或数组
      • 使用迭代器。

        • Iterator 是一个对象,它使您能够遍历集合,并根据需要有选择地从集合中删除元素。

        • 通过调用集合的 iterator 方法,可以获得集合的Iterator。

        • 如果迭代有更多元素,hasNext 方法返回 true, next 方法返回迭代中的下一个元素。remove 方法从底层Collection中 删除 next 返回的最后一个元素。remove 方法在每次调用 next 时只能被调用一次,如果违反此规则则抛出异常。

        • Iterator.remove 是在迭代过程中修改集合的唯一安全的方法;如果在迭代正在进行中,如果底层集合以其他方式修改,则该行为将被修改。

        • 使用时机

          • 删除当前元素
          • for-each 不能用于过滤
          • 并行地遍历多个集合。
  • Set

    • 不能包含重复元素的集合。

    • 该接口为数学集合抽象建模,并用于表示集合

    • Set 接口只包含从 Collection 继承的方法,并添加了禁止重复元素的限制。

    • Set 还在 equals 和 hashCode 操作的行为上添加了更强的契约,允许 Set 实例进行有意义的比较,即使它们的实现类型不同。如果两个 Set 实例包含相同的元素,则它们是相等的。

    • 通用实现

      • HashSet

        • 将其元素存储在哈希表中
        • 性能最好的实现,但是不保证迭代的顺序。
      • TreeSet

        • 将其元素存储在红黑树中,并根据其值对其元素进行排序
        • 比 HashSet 慢很多
      • LinkedHashSet

        • 实现为一个哈希表,其中运行一个链表,它根据元素插入到集合中的顺序(插入顺序)对其进行排序。
        • 代价只是略高一些。
    • 基本操作

    • 批量操作

    • 数组操作

  • List

    • 有序集合(有时称为序列)。

    • 列表可以包含重复的元素。

    • List 的用户通常可以精确地控制每个元素在列表中的插入位置,并且可以通过它们的整数索引(位置)访问元素。

    • 与 Set 接口一样,List 加强了对 equals 和 hashCode 方法的要求,以便可以比较两个 List 对象的逻辑相等性,而不考虑它们的实现类。如果两个 List 对象以相同的顺序包含相同的元素,则它们是相等的。

    • 额外的操作

      • 位置访问

        • get, set, add, addAll, remove
      • 搜索

        • indexOf,lastIndexOf
      • 迭代器

        • listIterator
        • 允许您向任意方向遍历列表,在迭代期间修改列表,并获得迭代器的当前位置。
        • 游标总是在两个元素之间——一个由调用 previous 返回,另一个由调用 next 返回。
        • 对 next 和 previous 的调用可以混合使用,但必须小心。第一次调用 previous 返回的元素与最后一次调用next 返回的元素相同。
        • indexOf 方法返回 previousindex(),即使它正向遍历列表。原因是 nextIndex() 将返回我们要检查的元素的索引,而我们希望返回刚刚检查的元素的索引。
        • Iterator 接口提供了 remove 操作,用于从集合中删除next 返回的最后一个元素。对于 ListIterator,该操作删除由 next 或 previous 返回的最后一个元素。
        • ListIterator 接口提供了两个额外的操作来修改列表——set 和 add。set 方法用指定的元素覆盖 next 或previous 返回的最后一个元素。
      • 范围视图

        • sublist
        • 返回的 List 是调用 subblist 的 List 的备份,因此前者的更改反映在后者中。
        • 这种方法消除了显式范围操作的需要(通常存在于数组的那种操作)。任何需要 List 的操作都可以通过传递一个subblist 视图而不是整个 List 来用作范围操作。
        • 任何操作 List 的多态算法,比如 replace 和 shuffle 的例子,都是使用由 subList 返回的 List。
        • 对于许多常见的 List 实现,例如 ArrayList,从列表末尾删除元素的性能要比从列表开头删除元素的性能好得多。
        • 如果元素不是通过返回的 List 以任何方式添加到或从支持列表中删除,那么由 subblist 返回的 List 的语义将变为未定义。因此,强烈建议您只将 subblist 返回的 List作为一个临时对象使用——在备份List上执行一个或一系列范围操作。
        • 使用 subblist 实例的时间越长,通过直接修改支持列表或通过另一个 subblist 对象来破坏它的可能性就越大。
    • Collections.shuffle

      • 使用指定的随机性源随机排列指定的列表。
      • 它从列表的底部向上运行,重复地将随机选择的元素交换到当前位置。
      • 它是公平的
    • Arrays.asList

      • 静态工厂方法,它允许将数组视为 List。
      • 此方法不复制数组。List 中的更改会被写入数组,反之亦然。得到的 List 不是一个通用的 List 实现,因为它没有实现(可选的)添加和删除操作:数组不能调整大小。
    • List 算法

      • sort,shuffle,reverse,rotate,swap,replaceAll,fill,copy,binarySearch,indexOfSubList,lastIndexOfSubList
  • Queue

    • 用于处理前保存多个元素的集合。

    • 除了基本的 Collection 操作之外,Queue 还提供额外的插入、提取和检查操作。

      • 失败时抛出异常

        • add,remove,element
      • 失败时返回特殊值

        • offer,poll,peak
    • 队列通常(但不一定)以 FIFO (先进先出)方式对元素进行排序。

    • 优先级队列是例外,它根据提供的比较器或元素的自然顺序对元素进行排序。

    • 无论使用何种顺序,队列的头都是将通过调用 remove 或 poll 删除的元素。

    • 在 FIFO 队列中,所有新元素都插入到队列的尾部。其他类型的队列可能使用不同的放置规则。

    • 每个 Queue 实现必须指定其排序属性。

    • 有界队列

      • 可以限制它所持有的元素的数量
      • java.util.concurrent 中的实现一部分是
      • java.util 中没有
    • 队列实现通常不允许插入空元素。LinkedList 实现是一个例外,它被改造为实现 Queue。由于历史原因,它允许空元素,但您应该避免利用这一点,因为 null 被 poll 和peek 方法用作特殊的返回值。

    • 队列实现通常不定义 equals 和 hashCode 方法的基于元素的版本,而是从 Object 继承基于标识的版本。

    • Queue 接口没有定义阻塞队列方法,而阻塞队列方法在并发编程中很常见。这些方法在接口 java.util.concurrent 中定义,用于等待元素出现或等待空间可用。BlockingQueue,它扩展了 Queue。

  • Deque

    • 既可以用作 FIFO (先进先出),也可以用于 LIFO (后进先出)。
    • 在 deque 中,所有新元素都可以在两端插入、检索和删除。
    • 像 ArrayDeque 和 LinkedList 这样的预定义类实现了Deque 接口。
    • 插入
    • 删除
    • 检索
  • Map

    • 将键映射到值的对象。

    • Map 不能包含重复的键;每个键最多可以映射到一个值。

    • 加强了对 equals 和 hashCode 方法的要求,以便可以比较两个 Map 对象的逻辑是否相等,而不考虑它们的实现类型。

    • 基本操作

      • put, get, remove, containsKey, containsValue, size,empty
    • 批量操作

      • putAll,clear
    • 集合视图

      • keySet, entrySet, values

      • 映射代数

        • 当应用于 Collection 视图时,批量操作(containsAll、removeAll 和 retainAll)是非常强大的工具。
    • 通用实现

      • HashMap
      • TreeMap
      • LinkedHashMap
    • 多重映射

      • 类似 Map,但它可以将每个键映射到多个值。
      • Java Collections Framework 没有为 multimap 提供接口,因为它们并不常用。将值为 List 实例的 Map 作为多重映射使用是相当简单的事情。
  • 对象排序

    • Collections.sort

    • Comparable 接口

    • 编写自己的可比类型

      • compareTo 方法的行为有四个限制,我们现在不深入讨论,因为它们相当技术性和枯燥,最好留在 API 文档中。
    • Comparator

      • 不能用于对已排序的集合(如 TreeSet)排序,因为它生成的排序与 equals 不兼容。
      • 要解决这个问题,只需调整 Comparator,使其生成与等号兼容的排序。换句话说,调整它,以便在使用compare 时被视为相等的元素是那些在使用 equals 进行比较时也被视为相等的元素。
  • SortedSet

    • 按升序保持其元素的集合。

    • 提供了几个额外的操作来利用排序。

    • 范围视图

      • subSet,headSet,tailSet
      • 范围视图操作有点类似于 List 接口提供的操作,但有一个很大的区别。即使直接修改了备份排序集,排序集的范围视图仍然有效。这是可行的,因为排序集的范围视图的端点是元素空间中的绝对点,而不是支持集合中的特定元素,就像列表的情况一样
      • 一个排序集合的范围视图实际上只是一个窗口,它指向集合中元素空间的指定部分。对范围视图的更改会写回备份排序集,反之亦然。因此,在排序集上长时间使用范围视图是可以的,不像在列表上使用范围视图。
      • 假设您想查看一个包含其两个端点的闭区间,而不是一个开区间。如果元素类型允许计算元素空间中给定值的继任者,则只需请求从 lowEndpoint 到继任者(highEndpoint)的子集。虽然不是很明显,但在 string 的自然顺序中,字符串s的继承者是 s + “\0”——也就是说,s 后面附加了一个空字符。
      • 类似的技术可以用于查看不包含端点的开放区间。
    • 端点

      • first,last
      • last 可以解决 SortedSet 接口中的缺陷。使用 SortedSet要做的一件事是进入 Set 的内部并向前或向后迭代。从内部向前走很简单:只需获得一个 tailSet 并迭代它。不幸的是,没有简单的方法可以倒退。
    • 比较器访问

      • comparator
      • 该方法返回用于对集合排序的 comparator,如果集合是根据其元素的自然顺序排序的,则返回 null。提供此方法是为了将排序集复制到具有相同排序的新排序集。
    • 尽管接口不能保证,但 Java 平台的 SortedSet 实现的toString 方法将返回一个字符串,其中按顺序包含已排序集合的所有元素。

    • 标准构造函数

  • SortedMap

    • 以升序键顺序维护其映射的映射。
    • 这是 SortedSet 的 Map 模拟。
    • 因为这个接口是 SortedSet 的精确映射模拟,所以SortedSet 接口部分中的所有习惯用法和代码示例都适用于 SortedMap,只需要稍加修改。

聚合操作

  • 管道与流

    • 管道是一系列聚合操作

    • 管道的组成

      • 一个源

        • 可以是一个集合、一个数组、一个生成器函数或一个 I/O通道
      • 零或多个中间操作

      • 一个终端操作

        • 会产生一个非流的结果
    • 流是元素的序列。与集合不同,它不是存储元素的数据结构。相反,流通过管道携带来自源的值。

  • 聚合操作和迭代器的区别

    • 他们使用内部迭代

      • 聚合操作不包含像 next 这样的方法来指示它们处理集合的下一个元素。使用内部委托,应用程序决定迭代什么集合,而 JDK 决定如何迭代集合。
      • 使用外部迭代,应用程序可以确定迭代什么集合以及如何迭代集合。但是,外部迭代只能按顺序遍历集合的元素。内部迭代没有这个限制。它可以更容易地利用并行计算的优势,这包括将一个问题划分为子问题,同时解决这些问题,然后将子问题的解决结果结合起来。
    • 它们处理来自流的元素

    • 它们支持行为作为参数

  • 约简

    • 约简操作

      • 通过组合流的内容返回一个值
      • average, sum, min, max, count
    • Stream.reduce 方法

      • 通用的约简操作

      • 参数

        • identity

          • 既是约简的初始值,也是流中没有元素时的默认结果。
        • accumulator

          • accumulator 函数接受两个参数:约简的部分结果和流的下一个元素
          • 它返回一个新的部分结果
    • Stream.collect 方法

      • 与 reduce 方法(在处理元素时总是创建一个新值)不同,collect 方法修改或改变现有值。

      • 参数

        • supplier

          • 一个工厂函数,它构造新的实例。对于 collect 操作,它创建结果容器的实例。
        • accumulator

          • 累加器函数,将流元素合并到结果容器中。
        • combiner

          • 组合器函数,接受两个结果容器并合并它们的内容。
        • 注意

          • 可以对并行流使用 collect 操作
          • 累加器和组合器函数不返回值。
          • supplier 是lambda表达式(或方法引用),而不是像reduce操作中的标识元素那样的值。
      • Collectors 类

        • 包含许多有用的约简操作,例如将元素积累到集合中,并根据各种标准汇总元素。这些缩减操作返回类 Collector的实例,因此您可以将它们用作 collect 操作的参数。
      • 下游收集器

        • Collector 参数称为下游收集器。这是 Java 运行时应用于另一个收集器的结果的收集器。
      • 多级还原

        • 包含一个或多个下游收集器的管道称为多级还原。
      • Collectors.reducing 方法

        • identity
        • mapper
        • operation
  • 并行

    • 聚合操作和并行流使您能够使用非线程安全的集合实现并行性,前提是在对集合进行操作时不修改集合。

    • 并行执行流

      • Collection.parallelStream
      • BaseStream.parallel
    • 减少并发

      • 并发缩减

        • 执行条件

          • 并行流
          • collect 方法参数 collector 具有 Collector.Characteristics.CONCURRENT 特征
          • 要么是无序流,要么 collector 具有 Collector.Characteristics.UNORDERED 特征
    • 顺序

      • 管道处理流元素的顺序取决于流是串行执行还是并行执行、流的源以及中间操作。
    • 副作用

      • 如果一个方法或表达式除了返回或产生一个值之外,还修改了计算机的状态,那么它就会产生副作用。

      • 永远不要在过滤器和映射等操作中将有副作用的 lambda表达式作为参数传递。

      • 惰性

        • 所有中间操作都是惰性操作。
        • 如果表达式、方法或算法仅在需要时才计算其值,则该表达式、方法或算法为懒惰的。
        • 中间操作是懒惰的,因为它们直到终端操作开始才开始处理流的内容。
        • 延迟处理流使 Java 编译器和运行时能够优化它们处理流的方式。
      • 干扰

        • 当管道处理流时修改了流的源,就会发生干扰。
        • 流操作中的 Lambda 表达式不应产生干扰。
      • 有状态的 Lambda 表达式

        • 有状态 lambda 表达式的结果依赖于在管道执行期间可能改变的任何状态。
        • 避免在流操作中使用有状态 lambda 表达式作为参数。

实现

  • 分类

    • 通用实现

      • Set

        • HashSet,TreeSet,LinkedHashSet
      • List

        • ArrayList,LinkedList
      • Queue

      • Deque

        • ArrayDeque,LinkedList
      • Map

        • HashMap,TreeMap,LinkedHashMap
      • 每个通用实现都提供其接口中包含的所有可选操作。所有都允许空元素、键和值。没有同步(线程安全)。

      • 它们都具有快速失败迭代器,可以在迭代过程中检测非法的并发修改,并快速而干净地失败,而不是在未来不确定的时间冒任意的、不确定的行为的风险。

      • 它们都是 Serializable,并且都支持公共克隆方法。

      • 这些实现是不同步的这一事实代表了与过去的决裂(遗留集合 Vector 和 Hashtable 是同步的)

      • 一般来说,不让用户为他们不使用的功能付费是一个很好的 API 设计实践。

      • 此外,在某些情况下,不必要的同步可能导致死锁。

      • 如果您需要线程安全的集合,同步包装器允许将任何集合转换为同步集合。因此,同步对于通用实现是可选的,而对于遗留实现是必须的。

      • java.util.concurrent 包提供了扩展 Queue 的BlockingQueue 接口和扩展 Map 的 ConcurrentMap 接口的并发实现。这些实现比单纯的同步实现提供了更高的并发性。

      • 通常,您应该考虑接口,而不是实现。在大多数情况下,实现的选择只影响性能。

      • 要记住的一点是,这种性能指标有其局限性。有时,名义上较慢的实现可能更快。当你有疑问的时候,测试你的性能!

    • 特殊实现

    • 并发实现

    • 包装器实现

      • 与其他类型的实现(通常是通用型实现)结合使用,以提供附加的或受限的功能。
    • 便利实现

      • 是小型实现,通常通过静态工厂方法提供,为特殊集合(例如,单例集)提供了方便、高效的通用实现替代方案。
    • 抽象实现

      • 是便于构造自定义实现的骨架实现
  • Set 实现

    • 通用实现

      • HashSet, TreeSet, LinkedHashSet
      • 如果您需要使用 SortedSet 接口中的操作,或者如果需要值顺序迭代,请使用 TreeSet;否则,使用 HashSet。
      • LinkedHashSet 在某种意义上介于 HashSet 和 TreeSet之间。
      • 它被实现为一个哈希表,其中运行一个链表,它提供了插入顺序迭代(从最近插入到最近插入),运行速度几乎与HashSet 一样快。
      • 关于 HashSet 值得记住的一点是,迭代是项数和桶数(容量)的线性总和。
      • 因此,选择过高的初始容量会浪费空间和时间。另一方面,选择一个太低的初始容量会浪费时间,因为每次被迫增加容量时都会复制数据结构。
      • 如果不指定初始容量,则默认为16。
      • 在过去,选择质数作为初始容量有一定的优势。这已经不是事实了。在内部,容量总是四舍五入到2的幂。初始容量由int构造函数指定。
      • HashSet 类有另一个调优参数,称为负载因子
      • LinkedHashSet 具有与 HashSet 相同的调优参数,但迭代时间不受容量的影响。
      • TreeSet 没有调优参数。
    • 特殊实现

      • EnumSet,CopyOnWriteArraySet
      • EnumSet 是枚举类型的高性能 Set 实现。
      • 枚举集的所有成员必须具有相同的枚举类型。在内部,它由一个位向量表示,通常是一个 long。枚举集支持枚举类型范围内的迭代
      • CopyOnWriteArraySet 是一个由写时复制数组备份的 Set 实现。
      • 所有可变操作,如添加、设置和删除,都是通过创建数组的新副本来实现的;不需要任何加锁。甚至迭代也可以安全地与元素插入和删除同时进行。
      • 添加、删除和包含方法所需的时间与 Set 的大小成正比。这种实现只适用于很少修改但经常迭代的集合。它非常适合于维护必须防止重复的事件处理程序列表。
  • List 实现

    • 通用实现

      • ArrayList,LinkedList
      • ArrayList,不必为 List 中的每个元素分配一个节点对象,并且可以利用 System.arraycopy。
      • ArrayList 有一个调优参数——初始容量,它指的是ArrayList 在必须增长之前可以容纳的元素数量
    • 特殊实现

      • CopyOnWriteArrayList
      • 这种实现非常适合维护事件处理程序列表,因为在事件处理程序列表中更改不频繁,遍历很频繁,而且可能很耗时。
      • 如果您的 List 的大小是固定的——也就是说,您永远不会使用除 containsAll 之外的删除、添加或任何批量操作,那么可以考虑 Arrays.asList
  • Map 实现

    • 通用实现

      • HashMap,TreeMap,LinkedHashMap
      • Map 的情况类似于 Set。
      • LinkedHashMap 提供了两个 LinkedHashSe t不具备的功能。在创建 LinkedHashMap 时,可以根据键访问而不是插入对其进行排序。换句话说,仅仅查找与某个键相关的值就可以将该键带到映射的末尾。
      • LinkedHashMap 提供了 removeEldestEntry 方法,当新映射添加到映射中时,可以覆盖该方法来强制执行自动删除旧映射的策略。这使得实现自定义缓存非常容易。
    • 特殊实现

      • EnumMap, WeakHashMap,IdentityHashMap. EnumMap
      • 这些实现在内部实现为数组,是用于枚举键的高性能Map 实现。
      • 这种实现将 Map 接口的丰富性和安全性与接近数组的速度相结合。
      • 如果你想要将一个枚举映射到一个值,你应该总是优先使用 EnumMap 而不是数组。
      • WeakHashMap 只存储对其键的弱引用。
      • 只存储弱引用允许在 WeakHashMap 之外不再引用键-值对时对其进行垃圾回收。该类提供了利用弱引用功能的最简单方法。
      • 它对于实现“类似注册表”的数据结构很有用,在这种结构中,当任何线程都无法访问条目的键时,条目的实用功能就会消失
      • IdentityHashMap 对于保持拓扑的对象图转换(如序列化或深度复制)非常有用。
      • 要执行这样的转换,您需要维护一个基于身份的“节点表”,以跟踪已经看到的对象。
      • 在动态调试器和类似的系统中,基于身份的映射也用于维护对象到元信息的映射。
      • 基于身份的映射在挫败“欺骗攻击”方面很有用,这种攻击是由故意错误的 equals 方法造成的,因为IdentityHashMap 从不在其键上调用 equals 方法。
      • 这种实现的另一个好处是速度快。
    • 并发实现

      • ConcurrentMap 接口
      • ConcurrentHashMap
      • 此实现在执行检索时不会阻塞,并允许客户端选择更新的并发级别。
      • 它的目的是作为 Hashtable 的直接替代品:除了实现ConcurrentMap 之外,它还支持 Hashtable 特有的所有遗留方法。
      • 如果您不需要遗留操作,请小心使用 ConcurrentMap 接口操作它。
  • Queue 实现

    • 通用实现

      • LinkedList,PriorityQueue
      • PriorityQueue 类是一个基于堆数据结构的优先级队列。该队列根据构造时指定的顺序对元素进行排序。
      • 队列的头是相对于指定顺序的最小元素。如果多个元素绑定最小值,则头部是其中一个元素
    • 并发实现

      • BlockingQueue 接口
      • LinkedBlockingQueue,ArrayBlockingQueue,PriorityBlockingQueue,DelayQueue,SynchronousQueue
      • TransferQueue 接口
      • 向队列中添加元素的代码可以选择等待(阻塞)另一个线程中的代码来检索元素。
      • LinkedTransferQueue
  • Deque 实现

    • 通用实现

      • LinkedList,ArrayDeque
      • 在 LinkedList 实现中允许空元素,但在 ArrayDeque 实现中不允许。
      • ArrayDeque 在两端的添加和删除操作上比 LinkedList 更高效。
      • LinkedList 实现中的最佳操作是在迭代期间删除当前元素。
    • 并发实现

      • LinkedBlockingDeque
  • 包装器 实现

    • 将其所有实际工作委托给指定的集合,但在该集合提供的功能之上添加额外的功能。

    • 这些实现是匿名的;标准库提供的不是公共类,而是静态工厂方法。

    • 所有这些实现都可以在 Collections 类中找到,该类仅由静态方法组成。

    • 同步包装器

      • 六个核心集合接口(collection、Set、List、Map、SortedSet 和 SortedMap)中的每一个都有一个静态工厂方法。
      • 这些方法都返回一个同步的(线程安全的)集合,该集合由指定的集合备份。
      • 为了保证串行访问,对备份集合的所有访问都必须通过返回的集合来完成。保证这一点的简单方法是不保留对备份集合的引用。使用以下技巧创建同步集合。
      • 面对并发访问,当迭代返回的集合时,用户必须手动同步它。原因是迭代是通过对集合的多次调用来完成的,而集合必须组合成单个原子操作。
      • 使用包装器实现的一个小缺点是您不能执行包装实现的任何非接口操作。
    • 不可变包装器

      • 使集合在构建后不可变。在这种情况下,最好不要维护对支持集合的引用。这绝对保证了不变性。
      • 允许某些客户端只读访问您的数据结构。您保留了对支持集合的引用,但分发了对包装器的引用。通过这种方式,客户端可以查看但不能修改,而您可以保持完全访问。
    • 已检查包装器

      • 可用于泛型集合。
      • 这些实现返回指定集合的动态类型安全视图,如果客户端试图添加错误类型的元素,该视图将抛出ClassCastException。
      • 该语言中的泛型机制提供了编译时(静态)类型检查,但也有可能破坏这种机制。动态类型安全视图完全消除了这种可能性。
  • 便利实现

    • 通过静态工厂方法而不是公共类提供

    • 当您不需要它们的全部功能时,它们比通用实现更方便、更高效。

    • 数组的 List 视图

      • Arrays.asList
      • 对 List 的更改将写入数组,反之亦然。
      • 集合的大小就是数组的大小,不能更改。如果在 List 上调用了 add 或 remove 方法,则会导致UnsupportedOperationException。
      • 作为基于数组和基于集合的 api 之间的桥梁。它允许您将数组传递给需要 Collection 或 List 的方法。
      • 如果您需要一个固定大小的 List,那么它比任何通用的List 实现都更有效。
      • 对后备数组的引用不会被保留。
    • 不可变多拷贝 List

      • Collections.nCopies

      • 用途

        • 初始化一个新创建的 List
        • 增长一个现有的 List
    • 不可变单例 Set

      • Collections.singleton

      • 用途

        • 从集合中删除所有指定元素的出现。
        • 相关的习惯用法从 map 中删除映射到指定值的所有元素。
        • 向编写为接受值集合的方法提供单个输入值。
    • 空 Set, List, 和 Map 常量

      • emptySet, emptyList, emptyMap
      • 主要用途是作为方法的输入,这些方法接受一个值集合

算法

  • Collections 类

  • 所有方法都采用静态方法的形式,其第一个参数是要对其执行操作的集合。

  • Java 平台提供的绝大多数算法都在 List 实例上操作,但也有少数算法在任意 Collection 实例上操作

  • 排序

    • sort
    • List 进行重新排序,使其元素按照排序关系升序排列。
    • 使用了一种稍作优化的归并排序算法,该算法快速而稳定
    • 它保证在 n log(n) 时间内运行,并且在几乎排序的列表上运行得更快。实证测试表明,它与高度优化的快速排序一样快。快速排序通常被认为比归并排序更快,但不稳定,不能保证 n log(n) 的性能。
  • 洗牌

    • shuffle
    • 基于来自随机源的输入对 List 进行重新排序
  • 常规数据操作

    • reverse,fill,copy,swap,addAll
  • 搜索

    • binarySearch
    • 在已排序的 List 中搜索指定元素。
  • 构成

    • frequency

      • 计数指定元素在指定集合中出现的次数
    • disjoint

      • 确定两个集合是否不相交;也就是说,它们是否不包含共同的元素
  • 极值

    • min,max

自定义实现

  • 借助 Java 平台提供的抽象实现

  • 动机

    • 持久性

      • 所有内置 Collection 实现都驻留在主存中,并在程序退出时消失。如果您想要一个在下次程序启动时仍然存在的集合,您可以通过在外部数据库上构建一个贴面来实现它。
    • 专用

      • 这是一个非常宽泛的范畴。一个例子是包含实时遥测数据的不可修改地图。键可以表示位置,值可以从这些位置的传感器读取,以响应 get 操作。
    • 高性能,特殊用途

    • 高性能、多用途的

    • 增强功能

    • 方便

    • 适配

  • 编写

    • 选择适当的抽象实现类。
    • 为类的所有抽象方法提供实现。如果您的自定义集合是可修改的,那么您还必须重写一个或多个具体方法。抽象实现类的 API 文档将告诉您要重写哪些方法。
    • 测试并在必要时调试实现。
    • 现在您有了一个工作的自定义集合实现,如果您关心性能,请阅读抽象实现类的 API 文档,了解您要继承其实现的所有方法。如果有太慢的,就重写它们。如果重写任何方法,请确保在重写之前和之后测量方法的性能。在性能调整上投入多少精力应该取决于实现的使用率以及它的使用对性能的重要性。(通常这一步最好省略。)

互操作性

  • 兼容性

    • 向上兼容性

      • 旧 API 返回一个对象数组,而新 API 需要一个 Collection

        • Arrays.asList
      • 如果旧的 API 返回 Vector 或 Hashtable,则根本不需要做任何工作,因为 Vector 被改造为实现 List 接口,而Hashtable 被改造为实现 Map。因此,Vector 可以直接传递给调用 Collection 或 List 的任何方法。

      • API 可能返回表示对象集合的 Enumeration。Collections.list 方法将枚举转换为集合。

    • 向下兼容

      • 新的 API 返回一个 Collection,而旧的 API 需要一个Object 数组。toArray

      • 如果旧的 API 需要 String 数组

        • (String[]) c.toArray(new String[0])
      • 如果旧的 API 需要 Vector,标准的集合构造函数就会派上用场。

      • 如果旧 API 需要枚举

        • Collections.enumeration
  • API 设计

    • 参数

      • 如果您的 API 包含一个集合作为输入的方法,那么将相关参数类型声明为集合接口类型之一是至关重要的。
      • 永远不要使用实现类型,因为这违背了基于接口的集合框架的目的,即允许在不考虑实现细节的情况下操作集合
      • 应该始终使用最不特定的有意义的类型。
      • 永远不要定义自己的临时集合类,并在输入时要求该类的对象。通过这样做,您将失去 Java 集合框架提供的所有好处。
    • 返回值

      • 返回值可以比输入参数灵活得多。可以返回实现或扩展某个集合接口的任何类型的对象。这可以是其中一个接口,也可以是扩展或实现这些接口之一的特殊用途类型。
      • 在某种意义上,返回值应该具有与输入参数相反的行为:最好返回最具体的适用集合接口,而不是最一般的。
    • 遗留的 API

      • 目前有很多 api 定义了它们自己的特别集合类型。虽然这很不幸,但这是事实,因为 Java 平台的前两个主要发行版中没有集合框架。
      • 如果可能的话,改造您的遗留收集类型以实现一个标准收集接口。然后,您返回的所有集合都将与其他基于集合的api 顺利互操作。
      • 如果这是不可能的,则定义一个适配器类,它包装一个遗留集合对象,允许它作为标准集合发挥作用。
      • 如果可能的话,使用遵循输入准则的新调用来改进 API,以接受标准集合接口的对象。这样的调用可以与采用遗留集合类型的调用共存。
      • 如果这是不可能的,为您的遗留类型提供一个构造函数或静态工厂,它接受一个标准接口的对象,并返回包含相同元素(或映射)的遗留集合。这两种方法都允许用户将任意集合传递到 API 中。

时间

Date-Time 包,java.time。在Java SE 8发行版中引入,提供了一个全面的日期和时间模型。

Date-Time API 使用 ISO-8601中定义的日历系统作为默认日历。此日历基于公历系统,并在全球范围内用作表示日期和时间的事实上标准。

核心类

  • LocalDateTime
  • ZonedDateTime
  • OffsetDateTime

如果你想使用另一种日历系统,java.time.chrono 包允许您使用一种预定义的日历系统。或者你也可以自己创建。

Date-Time API 使用 Unicode 公共区域数据存储库(CLDR)。这个存储库支持世界上的语言,并包含世界上最大的可用语言环境数据集合。这个存储库中的信息已经本地化为数百种语言。

Date-Time API 还使用时区数据库(TZDB)。该数据库提供了自1970年以来全球每次时区变化的信息,以及自该概念引入以来主要时区的历史。

Date-Time 设计原则

  • 清晰

    • 方法是定义良好的,它们的行为是明确的和预期的。
  • 流畅

    • 因为大多数方法不允许参数为空值,也不返回空值,所以方法调用可以链接在一起,得到的代码可以很快理解。
  • 不可变

    • 这意味着是线程安全的。这影响了API,因为大多数用于创建日期或时间对象的方法都以 of、from 或 with 作为前缀,而不是构造函数,并且没有 set 方法。
  • 可扩展

    • 您可以定义自己的时间调整器和查询,或者构建自己的日历系统。

Date-Time 包

  • java.time

    • 表示日期和时间的 API 的核心。
    • 它包括日期、时间、日期和时间组合、时区、瞬时、持续时间和时钟的类。
    • 这些类基于 ISO-8601中定义的日历系统,并且是不可变的和线程安全的。
  • java.time.chrono

    • 用于表示默认 ISO-8601以外的日历系统的 API。
    • 您还可以定义自己的日历系统。
  • java.time.format

    • 用于格式化和解析日期和时间的类
  • java.time.temporal

    • 扩展 API,主要用于框架和库编写者,允许日期和时间类之间的互操作、查询和调整。
    • 字段 (TemporalField 和 ChronoField) 和单位(TemporalUnit 和 ChronoUnit) 在这个包中定义。
  • java.time.zone

    • 支持时区、时区偏移量和时区规则的类。
    • 如果使用时区,大多数开发人员只需要使用ZonedDateTime 和 ZoneId 或 ZoneOffset。

方法命名习惯

  • 类之间的方法名尽可能保持一致。

  • 方法名前缀

    • of

      • 静态工厂方法,创建一个实例,其中工厂主要验证输入参数,而不是转换它们。
    • from

      • 静态工厂方法,将输入参数转换为目标类的实例,这可能涉及从输入中丢失信息。
    • parse

      • 静态工厂方法,解析输入字符串以生成目标类的实例。
    • format

      • 实例方法,使用指定的格式化程序格式化 temporal 对象中的值以生成字符串。
    • get

      • 实例方法,返回目标对象状态的一部分。
    • is

      • 实例方法,查询目标对象的状态。
    • with

      • 实例方法,返回目标对象的副本,其中一个元素被更改;这是 JavaBean 上不可变的 set 方法。
    • plus

      • 实例方法,返回目标对象的副本,并添加了时间量。
    • minus

      • 实例方法,返回目标对象的副本,减去时间量。
    • to

      • 实例方法,将此对象转换为另一类型。
    • at

      • 实例方法,将此对象与另一个对象组合。

标准日期

  • 简介

    • 时间表示方法

      • 人类时间
      • 机器时间
    • 在选择基于时间的类时,首先决定需要表示人类时间还是机器时间。然后确定需要表示时间的哪些方面。

    • java中基于时间的类

      • Instant

      • LocalDate

        • 年,月,日
      • LocalTime

        • 时,分,秒
      • LocalDateTime

        • 年,月,日,时,分,秒,时区
      • ZonedDateTime

        • 年,月,日,时,分,秒,时区,时区ID
      • Year

      • Month

      • YearMonth

        • 年,月
      • MonthDay

        • 月,天
      • OffsetTime

        • 时,分,秒,时区
      • OffsetDateTime

        • 年,月,日,时,分,秒,时区
      • Duration

        • 天,时,分,秒
      • Period

        • 年,月,日,时区,时区ID
      • 秒被捕捉到纳秒的精度。

      • 部分类不存储此信息,但具有以这些单位提供时间的方法

      • 将 Period 添加到 ZonedDateTime 时,将观察夏时制或其他当地时间差异。

  • DayOfWeek 和 Month 枚举

    • DayOfWeek 枚举由七个常量组成,用于描述一周中的天数
    • 通过使用 getDisplayName(TextStyle, Locale)方法,您可以检索一个字符串,以识别用户所在地区的星期几。
    • Month 枚举包含12个月的常量,从 JANUARY 到DECEMBER。
    • maxLength 方法打印月份中可能的最大天数
    • 还实现了getDisplayName(TextStyle, Locale)方法
  • 日期类

    • 专门处理日期信息,而不考虑时间或时区。

    • LocalDate

      • 表示 ISO 日历中的年-月-日
      • getDayOfWeek 方法返回特定日期所在的星期几
    • YearMonth

      • 表示特定年份的月份。
      • YearMonth.lengthOfMonth() 方法确定几个年和月组合的天数。
    • MonthDay

      • 表示特定月份的日期
      • MonthDay.isValidYear 方法
    • Year

      • 表示一年
      • Year.isLeap 方法确定给定的年份是否是闰年。
  • 日期和时间类

    • LocalTime

      • 类似于名称前缀为 Local 的其他类,但只处理时间。
      • 不存储时区或夏令时信息。
    • LocalDateTime

      • 用于表示日期(月-日-年)和时间(小时-分-秒-纳秒),实际上是 LocalDate 和 LocalTime 的组合。
  • 时区和偏移类

    • 时区是地球上使用相同标准时间的区域。

    • 每个时区都由一个标识符描述,通常有格式 区域/城市 和来自格林威治时间的偏移量。

    • ZoneId

      • 指定时区标识符,并提供在 Instant 和 LocalDateTime 之间转换的规则。
    • ZoneOffset

      • 指定与格林威治时间的时区偏移量,通常以整小时为单位定义,但也有例外。
    • 日期-时间类

      • 使用时区的基于时间的类

      • ZonedDateTime

        • 将 LocalDateTime 类与 ZoneId 类结合在一起。
        • DateTimeFormatter 对象用于格式化 ZonedDateTime 实例以进行打印
      • OffsetDateTime

        • 将 LocalDateTime 类与 ZoneOffset 类结合在一起。
      • OffsetTime

        • 将 LocalTime 类与 ZoneOffset 类结合在一起。
      • 尽管这三个类都维护了一个格林威治时间的偏移量,但只有 ZonedDateTime 使用 java.time.zone 包的ZoneRules 来确定特定时区的偏移量如何变化。

  • Instant 类

    • 它表示时间轴上一个纳秒的开始。
    • 该类用于生成表示机器时间的时间戳。
    • 从1970年1月1日(1970-01-01T00:00:00Z)的第一秒开始的时间,也称为 EPOCH。发生在这之前的瞬间为负值,发生在这之后的瞬间为正值。
    • 常量是 MIN,表示最小的(遥远过去的)瞬间,以及 MAX,表示最大的(遥远将来的)瞬间。
    • 可以通过将 Instant 与时区绑定,将其转换为其他类
    • 可以将 ZonedDateTime 或 OffsetTimeZone 对象转换为Instant 对象,因为每个对象都映射到时间轴上的确切时刻。然而,反过来就需要额外提供时区信息。
  • 解析与格式化

    • DateTimeFormatter 类

      • 提供一个模式以创建格式化器对象。然后将此格式化程序传递给 parse 或 format 方法。
      • 既是不可变的,又是线程安全的;它可以(也应该)在适当的地方赋给一个静态常数。
    • 解析

      • LocalDate.parse()

      • 格式化器

        • 预定义

          • DateTimeFormatter.BASIC_ISO_DATE
        • 自定义

          • DateTimeFormatter.ofPattern()
    • 格式化

      • format(DateTimeFormatter) 方法使用指定的格式将基于时间的对象转换为字符串表示形式
  • temporal 包

    • 提供了一组接口、类和枚举,用于支持日期和时间代码,特别是日期和时间计算。

    • 这些接口用于最底层。典型的应用程序代码应该根据具体类型(如 LocalDate 或 ZonedDateTime)来声明变量和参数,而不是根据 Temporal 接口。

    • Temporal 和 TemporalAccessor

      • Temporal 接口为访问基于时间的对象提供了一个框架,并由基于时间的类实现。该接口提供了添加或减去时间单位的方法,使得基于时间的算术在各种日期和时间类之间变得简单和一致。
      • TemporalAccessor 接口提供了一个只读版本的Temporal 接口。
      • 都是根据字段定义的,如在 TemporalField 接口中指定的那样。ChronoField 枚举是 TemporalField 接口的具体实现,并提供了一组丰富的已定义常量
      • 这些字段的单位由 TemporalUnit 接口指定。
      • Temporal 接口中基于算术的方法需要根据TemporalAmount 值定义参数。Period 和 Duration 类实现了 TemporalAmount 接口。
    • ChronoField 和 IsoFields

      • ChronoField 枚举实现了 TemporalField 接口,为访问日期和时间值提供了一组丰富的常量。
      • 当遇到未知类型的时态时,可以使用TemporalAccessor.isSupported(TemporalField)方法来确定时态是否支持特定的字段。
      • 其他特定于ISO-8601日历系统的字段在 IsoFields 类中定义。
    • ChronoUnit

      • ChronoUnit 枚举实现了 TemporalUnit 接口,并提供了一组基于日期和时间的标准单位,从毫秒到千年。
      • 注意,并非所有类都支持所有的 ChronoUnit 对象。
      • isSupported(TemporalUnit) 方法可用于验证类是否支持特定的时间单位。
    • 时间调整器

      • TemporalAdjuster 接口

        • 提供了接受一个 Temporal 值并返回一个调整后的值的方法。
        • 调整器可以与任何基于时间的类型一起使用。
        • 如果调整器与 ZonedDateTime 一起使用,则计算一个新的日期,该日期保留原始时间和时区值。
      • 预定义调节器

        • TemporalAdjusters 类
        • 用于查找一个月的第一天或最后一天、一年的第一天或最后一天、一个月的最后一个星期三或特定日期后的第一个星期二
      • 自定义调节器

        • 实现 TemporalAdjuster 接口。
    • 时间查询

      • TemporalQuery

        • 用于从基于时间的对象检索信息。
      • 预定义查询

        • TemporalQueries 类
        • 提供了几个预定义的查询,包括在应用程序无法识别基于时间的对象类型时非常有用的方法。
        • 与调整器一样,预定义查询被定义为静态方法,并被设计为与静态 import 语句一起使用。
      • 自定义查询

        • 实现 TemporalQuery 接口
  • Period 和 Duration

    • 指定时间量

    • Duration 使用基于时间的值(秒、纳秒)度量时间量。Period 使用基于日期的值(年、月、日)。

    • Duration

      • 适合用于测量基于机器的时间的情况
      • 没有连接到时间轴,因为它不跟踪时区或夏令时。
    • ChronoUnit

      • 当您希望仅以单个时间单位(如天或秒)测量时间量时,between 方法非常有用。
    • Period

      • 提供了各种 get 方法,例如 getMonths、getDays 和getYears,以便您可以从周期中提取时间量。
  • Clock 类

    • 大多数基于时间的对象提供无参数 now()方法,该方法使用系统时钟和默认时区提供当前日期和时间。
    • 这些基于时间的对象还提供了一个单参数 now(Clock)方法,允许您传入一个替代的 Clock。
    • Clock.offset(Clock, Duration)
    • Clock.systemUTC()
    • Clock.fixed(Instant, ZoneId)
  • 非iso日期转换

    • 转换为非iso的日期

      • from(TemporalAccessor)
    • 转换为基于iso的日期

      • LocalDate.from 静态方法
  • 遗留日期-时间代码

    • 在 Java SE 8发布之前,Java 日期和时间机制是由java.util.Date, java.util.Calendar, and java.util.TimeZone 提供

    • 缺点

      • Calendar 类不是类型安全的。
      • 因为类是可变的,所以不能在多线程应用程序中使用。
      • 由于不寻常的月份编号和缺乏类型安全性,应用程序代码中的错误很常见。
    • 与遗留代码的互操作性

      • Calendar.toInstant()
      • GregorianCalendar.toZonedDateTime()
      • GregorianCalendar.from(ZonedDateTime)
      • Date.from(Instant)
      • Date.toInstant()
      • TimeZone.toZoneId()
    • java.util Date and Time 到 java.time 功能映射

    • 日期和时间格式

部署

Java 富网络应用程序(RIA)是一种具有与桌面应用程序相似特征的应用程序,但是通过网络进行部署。Java ria 可以作为 Java applet 或 Java Web Start 应用程序开发和部署。

applet——Java applet 在浏览器上下文中运行。Java 插件软件控制 Java applet 的执行和生命周期。

Java Web Start 应用程序——Java Web Start 应用程序第一次通过浏览器启动。它们随后可能会从桌面快捷方式启动。一旦下载了 Java Web Start 应用程序,并且用户接受了它的安全证书,它的行为几乎就像一个独立的应用程序。

面向RIA的基于组件的体系结构

  • 在过去,决定是将 RIA 应用程序作为 applet 部署到浏览器内部,还是将其作为 Java Web Start 应用程序部署到浏览器外部,可能会对应用程序的设计产生重大影响。使用最新的 Java Plug-in,这个决定已经大大简化了。
  • 传统上,应用程序在 main 方法中构造它们的用户界面,包括顶层的 Frame。这种编程风格阻止了在浏览器中轻松地重新部署应用程序,因为它假定应用程序创建了自己的 Frame。当作为 applet 在浏览器中运行时,applet 是顶级容器,它应该容纳应用程序的用户界面。顶层框架不需要。
  • 在设计 RIA 应用程序时,使用基于组件的体系结构。尝试将其功能组织成一个或多个可以组合在一起的组件。在这个上下文中,术语“组件”指的是一个 GUI 元素,它是AWT component 类、Swing ,JComponent 类或其他子类的子类
  • 要将其部署为 Java applet,只需将核心功能包装在applet 或 JApplet 中,并在必要时添加特定于浏览器的功能。要将其部署为 Java Web Start 应用程序,请将该功能包装在 JFrame 中。

选择

  • RIA 决策指南

自包含应用程序替代方案

  • 自包含应用程序提供了不需要浏览器的部署选项。用户在本地安装应用程序,并像本地应用程序一样运行它。
  • 自包含的应用程序包括运行应用程序所需的 JRE,因此用户总是拥有正确的 JRE。

Java Applets

  • Java applet 是一种特殊的 Java 程序,支持 Java 技术的浏览器可以从互联网上下载并运行它。
  • applet 通常嵌入在网页中,并在浏览器上下文中运行。
  • applet必须是 java.applet.Applet 类的子类。
  • Applet 类提供 Applet 和浏览器环境之间的标准接口。
  • Swing 提供了 Applet 类的一个特殊子类,称为javax.swing.JApplet。
  • JApplet 类应该用于所有使用 Swing 组件构建图形用户界面的 applet。
  • 浏览器的 Java Plug-in 软件管理 applet 的生命周期。

Java Web Start

使用 RIA 做更多事情

部署深入

部署自包含应用程序

  • 一个自包含的应用程序由一个单独的可安装的包组成,该包包含您的应用程序和运行应用程序所需的 JRE 副本。当安装应用程序时,它的行为与任何本地应用程序相同。为用户提供一个自包含的应用程序可以避免与在浏览器中运行应用程序有关的安全问题。

  • 您可以通过提供自己的图标来定制自包含的应用程序。可以设置文件关联,这样当用户打开应用程序可以处理的文件时,应用程序就会自动启动。支持多个入口点,因此您可以在单个自包含的应用程序包中交付一套应用程序。

  • 可以使用 Java 打包工具打包自包含的应用程序。javapackager 命令从命令行为自包含的应用程序创建包。NetBeans 还可以用于创建自包含的应用程序包。

  • 打包自包含应用程序的先决条件

    • 编译和打包应用程序需要 Java 开发工具包 (JDK)。必须在运行自包含应用程序的平台上创建可安装包

    • 第三方工具

      • Windows

        • EXE

          • Inno Setup 5 or later
        • MSI

          • WiX Toolset 3.8 or later
      • Linux

        • RPM

          • RPMBuild
        • DEB

          • Debian packaging tools
      • OS X

        • DMG
        • PKG
  • 转换现有应用程序

    • 任何独立的 Java 应用程序或 Java Web Start 应用程序都可以打包为自包含的应用程序。

    • 从部署 Java Web Start 应用程序转换为自包含应用程序

      • 设置目录
      • 设置生成文件
      • 生成 bundle
      • 其他资料
  • 使用文件关联

    • 为用户提供自包含应用程序的优点之一是能够建立文件关联。可以根据 MIME 类型或文件扩展名将特定类型的文件与应用程序相关联,以便使用应用程序打开相关文件。

    • 建立文件关联

      • 用于生成自包含应用程序包的 Ant 任务位于文件关联演示的 build.xml 文件中。
      • <fx:association> Ant 元素用于将文件扩展名或 MIME 类型与应用程序关联起来。Linux 捆绑器要求 MIME 类型,Windows 捆绑器要求文件扩展名,OS X 捆绑器要求至少一个属性。
      • 使用 MIME 类型和文件扩展名之间的一对一映射来使用这两个属性是一个很好的实践,这使您能够在多个平台上使用相同的构建文件。
      • 如果绑定器不支持文件关联,则关联将被忽略。
    • 从关联文件启动

      • 启动应用程序所采取的操作取决于运行应用程序的平台。

      • Linux 和 Windows

        • 正在打开的文件将作为参数传递给主类,主类将覆盖类的默认参数。
      • OS X

        • 当打开关联文件时,将向应用程序发送一个事件。应用程序必须注册一个事件侦听器来处理事件。
    • 更多关于文件关联演示

    • 添加外部库

    • 提供默认参数

    • 为所有平台使用公共构建文件

  • 使用多个入口点

在JAR文件中打包程序

  • Java™Archive (JAR)文件格式使您能够将多个文件捆绑到单个归档文件中。

  • 通常,JAR 文件包含与 applet 和应用程序相关联的类文件和辅助资源。

  • JAR文件格式的好处

    • 安全

      • 您可以对JAR文件的内容进行数字签名。识别您的签名的用户可以有选择地授予您的软件安全特权,否则它不会拥有这些特权。
    • 减少下载时间

      • 如果 applet 捆绑在 JAR 文件中,则 applet 的类文件和相关资源可以在单个 HTTP 事务中下载到浏览器,而不需要为每个文件打开新连接。
    • 压缩

      • JAR 格式允许您压缩文件以实现高效存储。
    • 扩展的打包

      • 扩展框架提供了一种向 Java 核心平台添加功能的方法,并且 JAR 文件格式定义了扩展的打包。通过使用 JAR 文件格式,您还可以将您的软件转换为扩展。
    • 包密封

      • 存储在 JAR 文件中的包可以有选择地密封,这样包就可以加强版本一致性。在 JAR 文件中密封一个包意味着该包中定义的所有类必须在同一个 JAR 文件中找到。
    • 包版本

      • JAR 文件可以保存关于它所包含的文件的数据,例如供应商和版本信息。
    • 可移植性

      • 处理 JAR 文件的机制是 Java 平台核心 API 的标准部分。
  • 使用JAR文件

    • JAR 文件是用 ZIP 文件格式打包的,因此您可以将它们用于无损数据压缩、归档、解压缩和归档解包等任务。这些任务是 JAR 文件最常见的用途之一,仅使用这些基本特性就可以实现许多 JAR 文件的好处。

    • 要使用 JAR 文件执行基本任务,您可以使用作为 Java 开发工具包(JDK)一部分提供的 Java 存档工具。因为 Java Archive工具是通过使用 jar 命令调用的,所以本教程将其称为“jar工具”。

    • 创建JAR文件

      • jar cf jar-file input-file(s)

      • c 选项表示您想要创建一个 JAR 文件。

      • f 选项表示希望输出到文件中,而不是到标准输出中。

      • jar-file 是您希望生成的 JAR 文件具有的名称。您可以为JAR 文件使用任何文件名。按照惯例,JAR 文件名的扩展名是. JAR,尽管这不是必需的。

      • input-file(s) 参数是一个以空格分隔的列表,其中包含您希望包含在 JAR 文件中的一个或多个文件。input-file 参数可以包含通配符*符号。如果任何“输入文件”是目录,那么这些目录的内容将被递归地添加到 JAR 存档中。

      • c 和 f 选项可以任意一种顺序出现,但它们之间不能有任何空格。

      • 该命令将生成一个压缩 JAR 文件,并将其放置在当前目录中。该命令还将为 JAR 存档生成一个默认清单文件。

      • JAR 文件中的元数据,例如条目名称、注释和清单的内容,必须用 UTF8 编码。

      • Jar命令选项

        • v

          • 在构建 JAR 文件时在标准输出上生成详细输出。详细输出告诉您添加到 JAR 文件中的每个文件的名称。
        • 0(零)

          • 表示不希望压缩 JAR 文件。
        • M

          • 不应生成默认清单文件。
        • m

          • 用于包括现有清单文件中的清单信息
          • 清单必须以新行或回车符结束。如果最后一行没有以新行或回车符结束,则不能正确地解析它。
        • -C

          • 在执行命令时更改目录
    • 查看JAR文件的内容

      • jar tf jar-file
      • t 选项表示希望查看 JAR 文件的目录
      • f 选项表示在命令行上指定要查看的内容的 JAR 文件。
      • JAR -file 参数是您希望查看其内容的 JAR 文件的路径和名称。
      • 这个命令将把 JAR 文件的目录显示到标准输出。
      • 您可以选择添加详细选项 v,以在输出中产生关于文件大小和最后修改日期的额外信息。
    • 提取JAR文件的内容

      • jar xf jar-file [archived-file(s)]
      • x 选项表示希望从 JAR 归档文件中提取文件。
      • f 选项表示要从中提取文件的JAR文件在命令行上指定,而不是通过 stdin 指定。
      • JAR -file 参数是要从中提取文件的 JAR 文件的文件名(或路径和文件名)。
      • archive -file(s)是一个可选参数,由一个以空格分隔的从存档中提取的文件列表组成。如果这个参数不存在,Jar工具将提取存档中的所有文件。
      • 在提取文件时,Jar 工具将生成所需文件的副本,并将它们写入当前目录,从而重新生成归档文件中文件的目录结构。原始JAR文件保持不变。
      • 当 Jar 工具提取文件时,它将覆盖与提取文件具有相同路径名的任何现有文件。
    • 运行打包为JAR文件的应用程序

      • 包装在 JAR 文件中的 applet

        • 要从浏览器中运行的 HTML 文件中启动任何 applet,您可以使用 applet 标记。
        • 如果 applet 捆绑为 JAR 文件,惟一需要做的不同之处是使用存档参数指定 JAR 文件的相对路径。
      • JAR文件作为应用程序

        • java -jar app.jar
        • -jar 标志告诉启动程序应用程序是以 JAR 文件格式打包的。您只能指定一个 JAR 文件,该文件必须包含所有特定于应用程序的代码。
        • 在执行此命令之前,请确保运行时环境具有关于 JAR 文件中的哪个类是应用程序的入口点的信息。
        • 要指出哪个类是应用程序的入口点,您必须向 JAR 文件的清单中添加一个 Main-Class 头。
        • 要从另一个目录中的 JAR 文件运行应用程序,必须指定该目录的路径
    • 更新JAR文件

      • jar uf jar-file input-file(s)
      • u 选项表示您想要更新现有的 JAR 文件。
      • f 选项表示在命令行上指定要更新的 JAR 文件。
      • JAR -file 是要更新的现有 JAR 文件。
      • input-file(s) 是一个以空格分隔的列表,包含您想要添加到 JAR 文件中的一个或多个文件。
  • 使用 manifest 文件

    • JAR 文件支持广泛的功能,包括电子签名、版本控制、包密封等。是什么赋予 JAR 文件如此多功能性?答案是 JAR文件的清单。

    • 清单是一个特殊的文件,可以包含关于 JAR 文件中打包的文件的信息。通过裁剪清单所包含的“元”信息,您可以使 JAR 文件服务于各种目的。

    • 理解默认清单

      • 当您创建一个 JAR 文件时,它会自动接收一个默认的清单文件。存档中只能有一个清单文件,并且它总是有路径名 META-INF/MANIFEST.MF

      • 包含内容

        • Manifest-Version: 1.0
        • Created-By: 1.7.0_06 (Oracle Corporation)
      • 清单的条目采用“头:值”对的形式。标头的名称与其值之间用冒号分隔。

      • 清单还可以包含关于打包在存档中的其他文件的信息。清单中应该记录哪些文件信息取决于您打算如何使用 JAR 文件。默认清单没有假设它应该记录关于其他文件的什么信息。

      • 摘要信息不包括在默认清单中。

    • 修改清单文件

      • 在创建 JAR 文件期间,使用 m 命令行选项将自定义信息添加到清单中。
      • 要修改清单,必须首先准备一个文本文件,其中包含希望添加到清单中的信息。然后使用 Jar 工具的 m 选项将文件中的信息添加到清单中。
      • jar cfm jar-file manifest-addition input-file(s)
      • m 选项表示希望将现有文件中的信息合并到正在创建的JAR 文件的清单文件中。
      • m 和 f 选项必须与相应参数的顺序相同。
      • 清单的内容必须用 UTF-8 编码。
    • 设置应用程序的入口点

      • Main-Class: classname

      • 使用 JAR 工具设置入口点

        • ‘e’标志(代表’entrypoint’)创建或覆盖清单的 Main-Class 属性。
        • 它可以在创建或更新 JAR 文件时使用。
        • jar cfe app.jar MyApp MyApp.class
    • 向JAR文件的类路径中添加类

      • 从 JAR 文件中引用其他 JAR 文件中的类。
      • Class-Path: jar1-name jar2-name directory-name/jar3-name
      • 通过在清单中使用 Class-Path 报头,可以避免在调用 Java 运行应用程序时必须指定一个很长的 -classpath 标志。
      • 类路径头指向本地网络上的类或 JAR 文件,而不是 JAR 文件中的 JAR 文件或通过 Internet 协议访问的类。要将JAR 文件中的 JAR 文件中的类加载到类路径中,必须编写自定义代码来加载这些类
    • 设置包版本信息

      • Name

        • 规范的名称。
      • Specification-Title

        • 规范的标题。
      • Specification-Version

        • 规范的版本
      • Specification-Vendor

        • 规格的供应商。
      • Implementation-Title

        • 实现的标题
      • Implementation-Version

        • 实现的版本
      • Implementation-Vendor

        • 实现的供应商
    • 在JAR文件中密封包

      • Sealed: true
      • 如果您希望保证包中的所有类都来自相同的代码源,请使用 JAR 密封。密封的 JAR 指定由该 JAR 定义的所有包都是密封的,除非在每个包的基础上重写。
      • 指定此存档中的所有包都是密封的,除非在清单条目中显式地覆盖具有 sealed 属性的特定包。
    • 使用清单属性增强安全性

      • Permissions

        • 用于确保应用程序只请求用于调用应用程序的 applet 标记或 JNLP 文件中指定的权限级别。
        • 使用此属性可帮助防止某人重新部署使用您的证书签名的应用程序,并以不同的特权级别运行该应用程序。
        • 这个属性在主 JAR 文件的清单中是必需的。
      • Codebase

        • 用于确保 JAR 文件的代码库仅限于特定的域。使用此属性可防止某人出于恶意目的在其他网站上重新部署您的应用程序。
      • Application-Name

        • 用于为已签名的应用程序提供安全提示中显示的标题。
      • Application-Library-Allowable-Codebase

        • 用于确定您的应用程序将被找到的位置。当 JAR 文件位于与 JNLP 文件或 HTML 页面不同的位置时,使用此属性可以减少安全提示中显示的位置数量。
      • Caller-Allowable-Codebase

        • 用于识别 JavaScript 代码可以调用应用程序的域。使用此属性可防止未知 JavaScript 代码访问应用程序。
      • Entry-Point

        • 用于标识允许用作 RIA 入口点的类。使用此属性可以防止从 JAR 文件中的其他可用入口点运行未经授权的代码。
      • Trusted-Only

        • 用于防止加载不受信任的组件。
      • Trusted-Library

        • 用于允许特权 Java 代码和沙盒 Java 代码之间的调用,而无需提示用户获得权限。
  • 签名和验证JAR文件

    • 您可以选择使用您的电子“签名”对 JAR 文件进行签名。验证签名的用户可以授予与 jar 绑定的软件通常不具备的安全特权。相反,您可以验证希望使用的已签名JAR文件的签名。

    • 理解签名与验证

      • 您对文件进行数字签名的原因与您用钢笔和墨水在纸质文档上签名的原因相同——让读者知道文档是您写的,或者至少该文档得到了您的批准。

      • 当对 JAR 文件进行签名时,您还可以选择对签名加盖时间戳。与在纸质文档上添加日期类似,签名的时间戳可以识别 JAR 文件的签名时间。时间戳可用于验证用于签名JAR 文件的证书在签名时是否有效。

      • 签名和验证文件的能力是 Java 平台安全体系结构的重要组成部分。安全性由运行时生效的安全策略控制。您可以配置策略,为小程序和应用程序授予安全特权。

      • Java 平台通过使用称为公钥和私钥的特殊数字来支持签名和验证。公钥和私钥是成对出现的,它们的作用是互补的。

      • 仅靠公钥和私钥还不足以真正验证签名。即使您已经验证了签名文件包含一个匹配的密钥对,您仍然需要某种方法来确认公钥实际上来自它声称来自的签名者。

      • 签名和验证工作还需要一个因素。附加的元素是签名者包含在已签名 JAR 文件中的证书。证书是来自公认的证书颁发机构的数字签名声明,它指示谁拥有特定的公钥。

      • 当您对 JAR 文件进行签名时,您的公钥将与关联的证书一起放置在存档中,以便任何想要验证您的签名的人都可以轻松使用它。

      • 摘要和签名文件

        • 当您对 JAR 文件进行签名时,存档中的每个文件都会在存档的清单中获得一个摘要条目。
        • 摘要值是签名时文件内容的哈希值或编码表示。当且仅当文件本身发生变化时,文件的摘要才会发生变化。
        • 当对 JAR 文件进行签名时,将自动生成签名文件并将其放置在 JAR 文件的 META-INF 目录中,该目录包含存档的清单。签名文件具有扩展名为. sf 的文件名。
        • 签名文件包含归档文件的摘要项,这些摘要项与清单中的摘要值项类似。但是,清单中的摘要值是从文件本身计算出来的,而签名文件中的摘要值是从清单中的相应条目计算出来的。签名文件还包含整个清单的摘要值
        • 当验证已签名的 JAR 文件时,将重新计算其每个文件的摘要,并将其与清单中记录的摘要进行比较,以确保 JAR文件的内容自签名以来没有更改。作为额外的检查,将重新计算清单文件本身的摘要值,并将其与签名文件中记录的值进行比较。
      • 签名块文件

        • 除了签名文件之外,当对 JAR 文件进行签名时,还会自动将签名块文件放在 META-INF 目录中。

        • 与清单文件或签名文件不同,签名块文件不是人类可读的。

        • 主要元素

          • 使用签名者的私钥生成的 JAR 文件的数字签名
          • 包含签名者的公钥的证书,供任何想要验证已签名的 JAR文件的人使用
        • 签名块文件名通常具有. dsa 扩展名,表明它们是由默认的数字签名算法创建的。如果使用与其他标准算法相关联的密钥进行签名,则可以使用其他文件扩展名。

    • 签名JAR文件

      • 要对 JAR 文件进行签名,首先必须有一个私钥。私钥及其相关的公钥证书存储在名为密钥存储库的受密码保护的数据库中。密钥存储库可以保存许多潜在签名者的密钥。密钥存储库中的每个密钥都可以通过一个别名来标识,该别名通常是拥有该密钥的签名者的名称。

      • jarsigner jar-file alias

        • JAR -file 是要签名的 JAR 文件的路径名。
        • alias 是标识用于签署 JAR 文件的私钥和密钥的关联证书的别名。
      • Jarsigner工具将提示您输入密钥库和别名的密码。

      • 该命令的基本形式假设要使用的密钥存储库位于主目录中名为 .keystore 的文件中。它将分别创建名称为 x.SF 和 x.DSA 的签名和签名块文件,其中x是别名的前八个字母,全部转换为大写字母。这个基本命令将用签名的 JAR文件覆盖原始 JAR 文件。

      • Jarsigner命令选项

        • -keystore url

          • 如果不想使用 .keystore 默认数据库,则指定要使用的密钥存储库。
        • -sigfile file

          • 指定 . sf 和 . dsa 文件的基本名称(如果不希望从别名中获取基本名称)。文件只能由大写字母(A-Z)、数字(0-9)、中划线(-)和下划线(_)组成。
        • -signedjar file

          • 如果您不希望原始的未签名文件被签名文件覆盖,则指定要生成的签名 JAR 文件的名称。
        • -tsa url

          • 使用由 URL 标识的时间戳权限(TSA)为签名生成时间戳
        • -tsacert alias

          • 使用由别名标识的 TSA 公钥证书为签名生成时间戳。
        • -altsigner class

          • 指示使用替代签名机制对签名进行时间戳。完全限定类名标识所使用的类。
        • -altsignerpath classpathlist

          • 提供由 altsigner 选项标识的类的路径以及类所依赖的任何JAR文件。
    • 验证已签名的JAR文件

      • 通常,签名 JAR 文件的验证将由Java™运行时环境负责。您的浏览器将验证它下载的签名 applet。使用解释器的-jar 选项调用的签名应用程序将由运行时环境进行验证。
      • 您可以使用 jarsigner 工具自己验证已签名的 JAR 文件。
      • jarsigner -verify jar-file
  • 使用与jar相关的api

    • java.util.jar

    • java.net.JarURLConnection

    • java.net.URLClassLoader

      • JarClassLoader 类

        • 扩展了 java.net.URLClassLoader

        • 构造函数

          • 以java.net.URL 实例作为参数。传递给这个构造函数的URL 将在 JarClassLoader 中的其他地方使用,以查找将要装入类的 JAR 文件。
          • URL 对象被传递给超类的构造函数 URLClassLoader,它接受一个 URL[] 数组作为参数,而不是一个 URL 实例。
        • getMainClassName 方法

        • JarURLConnection 类和 JAR URLs

          • getMainClassName 方法使用由 java.net.JarURLConnection 类指定的 JAR URL格式
          • jar:http://www.example.com/jarfile.jar!/
          • 结尾的!/分隔符表示 URL 引用整个 JAR 文件。分隔符后面的任何内容都指向特定的 jar 文件内容
        • java.net.JarURLConnection 类

          • 该类表示应用程序和 JAR 文件之间的通信链接。它具有访问 JAR 文件清单的方法。
        • 获取清单属性

          • java.util.jar.Attributes
          • getValue
        • invokeClass 方法

          • JarURLClassLoader.invokeClass
          • 允许调用主类来启动 jar 绑定的应用程序

参考链接:The Java Tutorials

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号