华为Java工程师面经相关问题个人见解
- 简单说说面向对象的特征以及六大原则
- 谈谈final、finally、finalize的区别
- Java中==、equals与hashCode的区别和联系
- 谈谈Java容器ArrayList、LinkedList、HashMap、HashSet的理解,以及应用场景
- 谈谈线程的基本状态,其中的wait() sleep() yield()方法的区别。
- JVM性能调优的监控工具了解那些?
- 简单谈谈JVM内存模型,以及volatile关键字
- 垃圾收集器与内存分配策略
- 垃圾收集算法
- MySQL几种常用的存储引擎区别
- 数据库的隔离级别
个人见解:
面向对象三个基本特征:
- 封装:把客观的事务根据其一些共同特点,封装一个“类”别,并且类可以把自己的数据和方法让可信的对象操作,对不可信的对象隐藏,提高重用性和安全性。
- 继承:无需改变某类的原有内部数据,而通过继承子类的方式对功能进行扩展。
- 多态:同一个接口使用不同的实现,是基于继承、重写的基础上,通过父类的引用指向子类的实现。
六大原则:
- 单一职责,一个类只负责单一的封装
- 开放封闭原则,功能升级尽可能扩展子类的方式,不可修改原类
- 里氏替换原则,使用基类模块无需修改,子类型必能替换掉基类(实际和上面的开放封闭原则是一回事)
- 依赖倒置原则,依赖抽象、接口等,避免依赖于细节
- 接口隔离原则,单一职责用于接口设计,客户程序发生变化,影响到其他程序的可能性小
- 良性依赖原则,(说实话我对着文绉绉的术语没多少兴趣,不清出怎么个具体解释法)
final
- 修饰类,该类不能被继承,没有子类,final类中的方法默认是final的
- 修饰方法,该方法不能被覆盖,但可以被子类继承
- 修饰成员变量,只能赋值一次,且值不会改变
finally
异常处理的一个关键字,try...catch...finally
finalize
方法名,Object类的方法,finalize()方法在垃圾收集器将对象从内存清理出去之前必要的清理工作。
==
- 若操作数的类型是基本数据类型,则该关系操作符判断的是左右两边操作数的值是否相等
- 若操作数的类型是引用数据类型,则该关系操作符判断的是左右两边操作数的内存地址是否相同。
equals
Object 的 实例方法,比较两个对象的content是否相同
hashCode
Object 的 native方法 , 获取对象的哈希值,用于确定该对象在哈希表中的索引位置
ArrayList
继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。新增、删除元素时ArrayList需要使用到拷贝原数组
LinkedList
实现List接口与Deque接口双向链表,实现了列表的所有操作,并且允许包括null值的所有元素。LinkedList内部实现是双向链表结构。新增、删除元素时LinkedList只需移动指针,查找元素 ArrayList支持随机元素访问,而LinkedList只能一个节点的去遍历
HashMap
基于哈希表的 Map 接口的实现,以 Key-Value 的形式存在,即存储的对象是 Entry (同时包含了 Key 和 Value) 。根据hash算法来计算key-value的存储位置并进行快速存取,最多只允许一条Entry的键为Null(多条会覆盖),但允许多条Entry的值为Null。
HashSet
HashSet 而言,其采用 Hash 算法决定元素在Set中的存储位置,这样可以保证元素的快速存取。HashSet 底层采用 HashMap 来保存所有元素
线程
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。
一种是继承Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用很少用)
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。
基本状态以下几种:
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
sleep(long millis) 指定毫秒内当前线程休眠
join() 主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到子线程的join()方法了。
yield() 暂停当前正在执行的线程对象,并执行其他线程。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
wait() 必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。
常见的JVM性能调优工具
- jps java virtual machine process status tool 用来输出jvm中运行的进程状态信息 jps [参数] [ip]
- jstack jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:jstack [参数] pid/executable core jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码
- jmap memary map用来查看堆内存使用情况 jmap [option] pid jmap -heap pid查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况。
- jhat 用jmap把进程内存使用情况dump到文件中,再用jhat分析查看。 jmap -dump:format=b,file=dumpFileName pid
- hprof hprof能够展现CPU使用率,统计堆内存使用情况。java -agentlib:hprof[=options] ToBeProfiledClass
Java内存模型:
垃圾收集器:
下图展示了7种作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,还有用于回收整个Java堆的G1收集器。
不同收集器之间的连线表示它们可以搭配使用。