java核心技术-基础知识

一、java的语言特性

  • 简单性:可以说分为两个方面,一是相对于C++纯净:因为java诞生之初考虑要尽量符合当时的标准惯例,所以尽量接近了C++,但是它剔除了很多C++复杂少用或者易于混淆的特性,例如头文件、指针运算和语法、结构、联合、操作符重载、虚基类等;二是体量小,起初时其目标之一是在小型机器上独立运行,解释器和类支持加上标准类库、线程支持总共200kb多,适用于嵌入式设备;
  • 面向对象:编程重点放在了数据(对象)和对象接口上,面向对象的能力与C++一样;
  • 分布式:java应用可以通过其丰富的例程库使用URL打开访问网络上的对象;
  • 健壮性:java编写的程序具有多方面的可靠性,java的编译器可以发现许多其他语言在运行时才能检测出来的问题,java与C++最大的不同之处是:java采用的指针模型可以消除重写内存和损坏数据的可能性;
  • 安全性:java使用于网络/分布式环境,java将代码在沙箱中运行,避免恶意代码对系统的破坏;
  • 计算机体系结构中立:java编译器生成的是与特定计算机体系结构无关的字节码格式的文件,可以方便的在任何包含运行时系统的计算机上运行,并动态翻译为本机机器代码;
  • 可移植:java的数据类型是明确说明的,固定的,这很不同于c++根据厂商变化的类型大小,这保证了移植的可靠性。
  • 解释性:java解释器可以在任何移植了解释器的机器上执行java字节码,链接是快捷轻量的,提供了java开发同样的快捷探索性;
  • 高性能:字节码在翻译后还可以通过动态翻译为运行机器CPU的机器码进行性能拓展,现在java使用的即时编译器,通过利用更多运行时的有用信息,某些情况下的性能超过了传统编译器;
  • 多线程:摩尔定律的终结代表我们将追求运行更多的处理器而非更快的处理器,java专注于此;
  • 动态性:java与c++相比更具有动态性,能够自由在库中添加新方法和实例变量而对客户端没影响,能够轻易获得运行时的类型信息,动态地进行程序演进,更能适应不断发展的环境;

二、java数据类型

1、整型

在这里插入图片描述
int满足基本的需求,long满足更大的需求,byte和short用于特定的场合;
在任何平台的基本类型的大小都是固定的,不同于c++根据厂商不同而变化的特点,这保证了移植的有效;

2、浮点型

在这里插入图片描述
float精度是double的一半,难以满足需求,故不常用,一般使用double;
在这里插入图片描述
可以使用Double.isNaN(x)判断是否为有效数;对于金融计算等不接受舍入误差的计算,应该使用BigDecimal类;

3、char型

char原来用于表述单个字符,但由于大字符集的引用,2字节的char不足以描述所有符号,所以:
在这里插入图片描述

4、boolean型

boolean (布尔)类型有两个值:false 和 true, 用来判定逻辑条件 整型值和布尔值之间
不能进行相互转换。这不同于c++将0和1与false和true的转换;

精度

对于浮点型数据的运算,难于在不同平台上保证相同的结果:有些机器会对中间结果进行精度扩展,使结果不同,而虚拟机会将结果进行截断,这会降低运算效率和增加溢出可能性。
如果将一个类标记为strictfp,这个类中的所有方法都会使用严格的浮点计算,生成一个可再生的结果。不过,对大多数程序来说, 浮点溢出不属于大问题。

字符串

  • Java 没有内置的字符串类型, 而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String。
  • String是不可变的对象,修改它可以通过剪切.substring和拼接 + 修改;当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串;
  • 不可变字符串有一个优点:编译器可以让字符串共享。如果复制一个字符串变量, 原始字符串与复制的字符串共享相同的字符。总而言之,Java 的设计者认为共享带来的高效率远远胜过于提取、 拼接字符串所带来的低效率。
  • 使用SpringBuilder类及其append方法,可以快速拼接短字符串,用其toString方法可以得到String对象;这个类的前身是 StringBuffer, 其效率稍有些低, 但允许采用多线程的方式执行添加或删除字符的操作。如果所有字符串在一个单线程中编辑 (通常都是这样) , 则应该用 StringBuilder 替代它。

大数值

在这里插入图片描述

三、面向对象的java

针对规模比较大的问题,使用面向过程的程序设计方法相较而言是复杂难以调试,面向对象编程(OOP)更改以往首先选择如何操作数据(数据结构),再选择如何组织数据(算法)的面向过程的程序设计过程,OOP使用对象意味着它把数据放置在第一位,其次才是算法;这可以很好地应用在大型规模的应用,便于将数据耦合降低和组织分明,易于自定义设计并找出问题所在。

封装

封装是将数据和行为包含在一个类里面,实现了数据隐藏,访问者只能通过方法操作数据而看不到具体的数据组织,这意味着重用性和可靠性可以在内部数据组织改变而方法不变下仍然有效。

  • 类中包含数据和方法,其中数据域应该设为私有,保证封装性,提供公用访问器和更改器方法供用户使用,这保证了数据变化的可控性;
  • 注意访问器方法的返回值不要是可变的引用变量,例如Date等不同于String的可变变量,因为返回值和内部变量引用同一对象,这会使私有数据在类外不可控的变化,应该使用clone返回该数据的克隆副本;
  • 只要多个对象属于同一个类,一个对象可以通过公有方法访问另一个对象的私有域。这与c++是相似的;
  • 对于只访问类中静态域或不访问对象数据域的方法,编写为static方法(static表述为类域),显然,这类方法不使用隐形参数(即对象本身this),只使用显性参数(即方法传入参数);
  • 使用工厂方法而非构造器来构造对象,具有许多好处。例如,构造器的名字必须与类名相同,命名多个新建对象时,工厂方法的语义更为清晰;构造器无法改变构造的对象类型,而工厂方法可以返回父类及其子类类型的对象;

方法

  • 方法的名字和参数构成方法签名,不包含返回类型;使用…代表可以传入任意数量的参数,这与传入可变长度的数组是等效的;
  • java中的方法总是将参数按值调用。这意味着无法改变基本类型的值,因为方法得到了一份基本类型的值的拷贝,但是当传入对象引用时,方法得到的是引用的拷贝,该拷贝可以操作对象,因此可以改变对象的值;但要注意,这与c++中的引用调用不同,因为java中传入的是引用的拷贝(拷贝指向对象的地址),而非对象本身的地址。因此例如交换拷贝的操作是无法交换参数引用的对象的;
  • 保证编写了无参构造器,哪怕为空,即代表初始化为默认值。这可以避免仅构造有参构造器时使用无参构造的错误;
  • java中构造器中可以使用类似于this(…)来使用其他构造器,这是C++不允许的;
  • 如果对类的静态域进行初始化的代码比较复杂,那么可以使用静态的初始化块,即static{…}。在类第一次加载的时候, 将会进行静态域的初始化。所有的静态初始化语句以及静态初始化块都将依照类定义的顺序执行;

  • java通过包package将类组织起来,避免同名的类冲突;一个类可以使用所属包的所有类以及其他包的公有类;
  • java通过import导入包,实质是告诉编译器去查看使用的类的文件所在位置,这与C++的#include完全不同,#include是将外部声明导入到C++编译器来,因为它不能查看其它文件内部,而java的编译器可以,因此import应该是与c++的namespace和using相似的;
  • 类加载器禁止加载用户自定义的java.包,避免混入系统类包;当然,一般是通过包密封来保护不想受污染的类包;

四、继承与多态

  • java中的继承是公有继承,没有c++中的私有继承;子类可以通过super调用超类的方法、构造器等,尽管子类继承了超类的数据域,但不能直接访问这些私有数据域,必须通过super进行初始化和访问;这与c++中使用初始化列表和超类:: 访问数据是类似的;
  • java并没有c++的多继承;
  • 多态是指对象变量是多态的,对象变量可以引用超类的对象以及由它派生的任何子类的对象;
  • 不允许拓展的类可以添加final修饰符,禁止对其继承,其中的方法也会自动成为final,但不包括域;
  • 将一个超类的引用赋给一个子类变量, 必须进行类型转换, 这样才能够通过运行时的检査,并且在将超类转换成子类之前,应该使用 instance of进行检查。这与c++的dynamic_cast是类似的;
  • 包含一个或多个抽象方法(类似于c++中=0标记的抽象方法)的类本身必须被声明为抽象类abstract(c++没有抽象类的修饰符),当然其中可以包含具体的数据和方法;
  • 抽象类用于定义通用的属性、通用方法和不涉及具体信息的抽象方法;对抽象类的继承会有两种情况:一是未完全定义完抽象方法的抽象子类;二是完全定义抽象方法的具体子类;
  • 抽象类不能被实例化,不含抽象方法的类也可以声明为抽象类,避免其被实例化;定义一个抽象类的对象变量就只能引用非抽象子类的变量;
  • Java 中的 protected 概念要比 C++ 中的安全性差,因为Java 中的受保护部分对所有子类及同一个包中的所有其他类都可见。使用protected修饰超类的方法可以让受信任的子类直接使用该方法,这是除了super外让子类访问超类非protected数据的一种方法,它比直接让子类访问protected修饰的超类数据更安全;

Object

object类是所有类的超类,使用它可以引用任何类,这方便定制通用方法。正如c++中所有指针可以转换为void*指针;

相等检测equals

Object类具有equals方法。子类定义equals方法时,首先调用其超类的equals方法,再比较该类的实例域;在这里插入图片描述

散列码hashCode

  1. 散列码( hash code ) 是由对象导出的一个整型值,它由实例域根据特定规则产生。如果 x 和 y 是两个不同的对象, x.hashCode( ) 与 y.hashCode( ) 基本上不会相同。由于 hashCode方法定义在 Object 类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。
  2. 如果重新定义 equals方法,就必须重新定义 hashCode 方法, 以便用户可以将对象插人到散列表中,使其可以根据散列码进行快速查找;
  3. hashCode 方法应该返回一个整型数值(也可以是负数) ,并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。Equals 与 hashCode 的定义必须一致:如果 x.equals(y) 返回 true, 那么 x.hashCode( ) 就必须与 y.hashCode( ) 具有相同的值;

动态数组

  1. ArrayList类似于C++的vector模板,可以存放任何类型的对象数组;C++ vector是值拷贝。如果 a 和 b 是两个向量, 赋值操作 a = b 将会构造一个与 b 长度相同的新向量 a, 并将所有的元素由 b 拷贝到 a, 而在Java 中, 这条赋值语句的操作结果是让 a 和 b 引用同一个数组列表;
  2. java使用 get 和 set 方法实现访问或改变数组元素的操作,而不使用c++使用的 [ ]语法格式;

对象包装器与自动装箱

  1. 在构造小型的基本类型集合时,操作元素的便捷性(例如,定义一些类似于类型转换方法)要求将基本类型转换为对象,这就是java中类似于C++的包装器,例如Integer、Long、Float、Double、Short、Byte、Character 、Void 和Boolean;对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时, 对象包装器类还是 final , 因此不能定义它们的子类;
  2. 在添加基本类型的元素到这类集合时,会自动将其进行包装再添加到包装器集合,称为自动装箱。提取该元素并转换为基本类型,称为拆箱;

枚举类

  1. 一个典型的定义枚举类的例子:在这里插入图片描述
  2. 在这里插入图片描述
  3. 所有的枚举类型都是 Enum 类的子类。它们继承了这个类的许多方法。例如,toString这个方法能够返回枚举常量名;静态方法 valueOf则是逆操作;静态的 values 方法将返回一个包含全部枚举值的数组;

反射

  1. 能够分析类能力的程序称为反射(reflective )。反射机制的功能极其强大, 反射机制可以用来:
  • 在运行时分析类的能力;
  • 在运行时查看对象, 例如, 编写一个 toString 方法供所有类使用;
  • 实现通用的数组操作代码;
  • 利用 Method 对象, 这个对象很像c++中的函数指针;
  1. Class 类与 C++ 中的 type_info 类相似,getClass 方法与 C++ 中的 type_id 运算符等价。 但 Java 中的 Class 比 C++ 中的 type_info的功能强。C++ 中的 type_info 只能以字符串的形式显示一个类型的名字, 而不能创建那个类型的对象;
  2. 在这里插入图片描述
  3. 通过反射机制,java可以通过类名查找该类中特定名称和参数类的方法,将返回结果包装为Method类,然后通过传入对象实例和参数来让对象执行该方法,形如Method.invoke(Object obj, Obiject… args),返回的结果也会被包装为Object类(如果是基本类型则包装为对应的包装器)。这与C++的函数指针起到执行任意方法的作用相似;在这里插入图片描述
  4. 反射机制使得人们可以通过在运行时查看域和方法, 让人们编写出更具有通用性的程序。这种功能对于编写系统程序来说极其实用,但是通常不适于编写应用程序。反射是很脆弱的,即编译器很难帮助人们发现程序中的错误, 因此只有在运行时才发现错误并导致异常。

五、接口与lambda表达式

接口

  1. 接口主要描述类具有什么功能,而不给出具体的实现。一个类可以实现多个接口,并在需要接口的地方使用相应实现接口的对象;
  2. 接口只包含方法声明,所有方法自动声明为public,但是实现接口的类需要手动设置具体方法为public;
  3. c++的接口就是一种内部只包含纯虚函数的抽象类,借助多继承使一个类实现多个接口声明的方法;java不支持c++的多继承,无法像c++一样使用抽象类实现接口。但是,java中的类可以实现多个接口,避免了类只能继承一个抽象类的局限,这就是开发接口技术的原因。因此使用接口可以提供多重继承的大多数好处,同时还能避免c++中多重继承的复杂性和低效性;
  4. 接口内的方法可以添加default前缀,定义默认操作。这不仅有利于实现接口的类只重写它们感兴趣的方法,简化开发,而且有利于添加新方法的接口更新不会影响旧实现的编译;
  5. 类实现的多个接口中存在命名冲突,只要其中有默认实现,编译器就会报错;如果是接口与超类方法命名冲突,编译器会选择超类的实现,这避免了接口增加与类同名的方法使已有的类产生错误;

lambda表达式

  1. 带参数变量的表达式就被称为 lambda 表达式。将一个代码块传递给某个对象(例如定时器、sort方法),这个代码块会反复调用,可以考虑创建特定的类或者lambda表达式。换句话说,当需要定义只有一个抽象方法的接口时, 就可以提供一个 lambda 表达式来替代。这种接口称为函数式接口;
  2. 对于一些现成的方法,如果想要传递这种代码块,可以使用方法引用,它等价于lambda表达式,可以看成是现成代码的lambda写法;写法如下:在这里插入图片描述
  3. 类似于方法引用,构造器引用是class::new,根据上下文选择构造器;java有一个限制:无法构造泛型类型T的数组,这可以通过数组构造器引用来克服。通过传入构造器引用,可以构造想要的数组元素类型;例如,Person[] people = stream.toArray(Person[]::new),这克服了泛型返回Object引用的限制;
  4. 在 Java 中, lambda 表达式就是闭包。因为lambda表达式可以使用非参数且不在代码中定义的变量,即lambda表达式可以捕获外围作用域中不会改变的变量的值。使用不会改变的外围变量是出于安全考虑,因为lambda表达式会反复多次执行,在并发执行该动作时改变外围变量值或者此时外围变量值在改变,这时显然并不安全。
  5. 使用 lambda 表达式的重点是延迟执行。毕竟, 如果想要立即执行代码,完全可以直接执行, 而无需把它包装在一个lambda 表达式中;延迟执行的原因如下:
  • 在一个单独的线程中运行代码;
  • 多次运行代码;
  • 在算法的适当位置运行代码(例如, 排序中的比较操作);
  • 发生某种情况时执行代码(如点击了一个按钮,数据到达等等);
  • 只在必要时才运行代码;
  1. 能够接受lambda表达式的函数,也必须在参数列表中表明提供或者选择一个函数式接口,这个函数式接口会运行到时传入的lambda表达式;在这里插入图片描述
  2. Comparator 接口包含很多方便的静态方法来创建比较器。 这些方法可以用于 lambda 表达式或方法引用。静态 comparing 方法取一个“ 键提取器” 函数, 它将类型 T 映射为一个可比较的类型(如String)。对要比较的对象应用这个函数, 然后对返回的键完成比较。把比较器与 thenComparing 方法串起来,可以在第一比较相同时启用第二个;例如:在这里插入图片描述

六、异常、断言和日志

异常

  1. 异常处理是处理预期可能出现的程序错误,为不影响用户体验和程序正常运行,使得程序作出理智的行为;
  2. 需要关注的问题包括:用户输入错误(例如语法错误)、设备错误(因为设备的状态是不确定的)、物理限制(例如硬盘空间已满)、代码错误(例如数组越界,参数传入错误);
  3. java将派生于Error类或RuntimeException类的所有异常称为非受查异常,所有其他异常是受查异常。编译器会核查是否为所有的受查异常提供了相应的异常处理器;类似于c++中,logic_error相当于RuntimeException,runtime_error相当于其他异常;
  4. 何时抛出异常:1.调用一个抛出受查异常的方法;2.程序运行时会使用throw语句;3.程序可能出错,例如越界;4.jvm和运行时库出现内部错误;
  5. 在c++中,throw在运行时执行,并且不使用throw的函数也可能抛出异常。而java在编译时通过throw规范异常,不说明throw的函数不会抛出异常;
  6. 方法如果可能抛出异常则应该在声明中使用throw抛出,并让调用该函数的方法处理抛出的异常或者再将其传递出去。应该catch捕获可以处理异常,传递不知道如何处理的异常;
  7. 使用try语句()包裹资源的打开和{}使用,可以在抛出异常后安全地关闭资源;在这里插入图片描述
  8. 早抛出,晚捕获。传递异常比捕获异常更好,因为让更高层次的方法来通知用户发生了错误,放弃不成功的命令,这更适宜;

断言

  1. 断言是只在测试期间使用的自查工具,用于处理测试期间的验证性操作。在代码发布时会自动被移除,以避免程序运行时仍需要进行大量减缓速度的检测;
  2. 在启用或禁用断言时不必重新编译程序。因为启用或禁用断言是类加载器( class loader) 的功能。当断言被禁用时,类加载器将跳过断言代码,因此不会降低程序运行的速度;

日志

  1. 日志是在程序整个生命周期都使用的策略性工具,用于记录程序运行的各种状态和错误,以备后期分析;
  2. 日志的优势:1.开启和关闭某个级别的日志及其输出很容易,因此开销很小;2.日志记录可以被定位到不同的处理器,例如控制台和文件等;3.日志记录器和处理器都可以根据制定的标准丢弃无用的记录;4.日志系统的配置可以通过配置文件和应用程序控制;
  3. 使用静态变量引用一个创建或者获取的日志记录器,以免被垃圾回收器回收;在这里插入图片描述
  4. 日志记录器的父与子之间将共享某些属性。例如设置的日志级别,通常有7个记录级别:在这里插入图片描述
    所有级别为 INFO、 WARNING 和SEVERE 的消息都将显示到控制台上。因此, 最好只将对程序用户有意义的消息设置为这几个级别。将程序员想要的日志记录,设定为 FINE 是一个很好的选择。

七、泛型

  1. 泛型机制编写的程序代码,要比杂乱地使用需要强制类型转换的Object 变量,具有更好的安全性和可读性。泛型对于集合类尤其有用,例如ArrayList 就是一个无处不在的集合类;
  2. 类名后和方法返回值前添加修饰符,表示声明泛型类和方法,使用时再指明类型,这类似于C++的template;但是java可以在T后添加限制,例如要求其保证继承自某个接口,形如<T extends Coiparable & Serializable>,这是c++所不具备的;
  3. 虚拟机中没有泛型,只有普通的类和方法。所有的类型参数都会用它们的限定类型替换,如Object、限定的继承超类等等;C++中会根据使用的不同类型生成不同的类,称为模板代码膨胀,但java不会,只生成一个擦除泛型后的原始限定类型的普通类,因此使用T会发生自动而隐式的类型转换(使用object需要显式类型转换);
  4. 泛型方法的类型擦除会带来问题:类型擦除会与方法多态产生冲突。当泛型类的子类重写方法,那么擦除后,父类的方法使用的参数类型是原始限定类,而子类方法使用的参数类型是某种设置的类,那么子类中就会存在这两种方法而不是重写父类方法。当父类变量引用子类对象时,因为类型擦除,父类只能使用与子类共用的原始限定类的方法,因此调用的方法是父类的方法而非子类的,达不到预期使用子类方法的效果。解决方法是:编译器会自动合成桥方法,该方法会将子类中原始限定类的方法进行修改,在其中调用合适的子类的同名不同参方法或者同名不同返回类型的方法(虽然我们不能写后者但是虚拟机能正确处理生成的这种情况),并对参数进行类型转换,由此保证多态;
  5. 桥方法不仅在泛型中使用,还在重写方法时允许指定更严格类型时使用,它会在其中调用新写的方法;
  6. 使用通配符?允许类型参数变化。例如Pair<? extends Employee>表示任何 类型参数是 Employee 或者它的子类泛型 Pair , 如 Pair, 但不是Pair。这可以破除传入参数固定泛型的限制;
  7. ? 表示一个未知类型, T 是表示一个确定的类型。类型参数“”主要用于声明泛型类或泛型方法,无界通配符“<?>”主要使用泛型类或泛型方法,结合它的上下界限定来扩展泛型的灵活性。不过也有局限:上界<? extends T>不能往里存,只能往外取;下界<? super T>不影响往里存,但往外取只能放在Object对象里。可以很容易理解为这是继承带来的变化导致的。因此,频繁往外读取内容的,适合用上界Extends。经常往里插入的,适合用下界Super;

八、集合

  1. 在 Java 类库中,集合类的基本接口是 Collection 接口。该接口扩展了Iterable接口,因此任何集合都可以使用“for each"循环,该循环可以翻译为迭代器的循环,简化next和hasNext的调用;

  2. 使用集合的forEachRemaining(lambda)方法可以传入lambda表达式,遍历处理集合中所有的元素,不过在无序集合使用时,遍历次序是不确定的;

  3. 迭代器的next方法和remove方法具有依赖性,remove前必须使用next方法,代表删除刚才读取的元素。迭代器的位置可以想象为相邻元素的中间;

  4. 集合框架的接口如下,其中RandomAccess用来检测一个特定集合是否支持高速的随机访问;
    在这里插入图片描述

  5. java库中具体的集合:在这里插入图片描述

  6. 由于迭代器是用来描述集合中的位置的, 所以依赖于位置的添加 add 方法应该由迭代器负责。但是,这只有对自然有序的集合使用迭代器添加元素才有实际意义,而对于元素无序的类型set,并无意义。因此迭代器 Iterator 接口并没有定义add方法,而是提供了其子接口ListIterator,它包含add方法;这个add与Collection.add不同,前者并不返回boolean值,它假定所有添加都成立;

  7. 迭代器add可以连续使用,这不同于remove;因为add 方法只依赖于迭代器的位置, 而 remove 方法依赖于迭代器的状态。这可以避免多迭代器并发修改链表造成混乱,一般使用多个访问迭代器和一个读写迭代器;

  8. 在 Java 程序设计语言中,所有链表实际上都是双向链接的。ListIterator提供previous和hasPrevious反向遍历链表。同时,迭代器保留了移动计数,因此可以高效地通过previouslndex和nextlndex返回其前后元素的索引;

  9. 使用链表的唯一理由是尽可能地减少在列表中间插人或删除元素所付出的代价。如果列表只有少数几个元素, 就完全可以使用 ArrayList。链表使用迭代器访问元素,而ArrayList通过索引高效地对集合进行随机访问;

  10. Vector 类的所有方法都是同步的,而 ArrayList 方法不是同步的。因此,建议在不需要同步时使用 ArrayList,否则使用 Vector;

  11. 散列表(hash table),又称哈希表。不同的元素生成的散列码不同,因此散列表可以根据散列码快速在大量数据中进行查找;在 Java 中,散列表用链表数组实现。每个链表称为桶,查找对象的散列码与桶数求余即为所在桶的索引,如果该桶已满(称为散列冲突),则需要将其与桶中所有对象比较。因此合适的散列函数和大量的桶数可以减少比较次数,定义一个初始的桶数和设置一个0.75的装填因子(当大于该值时散列表进行再散列)扩展散列表是不错的选择。可见,可以看成是将元素根据散列码分成了桶数份,散列表的查找只会在桶中进行,而不是遍历所有元素,因此会快许多。在不关心集合内元素位置时可以选用HashSet;

  12. 树集(TreeSet)是一个有序集合( sorted collection),可以以任意顺序将元素插入到集合中。在对集合进行遍历时,每个值将自动地按照排序后的顺序呈现。付出的代价是查找和添加速度较散列表慢。要使用树集, 其中的元素必须实现Comparable接口,或者构造集时提供Comparator。

  13. 映射用来存放键 / 值对,Java 类库为此提供了两个通用的扩展了Map接口的实现:HashMap 和 TreeMap。散列映射对键进行散列, 树映射用键的整体顺序对元素进行排序, 并将其组织成搜索树。散列函数和比较函数只能作用于键,与键关联的值不能进行散列或比较。散列稍微快一些, 如果不需要按照排列顺序访问键, 就最好选择散列。

  14. 映射项的更新建议使用merge方法,它对不存在对应键值对进行新建添加,否则就合并原有和新加的值。映射可以通过方法返回三个实现了Collection接口或其子接口的视图:键集keySet、值集合values(不是一个集) 以及键 / 值对集entrySet;
    KeySet上调用迭代器的remove方法会同时删除映射对应的键值对,但不能用迭代器增加,毕竟没有对应的值,但是EntrySet也不能增加,尽管它可以传入键值对;

  15. 如果映射的键在以后不再使用,自然就无法通过键删除该项,因此弱散列映射(WeakHashMap)就是为了回收以后不再使用的键值对,当它发现某个键的唯一引用来自散列条目时,会协调垃圾回收器将其回收;

  16. 链接散列集与映射:在这里插入图片描述
    链接散列映射将用访问顺序, 而不是桶的顺序来对映射条目进行迭代。每次通过get和put访问一个映射项,它都会重新放在链表的尾部,当然桶中位置不变。因此,它可以通过链表位置查看被访问映射项的先后。这对于高速缓存删除最少使用的元素极其有用,因为位于链表开头的元素最少被访问,删除它们可以达到目的;

  17. EnumSet 是一个枚举类型元素集的高效实现。 由于枚举类型只有有限个实例, 所以EnumSet 内部用位序列实现。如果对应的值在集中, 则相应的位被置为 1;在这里插入图片描述
    EnumMap 是一个键类型为枚举类型的映射。它可以直接且高效地用一个值数组实现。在使用时, 需要在构造器中指定键类型:在这里插入图片描述

  18. 使用视图( views) 可以获得其他的实现了 Collection 接口和 Map 接口的对象。例如映射类的keySet 等视图方法,初看起来,好像这个方法创建了一个新集,并将映射中的所有键都填进去,然后返回这个集。但是,情况并非如此。取而代之的是:keySet 方法返回一个实现 Set接口的类对象, 这个类的方法对原映射进行操作;

  19. 正如c++,java也有泛型算法。例如编写使用Collection接口的算法,包含基本的排序、二分查找等实用算法;

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值