java 面试题总结(一)

从网上找了些面试题,本身手工总结了理解了一下,若有理解错误,还请指正。javascript

java基础html

1.String 为何是final的?
    String做为引用类型,类设计成final的,不让任何子类有机会继承它,目的时保证String能够做为一个不可变的对象存储起来。
    String使用很是频繁,所以设计成一种相似于int那样的值传递,须要有直接的值,并且值不能被改变,若是String的值可以被轻易改变将是很是不安全的。
    String做为HashMap的key以及其余场合的时候,须要保持不可变性,不然会发生错误,所以须要声明成final的。
    String底层使用char数组实现,char数组虽然是final的,可是数组的内容能够被改变(引用不变),所以String设计成final的,保证不会被其余程序修改String的行为,致使错误。
    final方法编译器会采起内联方式实现,JVM执行效率获得提升。
 
2.HashMap的源码,底层结构和实现原理
    底层结构是一个Entry数组+链表。
    HashMap存放元素时,获取key的hashCode而后调用hash方法,得到Entry数组的下标。而后指定的这个value就会存储到这个数组下标位置。若是这个数组下标的位置已经有元素存在了,就将value插入数组下标的头元素前,造成一个链表。Entry做为一个HashMap一个静态内部类,内部持有了key,hash,value和下一个Entry的引用以及。每一个Entry都是一个链表。
get元素时,根据传入的key,获取hash值,找到数组的下标。而后在这个链表中不停地迭代下一个Entry,根据key的字面值,获取对应的value。
源码中还包括了key值为null的put和get状况,数组初始化,超过负载因子时的再哈希算法等等内容。
    实际使用中要尽可能避免哈希表屡次扩容,由于扩容再哈希是十分消耗性能的作法;要注意key值必定要是不可变的元素并且符合equals约束和hashCode约束;尽可能不要用null值做为key等。贴上两篇经典文章:
 
3.说说你知道的java集合及其实现类,如list,set,map等
    java集合类主要是实现了Collection,Map接口。其中Collection实现了Iterable,这个顶级的接口提供了元素的迭代器,凡是须要遍历类的值,都须要这个迭代器(list虽然能够经过下标遍历,可是不能在遍历时作插入和删除操做)。
    此外还有一个ListIteraotr,能够用来在迭代时插入删除并支持双向遍历。
    List和Set实现了Collection接口,其中List是有序的,容许元素重复的相似于数组的集合。Set是无序的不容许重复的底层是HashMap实现的用于模拟数学意义上集合的集合
    List和Set依然是接口,他们向下生成了AbstractList和AbstractSet两个抽象类,抽象类定义了一些基本的约束条件,而后再向下延伸出具体平常使用中的工具类。
    List下面有ArrayList,LinkedList和Vector几种集合类型,其中ArrayList平常使用最多,LinkedList是使用链表实现的List,提供快速的插入和删除,可是遍历较慢。Vector是线程安全的List,可是设计上有一些问题,用得很少,就算是须要同步功能,也能够经过Collections来获取一个同步的ArrayList。
    Set下面有HashSet,EnumSet,TreeSet,LinkedHashSet等,其中HashSet底层是使用没有value的Map来实现的。TreeSet使用了一个排序的平衡二叉树,所以没有HashMap的性能困扰,须要时能够从HashSet直接转换为TreeSet。LinkedHashSet提供了为Set排序和保持顺序的功能。
    Map接口下面是AbstractMap,抽象类,接下来是Map的其余具体实现。
    HashMap,EnumMap,HashTable,LinkedHashMap,WeakHashMap等
    HashMap是标准实现,用得最多,上面已经解释过了。EnumMap是使用枚举类型做为key的map,HashTable是同步的Map,不容许null值,由于同步因此没有HashMap的快速失败机制。HashTable不方便转换为有序Map。LinkedHashMap只是对HashMap作了一层简单的包装,保证放进去和拿出来的顺序是同样的。TreeMap使用树实现,支持排序等操做。WeakHashMap内部使用弱引用来管理数据,能够被垃圾回收器回收,所以是做为缓存实现的极好的方式。Properties是继承自Map的实现键值对数据管理的一个简单的工具类。
     Collection的另外一个分支是队列,提供了ArrayDeque,PriorityQueue等实现,这些单线程的使用很少,而相似于ArrayBlockingQueue,ConcurrentLinkedDeque,LinkedBlockingDeque,SynchronousQueue等可用于并发环境下的队列在平常工做中使用最多!
     最后是关于线程安全的问题,虽然Vector和HashTable提供了这个功能,可是由于种种缘由实际工做中使用的并很少。就算是要对List,Set,Map作同步操做,也能够经过:Collections.synchronizedCollection / synchronizedList / synchronizedMap / synchronizedSet / synchronizedSortedMap / synchronizedSortedSet来获得一个安全的集合。另外concurrent包下的一些包装集合类,也能够实现同步的功能,内部机制暂时还不清楚,可是不管在何时,多线程的操做都是须要很是注意的,并非用一个class就可以一劳永逸不用管的。由于这些类也只能保证在读取的时候不修改,删除的时候不迭代,而没法阻止两个线程前后修改数据(业务上)。
     参考资料
    【java集合类详解 】

4. 描述一下ArrayList和LinkedList各自实现和区别  java

     ArrayList是线性表 ,随机取数快,数据删除插入较慢,占用内存小面试

     LinkedList是链表  插入删除快,查找遍历较慢占用内存稍大算法

5. Java中的队列都有哪些,有什么区别。编程

    队列是实现生产者消费者的重要工具,java中的队列通常氛围阻塞的和非阻塞的,或者是根据须要有其余多重的实现方式:api

    ArrayBlockingQueue——带边界的阻塞式队列数组

    ConcurrentLinkedDeque / ConcurrentLinkedQueue——无边界的链表队列(CAS)缓存

    DelayQueue——元素带有延迟的队列安全

    LinkedBlockingDeque / LinkedBlockingQueue——链表队列(带锁),可设定是否带边界

    LinkedTransferQueue——可将元素`transfer`进行w/o存储

    PriorityBlockingQueue——并发PriorityQueue

    SynchronousQueue——使用Queue接口进行Exchanger

    Queue在设计之处就考虑了多线程的安全性,表明性的Queue的两种实现,表示实现线程安全的两套方案:•BlockingQueue,•ConcurrentLinkedQueue

    前者是阻塞的同步的,后者是并发的。

    这里提一下多线程安全问题的含义,以前有些搞混了。线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的。若是因为多线程的访问(好比修改、遍历、查看)而使这些变量结构被破坏或者针对这些变量操做的原子性被破坏,则这个类就不是线程安全的。实现线程安全又有两种方式,一种是同步的,将多线程的请求排个队,A线程进来了关上厕所门,其余线程在外面等待。A线程执行结束,B线程进入一样关上门。这样的操做保证了多线程下共享变量不会被修改,可是效率比较差,若是线程太多会致使严重的锁争用。还有一种方式是并发的,多个线程能够同时进入一段代码,如何保证线程安全呢?就是经过各个线程之间对共享变量的修改和操做都是可见的,你改了什么我必须第一时间知道,而后针对性地作一些操做。这样极大地提升了处理速度,只是算法的实现会很是的复杂。另外,类自身实现线程安全,只是表明类自身的api保持原子性,可是外部使用多个api的状况下,仍是要手动进行加锁。concurrent包下的集合类实现,都是采用的并发方式,而Collections工具类包装的集合都是使用同步锁方式。其中CurrentHashMap应该是使用了分段锁和volatile。java并发:同步容器&并发容器 - 烟雨暗千家 - 博客园 

    LinkedList实现了Queue接口,也能够做为队列使用,可是不能够访问非Queue接口的方法,以避免发生错乱。

    BlocingQueue:ArrayBlockingQueue,LinkedBlockingQueue(入队和出队两把锁),DelayQueue,PriorityBlockingQueue(公平锁),SynchronousQueue。

    ReentrantLock     只锁部分代码块

    ConcurrentLinkedQueue:使用volatile关键字使共享变量可见,具体细节颇为复杂。

   【Java多线程总结之聊一聊Queue - I'm Sure - ITeye技术网站 】【java中线程队列BlockingQueue的用法-shwenwen-ITPUB博客 】

    这个坑很深,目前只能研究到这里了……

6. 反射中,Class.forName和classloader的区别。

    Class.forName()方法底层调用Class.forName(className,true,classloader);表示在加载类的时候,执行初始化步骤;

    ClassLoader.loadClass()方法底层调用ClassLoader.loadClass(className,false);表示不执行初始化步骤;

    初始化步骤会初始化静态变量和静态代码块,若是此处没有初始化,那就须要在newInstance时,初始化了。根据不一样的须要,调用相应的方法。

    可能会引伸到JVM装载类的过程:编译解释java文件为二进制class,装载class(经过类的彻底限定名和类加载器实例),连接(对二进制字节码校验,初始化静态变量,找到父类接口和类,肯定调用的方法和类都是存在的且具有权限),初始化(静态代码块其余)。 

7. Java七、Java8的新特性

    java 7 ,java8 新特征 

    java7的更新多数是语法糖:

    try包含资源代码块,自动释放;多个catch合并;更强的类型推测,new的时候再也不须要指定类型,直接<>便可;二进制字面量支持;经过[],{}初始化集合。

     java8

    Lambda表达式与函数接口,接口支持默认方法和静态方法,可以使用相似于C++的方法引用 class::method,重复注解,更强的类型推测,可将参数名称保存在字节码中,Optional 支持对null的处理,Stream 相似于mapReduce的集合处理方式,api能够直接连缀调用,Date/Time API 的更新,在兼容旧api分基础上提供了许多新的api,传闻java 9 将进一步支持货币api,base64直接支持,支持运行javascript以及js和java的相互调用(java8就有了?),jvm的permGen移除,类依赖分析器jdeps。

8. Java数组和链表两种结构的操做效率,在哪些状况下(从开头开始,从结尾开始,从中间开始),哪些操做(插入,查找,删除)的效率高。

    数组 不能动态扩容,占内存小,添加删除操做稍慢,查询快。

    链表 能够动态扩容,占内存大,添加删除操做快,查询慢。

9. Java内存泄露的问题调查定位:jmap,jstack的使用等等。

    没用过很差说

10. string、stringbuilder、stringbuffer区别

    String 是final的字符串,不可变。StringBuffer是线程安全的可变String,StringBuilder是简化的可变字符串,不是线程安全的。

11. hashtable和hashmap的区别

    上面有所说起:

    1. HashTable不支持用null做为key,而HashMap支持

    2. HashTable是线程安全的map集合,HashMap不是线程安全的。

    3. HashTable由于支持线程安全,因此没有HashMap的快速失败机制,可是HashTable不能保证多个api复合调用时保证安全。

    4. Map想要支持多线程安全能够经过Collections工具类获取同步的Map,或者经过concurrent包中的concurrentHashMap返回并发的Map,并且能够与有序的TreeMap转化,不少时候,即使是多线程环境下也并不必定须要HashTable。

13 .异常的结构,运行时异常和非运行时异常,各举个例子。

    单独再说

14. String 类的经常使用方法

     length() , subString , indexOf , lastIndexOf , charAt , equals ,toUpperCase , equalsIgnoreCase , trim , toString , toLowerCase , replace , match(用于正则) , startsWith , compareTo. format(容许相似于C语言的输出字符串格式控制)

15. Java 的引用类型有哪几种

      String , Object ,基本类型的包装类型:Integer,Long , Boolean , Short , Float,Double,Byte,Character,Void。Enum枚举类型

16. 抽象类和接口的区别

      从语法上来看:  

参数 抽象类 接口
默认的方法实现 它能够有默认的方法实现 接口彻底是抽象的。它根本不存在方法的实现
实现 子类使用extends关键字来继承抽象类。若是子类不是抽象类的话,它须要提供抽象类中全部声明的方法的实现。 子类使用关键字implements来实现接口。它须要提供接口中全部声明的方法的实现
构造器 抽象类能够有构造器 接口不能有构造器
与正常Java类的区别 除了你不能实例化抽象类以外,它和普通Java类没有任何区别 接口是彻底不一样的类型
访问修饰符 抽象方法能够有publicprotecteddefault这些修饰符 接口方法默认修饰符是public。你不可使用其它修饰符。
main方法 抽象方法能够有main方法而且咱们能够运行它 接口没有main方法,所以咱们不能运行它。
多继承 抽象方法能够继承一个类和实现多个接口 接口只能够继承一个或多个其它接口
速度 它比接口速度要快 接口是稍微有点慢的,由于它须要时间去寻找在类中实现的方法。
添加新方法 若是你往抽象类中添加新的方法,你能够给它提供默认的实现。所以你不须要改变你如今的代码。 若是你往接口中添加方法,那么你必须改变实现该接口的类。

       从本质上来看:

       抽象类是对子类的一个抽象,抽象类提供了子类的某些方法约束,做为公共的你们都必须遵照的约束,有利于直接复用代码。不能肯定的子类行为能够采用抽象方法定义,交给具体子类实现。抽象类更多的是在具体代码编写时的一种设计。

       接口是定义一组约束规则,做为完全的抽象层存在。它只要求实现者实现全部的方法,而不关心具体的实现方式,它只为子类指定功能,可是不存在对子类实现方式的要求,也就是说没有公共的已经实现的方法。更多的是用于软件架构设计中,对不一样模块的抽象。

       java8中支持了接口的默认方法,具体后面再研究。

18. java的基础类型和字节大小

       java中基本数据类型(不是引用类型)有8种

 

byte Byte 1 -128 127
char Character 2 unicode-0 unicode 2^16-1
short Short 2 -2^15  2^15-1
int Integer 4 -2^31 2^31-1
float Float 4    
double Double 8    
long Long 8    
boolean Boolean - true false
       对char不太理解,由于char是2个字节,可是unicode中utf-8的中文是3个字节,产生了混乱。后来有人建议不要用char类型。由于char类型使用的老旧的utf16编码,65535个字符根本不够囊括全部语言的字符。这里记录如下防止被坑吧……

19. Hashtable,HashMap,ConcurrentHashMap底层实现原理与线程安全问题。

      HashMap没有作多线程的控制,底层实现原理以前有提到过。HashTable是使用了重用锁,在操做时锁住了一段代码块,是同步的。ConcurrentHashMap,内部使用了volatile关键字,使得共享变量在多线程之间可见和同步,编程上考虑的比较多算法比较复杂。具体的上面的文章中有提到过。

20. Hash冲突怎么办?哪些解决散列冲突的方法?

      hash冲突应该是hash算法的问题,也有多是hashCode算法的问题。这个//todo

21. HashMap冲突很厉害,最差性能,你会怎么解决?从O(n)提高到log(n)。

      首先哈希表是经过哈希函数计算元素的哈希值,而后将次哈希值做为要存放的数组地址的下标,以便达到用数组存放元素,同时可以实现较快的查找删除操做。

      这里哈希函数就很是重要,若是哈希函数计算的不一样元素的哈希值容易重复,就会致使哈希冲突(不一样元素映射到同一下标),哈希表不得不额外地解决这些冲突。

      好的哈希函数会尽可能避免冲突,能均匀的分布。以及计算简单。

      解决哈希冲突的办法主要有下面几种:

      开放地址法:若是发现哈希值下标已经被占用,就向下查找下一个空元素存入,问题是很是容易产生集聚现象,使得冲突加重。其中也有一些变种办法,例如不直接向下查找下一个元素,而是生成不一样的查找步数,这个能够根据随机数或者二次哈希产生。

      再哈希法:冲突后再次执行另外一个哈希算法,获得新的地址。

      链地址法:若是哈希冲突了,就直接将冲突的元素接在原占有的元素后面,造成一个链表。

      而HashMap就是采用这种办法作的。HashMap的性能,也是与其hash函数有关,当存放的key是用户自定义的对象而不是String的时候,须要重写这个对象的hashCode方法,这个hashCode方法的效率就会影响到HashMap的哈希函数的效率,若是HashCode不能作到计算简单随机分布,hash函数就会容易冲突。

      HashMap冲突很严重的话,还能够将HashMap转化为平衡树实现的TreeMap,后者的性能是logn.

23. rehash

      看起来应该是HashMap的再哈希算法,主要用于当HashMap的容量超过负载因子(致使元素容易集聚成链表),HashMap就会扩容,全部的元素的哈希值就要从新计算,rehash大概就是用于此处的。rehash比较消耗性能,所以应该避免HashMap频繁扩容。

24. hashCode() 与 equals() 生成算法、方法怎么重写。

      hashCode

      hashCode用于生成标记一个对象的最好是惟一的散列码,它将被用在HashMap等散列表中,哈希函数会根据该散列码计算数组下标从而存放起来。

      所以若是散列码是同样的话,在HashMap中认为是同一个对象,若是不同,它就会认为这个一个新的对象。

      你们说在重写equals时重写hashCode方法,是为了不对象在用在哈希表的键时形成的问题。

      设想重写了equals方法,两个不一样的对象被认为是相等的,咱们把相等的对象存到map的key中,而后再根据这个相等的对象取value的时候,就会出错。由于HashMap根据hashCode计算出来的两个对象的值不一样,HashMap认为是不同的。因此就找不到当初存放的对象。

      因此hashCode须要重写,而且保证两个值相同的对象,它的hashCode必定是相同的。(若是固定地返回某个常量值,会致使系统会将全部的这种类型的对象都视为相同。)

      重写hashCode方法是一项技术活,不过一些简单的重写方案仍是有的。下面的代码从《effective java》中摘录

 

/**此处的变量都是int类型*/
@Override
public int hashCode(){
int result = 17;
result = 31*reuslt + areaCode;
result = 31*result + prefix;
result = 31*result + lineNumber;
return result;
}

 

  

 

      equals

/**
* equals方法须要知足自反性,对称性,传递性,一致性和非空性
* equasl方法写完了要进行相关的测试
* 过程是:
* 检查被比较的对象是否是本身,若是是返回true
* 检查被比较的对象是否是null,若是是返回false
* 检查被比较的对象是否是和本身是同一个类型的,若是不是返回false
* 将被比较对象强制转换成本身的类型,而后逐一比较值。
* 对于float,double,数组等格式的域,须要使用特殊的比较方法,而不能用==。
*
*/
@Override
public boolean equals(Object obj){
if(this == obj)return true;
if(obj == null)return false;
if(obj.getClass() != this.getClass())return false;//!(obj instanceof Te1)
Te1 t = (Te1)obj;
if(!t.name.equals(name))return false;
if(!(t.id==id))return false;
if(Double.compare(salary,t.salary)!=0)return false;
if(!Arrays.equals(strs,t.strs))return false;
return true;
}

  

----------------------------------------------------------------------
以上
相关文章
相关标签/搜索