《Java核心技术》知识点总结

以下内容纯属个人扯淡,仅供参考

目录

读后感

第5章:继承

第6章:接口、lambda表达式、内部类、代理

第7章:异常、断言、日志

第8章:泛型程序设计

第9章:集合

第10章:图形程序设计(忽略)

第11章:事件处理(忽略)

第12章:Swing用户界面组件(忽略)

第13章:部署Java应用程序

第14章:并发


 

读后感

第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章:并发

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值