泛型:
为什么要引入泛型:
泛型提供编译的时候进行类型的检测(compile-time type checking),防止发生ClassCastException 异常的发生,是Collection常见的异常
泛型增加代码的鲁棒性,它可以在编译的时候发现bug
泛型的类型:
通配符上限
List<? extends Number>,Integer,Double等子类型都是合法的。
通配符下限
List<? super Integer> ,Number ,Object,等父类型都是合法的
无界通配符下限
LIst<?>list 接收任何类型,等价于<? extends Object>
fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。
fail-fast会在以下两种情况下抛出ConcurrentModificationException
fail-fast机制是如何检测的?
迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个标记 “mode” ,当集合结构改变(添加删除或者修改),标记"mode"会被修改,而迭代器每次的hasNext()和next()方法都会检查该"mode"是否被改变,当检测到被修改时,抛出Concurrent Modification Exception
fail-safe机制
fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException
fail-safe机制有两个问题
(1)需要复制集合,产生大量的无效对象,开销大
(2)无法保证读取的数据是目前原始数据结构中的数据。
HashMap
HashMap基于hash表的,继承于AbstractMap,实现Map 接口
HashMap 允许null key 和null value
HashMap 是无序的
HashMap除了非同步和允许null key/value,它和HashTable 一样
HashMap使用链表实现Map(entry)的存储,叫做bucket或者bin。默认箱空间是16 ,权值是2
HashMap是非线程安全,对于多线程环境应该ConcurrentHashMap方法
Lock 和Synchronized block区别
- 同步锁完全包含在一个方法中,而锁通过lock() 和unlock()两个独立的方法
- 同步锁不能保证公平性,当一个线程释放掉锁后,谁都可以取得,无法估计等待最久的线程等情况。
- 同步锁在在无法获得资源的时候会进入阻塞状态,而锁提供了tryLock方法,减少不必要的等待
- 同步锁的线程进入阻塞后无法停止, 而锁提供了lockInterruptibly方法,强制结束当前线程
Lock 锁实现
- ReentrantLock
这是最常用的Lock接口实现类 ,它和Synchronized类似,但提供很多的工具方法,例如持有锁和等待锁
重入锁天生就有重入属性
如果一个线程通过一个同步锁锁住一个监视器对象,而另一个同步锁需要锁在同一个监视器对象上那么该线程也能操作该同步锁
2、ReentrantReadWriteLock
它包括只读锁和写锁组成,只对锁可以被几个线程拥有,而写锁只能被一个线程拥有
-
- StampedLock
Java8引入,和ReadWriteLock 类似,增加stamp标记
-
- condition
条件对象由锁对象创建
抽象类和接口的区别
Java8
Java 8在接口中可以使用default定义的方法
防御方法或者虚拟扩展方法
- 不一定要复写默认方法
- 如果类要实现两个拥有同名的默认方法的接口,那么该默认方法必须重写
3、默认方法使得接口和抽象类的区别更加微妙
4、共同的工具方法可以定义为默认方法
5、避免基础实现类
6、默认方法不能复写Object的方法
静态方法
- 静态方法和默认方法一样,除了静态方法 不能被复写
- 接口静态方法只对接口可见,即不能再接口实现类对象上调用
- 更适合做工具方法
函数式接口
如果一个接口只有一个抽象方法,那么它叫函数式接口
Lamada表达式
- 不能有函数名,代表匿名类
- 代表函数式接口实现
- 返回值类型由编译器推测
- 不能出现泛型,泛型定义在函数式接口中
- 表达式内变量必须是final,不能在表达式内修改,即表达式内的变量必须为final
- 没有自己的作用域,this或super指向lamada表达式的外部
- 表达式内部可以使用break,continue,return,throw等
函数接口(Function<T,R>):代表一个函数接受一个参数,返回一个结果
多线程:
1、线程的生命周期:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5中状态。
2、当线程对象调用了start()方法之后,该线程处于就绪状态,Java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,只是表示该线程可以运行了。至于该线程何时开始运行,取决于JVM里线程调度器的调度。
3、启动线程使用start()方法,而不是run()方法。
4、只能对处于新建状态的线程调用start(),否则将引发IllegalThreadStateException异常。
5、所有现代的桌面和服务器操作系统都采用抢占式调度策略,只有当一个线程调用了它的sleep()方法或yield()方法后才会放弃所占用的资源。
6、线程只能从阻塞进入就绪状态,无法进入运行状态。
7、线程会以如下三种方式结束,结束后就处于死亡状态
(1)run()方法执行完成以后,线程正常结束,
(2)线程抛出一个未捕获的异常或者error
(3)直接掉用该线程的stop()方法来结束该线程,但是很容易导致死锁,通常不推荐使用。
8、isAlive()方法测试某个线程是否已经死亡。
四种引用类型的概念
1、强引用 StrongReference
垃圾回收器不会回收,当内存不够的情况抛出OOM错误,使得程序异常终止。
2、软引用SoftReference
垃圾回收器内存充足的时候不会回收,当内存不够的时候,会进行回收。软引用回收之后,虚拟机会将这个软引用加入到与之关联的引用队列之中。软引用非常适合于创建缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。
- WeakReference
在垃圾回收器扫描到改对象的时候就会进行垃圾的回收,弱引用回收之后,虚拟机会将这个弱引用回收到与之关联的弱引用队列之中。
- 虚引用PhantomReference
虚引用不决定生命周期,如果一个对象有虚引用,则它跟没有任何引用一样,任何时候都有可能会被回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动,与软引用和弱引用不同的是,虚引用必须关联一个引用队列。
ConcurrentHashMap和Hashtable的区别
Hashtable的大小增加到一定程度的时候,性能会急剧下降,因为迭代的时候需要被锁定很长的时间,因为ConcurrentHashMap引入了分割,不论它变得多么大,仅仅锁定map的某个部分,而其他的线程不需要等到迭代等待完成才能访问map,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable会锁定整个map
HashMap和Hashtable的区别
两者的主要区别在于线程安全性,同步以及速度。
- HashMap几乎等价于Hashtable,除了HashMap不是同步的,它可以接受null键和null值,而Hashtable不可以。
- HashMap是非synchronized,而Hashtable是synchronized,意味着多个线程可以同时操作一个Hashtable,
- 另一个区别是HashMap的迭代器是fail-fast,而Hashtable 的迭代器不是,当有线程改变了HashMap得结构,将会抛出并发修改异常,
- 由于Hashtable是线程安全的,所在在单线程得环境中他比HashMap要慢,
- HashMap不能保证随着时间的推移Map 中得元素次序是不变的。
为什么Java中具有包装类型?
Java是面向对象的语言,基本类型不具有对象的性质,为了让基本类型具有对象的性质,就出现了包装类型,它相当于把基本类型 包装起来,使得它具有了对象的性质。,并为其增加了属性和方法,丰富了基本类型的操作。
向ArrayList、HashMap中放东西的时候,像int,double这种基本类型是放不进去的,因为容器都是装对象的,就需要基本类型的包装类了。
Java程序中初始化的顺序:
父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。
反射机制提供的功能:
得到一个对象所属的类,获取一个类的所有成员变量和方法;在运行时创建对象,在运行时候调用对象的方法。
Servlet 与其他动态页面技术相比有啥优点:
- 较好的可移植性
- 执行效率高 CGI针对每个请求都会创建一个进程来处理,而servlet针对每个请求创建一个线程来执行,创建线程比创建进程开销小,因此与CGI相比,Servlet在交互的中有更短的响应时间,响应效率更高。
- 功能强大,Servlet可以与Web服务器进行交互,而CGI却无法与web服务器直接交互。
- 使用方便:Servlet 提供了许多非常有用的接口以来读取或设置HTTP头信息,处理Cookie和跟踪会话状态。