jdk7笔记记一笔

年关将至,加上海外新项目启动,趁这个机会把依赖jdk上到jdk8,为未来版本兼容降低点复杂度,同时可以带来一些jdk8的特性,但是在jdk8各种新概念新功能通读之前,还是先把jdk7的一些技术概念再通读回忆一次,温故而知新。

new

jdk7新增并更新了的一些主要功能

  1. switch支持字符串,使用的是hash值,也就是使用equals比较
  2. 数值字面量改进,十进制字面量下划线使用 100_000_000,便于阅读
  3. 优化异常处理,添加addSuppressed,多Exception catch |
  4. try with resources,AutoCloseable接口的实现
  5. 变长参数调用的 @SafeVarargs编译器提示
  6. 包装类型更新(语法糖,String== intern)Integer.cache,jvm可以设置IntegerCache.high=2000
  7. 新增jdbc4.1规范
  8. objects的静态方法

switch的字符串支持是jdk7的一个家喻户晓的用法,从支持上面来讲这里还是使用了语法糖的概念,也就是在编译成class文件的时候,使用了String的hash的值,最后通过equals进行比较,从switch的语法编译成class文件中可以了解到,switch会根据case的情况编译成相应的if else,所以这些也只是编码层面的不同,而性能上两者是相似的。

数值字面量,之前用的本来就很少,这里理解上也是编码过程中代码的字面概念,便于编写和阅读用,jdk7增加了二进制的字面量ob,加上之前支持的十进制、八进制o、十六进制ox。

异常处理,jdk7对异常的新增了多个异常的catch支持,新增了可能存在的异常信息丢失拦截情况下的addSuppressed方法。
平常开发过程中经常需要考虑如何设计异常的处理结构,个人认为没有一统的异常结构设计规范,比如分布式或者微服务情况下的异常和常规的部署在war包中的web结构下的异常以及各种中间件内部的异常体系,各自的侧重点都是不同的,但是异常的体系结构都必须按照分层+实际情况两个维度去设计,在异常的拦截上也存在在什么地方进行拦截的问题,考虑完这些之后,还需要再考虑受检异常和非受检异常如何继承和代码编写。

AutoCloaseable接口仅仅是免除了finally下的异常中自动调用close()方法的代码。通过继承该接口只需要编写try 语句块即可,通常调用的方法需要抛出受检异常比如 IOException,如果存在上述的异常丢失情况,可以添加jdk7异常增强的功能。

变长参数+泛型的使用在编译器中会显示警告的方式,在jdk7中增加了@SaveVarargs的注解不提示警告。

基本类型的包装类的增强,添加了Compare的静态方法,可以不再使用比如Integer.intValue()进行比较了,这是一种语法糖的概念,意味着编译后的效果是一致的。
==的使用在String类型的表现上使用了interning字符串内部化技术,jdk7把这种概念扩大到了-128到127的数字,意味着short,int,long,char都可以使用==进行比较包装类,其中Character仅包含该0-127的范围,这里都使用了内部缓存的机制,需要注意的是,这里的数字范围可以通过jdk的配置进行,也就意味着不同的配置下的返回值是不一样的,而不是网上流传的面试题讲述的那样一定是true或者false,这种貌似很有意思的题目给了很多人误导,高性能编程时这种范围的配置十分重要。

1
2
-Djava.lang.Integer.IntegerCache.high=256
-Djava.lant.Long.LongCache.high=20000

jdbc4.1的规范,需要对应的数据库的支持,AutoCloseable的加入,默认的schema数据库的加入,数据库连接超时和终止等,覆盖mysql4.1-mysql5.6的所有版本。

Thread这种底层的线程类的更新,加入了一些特殊情况下异常抛出,防御式编程中的校验提示。

Objects类的静态方法在开发过程中通常被一些第三方工具类替代,但是随着JDK8的更新换代,Objects在很多情况下可以替代guava工具类的很多方法,原生jdk的支持在很多场景下方便了编码的使用范围。

1
2
3
4
5
Connector/J version Driver Type JDBC version MySQL Server version Status
5.1 4 3.0, 4.0, 4.1, 4.2 4.1, 5.0, 5.1, 5.5, 5.6, 5.7 Recommended version
5.0 4 3.0 4.1, 5.0 Released version
3.1 4 3.0 4.1, 5.0 Obsolete
3.0 4 3.0 3.x, 4.1 Obsolete

动态性

java语言在动态性方面的概念

  1. 脚本语言,使用较少
  2. 反射
  3. 动态代理
  4. 动态语言 jpython,jrube的调用,方法句柄

动态性在java中使用最多的莫过于反射和基于反射的动态代理,通常还包括对JavaScript的支持和其他基于jvm的语言的支持,其中js是基于js引擎的支持,而其他语言则是jvm的功劳,在平常开发过程中在反射功能上的使用几乎是绝对的。

反射通常是对一个类的构造方法、域以及方法的控制,可以在运行时提供java语言的动态支持。使用最多的场景比如一些Map2Bean,Json2Bean等场景。

1
2
3
4
5
Class<?> clazz = obj.getClass();
clazz.getMethod(xxx,xxx,xxx);
clazz.getDeclareConstructors();
clazz.getDeclareFields();
clazz.getDeclareMethods();

细节

  1. 方法通过反射可以得到私有方法并调用,通过method.setAccessible(true);
  2. 单例的编写因为反射可能会变成非单例的情况是反射构造函数的功劳,是一个特别需要注意的地方,变长的构造函数参数需要使用数组方式调用;
  3. 静态内部类的构造方法进行反射初始化的方法第一个参数使用this关键字,比如newinstance(this,xxx)表示 NestedClass(xxx)构造方法;
  4. 静态域的反射赋值操作,setXXX(null,3)第一个参数为null,不需要提供具体的对象的实例。私有域无法通过反射进行控制,静态域的访问只能在初始化之后才会被执行,在类生命周期中会提到;
  5. 对反射的操作在jdk7中添加了相应的异常进行捕捉。

动态代理是对一个类的代理实例化,通过Proxy类进行初始化,分别传入classLoader,clazz和handler的实现类即可。本质上是通过反射构造方法进行实例化的。

1
Proxy.newProxyInstance(classLoader,clazz,handler)

动态代理的操作都封装在InvocationHandler的实现类中,通过invoke方法进行调用

1
invoke(Object proxy,Method method,Object[] args)

实现动态代理接口的工具类

1
2
3
4
5
6
public static<T> T makeProxy(Class<T> intf,final T object){
ImplInvocationHandler handler = new ImplInvocationHandler(onject);
ClassLoader cl = object.getClass().getClassLoader();
return (T) Proxy.newProxyInstance(cl,new Class<?>[](intf),handler);
}

java被设计成可以实现多个接口,以为这反射也需要可以反射到多个接口

1
2
将class的参数设计成数组
object.getClass().getInterfaces();

多个接口相同类型的方法会按照顺序产生代理,后面的方法会被之前缓存的方法替代,代理类只会创建一次。

方法句柄在开发过程中若干年前在基于jvm的IBM中间件产品上用的很多,大多是基于多语言见调用场景使用到,后续再来更新。

io增强

nio nio2(aio)

nio

Channel

  • FileChanel
  • SocketChannel
  • ServerSocketChannel
  • DataPargramChannel

Buffer

  • ByteBuffer,MappedByteBuffer
  • IntBuffer,LongBuffer,CharBuffer,FloatBuffer,DoubleBuffer,ShortBuffer

select操作会让程序处于运行不退出状态

nio2

  • Path
  • FileSystem (FileAttributes)
  • Zip/Jar access
  • Files
  • Watch Service API
  • Random Access Files(SeekableByteChannel)
  • AsynchronousChannel(FileXXX、ServerSocketXXX、SocketXXX)
  • Groups(thread pool)
  • AsynchronousDatagramChannel从正式版java7去除了,至今jdk8还没有找到这个类
  • NIO.2 provides support for both hard links and symbolic links(Linux)

async建议:

  1. ReadOperations 使用ByteBuffer Poll
  2. Short Read Operations使用Bolcking Only
  3. Write Operations使用FIFO-Q并允许Blocking

nio是jdk4引入的new io功能

nio2是jdk7对nio的功能升级

io这一章节让我陷入了年末4书连弹的节奏,不可自拔,单独好好总结一个gitbook吧,主要围绕搞清楚一下几件事情:

1.io围绕了哪些概念进行io操作
2.nio是怎么做的
3.nio2补充了哪些概念
4.阻塞和异步分别在这里讲的是什么
5.如何针对io nio进行编程
6.常用的io工具类的工作方式
7.netty中的nio工作方式
8.jdk8中的io有什么变化

https://www.gitbook.com/book/xuminwlt/java-io-nio-nio2/

i18n

  • 字符集和编码
  • 国际化

字符集和编码在pro java nio2中的章节中描述的很详细,java平台一直支持使用unicode规范,这也是最常用的字符集,在编码中使用最常用的utf-8编码规范,解决所有的乱码问题,对其他的字符集的支持以及编码只需要了解一进一出的规范是什么,在平台本身并不需要进行编解码问题,更多地是在进行多平台信息交互时需要对信息进行的处理操作,比如nio中的编码和解码。CharSetEncode,CharsetDecode。

Web开发中,针对GET和POST都统一使用utf-8进行编码,解决web中常见的乱码问题。

国际化在java中最常用的实践是多语言的支持,国际化工作在开发过程中首先需要进行本地化工作,即该对那些数据进行本地化抽取,最后在后期再对这些数据进行国际化配置,在java中使用ResourceBundle和Locale进行国际化的信息配置和地区的设置,Locale会对ResourceBundle中的数据按照地区进行匹配,完成国际化的工作。

Web开发过程中的国际化工作,可以使用标准的Accept-Languate的头部或者cookie的设置,传递到Request进行Locale的配置,这里的定义通常使用TheadLocal进行线程级别的Locale配置,还可以使用Http的Session级别进行配置,该部分功能在SpringMVC中可以通过简单的配置加上IDEA IDE的ResourceBundle功能很好解决。

虚拟机

  • gc
  • strong reference 强引用
  • soft reference 软引用
  • weak reference 弱引用
  • phantom reference 幽灵引用
  • HotSpot虚拟机

java虚拟机解决java世界写一次到处运行的最根本原因,在于java虚拟机对每个平台提供的特定的虚拟机支持。

java虚拟机做的最多的两个工作,第一个是针对底层平台的原生调用,这部分封装在java虚拟机中,其次就是所有javaer最关心的内存调用问题,和C不一样的地方也就是java不对内存进行直接的控制,而是通过java虚拟机进行内存控制也就是gc的部分,其中内存管理最关心的就是内存引用类型。

内存管理在C系列中使用引用计数来对对象进行回收,但是容易造成互相引用和错误引用导致的悬挂引用问题。

java中只有创建对象的申请内存操作,但是并没有delete退出内存的操作,这里都是通过gc垃圾回收来执行的。

java中的内存

  1. 未被使用的内存
  2. 正在使用的内存
  3. 不在使用但是包含数据的内存空间,也就是需要清理的内存

java中清理内存的标准是对象指向的应用是不是处于活跃状态,也就是引用状态,java gc的算法就是不同的进行这种判断的算法,会存在并发执行和暂停执行的区别。

java默认的引用类型为强引用类型,guava中可以weak的引用类型。

强引用类型表现为强引用可达 strongly reachable,也就是进行引用树遍历过程中发现了活跃对象。 java定义的变量引用关系使用栈存储,而数据使用堆进行存储,不同的溢出分别是StackOverflow和Outofmemory,不过有些情况下也并不是真的表现为这样,具体情况还的具体分析,说了等于没说。

jdk1.2新增了ref包新增了soft、weak、phantom引用,可以不同程度影响java虚拟机的回收工作。

所有引用类型都是java.lang.ref.Reference类的子类,清除引用类型

1
2
3
Object obj = new Object(); //创建对象
SoftReference<Object> ref = new SoftReference<Object>(obj); //创建软引用关系
obj = null; //需要显示地强引用清除

Reference:

  1. get 拿到引用的实际的对象
  2. clear 清除引用的对象
  3. enqueue 当前的引用队列
  4. isEnqueued 是否在当前的引用队列

SoftReference,java虚拟机在抛出Outofmemory之前会清除素有的软引用

WeakReference,java虚拟机在进行垃圾回收引用的判断时,无视弱引用的存在,解决对象存活时间过长的问题,典型的场景的是HashMap场景,比如前面说的Guava的Cache的WeakHashMap的场景

phantomRefecence,被实际回收是得到一个通知,必须提供一个引用队列,get总是返回null,Object的finalize的方法添加通知后的执行内容,可以覆写finalize方法,finalize方法只会被调用一次。

HotSpot优化的两点:

  1. 字节代码优化,热点的优化
  2. 方法内联,优化方法调用

垃圾回收:分代回收,分别是年轻代、老年代、永久代;

年轻代:伊甸园eden和两个存活区survive space,保证其中一个为空白的存活区,eden复制到空白survive space,非空survive space gc,短存活对象到空白space,长存活对象到老年代,非空变空白,实现交换。

年老代、永久代:标记-清除-压缩 mark sweep compact,其中永久代内存溢出会抛出OutofmemoryError,PermGen space,而堆内存溢出则是Heap space。String 的intern方法的缓存机制会使用大量的永久代内存。==操作会直接使用intern方法,这个native方法会容易造成outofmemory异常导致会存在恶意攻击,在servlet方法上不适用该方法缓存String字符串。

源代码字节码注解

.class
asm
preagent+premain
annotation (元注解、process)

源代码就是作为程序猿一枚的我平常敲的代码,java平台能够运行不同形式的字节代码,其中最常用的就是经过编译的java源码 class文件,还包含其他形式存在的(网上下载,或者内存中的二进制形式的流)。

class文件中的目录树常见的常量池、域、方法名+参数的定义,这种标准的格式为字节码增强提供了基础,比如javasist、asm等。

编译是源代码到运行的必经之路,运行时的编译称之为动态编译,达到运行时动态修改程序,动态编译的对象是源代码,而类加载器达到动态修改程序的行为的对象是字节代码,对字节代码进行修改的行为称之为字节代码增强。

动态编译就是在运行时执行类似javac的功能,java提供了javax.tools.JavaCompile的接口提供动态编译功能。通过该接口可以动态编译各种形式的源文件,包括字符串类型的源代码等。

asm是字节代码增强的给力工具,可以实现读取、创建、修改字节代码的功能,分别对应ClassReader/ClassWriter类。可以完成对DSL的解析执行。

增强代理是字节码增强技术使用的场景之一,通过preagent和premain的执行方式,在程序运行时或执行前对程序进行预处理,字节代码的重新转化可以动态灵活实现代码的转换或者不转换工作。

注解是jdk5之后引入的牛逼功能,绝对的一等公民,注解的元数据包括Target对象、Retention保留策略、Injection继承。可插播的注解处理机制是一项非常重要的功能,通过注解可以实现对源码和字节代码进行处理,可以生成新的源代码和字节代码,lombok和autovalue就是最好的代表,通过这种机制也可以实现国际化的重要工作。

步骤为:

  1. 定义注解
  2. 定义注解的Process类,Process类中使用ProcessEnvironment的getFilter方法得到的Filter的createSourceFile创建JavaFileObject的java源文件实现创建源代码,只需要将openOutputStream输出到outputStream类即可,另外通过asm实现字节代码增强
  3. 通过编译javac -process或者IDE自带的编译生成源码或字节代码
  4. 运行代码查看结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@MessageBundle(name="Messages")
public class DemoClass {
public void output(){
System.out.println(getTestMessage("hello"));
}
@Message(key="1",value="%1$s")
public String getTestMessage(Object... args){
return "";
}
public static void main(String[] args){
DemoClass demoClass = new DemoClass();
demoClass.output();
}
}
public class Messages {
private static ResourceBundle bundle;
static {
bundle = ResourceBundle.getBundle("me.j360.jdk7.i18n");
}
public static String getMessage(String key,Object... args){
String message = bundle.getString(key);
return String.format(message,args);
}
public static String getMessage(Locale locale,String key,Object... args){
String message = bundle.getString(key);
return String.format(locale,key,args);
}
}

类加载器

  • system classloader
  • extension classloader
  • bootstrap classloader (by nativa code)
  • context classloader for spi
  • class.forName
  • 自定义加载器+asm
  • web下的自定义类加载器,Tomcat webAppClassLoader

java平台的3个自带的类加载器,分别是系统加载器,扩展加载器,启动加载器,其中启动加载器是java启动的类加载的入口,非java代码实现,使用原生代码编写。

ClassLoader类经常使用loadClass(类的二进制名称),返回java类的Class对象。类加载器的作用是从字节码中定义出表示Java类的Class的对象,这个由ClassLoader中的defineClass方法实现。

1
2
3
4
5
6
public void loadClass() throws Exception{
ClassLoader current = getClass().getClassLoader();
Class<?> clazz = current.loadClass(java.lang.String);
Object obj = clazz.newInstance();
System.out.pringln(obj.getClass());
}

双亲加载机制:

  1. 通过构造方法在创建时指定类加载器对象的双亲类加载器对象
  2. 自定义类加载器继承ClassLoader,构造方法调用弗雷的构造方法,置顶双亲类加载器
  3. 不指定双亲类加载器,默认双亲类加载器是系统加载器
  4. 默认使用双亲优先策略,双亲类加载器对象尝试加载,找不到再由当前类加载器尝试加载
  5. 默认的双亲优先也可以定义成当前加载器优先

创建类加载器:

  1. loadClass,和ClassLoader类公开方法同名,代理模式生效的地方
  2. findloadedClass,虚拟机记录下已经加载的Java类的初始类加载器
  3. findClass,当前代理模式无法使用双亲类加载器对象成功加载Class类,findClass方法会被调用,异常时ClassNotFoundException
  4. resolveClass,链接一个定义好的Class对象,异常时ClassNotDefException
1
protected Class<?> loadClass(String name, boolean resolve)

类加载器的隔离作用:对象间的赋值作用,可以使用类加载器的这个特性是的程序具备版本的特性,比如Dubbo中的接口的多版本支持。

  1. Class类的对象的全名是否相同
  2. Class类的对象的加载器对象是否相同

Class.forName()也可以完成加载器的作用,不同的是forName同时还会完成 初始化的功能,初始化意味着静态变量会被初始化,同时静态代码块会被执行。具体见对象生命周期。

ClassLoader的加载资源的作用平常使用的很频繁,需要注意的时资源的路径问题。

Web中的类加载器:默认使用当前加载器优先的策略,目的是解决容器中第三方库的冲突问题,通过隔离的方式进行版本的兼容

Tomcat7 org.apache.catalina.loader.WebAppClassLoader

  1. findLoadedClass
  2. loadClass
  3. 代理使用模式开关
  4. findClass查找Web应用本身的java类

Web应用本身java类,Tomcat按照划分不同仓库进行管理:外部仓库+内部仓库(WEB-INF/classes+lib)

对象生命周期

  • create resolver init
  • properties field
  • interface
  • destroy
  • clone,深拷贝浅拷贝
  • serilazable (writeObject,readObject)

链接:验证、准备、解析

初始化:初始化后的对象才能被java虚拟机使用,java类第一次使用会被初始化,初始化分为:

  1. 静态代码块
  2. 初始化静态域

重要 访问java类或者接口的静态域,只有真正声明这个域的类或接口才会被初始化,意味着继承父类并在子类中声明父类的域不会是子类初始化。

类初始化的场景:

  1. 创建实例 new Class()
  2. 调用静态方法 static method()
  3. 给静态域赋值,myClass.myField=10
  4. 访问类或者接口声明的静态域,非常量final +基本数据类型+String,常量在编译时就被固定了。
  5. 顶层的java类执行assert
  6. 通过Class类的反射API

对象的创建和初始化:

  1. 父类和祖先类初始化会往上传递依次执行
  2. 构造方法调用:1调用父类,2按照代码顺序初始化实例域的值,3执行构造方法其他代码

构造方法不能调用被子类覆写的方法因为子类的覆写方法会被父类调用,并且子类的构造方法还未执行。

终止:

如果满足gc条件,也就是没有引用指向一个对象,则可以被销毁

  1. 内存对象,交给虚拟机垃圾回收
  2. 非内存你对象,比如打开的文件,套接字,数据库连接,需要显示地进行释放,java提供finalization终止机制解决非内存资源释放,但是没有发挥应有的作用

finalize方法如果出现异常,则抛出的异常会被忽略,方法被终止,异常不会被记录,可以见gc章节

复制:java通过Cloneable接口提供标准的赋值功能,提供的是浅拷贝的功能。如果是基本数据类型或者不可变对象,浅拷贝即可,否则可能需要基于clone自定义深拷贝。

序列化是内存对象和持久化存储格式对象转化的过程,过程是一个互相转换的双向过程。分别是序列化和反序列化,需要实现接口Serializable,同时不同版本可以通过版本号控制。

默认的java平台提供的对象序列化是ObjectInputStream和ObjectOutputStream,分别通过readObject和writeObject进行序列化工作,非静态和非瞬时域会被记录下来。

完全的控制序列化过程需要继承Externalizable接口。

多线程

  • thread
  • concurrent
  • threadLocal

多线程中的内存模型中的数据竞争是导致多线程异常的根本原因。需要通过线程同步的策略实现多线程的数据共享。

volatile:确保数据的读取是在上一次写入之后的数据,存在hapends-before关系,数据的变更不依赖数据本身的操作可以使用volatile关键字。

final关键字:final被定义成只被初始化一次的情况,通常定以后再构造方法中进行初始化,保证其他线程访问该类的对象是在初始化之后的值。

synchronized关键字:同步代码块,同步方法。只能有一个线程对此进行访问。这里使用虚拟机的监视器加锁释放锁的功能。

Object类的wait,notify,notifyall是配合虚拟机监视器上的功能的3个方法。

Thread:join(),sleep(),yield()

JDK7:fork/join框架、Phaser多线程同步工具,可以替代CountDownLauch和CycleBarry

ThreadLocal:线程局部变量,局部变量中尽可能简单避免引用造成无法回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public class ForJoinsTest {
public static void main(String[] args){
int array[]=new int[100];
Task task=new Task(array,0,100);
ForkJoinPool pool=new ForkJoinPool();
pool.execute(task);
pool.shutdown();
try {
pool.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (task.isCompletedAbnormally()) {
System.out.printf("Main: An exception has ocurred\n");
System.out.printf("Main: %s\n",task.getException());
}
System.out.printf("Main: Result: %d",task.join());
}
static class Task extends RecursiveTask<Integer> {
private int array[];
private int start,end;
public Task(int array[],int start,int end){
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
System.out.printf("Task: Start from %d to %d\n",start,end);
if (end-start<10) {
if ((3 > start) && (3 < end)) {
throw new RuntimeException("This task throws an" +
"Exception: Task from " + start + " to " + end);
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
int mid=(end+start)/2;
Task task1=new Task(array,start,mid);
Task task2=new Task(array,mid,end);
invokeAll(task1, task2);
}
return 0;
}
}
}
public class PhaserTest_1 {
public static void main(String[] args) {
Phaser phaser = new Phaser(5);
for(int i = 0 ; i < 5 ; i++){
Task_01 task_01 = new Task_01(phaser);
Thread thread = new Thread(task_01, "PhaseTest_" + i);
thread.start();
}
}
static class Task_01 implements Runnable{
private final Phaser phaser;
public Task_01(Phaser phaser){
this.phaser = phaser;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行任务完成,等待其他任务执行......");
//等待其他任务执行完成
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + "继续执行任务...");
}
}
}

泛型

  • 泛型、通配符,SuppressWarning(“unchecked”)
  • 反射
  • 覆写、重载
  • 类型推断

泛型是为了更加安全地集合类,之前在运行时必须使用Object作为中间过度对象,容易造成ClassCaseException,运行时的异常代价太高。

泛型:形式类型参数、实际类型参数,泛型类型+泛型方法,使用 表示形式类型参数。

通配符:? ,上界-下界 <? extend X> - <? super x>

形式类型参数:源于类型擦除的原因,不能再具体化时使用,错误:new T(),instance of T

类型推断:编译器根据上下文进行推断:

  1. 方法调用的实际参数推断,优先级高
  2. 调用结果赋值给另外一个变量

安全

  • javax.secret
  • web

javax.secret提供了原生的安全支持框架,包括认证、鉴权的定义,加密机密相关算法支持等,在web中的安全是目前使用最多的地方,通常在web开发中使用shiro或者spring secret来作为安全框架底层支持,这里都会采用和javax.secret相同的认证鉴权的概念来实现安全校验。

jdk8

  • 见jdk8笔记

PS:不定期更新