泛型
泛型就是一个参数化模板,具体的实际类型需要使用的时候才能确定。
泛型的优点,不需要进行类型强制,代码编译的就可以检查错误。
泛型具体的实现有2类
- code special 为每一个实际参数都生成一份代码,如List和List会生成2份代码,这样效率会高很多而且支持基本数据类型的泛型,但是会有很多代码冗余
- code share 不会为实际参数生成一份代码,而且通过类型擦除实现泛型来复用代码,所有类型都会在编译的时候存放的object,拿数据的时候会进行一个数据校验,缺点是实现对象泛型,不能实现基本数据的泛型化
List<,Object> 和List<,?>的区别在于,List<,?> 可以指向任何一个泛型对象,但是不能放元素,而List<,Object>就是表示实际类型是Object
<T, extend Number> 泛型的下界通配符,T是 Number的子类,所以没法确认放入数据的具体类型不允许放数据,但是可以去get()拿数据 <T, super Number> 泛型的下界通配符,T是 Number的父类,可以Number的对象及其子类对象,但是往外拿数据只能用Object
集合类
ArrayList
ArrayList的内部实现是基于数组的实现的,所以它有数组的特点
- 支持随机访问,
- 尾部操作时间复杂度O1
- 插入删除操作ON而且需要移动元素
- 支持动态扩容,当内部数组元素大小不够用的时候,会进行一次扩容每次扩容大小为之前的1.5倍
LinkedList
LinkedList是基于链表实现的,所以它有链表的特点
- 随机访问时间复杂度ON
- 头尾2端操作时间复杂度O1
- 插入删除操作ON但是不需要移动元素
- linkedList同时实现了Queue和Deque接口,可以用作栈,队列和双端队列
vector
vector是一个线程的安全的list,它内部的方法都被synchronized的关键字修饰
- 特点和ArrayList差不多
- 每次扩容大小为之前的2倍
stack
stack是继承自vector,同时实现了一些栈的方法
HashMap 1.7
hashMap是一个经典的容器,基于hash实现的O1时间复杂度内查询的数据结构, 内部包含这几个重要的参数
- size 元素个数
- capacity entry数组的大小,默认是16,每次扩容也是2倍扩容这样做的好处就是,用(capacity-1)&hash操作来代替直接求余,第二个就是每次扩容的失败不用重新计算hashcode直接用与操作来判断当前节点是否是需要移动,不用重新计算hash
- Entry数组 实际存放数据的数组
- loadFactor 负载因子=usedBucket/totalBucket 如果loadfactor=1表示所有的所有的entry都满了,但是这个时候肯定是有hash冲突的导致查询率低下,loadfactor<1表示当前空间的未满,查询效率较高,但是空间利用率低。loadfactor默认是0.75,一个经典的比值
HashMap 1.8
1.8的实现和1.7具体实现是一致的。但是还有几个不同点
- 1.7的hash扰动函数是多次异或移位,1.8是高16位和低16位进行异或
- 1.7的hash冲突是用拉链法来解决,1.8也是使用拉链法,但是在链表长度>=8则转换为红黑树,长度小于6则转换为链表,用来加快查询,至于为什么是8,是因为链表的个数符合一个概率分布,长度=8是一个小概率事件,如果发生了,那么很有可能是key的hash算法实现很糟糕或者遭到了hashDoc攻击。小于6进行转换链表是为了避免频繁的转换。
- 1.7节点插入是头插法,容易出现死锁,1.8改用了尾插法避免了这个问题
- 1.7是先扩容后插入,1.8由于引入了红黑树,如果是红黑树进行扩容的话很有可能导致树的退化,变成一个链表插入,同时链表的插入又可能导致链表变化成红黑树,所以未避免这种情况同时降低代码的耦合性,把扩容操作统一移到后置处理
hashTable
hashtable也是一个线程安全的容器,它的内部方法都被synchronized关键字修饰
- hashcode没有进行扰动直接进行使用
- 不允许null key和val
- 每次扩容为2倍+1
- 初始化大小为11
Set 没啥好讲的,就是使用组合设计模式,内部包含了相应的map对象。
IO流
io流使用的是装饰者设计模式,持有目标类后对目标类进行一个功能加强
举个经典的例子
FileInpuStream fi=new FileInputStram("fileName"); 为了读取我们的数据,我们一般使用一个byte数组来多次读取,这样的操作是十分耗时的,我们希望有个Buff缓存区,把数据读到缓存区然后从缓存区读数据的功能
BuffedInputStram bi=new BuffedInputStream(fi) 来加强fi。
装饰者和代理模式很像,但是代理模式的只能增强一次,而装饰者模式可以进行多层包装获取不同的功能,
反射
java中一切都是对象,包括我们的class类,也个Class对象。该对象包含了类的原数据(属性和方法),只要我们能获取到这个对象,就能在的时候新建一个对象或者改变类的行为。很多框架都是基于反射实现的。
动态代理
jdk动态代理
User proxyInstance = (User) Proxy.newProxyInstance(test.class.getClassLoader(),
new Class[]{User.class},
new MyInvoctionHanlder(new XM("11")));
- jdk动态代理是基于接口实现的,proxy对象持有目标类并且通过持有一个InvocationHandler,
- jdk动态代理调用目标类的方法,是method.invoke反射实现的调用的 CGLIB动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(XM.class);
enhancer.setCallback(new MyMethondIncepter());
return (User) enhancer.create();
- cglib动态代理是基于继承实现的代理
- cglib动态代理调用目标类的方法,是通过一种叫fastClass(建立映射关系)来直接调用的,不走反射
网络编程
Server端
- bind()绑定端口
- listen()监听端口
- accept()获取连接 client端
- socket()操作系统随机找一个端口。
- connect()连接server端
BIO
同步阻塞式 accept()方法是阻塞的,每次有连接的时候都会返回一个socket,一般会创建线程去处理这个socket,而每个socket.read()方法是阻塞的。
NIO
NIO是同步非阻塞,这个非阻塞是指selector.select()方法会返回相应的事件,我根据事件进行一个操作,比如创建线程去读socket的数据等,这个时候的线程都是出于工作中不是阻塞中。