重学JAVA--day02

140 阅读5分钟

泛型

泛型就是一个参数化模板,具体的实际类型需要使用的时候才能确定。
泛型的优点,不需要进行类型强制,代码编译的就可以检查错误。 泛型具体的实现有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的数据等,这个时候的线程都是出于工作中不是阻塞中。