以下内容纯属个人扯淡,仅供参考
目录
读后感
第1章:概述
第2章:环境
第3章:基础语法
第4章:对象与类
概览
面向对象
使用预定义类
自定义类
静态域与静态方法
方法参数
对象构造
包
类路径
文档注释
类设计技巧
1、面向对象
2、使用预定义类
3、自定义类
4、静态域与静态方法
5、方法参数
6、对象构造
7、包
8、类路径
9、文档注释
10、类设计技巧
第5章:继承
1、类、超类、子类、Object
1》
2、泛型数组列表
4、自动拆箱与拆箱
所有的基本类型都有一个与之对应的包装器类。注意事项:
这些包装器类是不可变类
一旦构造了包装器,就不允许更改包装在其中的值
这些包装器类是final
不允许定义子类
5、参数可变的方法
6、枚举
7、反射
8、继承的设计技巧
第6章:接口、lambda表达式、内部类、代理
概览
1、接口
接口是用来描述类具有什么功能,而不是给出每个功能的具体实现。概览:
概念
特性
与抽象类的联系
静态方法
默认方法
应用
回调
Comparator
Clone对象克隆
1》概念
接口不是类,它是对类的一组需求描述,这些类要遵从接口描述的方法统一格式进行定义。
以Arrays为例(Arrays是jdk提供的一个工具类,用于数组int[]等进行排序、二分查找、拷贝等操作),其中sort可以对对象数组进行排序,但是有个前提是:该对象所属类必须实现Comparable接口(jdk5之后改进为泛型T),该方法要求小于时返回一个负数,等于时返回0,大于时返回一个正数。如果没提供该方法,则虚拟机会抛出运行时异常
问题:为什么不直接提供一个compareTo方法,而是必须实现Comparable接口呢?因为java是强类型语言,编译器必须确认对象一定具有compareTo方法。
public interface Comparable<T> {
int compareTo(T var1);
}
规则1:接口中声明的方法自动归属public
规则2:接口中可以定义常量,jdk8后可以在接口中实现方法,但接口绝不能包含实例域。
规则3:实现接口的那个类,需要实现接口声明的所有方法
2》特性
3》与抽象类的联系
4》静态方法
5》默认方法
6》应用
1)回调
2)Comparator
3)Clone对象克隆
2、lambda表达式
一种表示可以在将来某个时间点执行的代码块。可以精巧而简洁的方式表示使用回调/变量行为的代码。概览
为什么引入lambda
语法
函数式接口
方法引用
构造器引用
变量作用域
1》为什么引入lambda
引例1:指定时间间隔完成某项工作。以前的做法:将工作放在ActionListener#actionPerformed方法中,然后构造Woker实例并构造Timer
class Worker implements ActionListener {
@Override
public void actionPerformed(ActionEvent event) {
//工作
}
}
new Timer(1000,new Worker());
引例2:用一个定制比较器完成排序,按长度而不是默认的字典顺序对字符串进行排序。以前的做法:向sort方法传入一个Comparator对象
class LengthComparator implements Comparator {
@Override
public int compare(String first,String second) {
return first.length() - second.length();
}
}
调用:
String[] strings = ...
Arrays.sort(strings,new LengthComparator())
目的:将一个代码块传递给某个对象(一个定时器,或一个sort方法),该代码块会在将来某个时间执行一次或多次。
但是在Java中,不能直接传递一个代码块给方法处理,而是要用一个对象去接收
2》语法:要注意每个例子里是否有花括号、分号、return
规则1:如果表达式代码体只有1句就能完成,那么是可以省略最外层花括号,并无需return:
(String first,String second)
-> first.length() - second.length()
否则,就需要闭合逻辑的return+花括号来完成:
(String first,String second) -> {
if(first.length() < second.length()) return -1;
else if(first.length() > second.length()) return 1;
else return 0;
}
规则2:即便没有形参,那么也要提供空括号,类似无参方法
() -> {
for (100){
syso(i);
}
}
规则3:若形参类型可以推导出来,那么可以忽略它的类型。下例中,编译器可以根据泛型来推导first、second的类型;
Comparator<String> comp = (first,second) -> first.length() - second.length();
若只有一个参数,那么还可以省略形参的小括号
规则4:不需要指定返回类型,它总是由推导得到的。下例中,可在需要int类型结果的上下文中使用
(String first,String second) -> {
first.length() - second.length()
}
规则5:lambda表达式中,若存在if分支,那么要确保所有分支都有返回值(闭合逻辑的return)
3》函数式接口
定义:对于有且只有1个抽象方法的接口,当需要这种接口的对象时,就可以提供一个lambda表达式,即:可以将1个lambda表达式赋值给1个函数式接口类型变量。规则:
有且只有1个抽象方法
允许定义静态方法
允许定义默认方法
允许java.lang.Object中的public方法
可以注解上@FunctionalInterface来区分
该注解非必须,而是为了编译器语法检查,类似@Override的作用
例子1:Arrays.sort承诺为一个String数组words排序,它第2个参数需要一个Comparator实例,这个类是一个函数式接口,因此可以提供一个lambda表达式(底层:该方法实际是接收一个实现了Comparator<String>对象,并再调用该对象的compare方法来执行这个lambda表达式体)
Arrays.sort(
words,
(first,second) -> first.length()-second.length()
)
4》方法引用
应用场景:有现成的方法可以完成想要传递到其他代码的某个动作。
例子1:希望只要出现一个定时器事件就打印这个事件对象,那么;
普通lambda表达式方式:
Timer t = new Timer(1000, event -> syso(event));
方法引用方式:
//System.out::println就是一个方法引用,它等价于:x -> System.out.println(x)
Timer t = new Timer(1000, System.out::println);
例子2:想对字符串排序,不考虑大小写
Arrays.sort(strings, String::compareToIgnoreCase)
5》构造器引用
6》变量作用域
7》处理lambda表达式
8》再谈Comparator
3、内部类
用于设计具有相互协作关系的类集合
4、代理
一种可以实现任意接口的对象
第7章:异常、断言、日志
1、异常
概述
为什么要设计异常
什么是异常
异常分类
声明受查异常
怎么使用异常
抛出
捕获与处理
资源处理
异常分析
使用建议
异常处理不能代替简单的测试
不要过分细化异常
利用异常层次结构
不要压制异常
在检测错误时,苛刻比放任要好
尝试传递异常
1。为什么要设计异常
程序并非理想中的一直正常运行,当在运行时出现错误(文件包含错误信息、网络问题、数组越界、NPE等),程序应该:返回一个安全状态并能让用户执行其他命令;或者,允许用户保存所操作的结果,并以妥善方式终止程序。
为了解决上述问题,传统做法:
对方法返回错误码,是否有错误/异常就需要由调用者去判断
无法明确将有效数据与无效数据加以区分;并且,调用者必须清除错误约束,若约束有修改,则调用者也要修改
一个方法若不能采用正常的途径来完成它的任务,那么就可通过另外一个路径来退出方法,此时方法不会立即退出,并没有返回值,而是抛出一个封装了错误信息的异常对象。
同时该方法调用者也无法继续运行,它需要搜索能处理这种异常的异常处理器
2。什么是异常
1)异常分类
Error:OS内部错误、资源不足错误,程序如果出现这种异常,只能给出通知并尽力安全地退出程序之外
RuntimeException:程序本身错误。如:错误的类型转换、数组访问越界、使用null指针。编写代码时应该尽量避免这种异常发生
IOException:IO异常(系统之外的访问异常)。如:读取文件数据、打开不存在的文件、根据字符串查找一个不存在的Class对象
非受查异常=RuntimeException+Error
受查异常=IOException
2)声明受查异常
结论:一个方法必须声明所有可能抛出的受查异常,而非受查异常中要么不可控制-Error要么就应该避免发生-RuntimeException。否则,编译器会报错而无法通过编译;也可以捕获异常,这样会使异常不被抛到方法之外(若你知道该如何处理异常则使用捕获器处理,否则就继续抛出)
3。怎么使用异常
1)抛出
找到一个合适的异常类;创建该类的一个对象;将该异常对象抛出,例如:读文件时,文件首部声明了len=1024,但只读了n=733长度后,文件就结束了
String readData(Scanner in) throws EOFException {
...
while(...){
if(!in.hasNext()){
if(n<len){
throw new EOFException("长度len="+len+"但只收到了n="+n);
}
}
}
}
当标准异常类无法充分描述错误时,就应该创建自定义的异常
public class FileFormatException extends IOException { //受查型异常
public FileFormatException(){}
public FileFormatException(String msg){
super(msg); //异常描述信息,异常被捕获时,通过e.getMessage()可以拿到
}
}
2)捕获与处理
如果某个异常在抛出之后没有任何地方被捕获,那么程序就会终止运行。
疑问:为什么有些异常会导致程序终止,有些又不会?答:虚拟机有定义一个异常处理器。首先,当一个异常未被应用捕获时(具体说来是main方法,main方法中发生了运行时异常-非受查异常,或者在main方法签名上throws传递出来的异常),就会抛出应用之外。然后,由虚拟机定义的这个捕获器进行捕获,它的catch处理中有一行就是将堆栈轨迹输出到Console控制台(相当于调用e.printStackTrace()方法。因此我们平时调试代码时发生了异常然后在Console中看到的那些信息,其实可能是由虚拟机异常捕获器处理所输出的)最后,也就是说这个异常最终会被虚拟机异常捕获器所处理,因此应用并不会停止。但是,对于虚拟机也无法处理的异常或虚拟机本身抛出的异常,如:OOM、StackOverFlow等异常,那么就会除了打印堆栈轨迹,应用会被停止。
主动捕获:
try {
代码1
代码2
}catch (ExceptionType1 | ExceptionType2 e){
//e为final类型。因此不能被重新赋值,即在catch中不能执行e = ...
//ExceptionType1和ExceptionType2不能存在继承关系
该异常类型的处理
}
//可以添加多个catch进行不同类型异常的处理,同时1个catch可以处理多个类型的异常
1.try中没有抛出任何异常,则直接跳过catch
2.当try中的代码抛出了catch中声明的异常类,那么程序将跳过try中剩余代码,执行catch中代码
3.若try中抛出了未在catch中声明的异常(一般是非受查异常),那么该方法立即退出并抛出该异常,若调用者
也未处理该异常,则继续上抛,若虚拟机仍然未处理则应用终止
传递:使用throws将异常传递给调用者(一般是受查型异常,非受查型异常可以不声明)。若是重写父类的方法,则throws声明的异常类型不能超过父类所列出的异常类范围,疑问:什么时候该捕获处理,什么该传递?答:方法应该捕获那些知道如何处理的异常,而不知道怎么处理的应该继续传递
再抛出:在catch中重新throw出一个新的异常类型。目的:改变异常类型。当在一个方法中发生了受查异常而又不允许抛出到该方法之外,那么可以使用包装技术将它包装成运行时异常(Spring设计的数据库访问异常体系,就是按照这种思想实现)
try {
code
}catch(SQLException e){
Throwable se = new ServletException("database error");
se.initCause(e);
throw se;
}
//异常捕获器,可以得到原始异常相关信息
Throwable e = se.getCause();
3)资源处理
确保在异常处理器中获取的本地资源被关闭,如:文件访问,数据库访问等
普通格式
try{
1
可能抛出异常E1、E3
2
}catch (E1 e){
3
可能抛出异常E2
4
}finally{
5
in.close()
}
6
1.代码没有异常。
1、2、5、6
2.抛出一个在catch中声明的异常E1。
1)catch执行正常。
1、3、4、5、6
2)catch中又抛出异常E2。
1、3、5
3.方法直接抛出一个E3。
1、5
4.catch是可选的,若不写则异常会被抛出方法之外
推荐格式。目的:代码更清晰
try{ //外层try只有一个职责,确保错误被报告,包括finally中的错误
try { //内层try只有一个职责,确保关闭输入流
code;
}finally{
in.close();
}
}catch (IOE e){
处理
}
注意:不要在finally中使用return,它会覆盖try/catch中的返回值
带资源的try。目的:代码更简洁。条件:JavaSE7、资源需要实现AutoCloseable接口
try (Resouce res = ...){
}catch (IOE e){
处理
}
1.catch是可选的。不写时,异常将被抛出方法之外
2.finally是可选的。不建议使用
3.实际上,in.close()所可能抛出的异常会被抑制,将由该捕获器自动捕获,无需像前面一样定义另外一个捕获器
另外,这些被抑制的异常会被addSuppressed追加到原来的异常上
使用getSuppressed可以得到从close方法抛出的被抑制的异常列表
4)异常分析
堆栈轨迹是一个方法调用过程的列表。e.printStackTrace()可以将信息打印到控制台,getStackTrace()可以获取到StackTraceElement对象数组,用于程序中分析(文件名、代码行号、类名、方法名等)
注意:要谨慎在应用中使用e.printStackTrace(),该方法是将异常错误信息输出到控制台,而部署到产线的应用程序,输出错误信息到控制台几乎是没有意义的,而是应该记录到错误日志文件中
4。使用建议
1)异常处理不能代替简单的测试
需求:尝试对空栈进行出栈操作
//版本1
if(!s.empty()) {
s.pop();
}
//版本2
try{
s.pop();
}catch (EmptyStackEmpty e){
}
//版本3
s.pop()
版本1是先查看栈是否为空,避免直接退栈操作;版本2是直接尝试退栈操作并捕获异常,前者的性能更好。结论:对于非受查型异常(本例中若s.pop()是受查异常,那么还是需要使用捕获器才能通过编译),若能使用程序逻辑来代替异常捕获,甚至直接用版本3。
2)不要过分细化异常
//对于各种异常,分别都提供一个捕获器,但实际上每种异常都没有办法解决
try{
A();
}catch(AE e){
}
try{
B();
}catch(BE e){
}
//正确的做法
try{
A();
B();
}catch(AE e){
}
结论:与1)可以归纳为一点,尽量不手动写、少写try/cacth
3)利用异常层次结构
1.不要只抛出RuntimeException,而是寻找更加适合的子类或自定义异常类,来更精确的表意,因为RTE太广泛
2.不要只捕获Thowable,代码会更难读懂,根本不知道要处理具体什么样的异常
3.不要为代码逻辑问题而抛出受查型异常,实际上我们一般写代码都是抛出RTE
4.有时需要异常转换
当在一个文件中去解析一个整数时,应该将NumberFormatException转换为IOException
Spring将对数据库的操作的受查异常,转换为自体系的非受查异常
4)不要压制异常
和6)相反,对于一些大概率不会抛异常的代码,我们可以早捕获,使用try/catch来"关闭"这个异常,对上层调用者而言它根本不知道这个异常的存在,在它看来它所有调用的方法是正常返回的
5)在检测错误时,苛刻比放任要好
场景1:无效参数调用一个方法,是该返回一个虚拟值,还是抛异常?
场景2:栈空时,是返回null,还是抛异常?
都应该抛异常。在编写方法时,不要试图返回非法值让调用者遵守某种约定来判断错误。因为你必须确保这个非法值是绝对唯一的,并且调用者也必须遵守这个约束。这实际上有一定的代码耦合性。如:0表示非法,那么调用者需要对返回值进行检查,并且当后期要更改为-1为非法,那么调用者也需要做相应更改
若返回null,则调用者需要进行null判断,否则会抛出NPE
并且,上述2个场景下,若不使用抛异常的方式,那么相当于将错误的发现往后延迟了;而不是在发生的地方就抛出错误
结论:不要在方法中返回依赖约定的非法值或null来表示错误,应该用异常来终止执行链,而不是让该调用者拿到返回值后去判断
6)尝试传递异常
让更高层次的方法通知用户发生错误,或放弃不成功的命令是更好的。如:在MVC开发模式中,可以不在Dao、Service层对受查异常进行捕获,而是传递给Controller,利用ControllerAdvice去统一捕获
结论:5)和6)总结为早抛出、晚捕获
API
Throwable
Exception
RuntimeException
StackTraceElement
2、断言
1。概述
当确信某个属性需要符合要求,而代码执行依赖于这个属性时就需要使用断言。疑问:为什么不使用异常呢?答:异常相关的代码throw new XxxException()会一直存在于代码之中,并且若有大量的参数检查使用异常来完成的话,性能会有所下降
断言机制允许在测试期间向代码中插入一些检查语句,而代码发布时,这些断言语句会自动被去掉。默认情况下断言被禁用,通过以下语句开启(启用/禁用断言是类加载器的功能,当断言被禁用时会跳过断言代码,而不会影响程序性能)
java -enableassertions MyApp //启用
java -ea:MyClass -ea:com.mycompany.mylib...MyApp //开启MyClass类、com.mycompany.mylib包下所有断言
java -ea:... -da:MyClass MyApp //禁用
1.-ea、-da开关不能应用到那些没有类加载器的系统类上,这些需要通过-enablesystemassertions/-esa来开关
注意:也可以在程序中运行时控制类加载器的断言状态
2。案例
疑问:什么时候应该使用断言呢?
断言失败是致命的、不可恢复的错误
断言检查只用于开发、测试阶段
3、日志
第8章:泛型程序设计
概览
为何泛型
泛型类
泛型方法
类型变量限定
泛型与虚拟机
约束与局限性
继承规则
通配符类型
泛型与反射
1、为何泛型
之前,ArrayList只维护一个Object引用数组,因此可以向数组列表中添加任何类型的对象,但获取时必须强制转换,并且类型转换失败将抛出异常
使用泛型后:程序拥有更好的阅读性;类型安全,将可能出现的运行时类型转换异常提前暴露在编辑期间。
2、泛型类
//E:集合的元素类型
//K、V:映射的键和值
//T、U、S:任意类型
public class Pair<T> {
//定义属性的类型
private T first;
private T second;
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; }
//定义返回类型
public T getFirst() { return first; }
public T getSecond() { return second; }
//定义形参类型
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
使用示例
String[] words = { "Mary", "had", "a", "little", "lamb" };
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
class ArrayAlg
{
//普通方法,它返回一个Pair<String>类型的对象
public static Pair<String> minmax(String[] a) {
if (a == null || a.length == 0) return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}
3、泛型方法
public static <T> T getMiddle(T... a) {
return a[a.length/2];
}
注意:这个泛型方法可以定义在泛型类中,也可以定在普通类中。它接收一个T泛型数组,返回其中位的那个元素;<T>是类型变量,它要放在修饰符的后面,返回类型的前面。使用示例:
String middle = Xxx.<String>getMiddle("asdas","asdasd","zxcsad");
或者
String middle = Xxx.getMiddle("asdas","asdasd","zxcsad"); //类型推断
注意:大多数情况下对泛型方法的类型引用是没问题的,以下
double middle = ArrayAlg.getMiddle(3.14,1729.0);
编译器将给出歧义:解释这句代码将有2种方法,且都是合法的,第1种是理解为3个数的逗号运算结果传递给形参,第2种理解是传递3个参数,此时编译器将自动打包参数为1个Double和2个Integer对象,再找它们共同的超类即Number、Comparable,这俩本身也是泛型。建议:都写为double类型
技巧:有目的的引入一个错误,然后分析错误信息,这样可以知道编译器对一个泛型方法调用所推断出来的类型。
4、类型变量限定
作用:用于限定类型变量的类型。
//限定泛型T必须实现/继承Comparable类,只有实现Comparable#compare方法的类才有比较的意义
public static <T extends Comparable> Pair<T> minmax(T[] a) {
if (a == null || a.length == 0) return null;
T min = a[0];
T max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
//多个限定
<T extends Comparable & Serializable>
5、泛型与虚拟机
1。Java泛型转换
1)类型擦除
规则:某个泛型类型的原始类型是唯一的,它是删去类型参数后的泛型类型名,类型变量会被擦除并替换为第一个限定类型(无限定的用Object);对虚拟机来说是没有泛型类型对象的,所有对象都是非泛型类型的
注意:多个限定类型时,编译器会在必要时进行多个限定类型的转换,为了提高效率,因此应该将标签接口(没方法的接口)放在限定列表的末尾,例如:<T extends Comparable & Serializable>不应该叫Serializable放在前面
Pair<T>的原始类型为
public class Pair {
//定义属性的类型
private Object first;
private Object second;
public Pair() { first = null; second = null; }
public Pair(Object first, Object second) { this.first = first; this.second = second; }
//定义返回类型
public Object getFirst() { return first; }
public Object getSecond() { return second; }
//定义形参类型
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
}
2)翻译泛型表达式
//buddies是Pair<Employee>类型的
Employee buddy = buddies.getFirst();
//假设first域是公有域public的(虽然这不是好的代码风格)
Employee buddy = buddies.first;
规则:编译器将方法调用翻译为2条虚拟机指令:对原始方法Pair.getFirst()的调用;将返回的Object类型强制转换为Employee类型
规则:编译器也会在结果字节码中插入强制类型转换
3)翻译泛型方法
由1)类型擦除也会作用在泛型方法上,因此泛型方法族经过类型擦除后会只剩一个方法,如
//泛型方法族
public static <T extends Comparable> T min(T[] a);
//类型擦除后,只剩一个方法
public static Comparable min(Comparable[] a);
但是,当子类有覆盖父类的方法时,此时多态与类型擦除会发生冲突。带来2个复杂的问题:
问题1:覆盖有形参的方法,如:
Class DateInterval extends Pair<LocalDate> {
@Override
public void setSecond(LocalDate second){
if(second.compareTo(getFirst()) >= 0 ){
super.setSecond(second);
}
}
}
一个日期区间DateInterval是一对LocalDate对象,该类覆盖setSecond方法的目的是保证第2个参数值永远不小于第1个值,该类经过类型擦除后,代码如下:
Class DateInterval extends Pair {
@Override
public void setSecond(LocalDate second){
if(second.compareTo(getFirst()) >= 0 ){
super.setSecond(second);
}
}
}
DateInterval从父类继承了一个setSecond方法,该方法定义如下
public void setSecond(Object second){}
冲突点:示例
//interval是DateInterval类型的
Pair<LocalDate> pair = interval; //DateInterval是Pair<LocalDate>的子类,因此可以使用"父类指针指向子类"
pair.setSecond(aDate);
由于pair是"父类指针指向子类",所以会希望setSecond调用的是具体子类定义的(这是Java多态性)DateInterval#setSecond;但是由1)类型擦除又可知,应该是对原始方法Pair#setSecond方法调用。
解决办法:编译器会在子类DateInterval生成一个桥方法
public void setSecond(Object second){
setSecond((Date) second);
}
上述示例setSecond调用过程将变为
1.pair是Pair<LocalDate>类型,该类型只有一个简单方法setSecond(Object)
虚拟机用pair引用的对象先调用该方法-满足类型擦除的规则
2.由于该对象的实际类型是DateInterval,因此会调用编译器所合成的桥方法
DateInterval.setSecond(Object)
3.桥方法中,所做的事情只有一个:调用对象实际类型的方法-满足多态特性
DateInterval.setSecond(Date)
问题2:覆盖空参方法
如果上述子类DateInterval覆盖了getSecond
@Override
public LocalDate getSecond(){
return (Date)super.getSecond().clone();
}
那么编译器为该类也会生成桥方法
LocalDate getSecond();
Object getSecond(); //合成的桥方法,它将调用上面方法
注意:在编辑器里是非法的,不能这样写Java代码;但是在虚拟机中是合法的,因为虚拟机根据参数类型+返回类型来确定一个方法
扩展知识:桥方法还应用于子类覆盖父类方法或实现父接口中
public class Employee implements Cloneable {
public Employee clone() throws CloneNotSupportedExcetion {
...
}
}
Object#clone和Employee#clone方法称为:具有协变的返回类型,实际上Employee具有2个clone方法
Employee clone() throws CloneNotSupportedExcetion //上面定义的
Object clone //桥方法,实际上是它覆盖父类的方法,它所做的事情是调用子类对应方法
结论:Java泛型转换
虚拟机中没有泛型,只有普通的类和方法
所有类型参数都用它们的限定类型替换(无则使用Object替换)
桥方法被合成,来保证多态
为保证类型安全性,必要时插入强制类型转换
2。调用遗留代码
疑问:没太看明白
大概意思是要使用@SuppressWarnings("unchecked")标注在方法会调用处以抑制警告
6、约束与局限性
7、继承规则
1。简单规则
规则:无论S与T是什么关系,通常Pair<S>与Pair<T>是没关系的。但是,将子类型赋父类型是可以的
Pair<Employee> t = s; //s是Pair<Manager>。这是非法的
ArrayList<Peaple> peaple = ...;
a.add(stu); //stu是Stu extends Peaple类型的。这是合法的
但是它们都是原始类型Pair的子类,因此若用Pair类型去接收,那是合法的
2。通配符
8、通配符类型
9、泛型与反射
第9章:集合
1、概述
概览
设计思想
Collection
迭代器
泛型约束
1。设计思想
总结:将集合的接口与实现分离。例如:Queue是接口,其有ArrayDeque、LinkedList两个子类,使用父类型引用子类型对象,这样在切换实现实现特别方便()
总结:AbstractXxx是jdk提供的,若要实现自己的队列类,扩展AbstractXxx比实现Queue接口要轻松很多(这也是框架设计的一种思想,比如Servlet和HttpServlet也是如此),提供一个顶层的设计接口,再给出一个默认的实现
2。Collection
3。迭代器
4。泛型约束
2、集合
概览
链表
数组列表
散列集
树集
队列
ArrayList | 动态增长和缩减的索引序列 |
LinkedList | 可在任何位置高效插入删除操作的有序序列 |
ArrayDeque | 基于循环数组实现的双端队列 |
HashSet | 没有重复元素的无序集合 |
TreeSet | 有序集 |
EnumSet | 包含枚举类型值的集 |
LinkedHashSet | 可记住元素插入次序的集 |
PriorityQueue | 允许高效删除最小元素的集 |
1。链表
数组、ArrayList的缺陷:从中间位置删除、添加一个元素需要移动后面的元素需要付出很大的代价
2。数组列表
3。散列集
4。树集
5。队列
3、映射
概览
映射操作
映射视图<>
弱散列映射<WeakHashMap>
链接散列集与映射<LinkedHashSet、LinkedHashMap>
枚举集与映射<EnumSet、EnumMap>
标识散列映射<IdentityHashMap>
HashMap | 映射表 |
TreeMap | 键值有序排列的 |
EnumMap | 键值属于枚举类型 |
LinkedHashMap | 可记住键值项添加次序 |
WeakHashMap | 值无用后可被垃圾回收 |
IdentityHashMap | 用==而不是equals比较键值 |
1。映射操作
2。映射视图<>
3。弱散列映射<WeakHashMap>
4。链接散列集与映射<LinkedHashSet、LinkedHashMap>
5。枚举集与映射<EnumSet、EnumMap>
6。标识散列映射<IdentityHashMap>
4、视图与包装器
概览
轻量级集合包装器
子范围
各种视图
可选操作
1。轻量级集合包装器
2。子范围
3。各种视图
4。可选操作
5、算法
概览
排序与混排
二分查找
简单算法
批操作
集合与数组转换
编写自己的算法
1。排序与混排
2。二分查找
3。简单算法
4。批操作
5。集合与数组转换
6。编写自己的算法
6、遗留的集合
概览
Hashtable
枚举<Enumeration>
属性映射<Properties>
栈<Stack>
位集<BitSet>
1。Hashtable
2。枚举<Enumeration>
3。属性映射<Properties>
4。栈<Stack>
5。位集<BitSet>
第10章:图形程序设计(忽略)
第11章:事件处理(忽略)
第12章:Swing用户界面组件(忽略)
第13章:部署Java应用程序
1、jar文件
2、应用首选项的存储
3、ServiceLoader
4、applet
5、Java Web Start
第14章:并发