Java
1. java语言有什么特性,继承有什么用处,多态有什么用处
答:java有八大特性:
- 面向对象语言,具备抽象、封装、继承、多态特性。
- 平台无关性,jvm屏蔽了操作系统差异,使得java语言通过字节码在不同操作系统的jvm上被差异化解释,实现java语言的平台无关性。
- 解释型语言,不同于C++、C语言,编译生成针对CPU的机器码,java语言编译生成字节码,会按句翻译执行。在jit即使编译技术加持下,可以将字节码转化成高效的本地机器码,提高执行效率。
- 多线程,java内置多线程支持,比C++语言更加方便,不需要调用操作系统多线程功能接口即可完成程序设计。
- 安全,java小程序运行在java环境中,不允许它访问计算机的其他部分。
- 动态编译,由于java类来自于开发者编写、类库引入,类是动态加载的,可以在分布式环境中动态地维护程序和类库,不用像C++,每次类库升级需要重新编译整个程序。 继承的作用:很方便地复用代码+不破坏现有代码的结构和功能。多态的用处:分离做什么和怎么做,进一步分离接口和实现。不仅可以改善代码组织结构和可读性,而且可扩展性也很强。
2. 反射是什么,在哪里用到,怎么利用反射创建一个对象
定义:在运行期间打开和检查java类,并提供获取方法、构造器和字段的接口的类库,在跨网络远程调用、基于RAD可视化编程中用到;
反射获取对象:根据class名称获取类字节码对象,通过newInstance()调用无参构造器;getConstructor获取有参构造器,并完成对象构造。
1. Class<?> cls = Class.forName("class.name");
Object obj = cls.newInstance();
2. Class<?> cls = Class.forName("class.name");
Construct<?> con = cls.getConstructor(String.class,String.class);
Object obj = con.newInstance(str1,str2);
3. 对象加载的过程,属性先加载还是方法先加载
1. 规律
- 静态属性和代码块,跨类高优先级;
- 非静态属性和代码块同类中优先级高于构造函数;
- 静态和非静态的属性和代码块的加载顺序,同对应的定义顺序。
2. 结果
- 父类静态属性 (可以是对象) 和静态代码块,看其在类中的先后顺序
- 子类静态属性和静态代码块 ,看其在类中的先后顺序
- 父类非静态属性和非静态代码块 ,看其在类中的先后顺序
- 父类构造方法
- 子类非静态属性和非静态代码块 ,看其在类中的先后顺序
- 子类构造方法
4. 垃圾回收机制与jvm结构
类加载: java虚拟机是由启动类加载器加载的,负责执行java程序;java类加载器的结构:启动类加载器-->扩展类加载器-->应用程序类加载器-->用户自动定义加载器,满足双亲委派原则,保护java核心库类不被轻易篡改、避免类重复加载。
java类实例相同的判断:1.相同的类路径 2.同一个类加载器(避免不同虚拟机加载同一个class文件的情况)
内存结构:
本地方法栈
1. 线程私有
2. 存储的是本地方法的栈帧
程序计数器
1. 线程私有
2. 当前线程所执行的字节码的指示器,
3. Java方法:存储的字节码指令地址;native方法,计数器值为空。
4. 唯一没有oom异常的数据区,因为只需要存储下一条指令地址,不会存在空间扩张。
方法区
1. 线程共享
2. 存储加载的类信息、常量池、静态变量
线程栈
1. 线程私有
2. 保存方法执行的栈帧
3. 栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址
堆
1. 线程共享
2. 存储对象实例
3. 物理空间可以不连续
4. 堆中的代划分
可达性分析:由GC root(线程栈中的对象、方法区的静态对象和常量对象、本地方法区对象)开始,寻找所持有的引用节点,不可达的对象需要回收。
拷贝复制:新生代的minor GC采用本方式,对象中存活率低,只需要进行少量对象复制即可完成垃圾回收;缺点是需要牺牲一部分存储开销。优点是:效率高,且没有空间间隙产生。
标记删除/整理:老年代FullGC采用此方法,持续存活的对象,采用复制方式成本很高。
Eden满了,会进入survivor区进行迭代(复制算法),达到迭代的阈值没销毁的对象,会进入老年区,老年区无法放入,进行FullGC(标记—整理算法)。永久代放置静态变量和常量、类信息等。
数据结构
hashMap
1. 结构
数组+链表组成,数组中存储Node节点,ArrayList,Node节点中存储hash、key、Value、Node成员。
2. 扩容规则
- Capacity:HashMap长度。
- LoadFactor:负载因子,默认0.75f,控制数据密度或作为扩容阈值。 当数据总量达到负载因子密度时,则需要进行扩容。
3. 链表插入方式:尾插法,保证转换为红黑树时,不会出现循环指针;hash规则,(length-1)& key;
4. 红黑树:引入原因,hash冲突过多时,链表查询效率过低;不用二叉树,不需要过多的平衡,红黑树最多三次自旋调节。
5. 链表和红黑树转换:- TREEIFY_THRESHOLD8 桶中数据数量 MIN_TREEIFY_CAPACITY64元素总量,都超过阈值时,进行数据结构升级;同时,当节点个数小于UNTREEIFY_THRESHOLD6,降级为链表
TreeMap
1.结构
private transient Entry<K,V> root,实际是红黑树。
2. 和hashMap比较区别
- 数据存储是有序的
- 不允许有null值的key
- 因为hashMap底层是Array,添加、查找、删除方法上效率更高,treeMap更慢
- 不需要额外的数组开销,空间上更省
- hashMap存在hash冲突,但是java 8后支持treeNode转换效率有很大提升。
- treeMap添加节点和删除节点会有自旋调整,对性能有一定影响。
3. 和hashMap的共同点
- 都不允许key重复
- 两者都是线程不安全的
多线程
1. 多线程安全保障机制
- 使用线程安全的类
- 使用synchronized锁、Lock锁
- 多线程并发情况下,线程共享的变量改为方法局部级变量。
2. 线程池
拒绝策略
-
AbortPolicy(抛出异常,default)
-
DiscardPolicy(丢弃任务)
-
DiscardOldestPolicy(Queue队列出队一个,将当前这个任务继续提交给线程池)
-
CallerRunsPolicy(交给线程池调用所在的线程进行处理)
runnable执行异常处理
- try-catch捕获异常
- submit执行任务,从返回的Future对象,调用get方法捕获异常
- 重新poolexcutor.afterExcue方法,处理传递的异常引用
- 线程工厂中,对线程设置
1. newCachedThreadPool (并发执行大量短期的小任务)
可缓存线程池:不固定线程数量,且支持最大为Integer.MAX_VALUE的线程数量。
ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>())
- 线程数无限制。
- 有空闲线程则复用空闲线程,若无空闲线程则新建线程。
- 一定程度减少频繁创建/销毁线程,减少系统开销。
2. newFixedThreadPool (CPU密集型的耗时任务)
定长线程池:一个固定线程数量的线程池。
ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
- 可控制线程最大并发数(同时执行的线程数),核心线程数和最大线程数相等。
- 配置无界阻塞队列,超出的线程会在队列中等待。
3. newSingleThreadExecutor (串行执行任务)
单线程的线程池:一个固定线程数量的线程池。
new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()))
- 有且仅有一个工作线程执行任务。
- 所有任务按照指定顺序执行,即FIFO规则。
- FinalizableDelegatedExecutorService类实现了finalize方法,在JVM垃圾回收的时候会关闭线程池。
4. newScheduledThreadPool (周期性执行任务且限制线程数量)
定时任务的线程池:定时周期性执行任务。
ScheduledThreadPoolExecutor(corePoolSize);