java 常见高级开发面试题 非算法等特定岗 一
基础
1.List和Set区别
List:1.可以允许重复的对象。
2.可以插入多个null元素。
3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
Set:1.不允许重复对象
2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
3. 只允许一个 null 元素
4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
2.HashSet如何保证不重复
实际hashSet是使用HashMap的key来进行元素存储
首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;
如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。
3.HashMap是线程安全的吗,为什么不是线程安全的
HashMap没有考虑线程安全,源码全程没有锁等的操作
比如put数据覆盖,同时扩容,链表死循环等
4.HashMap扩容过程(数组、链表、链表大于8转红黑树)
1.hash数组大于MAX不在扩充数组大小
2.数组扩充2倍,创建新数组
3.遍历原数组
不存在链表 直接hash存储
原位置是红黑树,分解红黑树,分解后节点小于6,讲解为链表,大于6继续使用红黑树
原位置是链表 直接转移
期间java8 算法 使用hash和原容量进行按位与计算,0不移动,1移动 +原索引大小 优化后的算法
java8 链表转移不会倒置元素顺序 java7会倒置
5.HashMap 1.7 1.8区别,1.8的优化,如何优化的
最大不同1.8 基本结构改为数组+链表+红黑树
java7 HashMap 查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为O(n)
Java8 中,当链表中的元素达到了 8 个时,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为O(logN)
Java7 是先扩容后插入新值的,Java8 先插值再扩容,不过这个不重要
java7扩容时链表拆解重新插入会元素倒置,java8不会
6.final finally finalize
1、final修饰符(关键字)。被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。
将变量或方法声明为final,可以保证他们在使用的过程中不被修改。
被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,不能重载
2、finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。
try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。
finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。
3、finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。
子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
用的较少
7.强引用,软引用,弱引用,虚引用
强引用:
只要引用存在,垃圾回收器永远不会回收
Object obj = new Object();
//可直接通过obj取得对应的对象 如obj.equels(new Object());
而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。
软引用:
软引用有对应的实体列为SoftReference,使用软引用引用的对象只有在程序发生oom异常前才会回收,
也就是说如果内存充足永远不会被回收,只有在内存不足时才会回收,很好的避免oom,非常适合做缓存。
软引用可用来实现内存敏感的高速缓存
弱引用:
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。
在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
虚引用
虚引用顾名思义就是形同虚设,虚引用也称为幻影引用:
一个对象是都有虚引用的存在都不会对生存时间都构成影响,也无法通过虚引用来获取对一个对象的真实引用。
唯一的用处:能在对象被GC时收到系统通知,JAVA中用PhantomReference来实现虚引用。
8.java反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
反射就是把java类中的各种成分映射成一个个的Java对象
类加载器加载类时每个类会生成一个Class对象,通过这个对象我们能知道类的详细信息包括对应的方法、成员以及构造方法的声明和定义等,
并能利用class,Constructor,Field,Method4个类完成单独的某个类某属性的获取或调用
9.Arrays.sort实现原理和Collections.sort实现原理
Arrays.sort 基本类型排序使用DualPivotQuicksort排序类进行排序 该类根据数组大小自动选择不同的排序算法 使用优化的快速排序
Arrays.sort 带有comparator排序方案 实际使用TimSort.sort进行排序 使用优化后的归并排序
Collections.sort 实际也是调用Arrays.sort进行排序
10.LinkedHashMap的应用
其实现原理和HashMap基本一样,基本Node其维护了after befor,使用的是双向链表,主要作用是保证链表的顺序 按插入顺序或访问顺序
内部结果存 取一样 LinkedHashMap把所有存入的元素按双向链表追加到最后来保证遍历顺序
11.cloneable接口实现原理
实现了Cloneable接口的类跟一个没实现该接口的类有啥区别呢?
从JVM的角度看,这就是一个标记接口而已。实现了就是打上cloneable标记,没实现就是没这个标记。
然后到clone()的基本实现中,JVM会去检测要clone的对象的类有没有被打上这个标记,有就让clone,没有就抛异常。就这么简单。
1)Cloneable接口里面没有任何方法,Cloneable接口只是起一个标记作用,表明当一个类实现了Cloneable接口时,该类有可能通过调用Object类的clone()方法来克隆类的实例
2)仅仅实现了Cloneable接口是不够的,Object类的clone方法是Protected,所以你必须覆盖Object里面的clone()方法才能让其它的类可以使用该类的clone方法
拷贝
1、基本类型
如果变量是基本类型,则拷贝其值,比如:int、float、long等。
2、String字符串
这个比较特殊,拷贝的是地址,是个引用,但是在修改的时候,它会从字符串池(String Pool)中重新生成新的字符串,
原有的字符串对象保持不变,此处可以认为String是个基本类型。
3、对象
如果变量时一个实例对象,则拷贝地址引用,也就是说此时新拷贝出的对象与原有对象共享该实例变量,
不受访问权限的限制。这在Java中很疯狂,因为它突破了访问权限的定义,一个private修饰的变量,竟然可以被两个实例对象访问。
深拷贝
即不但拷贝引用同时拷贝堆中实际内容
1.简单深拷贝
在clone方法中重新为每个对象进行赋新的实例即重新初始化避免和现有对象同引用,但有缺陷需要每个类单独实现不同的clone方法,繁琐
2.进行序列化,序列化对象再反序列化 简方便也能序列化的字节进行远程传输实现异地拷贝
12.异常分类以及处理机制
–>Throwable是 Java 语言中所有错误或异常的超类。下一层分为Error和Exception
–>Error类是指java运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止。
–>Exception又有两个分支,一个是运行时异常RuntimeException,如:NullPointerException、ClassCastException;一个是检查异常CheckedException,如I/O错误导致的IOException、SQLException。
–>RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。派生RuntimeException的异常一般包含几个方面:
1、错误的类型转换
2、数组访问越界
3、访问空指针
如果出现RuntimeException,那么一定是程序员的错误
RuntimeException不会有提示往往我们的代码中不会有throws try catch等异常处理,比如空指针异常
–>检查异常CheckedException一般是外部错误,这种异常都发生在编译阶段,Java编译器会强制程序去捕获此类异常,
即会出现要求你把这段可能出现异常的程序进行try catch
该类异常一般包括几个方面:
1、试图在文件尾部读取数据
2、试图打开一个错误格式的URL
3、试图根据给定的字符串查找class对象,而这个字符串表示的类并不存在
CheckedException编辑异常,即写代码时编译器就强制让我们加异常处理代码try catch等
处理方式
1、遇到问题不进行具体处理,而是继续抛给调用者
抛出异常有三种形式,一是throw,一个throws,还有一种系统自动抛异常。
throw,throws 简单
自动抛异常 没有任何处理和提示 比如除以0的错误这样的
2、针对性处理方式:捕获异常
try catch finally
13.wait sleep区别
1.这两个方法来自不同的类分别是Thread和Object
2.最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法(锁代码块和方法锁)。
3.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)
4.sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
5.sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,
自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,
一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。
但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,
如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,
那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
注意sleep()方法是一个静态方法,也就是说他只对当前对象有效,通过t.sleep()让t对象进入sleep,这样的做法是错误的,
它只会是使当前线程被sleep 而不是t线程
wait属于Object的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;
如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会释放它持有的所有同步资源,
而不限于这个被调用了wait()方法的对象。wait()方法也同样会在wait的过程中有可能被其他对象调用interrupt()方法而产生
14.数组在内存中如何分配
数组的静态和动态初始化 实际没什么区别数组大小一个是系统根据元素个数自动计算一个是我们指定的大小,数组内容一个是我们写好的,一个是系统使用默认值进行
内存分配 数组实际是引用类型,在栈中存放数组的引用也是数组的首地址,后在堆中分配相连的内存地址存放数组内容