Java基础
面向对象
面向对象的特征
封装、继承、多态
多态的实现
运行时多态和编译时多态
接口、继承重写、方法重载
运行时多态:动态绑定技术,执行期间判断所引用对象的实际类型。
抽象类和接口
都不能够实例化
-
抽象类可以有构造方法,接口中不能有构造方法。
-
抽象类中可以有普通成员变量,接口中没有普通成员变量
-
抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
-
抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
-
抽象类中可以包含静态方法,接口中不能包含静态方法
-
抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
-
一个类可以实现多个接口,但只能继承一个抽象类。
基础
Java中基本数据类型
有八个,byte、char、short、int、long、float、double、boolean
除了基本类型还有枚举类型
抽象方法是否可以是静态的?是否可以是本地方法?是否可以被synchronize修饰
都不能
如何实现对象克隆
-
实现Cloneable接口并重写clone()
-
实现Serializable接口,通过序列化和反序列化实现克隆,实现的是深拷贝克隆
String s = new String("test")创建了几个字符串对象
至少两个,一个是静态区域的,另一个是new创建在堆上的对象
final关键字修饰的作用
类不能被继承
方法不能被重写
变量不能被更改
常见的异常
ArithmeticException
ClassCastException
IllegalArgumentException
IndexOutOfBoundException
NullPointException
FileNotFoundException
内部类的作用
提供了更好的封装,除了外围类,其他类都不能访问
内部类可以有多个实例
集合
ArrayList、Vector的区别
都是基于数组实现,ArrayList是非同步,Vector是同步的
ArrayList、Vector、LinkedList的实现方式
ArrayList、Vector都是基于数组实现,LinkedList基于双向链表
HashMap和HashTable的区别
HashMap允许键和值为null,而HashTable不允许
HashTable是同步的,而HashMap不是
HashMap和ConcurrentHashMap的区别
-
ConcurrentHashMap对整个桶数组进行了分段,而HashMap没有
-
ConcurrentHashMap在每一个分段上都用锁进行保护,而HashMap没有锁机制
ConcurrentHashMap的实现原理
采用了分段锁,就是把Map分成了N个Segment,put和get的时候,都是根据key.hashCode()算出放到哪个Sagement中
线程
线程实现的方式
-
继承Thread类
-
实现Runnable接口
-
实现Callable接口通过FutureTask包装器来创建Thread线程
-
使用线程池接口ExecutorService结合Callable、Future实现有返回结果的线程
ThreadLocal是什么?和Thread有什么关系?
ThreadLocal用于存储Thread的局部变量,从而达到各个Thread之间的隔离运行。但是使用不好会导致内存泄漏问题。
手动释放ThreadLocal遗留存储?你怎么去设计/实现?
这里主要是强化一下手动remove的思想和必要性,设计思想与连接池类似。
包装其父类remove方法为静态方法,如果是spring项目, 可以借助于bean的声明周期, 在拦截器的afterCompletion阶段进行调用。
弱引用导致内存泄漏,那为什么key不设置为强引用
如果key设置为强引用, 当threadLocal实例释放后, threadLocal=null, 但是threadLocal会有强引用指向threadLocalMap,threadLocalMap.Entry又强引用threadLocal, 这样会导致threadLocal不能正常被GC回收。
弱引用虽然会引起内存泄漏, 但是也有set、get、remove方法操作对null key进行擦除的补救措施, 方案上略胜一筹。
线程执行结束后会不会自动清空Entry的value
事实上,当currentThread执行结束后, threadLocalMap变得不可达从而被回收,Entry等也就都被回收了,但这个环境就要求不对Thread进行复用,但是我们项目中经常会复用线程来提高性能, 所以currentThread一般不会处于终止状态。
常见的线程池
Executors.newCachedThreadPool :创建可缓存的线程池
特点:核心线程数0,工作队列SynchronizedQueue是一个不存储元素的队列,可以理解为里面永远是满的,最终会创建非核心线程来执行任务.非核心线程空闲回收时间为60s因为Integer.MAX_VALUE非常大,可以认为可以无限制的创建,所以在资源有限的情况下,容易造成OOM
Executors:newSingleThreadExecutor:创建一个单线程的线程池
特点:只有一个核心线程,超过核心线程数,会放入队列中,LinkedBlockingQueue是长度为Integer.MAX_VALUE,所以可以认为无界,那往队列里面插入任务,容易造成OOM.因为无界,所以,最大线程数和空闲时间这两个参数是无效的.
Executor.newFixedThreadPool:创建固定长度的线程池
特点:固定的核心线程池由用户传入.这个和SingleThreadExecutor相似,只是核心线程数不同,所以,在大量线程时容易造成OOM
为什么不推荐Executors直接创建线程池,而用ThreadPoolExecutor
-
缓存队列 LinkedBlockingQueue 没有设置固定容量大小
-
最大线程数量是 Integer.MAX_VALUE
-
不支持自定义拒绝策略
-
创建线程 或 线程池时请指定有意义的线程名称,方便出错时回溯(这个不是重点)
sleep()、join()、yeild()有什么区别
sleep()不考虑其他线程运行机会的优先级,yeild()会给同级别或者更高
执行sleep后会进入阻塞状态,执行yeild后会进入就绪状态
sleep有InterruptedException异常,yeild没有任何异常
join() 方法会使当前线程等待调用 join() 方法的线程结束后才能继续执行
sleep()和wait()有什么区别
sleep()是让出cpu,而wait()是放弃当前持有的锁
wait()与notify()/notifyAll()在放弃对象监视器上有什么区别
wait()方法是立即释放锁,notify()/notifyAll()是等待线程执行完成后释放锁
线程有哪几种锁
-
自旋锁
-
偏向锁:偏向于第一个获取他的线程,如果该锁没有被其他线程获取,那么持有偏向锁的线程不需要再进行同步。
-
轻量级锁