面试心得与总结-——答案整理_2 持续更新

11. HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
1:hashMap与是采用链式hash表实现。也就是HashMap表里面有一个Entry[]数组,当一个键值对加入该数据的时候,会通过key的hashcode去计算存在那一个数组位置,当通过不同的hashCode计算出出现相同的entry数组位置的时候,则会在该位置以链表的形式存储。
2:hashMap存在一个问题,就是会当hash表的容量超过设定的容量的时候,就会出现rehash,此时会创建一个新的entry数组,然后将原来的entry[]数组中的数据复制到新的数组中,但是复制的时候就会重新根据key的hashCode来计算存入位置,数据量大的时候比较耗费资源,所以我们在能预估hashMap大小的是时候尽量在初始化的时候给适当的值。(hashMap每次扩容为原来的两倍)
3:hash表解决冲突的做法:1:保证每一扩容都为2的n次方(int capacity = 1;  while (capacity < initialCapacity)  capacity <<= 1;  ),从这儿我们可以看出,我们不是我们给的初值是多少,map的容量就是多少,而已2的n次方,然后跟我们给的初值很相近(具体原因可以根据数学上的计算的来的)。2:负载因子取一个平衡值,负载因子也是影响冲突的一个因素,当负载因子越大,则数组利用率越高,但是对应查询的时间复杂度增加(因为变密集了吗),当负载在因子越小,则越稀疏,则越不容易冲突。
4:hashMap初始容量为16,负载因子默认为0.75(平衡值);扩容阈值=原容量*负载因子。16*0.75=12,也就是存入了12个就扩容,扩容以后是2*16=32;
5:ConcurrentHashMap采用分段锁的概念,将一个数组分成一个个小的HashTable,因此就是提高类并发编程类Map的效率。线程只是对一个段里面HashTable加锁,而不是整个map对象加锁。
6:具体的做法就是有一个segmetn[]数组,该数组中segment对象就相当于一个HashMap,当一个键值对put进来的时候,首先通过key值计算该对象存在哪一个segment中,当定到哪一个段以后,再在段中计算hash值,然后存入hashEntry[]数组中的对应位置,接下来就和hashMap差不多了。这样的话我们只要锁定一个段就可以了,而不用锁定整个segment数组,如过一个线程操作其中的一个段,另个一线操作的另一个段,那么他们就不存在竞争的现象。


12. TreeMap、HashMap、LindedHashMap的区别。

1:TreeMap采用的是红黑树实现的,他的查询时间复杂度是o(logn),而LinkedHashMap遍历采用的哈希表加双向循环列表实现的,通过key查询value时间复杂度是o(1),LinkedHashMap就是解决TreeMap的不足的;
2:LinkedHashMap可以通过设置accessOrder来确定是否使用LRU(Least Recently Used)算法,如果accessOrder=true,则表示使用lru,如果是false,则不用,遍历循序和插入循序一致。
3:LinkedHashMap实现原理:具体做法是在存入第一entry的时候,将其赋值给head,当第二来entry来的时候,先和head建立起双向循环链表的关系,然后在存入entry[]数组结构中。以后没来一个就先和之前的已经建立好的循环链表中加入自己,然后按照hashMap的规则存到数组当中。这样查找和遍历就相互分开了。



13. Collection包结构,与Collections的区别。

1:Collection叫类集,他的包含了4个结构,List接口,Set接口,Queue接口,BeanContext接口。对于List接口的标准实现有abstractList类,Vector类,LinkedList类,ArrayList类(同时继承abstractList),copyOnwriteArrayLIst类,对于Set的被一个AbstractSet实现,其他标准继承abstractSet,实现由hashSet类,copyonwriteArraySet类,TreeSet类(跟hashSet类似,也是用TreeMap实现的),ConcurrentSkipArraySet;
2:使用类集的总用统一管理单列集合容器,对各个容器实现了标准化。易于我们扩展自己的类。
3:collections是集合类的一个帮助类(工具类),提供了一系列静态方法对集合进行排序,搜索线程安全等操作(Collections.sort(),Collections.Copy(),Colections。sysnchronizedList()Collections.sysnchronizedMap()等)
14. try catch finally,try里有return,finally还执行么?
是先执行finally,然后在返回去执行try块或者catch块里面的return。


15. Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。
[img][/img]

1:java异常结构:
2:Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。
3:运行时异常也就是在代码中不用try块或者throws捕获或者抛出异常,而是由系统在运行时出现时自动抛出,如果在代码中用try块捕获,会出现一个问题就是抛异常的时候不会打印,也就是不会显示出来,对于查找有一定困难。
4:Error一般是程序无法处理的错误,如OutofMemoryError,StackOverFlowError,ThreadDeath错误等。这类一般java虚拟机直接选择线程终止。Exception异常中运行时异常包括NullPointerException,IndexOutOfBoundsException等,其他的异常则为检查异常,如IoException,SqlException或者自己写的异常。
5:OOM(outOfMemoryError)(统一将Error也叫异常,因为他是异常体系的一部分),如果往一个List列表里面不断的加入对象,则最终会出现OOM异常主要是针对java堆的。
6:Sof(stackOverflowError)对于栈溢出这种情况,我们可以通过设置-Xss参数来控制每一线程栈的大小,当减小每个线程栈大小,同时在函数方法内存放比较大或者多的局部变量,就会发生栈溢出。当然sof只放生在虚拟机栈和本地方法栈,不会发生在java堆和方法区中。如果要在方法区发生sof,可以设置MaxPermSize参数,然后调用String.inter()方法不断的往方法区中加入字符串。
7:java运行时常量池和普通常量池区别:如果在编译时的常量,则存放入普通常量池,当运行时时如采用String.intern()动态加入的常量或者从远端调用产生的常量,则存入运行时常量池中。当然,运行时常量池和静态常量池都是属于方法区的一部分,受maxpermsize参数的影响。



16. Java面向对象的三个特征与含义。

1:封装:也就是将一个实物抽象化为一个类,然后通过一个类去创建一个对象;我们常用的进程就是有一个进程类,然后创建进程对象产生的。
2:继承:也就是对父类方法的继承,包括对接口的实现也属于继承。易于扩展,降低耦合。
3:多态:当子类对象的引用赋给父类引用的时候就会发生多态(简单说就是向上转型。向上转型是多态机制的一种表现形式)多态的实现依赖于多态机制,即动态绑定:也就是在编译的时候编译器只通过父类引用只知道调用方法的签名,无法知道调用哪一个方法体,要确定这个调用的方法体,只有通过new 创建一个对象以后,并将该方法对应的栈帧加入虚拟机栈中才能知道实际的方法体,也就是方法体的后期绑定,因此就不能像使用final修饰方法一样,编译器自己可以优化方法。Final修饰的方法不可覆盖,也就关闭了动态绑定。由此可知覆盖和重载都是多态的一种表现形式


17. Override和Overload的含义去区别。
1:override是覆盖,覆盖是子类和父类之间多态性的表象形式
2:overload是重载。重载的原则是方法的签名不同,方法签名不包括返回值哦,重载是一个类中多态的表现形式


18. Interface与abstract类的区别。
语法上区分:
1:interface是abstract的一种极端。
2:interface和abstract都不可以创建实例,interface中所有方法默认都是public,所有属性都是public static final修饰的,因此必须初始化(但是一般不再接口中定义属性),(final修饰的可以在构造函数中初始化)因为interface和abstract都不可以创建实例,所以没有构造函数。
3:abstract 类中可以定义方法体,而interface中不可以定义方法体。

使用理念上区分:
1:并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
2:要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的,因此抽象类要满足这种情况。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。interface表示的是"like a"关系。


19. Static class 与non static class的区别。
1:在java中静态类只有静态内部类这一种定义,其他的都是非静态类。
2:java中静态内部类和非静态内部类的区别:静态内部类相当于外部类的一个静态属性,因此他不能使用外部类的非静态属性和非静态方法。而非静态内部类相当于外部类非静态属性,因此可以使用静态字段和非静态字段或者方法。
3:静态内部类存在的价值在于:静态内部类可以创建静态属性,而非静态内部类不可以创建静态属性。(内部类只有在需要的时候才会加载),在这里在每个类中用静态main()方法做测试就派上用场了啊
4:静态内部类在平时很少用,但是有一种是用来测试使用的时候特别好用,但是现在都用单元测试也很方便:在电子所的时候这用方法就被使用了。http://blog.sina.com.cn/s/blog_605f5b4f0100zbps.html
5:静态类我的理解是所有的属性或者方法都是静态的,然后这儿非静态类没有本质的区别,有区别的是静态方法和静态属性的区别。

20. java多态的实现原理。
1:多态简单的讲就是一个类有多种状态,他的具体表现为方法的重载和方法覆盖。
2:多态的实现依赖于编译阶段和运行时阶段:在编译阶段主要表现在静态分派,静态分派就是通过静态类型和方法参数个数来选择哪一个方法版本,这就是主要体现了方法的重载;
3:在运行时阶段体现在动态分派(动态绑定),也就是当一个父类引用指向子类对象,通过该父类引用去调用一个该方法,由于在编译阶段生产的调用函数巷道代码的字节码指向的是父类(静态类型)被调用方法,并不知道具体要去调用哪一个实际类型的方法,因此会发生这样一个过程,虚拟机找到操作数栈中位于栈顶获取该操作数的指所指向的类,然后到常量池中去搜索与被调用的方法匹配的方法名和描述符,如果找到,就进行权限校验(校验失败就抛出异常),如果可以访问,则返回该方法的符号引用,并转换成直接引用,调用该执行,如果找不到就到父类中去找,然后重复上面动作,最后找不到就抛出异常。
4:对动态绑定的优化:由于要去常量池中搜索每一类的方法名和描述符,因此效率比较低,所以最后进行了优化,就是在方法区为每一类维护一张虚方法表或者接口方法表(虚表中存放了该方法的实际入口地址),让该类的所有方法都维护进去(包括父类的方法),因此要查找方法名的时候,直接去该虚表中去搜索到该方法名对应的直接地址然后执行。对于没有被重写的方法,子类中直接存放父类的入口地址,如果该方法被重写,在存放子类的方法入口地址。


猜你喜欢

转载自fjding.iteye.com/blog/2319518
今日推荐