BAT常问问题总结以及回答(java基础回答一)

java 基础

  1. 八种基本数据类型的大小,以及他们的封装类
     答:八种数据类型分别是byte(1字节)-128~12七、short(2字节)-32768~3276七、char(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节)、boolean(原本是1bit的,可是计算机处理最小的单位是1字节),参数传递时候用的是参数传递,方法的修改并不能改变它。
    1.  
  2. 引用数据类型
      答:类、接口类型、数组类型、枚举类型、注解类型、String    使用的是引用传递(传递的是地址),方法的修改能改变原来的值。
            使用String时候也是引用传递,传的是地址,方法修改却并不能修改他
        其余的引用类能够经过引用传递修改内容。
     
  3. Switch可否用string作参数
    答:jdk1.7以前不能够,可是以后能够了,将string类型转化为hashcode的int类型,从而能够switch
     
  4. equals与==的区别
    equals比较的是内容是否相同,能够经过重写修改其具体含义;
    ==比较的是栈里的地址,除了内容要同样之外还要让他们的类型一致,地址也一致。
     
     
  5. static的用途
    答:static是没有this的,在类加载时进行加载的。分为三种:静态变量,静态方法,静态代码块
    1. 因为静态的在类加载以前就开始加载,而动态的是当new实例化的时候才开始加载,因此在动态中方法中可使用静态方法,而在静态方法中没法使用动态变量和动态方法。
    2. 一、静态变量:
        静态变量是供全部对象享有,只有一个副本,因此在改变他的值的时候,改变以后值就肯定为修改后的了。而动态变量在每一个对象中有他的对应副本,修改单个对象的动态变量的时候不会修改其余对象的动态变量。
      二、静态方法:
            静态方法不存在this,因此静态方法能够不依赖对象而去访问。动态方法能够调用静态方法,相反不行。
    3. 三、静态代码块:加载顺序:父类静态代码块,子类静态代码块,其余静态代码块,父类构造器,子类构造器。静态代码块只会被加载一次,因此对于屡次须要的变量能够放到静态代码块当中。使用场景:须要初始化的方法和变量,常用的变量。
    4. 优势:能够不实例化对象直接使用static,使用方便,效率更高不会随对象清理自动清理。
    5. 缺点:若是建立了该类的任何实例,不能使用实例来访问静态成员,由于静态成员管理的是整个类的信息,须要的是对象的成员变量。另外加载了静态,长时间不适用会耗费内存。
  6. 自动装箱,常量池
      自动装箱和自动拆箱相对应。自动装箱的过程是自动将基本类型转变成包装器类型;而自动拆箱过程是自动将包装器类型转变成基本类型。
      基本类型对应的包装类型:object——>Number、Boolean->boolean、Character->char
    1. number:byte->Byte   short->Short   int->Integer  long->Long   float->Float  double ->Double装箱:Integer num = 99;   Integer num = Integer.valueOf(99);
    2. 拆箱:int number = num;  int number = num.intValue();
    3.  
  7. Object有哪些公用方法
    hashcode()  equals()  finalized()  wait() notify()  notifyAll() toString()  clone()  getclass()
     
  8. clone的两种类型及其原理
      clone分为深克隆和浅克隆,深浅克隆都须要重写方法clone,clone()方法须要类实现clonable接口 ,这个接口至关于一个许可,他自己不带有clone方法,可是没有实现这个接口的类调用clone方法的话会出现clonenotsupportexception异常。
          单纯的object1=object1;  实际上是两个变量同时指向同一个地址而已,并无生成新的对象。
         
    package test;
    import java.util.ArrayList;
    
    public class A implements Cloneable {
        public ArrayList b = new ArrayList();
    
    
    //    浅克隆
    //    @Override
    //    protected Object clone() throws CloneNotSupportedException {
    //        A a = (A) super.clone();
    //        return a;
    //    }
    //
    //   深克隆
    //    public Object clone() throws CloneNotSupportedException {
    //        A a = (A) super.clone();
    //        a.b = (ArrayList) b.clone();
    //        return a;
    //    }
    
        public static void main(String[] args) throws CloneNotSupportedException {
            A a = new A();
            a.b.add("s");
            A b = (A) a.clone();
            a.b.add("b");
            System.out.println(b.b.size());
        }
    }
  9. Java的四种引用,强弱软虚,用到的场景
    强引用:强引用的使用就是咱们经常使用到的实例化,A a = new A(),垃圾回收器不能随意清理强引用的对象,即便是抛出oom异常也不能清理(生活必需品,不能清理)
    软引用:软引用通常配合一个引用队列使用
           Object obj=new Object();
            ReferenceQueue refQueue=new ReferenceQueue();
            SoftReference softRef=new SoftReference(obj,refQueue);
     String str=new String("abc");                                     // 强引用
    
     SoftReference<String> softRef=new SoftReference<String>(str);     // 软引用

                软引用通常是当内存不足的时候才会清除,内存充足的时候调用system.gc()不会清除掉软引用。(生活无关紧要品,家满了的时候清除,平时不动)html

    弱引用:也是配合一个引用队列使用,当被清理的时候,将其放入引用队列中。当gc扫描到该对象的时候,不管内存是否已满,都会回收。是一种比软引用生命力更弱的引用。可是因为gc的优先级较低,因此被清理的
    String str=new String("abc");    
    
    WeakReference<String> abcWeakRef = new WeakReference<String>(str);
    虚引用:也叫幽灵引用,用来跟踪对象被垃圾回收器回收的活动。与强引用和软引用的区别在与他必须和引用队列配合使用,在虚引用指向的对象要被清除的时候,先将虚引用放入到引用队列中去,才是清理所要清理的对象指向。
     
     
  10. Hashcode的做用
    Java中的hashCode方法就是根据必定的规则将与对象相关的信息(好比对象的存储地址,对象的 字段等)映射成一个数值,这个数值称做为散列值。用来快速查找对应对象的,若是两个对象equals相等(地址和内容都相等),那么他们的hashcode也必定相同。当重写对象的equals方法时候,尽可能重写对象的hashcode方法(在hashmap等须要处理冲突问题的地方是必须重写hashcode方法的)。若是两个对象的hashcode同样,那么只能证实他们在一个存储位置,可是不能证实他们彻底相同,由于可能会出现碰撞。
     
  11. HashMap的hashcode的做用
    public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key.hashCode());
            int i = indexFor(hash, table.length);
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
     
            modCount++;
            addEntry(hash, key, value, i);
            return null;
        }

     

    做用:一、为了保证查找的快捷性;二、在使用equals时必定要重写hashcode方法,由于hashmap采起桶机制来解决map冲突问题,当两个对象的hashcode相同的时候,他们可能同时处于同一个bucket中的一个队列上。因此hashmap中hashcode相同的,其值不必定相同。
     
  12. 为何重载hashCode方法?

    一、重写equals方法时须要重写hashCode方法,主要是针对Map、Set等集合类型的使用;java

    a: Map、Set等集合类型存放的对象必须是惟一的;node

    b: 集合类判断两个对象是否相等,是先判断equals是否相等,若是equals返回TRUE,还要再判断HashCode返回值是否ture,只有二者都返回ture,才认为该两个对象是相等的。算法

    二、因为Object的hashCode返回的是对象的hash值,因此即便equals返回TRUE,集合也可能断定两个对象不等,因此必须重写hashCode方法,以保证当equals返回TRUE时,hashCode也返回Ture,这样才能使得集合中存放的对象惟一。spring

     
  13. ArrayList、LinkedList、Vector的区别
    答:Arraylist和Vector底层都是数组实现的,可是Arraylist是线程不安全的,而Vector是线程安全的;性能上因为Vector使用了synchronized方法来保证同步,因此性能上要比arraylist差。
             两者的插入速度: 从内部实现机制来说ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增长元素的时候,若是元素的数目超出了内部数组目前的长度它们都须要扩展内部数组的长度,Vector缺省状况下自动增加原来一倍的数组长度,ArrayList是原来的50%,因此最后你得到的这个集合所占的空间老是比你实际须要的要大。因此若是你要在集合中保存大量的数据那么使用Vector有一些优点,由于你能够经过设置集合的初始化大小来避免没必要要的资源开销。
          LinkedList也是线程不安全的,底层是链表。当向插入和删除的时间复杂度是O(1),可是查找是很慢的,须要沿着链表去找。
         当在Arraylist和linkedlist末尾插入一个数据,哪一个时间快?  
           答:若是Arraylist已满,则须要扩充容量(扩充以前的一半容量),则linkedlist速度快
                 正常状况下应该是arraylist更快,由于他只须要设置数组下标,而linkedlist还须要加指针进行链接。
          但若是是在列表的中间插入的话,那么linkedlist就比arraylist速度要快了,由于arraylist须要将数组中元素位置移动,而linkedlist直接指针操做。
     
  14. String、StringBuffer与StringBuilder的区别
    1、执行速度比较:StringBuilder>StringBuffer>String
           String最慢的缘由是,String存的是不可变的字符串,而咱们使用String str = "www"; str=str+"asd";时候看起来他是可变的,可是其实是新建立了一个名为str的String变量,将以前的那个gc掉了。因此每次都须要进行产生副本才能进行String扩充,因此速度慢。而当咱们使用str=“www"+"asd";的时候实际上是wwwasd一块儿的。
     2、线程安全:
       StringBuilder是线程不安全的,StringBuffer是线程安全的(使用Sychonized来保证线程安全)。   
    3、总结:
    1. String用来表示字符不是不少的字符串操做
    2. StringBuffer当多线程的时候使用
    3. StirngBuilder当单线程的时候使用
    4.  
  15. Map、Set、List、Queue、Stack的特色与用法 
    1. Interface Iterable 迭代器接口,这是Collection类的父接口。实现这个Iterable接口的对象容许使用foreach进行遍历,也就是说,全部的Collection集合对象都具备"foreach可遍历性"。这个Iterable接口只
    有一个方法: iterator()。它返回一个表明当前集合对象的泛型<T>迭代器,用于以后的遍历操做 1.1 Collection Collection是最基本的集合接口,一个Collection表明一组Object的集合,这些Object被称做Collection的元素。Collection是一个接口,用以提供规范定义,不能被实例化使用 1) Set Set集合相似于一个罐子,"丢进"Set集合里的多个对象之间没有明显的顺序。Set继承自Collection接口,不能包含有重复元素(记住,这是整个Set类层次的共有属性)。 Set判断两个对象相同不是使用"=="运算符,而是根据equals方法。也就是说,咱们在加入一个新元素的时候,若是这个新元素对象和Set中已有对象进行注意equals比较都返回false,  
      则Set就会接受这个新元素对象,不然拒绝。 由于Set的这个制约,在使用Set集合的时候,应该注意两点:1) 为Set集合里的元素的实现类实现一个有效的equals(Object)方法、2) 对Set的构造函数,传入的Collection参数不能包
      含重复的元素 1.1) HashSet HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,所以具备良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的
         hashCode()方法来获得该对象的hashCode值,而后根据该HashCode值决定该对象在HashSet中的存储位置。 值得主要的是,HashSet集合判断两个元素相等的标准是两个对象经过equals()方法比较相等,而且两个对象的hashCode()方法的返回值相等 1.1.1) LinkedHashSet LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不一样的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。
           当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。 LinkedHashSet须要维护元素的插入顺序,所以性能略低于HashSet的性能,但在迭代访问Set里的所有元素时(遍历)将有很好的性能(链表很适合进行遍历) 1.2) SortedSet 此接口主要用于排序操做,即实现此接口的子类都属于排序的子类 1.2.1) TreeSet TreeSet是SortedSet接口的实现类,TreeSet能够确保集合元素处于排序状态 1.3) EnumSet EnumSet是一个专门为枚举类设计的集合类,EnumSet中全部元素都必须是指定枚举类型的枚举值,该枚举类型在建立EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,
         它们以枚举值在Enum类内的定义顺序来决定集合元素的顺序 2) List List集合表明一个元素有序、可重复的集合,集合中每一个元素都有其对应的顺序索引。List集合容许加入重复元素,由于它能够经过索引来访问指定位置的集合元素。List集合默认按元素
       的添加顺序设置元素的索引 2.1) ArrayList ArrayList是基于数组实现的List类,它封装了一个动态的增加的、容许再分配的Object[]数组。 2.2) Vector Vector和ArrayList在用法上几乎彻底相同,但因为Vector是一个古老的集合,因此Vector提供了一些方法名很长的方法,但随着JDK1.2之后,java提供了系统的集合框架,就将
         Vector改成实现List接口,统一纳入集合框架体系中 2.2.1) Stack Stack是Vector提供的一个子类,用于模拟""这种数据结构(LIFO后进先出) 2.3) LinkedList implements List<E>, Deque<E>。实现List接口,能对它进行队列操做,便可以根据索引来随机访问集合中的元素。同时它还实现Deque接口,即能将LinkedList看成双端队列
         使用。天然也能够被看成"栈来使用" 3) Queue Queue用于模拟"队列"这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,
       访问元素(poll)操做会返回队列头部的元素,队列不容许随机访问队列中的元素。结合生活中常见的排队就会很好理解这个概念 3.1) PriorityQueue PriorityQueue并非一个比较标准的队列实现,PriorityQueue保存队列元素的顺序并非按照加入队列的顺序,而是按照队列元素的大小进行从新排序,这点从它的类名也能够
         看出来 3.2) Deque Deque接口表明一个"双端队列",双端队列能够同时从两端来添加、删除元素,所以Deque的实现类既能够当成队列使用、也能够当成栈使用 3.2.1) ArrayDeque 是一个基于数组的双端队列,和ArrayList相似,它们的底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会在底层重
           新分配一个Object[]数组来存储集合元素 3.2.2) LinkedList 1.2 Map Map用于保存具备"映射关系"的数据,所以Map集合里保存着两组值,一组值用于保存Map里的key,另一组值用于保存Map里的value。key和value均可以是任何引用类型的数据。Map的key不允
    许重复,即同一个Map对象的任何两个key经过equals方法比较结果老是返回false。 关于Map,咱们要从代码复用的角度去理解,java是先实现了Map,而后经过包装了一个全部value都为null的Map就实现了Set集合 Map的这些实现类和子接口中key集的存储形式和Set集合彻底相同(即key不能重复) Map的这些实现类和子接口中value集的存储形式和List很是相似(即value能够重复、根据索引来查找) 1) HashMap 和HashSet集合不能保证元素的顺序同样,HashMap也不能保证key-value对的顺序。而且相似于HashSet判断两个key是否相等的标准也是: 两个key经过equals()方法比较返回true、
       同时两个key的hashCode值也必须相等 1.1) LinkedHashMap LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对全部的key-value进行排序进行区
    分) 2) Hashtable 是一个古老的Map实现类 2.1) Properties Properties对象在处理属性文件时特别方便(windows平台上的.ini文件),Properties类能够把Map对象和属性文件关联起来,从而能够把Map对象中的key-value对写入到属性文
         件中,也能够把属性文件中的"属性名-属性值"加载到Map对象中 3) SortedMap 正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类同样,Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类 3.1) TreeMap TreeMap就是一个红黑树数据结构,每一个key-value对即做为红黑树的一个节点。TreeMap存储key-value对(节点)时,须要根据key对节点进行排序。TreeMap能够保证全部的
         key-value对处于有序状态。一样,TreeMap也有两种排序方式: 天然排序、定制排序 4) WeakHashMap WeakHashMap与HashMap的用法基本类似。区别在于,HashMap的key保留了对实际对象的"强引用",这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被垃圾回收。
      但WeakHashMap的key只保留了对实际对象的弱引用,这意味着若是WeakHashMap对象的key所引用的对象没有被其余强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃
      圾回收了该key所对应的实际对象以后,WeakHashMap也可能自动删除这些key所对应的key-value对 5) IdentityHashMap IdentityHashMap的实现机制与HashMap基本类似,在IdentityHashMap中,当且仅当两个key严格相等(key1 == key2)时,IdentityHashMap才认为两个key相等 6) EnumMap EnumMap是一个与枚举类一块儿使用的Map实现,EnumMap中的全部key都必须是单个枚举类的枚举值。建立EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的天然顺序
      (即枚举值在枚举类中的定义顺序)
     
  16. HashMap和HashTable的区别
    区别一:出现时间不一样,HashMap比HashTable上线较晚。HashMap和HashTable都是基于HashCode来实现的,因此从他们暴露在外的api看起
    区别二:接口不一样,HashMap继承了Abstract Map抽象类,而Abstract Map实现了Map接口;HashTable以前是继承了Dictionary抽象类,可是该抽象类已通过时,因此如今是实现了Map接口。
    他俩都实现了Map、Cloneable、Serializable接口。
    区别三:支持空不一样,HashMap支持null值和null键(将null的hashcode设置为0),而HashTable传入null值和null键的时候会抛出NullPointerException.
    区别四:数据结构相同,HashMap和HashTable都实现了entry接口,entry对象在内部保存一个键值对。

    Entry对象惟一表示一个键值对,有四个属性:数据库

    -K key 键对象
    -V value 值对象
    -int hash 键对象的hash值
    -Entry entry 指向链表中下一个Entry对象,可为null,表示当前Entry对象在链表尾部编程

    内部有一个指向entry的数组,数组的每一个元素都是一个entry,每一个entry结构又是个链表结构,指向下一个entry。windows

    1.8以前当发生hash冲突的时候可使用entry的链表来解决冲突;可是很难保证hashmap的均匀性,很容易出现很长的链表,影响查询效率。设计模式

    1.8以后使用了红黑树解决hash冲突,具体的能够看:http://www.javashuo.com/article/p-majqhclb-n.htmlapi

    以上两者是相同的
    可是两者在实现数据结构时的算法不一样:
          须要有算法在哈希桶内的键值对多到必定程度时,扩充哈希表的大小(数组的大小)。
          HashTable的初始大小为11,他每次扩充为原来的2*n+1,第二次为23;HashMap的初始大小为16,他每次扩充为原来的2*n,第二次为32.
           若是在建立时给定了初始化大小(5),那么HashTable会直接使用你给定的大小(5),而HashMap会将其扩充为2的幂次方大小(32)
          能够看出hashtable取得的大小通常为素数,由于使用素数做为大小的话能够取模hash更均匀;可是若是是2的幂次方的话,能够直接使用位运算,比除法效率更高;因此整体说仍是hashmap的效率更高。
    区别五:hashtable是线程安全的而hashmap是线程不安全的。hashtable使用了synchronized描述符。
     
    若是你不须要线程安全,那么使用HashMap,若是须要线程安全,那么使用ConcurrentHashMap。HashTable已经被淘汰了,不要在新的代码中再使用它。
     
     
     
     
  17. JDK7与JDK8中HashMap的实现
     JDK7就是个数组和entry链表的结合,在数组中的位置就是经过hashcode算法得出,而若是发生了hash冲突,就将旧值放到链表,将新值放到数组的entry中;当键为null时,全都保存到table[0]上,当数组不够用的时候会以原先值的2倍扩充它。
    JDK8则是利用红黑树和链表结合解决的hash冲突问题,当同数组节点上的链表元素小于8个的话就用链表表示,大于等于8个时才转化为红黑树;链表的查找时间复杂度最坏状况下为O(n),而红黑树的时间复杂度一直是O(lgn);同时entry也变成了node,由于红黑树和node有关。
     
     
  18. HashMap和ConcurrentHashMap的区别,HashMap的底层源码
    HashMap是线程不安全的,而ConcurrentHashMap是线程安全的;HashMap能够存null值而ConcurrentHashMap不能够存放null键和null值。
    ConcurrentHashMap的实现原理是:引入一个"分段锁"机制,能够理解为把一个Map拆分红n个小的table, 根据key.hashCode()来决定把key放到哪一个HashTable中                       每一个segement是一个数组单元,使用ReentrantLock来保证每一个segement是线程安全的,以保证全局的线程安全。每一个segement中的结构相似于hashmap。
    一个重要指标是concurrencyLevel能够翻译成并行级别,并行数,segement数等,默认为16,表示默认有16个segement字段,不一样线程的写操做放在16个不一样的segement中就能够实现多线程。这个指标是能够初始化的,初始化后的值不能够更改。
     
     
  19. ConcurrentHashMap能彻底替代HashTable吗?????????(这道题保留)
    HashTable能够当作是Hashmap的线程安全整合版,当HashTable大小增长到必定程度的时候,性能会急剧降低,由于迭代时须要长时间锁定,因此效率很低
     
     
  20. 为何HashMap是线程不安全的
    由于Hashmap中的put方法并非线程同步的(他所调用的addentry()entry链表增加方法也不是线程同步的),容易出现两个线程分别同时加入一个元素,让后hash冲突,进入同一个链进行addEntry可能会出现线程竞技,致使一个元素丢失。
    另外一方面,resize()方法(增长entry方法)也不是线程安全的,多个线程同时到达entry数量阈值的时候就会致使只有一个线程的entry扩容成功,其余的所有新增数据丢失。
     
     
  21. 如何线程安全的使用HashMap

    了解了HashMap为何线程不安全,那如今看看如何线程安全的使用HashMap。这个无非就是如下三种方式:

    • Hashtable
    • ConcurrentHashMap
    • Synchronized Map

    例子:

    //Hashtable
    Map<String, String> hashtable = new Hashtable<>();
     
    //synchronizedMap
    Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
     
    //ConcurrentHashMap
    Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();
     

    依次来看看。

    Hashtable

    先稍微吐槽一下,为啥命名不是HashTable啊,看着好难受,无论了就装做它叫HashTable吧。这货已经不经常使用了,就简单说说吧。HashTable源码中是使用synchronized来保证线程安全的,好比下面的get方法和put方法:

    public synchronized V get(Object key) {
           // 省略实现
        }
    public synchronized V put(K key, V value) {
        // 省略实现
        }
     

    因此当一个线程访问HashTable的同步方法时,其余线程若是也要访问同步方法,会被阻塞住。举个例子,当一个线程使用put方法时,另外一个线程不但不可使用put方法,连get方法都不能够,好霸道啊!!!so~~,效率很低,如今基本不会选择它了。

    ConcurrentHashMap

    ConcurrentHashMap(如下简称CHM)是JUC包中的一个类,Spring的源码中有不少使用CHM的地方。以前已经翻译过一篇关于ConcurrentHashMap的博客,如何在java中使用ConcurrentHashMap,里面介绍了CHM在Java中的实现,CHM的一些重要特性和什么状况下应该使用CHM。须要注意的是,上面博客是基于Java 7的,和8有区别,在8中CHM摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法,有时间会从新总结一下。

    SynchronizedMap

    看了一下源码,SynchronizedMap的实现仍是很简单的。

    // synchronizedMap方法
    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
           return new SynchronizedMap<>(m);
       }
    // SynchronizedMap类
    private static class SynchronizedMap<K,V>
           implements Map<K,V>, Serializable {
           private static final long serialVersionUID = 1978198479659022715L;
     
           private final Map<K,V> m;     // Backing Map
           final Object      mutex;        // Object on which to synchronize
     
           SynchronizedMap(Map<K,V> m) {
               this.m = Objects.requireNonNull(m);
               mutex = this;
           }
     
           SynchronizedMap(Map<K,V> m, Object mutex) {
               this.m = m;
               this.mutex = mutex;
           }
     
           public int size() {
               synchronized (mutex) {return m.size();}
           }
           public boolean isEmpty() {
               synchronized (mutex) {return m.isEmpty();}
           }
           public boolean containsKey(Object key) {
               synchronized (mutex) {return m.containsKey(key);}
           }
           public boolean containsValue(Object value) {
               synchronized (mutex) {return m.containsValue(value);}
           }
           public V get(Object key) {
               synchronized (mutex) {return m.get(key);}
           }
     
           public V put(K key, V value) {
               synchronized (mutex) {return m.put(key, value);}
           }
           public V remove(Object key) {
               synchronized (mutex) {return m.remove(key);}
           }
           // 省略其余方法
       }

    从源码中能够看出调用synchronizedMap()方法后会返回一个SynchronizedMap类的对象,而在SynchronizedMap类中使用了synchronized同步关键字来保证对Map的操做是线程安全的。

    性能对比

    这是要靠数听说话的时代,因此不能只靠嘴说CHM快,它就快了。写个测试用例,实际的比较一下这三种方式的效率(源码来源),下面的代码分别经过三种方式建立Map对象,使用ExecutorService来并发运行5个线程,每一个线程添加/获取500K个元素。

    public class CrunchifyConcurrentHashMapVsSynchronizedMap {
     
        public final static int THREAD_POOL_SIZE = 5;
     
        public static Map<String, Integer> crunchifyHashTableObject = null;
        public static Map<String, Integer> crunchifySynchronizedMapObject = null;
        public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;
     
        public static void main(String[] args) throws InterruptedException {
     
            // Test with Hashtable Object
            crunchifyHashTableObject = new Hashtable<>();
            crunchifyPerformTest(crunchifyHashTableObject);
     
            // Test with synchronizedMap Object
            crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());
            crunchifyPerformTest(crunchifySynchronizedMapObject);
     
            // Test with ConcurrentHashMap Object
            crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>();
            crunchifyPerformTest(crunchifyConcurrentHashMapObject);
     
        }
     
        public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException {
     
            System.out.println("Test started for: " + crunchifyThreads.getClass());
            long averageTime = 0;
            for (int i = 0; i < 5; i++) {
     
                long startTime = System.nanoTime();
                ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
     
                for (int j = 0; j < THREAD_POOL_SIZE; j++) {
                    crunchifyExServer.execute(new Runnable() {
                        @SuppressWarnings("unused")
                        @Override
                        public void run() {
     
                            for (int i = 0; i < 500000; i++) {
                                Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);
     
                                // Retrieve value. We are not using it anywhere
                                Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));
     
                                // Put value
                                crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);
                            }
                        }
                    });
                }
     
                // Make sure executor stops
                crunchifyExServer.shutdown();
     
                // Blocks until all tasks have completed execution after a shutdown request
                crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
     
                long entTime = System.nanoTime();
                long totalTime = (entTime - startTime) / 1000000L;
                averageTime += totalTime;
                System.out.println("2500K entried added/retrieved in " + totalTime + " ms");
            }
            System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n");
        }
    }

     

    测试结果:

     

    这个就不用废话了,CHM性能是明显优于Hashtable和SynchronizedMap的,CHM花费的时间比前两个的一半还少,哈哈,之后再有人问就能够甩数据了。

    Test started for: class java.util.Hashtable
    2500K entried added/retrieved in 2018 ms
    2500K entried added/retrieved in 1746 ms
    2500K entried added/retrieved in 1806 ms
    2500K entried added/retrieved in 1801 ms
    2500K entried added/retrieved in 1804 ms
    For class java.util.Hashtable the average time is 1835 ms
     
    Test started for: class java.util.Collections$SynchronizedMap
    2500K entried added/retrieved in 3041 ms
    2500K entried added/retrieved in 1690 ms
    2500K entried added/retrieved in 1740 ms
    2500K entried added/retrieved in 1649 ms
    2500K entried added/retrieved in 1696 ms
    For class java.util.Collections$SynchronizedMap the average time is 1963 ms
     
    Test started for: class java.util.concurrent.ConcurrentHashMap
    2500K entried added/retrieved in 738 ms
    2500K entried added/retrieved in 696 ms
    2500K entried added/retrieved in 548 ms
    2500K entried added/retrieved in 1447 ms
    2500K entried added/retrieved in 531 ms
    For class java.util.concurrent.ConcurrentHashMap the average time is 792 ms
  22. 多并发状况下HashMap是否还会产生死循环
     由于在hash冲突时链表添加新的entry的时候会使用头插法插入新节点,因此可能会出现多线程的时候产生环。
     具体缘由,当size已满,两个线程put新元素时,一个线程读取完当前链表的顺序后,就另外一个线程进行扩容,扩容须要调用transfer函数,由于头插法因此致使刚才的链表顺序在rehash后顺序反转,而后切回第一个线程,从新rehash的时候,因为第二个线程致使的顺序反转而产生环。
    1.  
  23. TreeMap、HashMap、LindedHashMap的区别
    Map主要用于存储健值对,根据键获得值,所以不容许键重复(重复了覆盖了),但容许值重复。
    Hashmap 是一个最经常使用的Map,它根据键的HashCode 值存储数据,根据键能够直接获取它的值,具备很快的访问速度,遍历时,取得数据的顺序是彻底随机的。HashMap最多只容许一条记录的键为Null;容许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻能够有多个线程同时写HashMap;可能会致使数据的不一致。若是须要同步,能够用 Collections的synchronizedMap方法使HashMap具备同步的能力,或者使用ConcurrentHashMap。
    LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先获得的记录确定是先插入的.也能够在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种状况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,由于LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
    TreeMap实现SortMap接口,可以把它保存的记录根据键排序,默认是按键值的升序排序,也能够指定排序的比较器,当用Iterator 遍历TreeMap时,获得的记录是排过序的。
     

    通常状况下,咱们用的最多的是HashMap,HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键能够直接获取它的值,具备很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。
    TreeMap取出来的是排序后的键值对。但若是您要按天然顺序或自定义顺序遍历键,那么TreeMap会更好。
    LinkedHashMap 是HashMap的一个子类,若是须要输出的顺序和输入的相同,那么用LinkedHashMap能够实现,它还能够按读取顺序来排列,像链接池中能够应用。

    LinkedHashMap使用after  before指针来控制插入的顺序。
     
  24. Collection包结构,与Collections的区别
     
     
    Collection是单列集合,是集合类的上级接口,子接口主要有Set和List、Map
    Collections是针对集合类的一个帮助类,提供了操做集合的工具方法:一系列静态方法实现对各类集合的搜索、排序、替换和线程安全化等操做。
     
  25. try?catch?finally,try里有return,finally还执行么
    还执行。
     
  26. Excption与Error包结构,OOM你遇到过哪些状况,SOF你遇到过哪些状况
    他俩都实现throwable接口,Exception下还继承了RuntimeException。java可抛出的分为三种可检查异常,运行时异常,错误error
    第一种:运行时异常:若是异常没有人为的被throws和try catch捕获的话就直接被编译器经过,例如byzeroexception
    第二种:可抛出异常:java编译器会检查到他,除非使用异常捕获进行捕获不然将不能经过编译。例如ClassNotFoundException。被检查异常通常都是能够恢复的
    第三种:错误: 编译器也不会对错误进行检查。 当资源不足、约束失败、或是其它程序没法继续运行的条件发生时,就产生错误。程序自己没法修复这些错误的。例如,VirtualMachineError就属于错误。
    OOM异常是内存溢出异常:??????????????????
     
     
  27. Java(OOP)面向对象的三个特征与含义
    三个特征是:封装,继承,多态
      封装:是指某个类经过权限控制来限制其余类直接读取该类的成员变量和成员方法,使用getset有选择的公布部分红员。
      继承: 继承是子对象能够继承父对象的属性和行为
          多态:多态是指父对象中的同一个行为能在其多个子对象中有不一样的表现
  28. Override和Overload的含义去区别
     
     
     
  29. Interface与abstract类的区别
  30. Static?class?与non?static?class的区别
  31. java多态的实现原理
  32. foreach与正常for循环效率对比
  33. Java?IO与NIO
  34. java反射的做用于原理

    什么是反射:

    一、在运行状态中,对于任意一个类,都可以知道这个类的属性和方法。

    二、对于任意一个对象,都可以调用它的任何方法和属性。

    这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射。

     

    反射的做用:

    在JAVA中,只有给定类的名字,就能够经过反射机制来获取类的全部信息,能够动态的建立对象和编译。

     

    实现原理:

    JAVA语言编译以后会生成一个.class文件,反射就是经过字节码文件找到某一个类、类中的方法以及属性等。

    反射的实现主要借助如下四个类:

    Class:类的对象

    Constructor:类的构造方法

    Field:类中的属性对象

    Method:类中的方法对象

    根据类的名字获取类:Class<T> c  = Class.forName("类名");

    根据类c获取对象:Object o = c.newInstance();

    根据类c获取实现的接口:Class<?> interfaces = c.getInterfaces();

    获取有参构造器:Constructor<?> con = c.getConstructor(String.class,int.class); 

    获取属性:Field f2 = c.getField("age"); 

    方法调用:

    public class ReflectCase {
     
        public static void main(String[] args) throws Exception {
            Proxy target = new Proxy();
            Method method = Proxy.class.getDeclaredMethod("run");
            method.invoke(target);
        }
     
        static class Proxy {
            public void run() {
                System.out.println("run");
            }
        }
    }

     

     
  35. 泛型经常使用特色
  36. 解析XML的几种方式的原理与特色:DOM、SAX
  37. Java1.7与1.8,1.9,10 新特性
  38. 设计模式:单例、工厂、适配器、责任链、观察者等等
  39. JNI的使用
  40. AOP是什么
  41. OOP是什么
  42. AOP与OOP的区别

           AOP是面向切面编程,是spring中的一个特性,能够增长业务的重用性,一旦某一功能出现问题直接修改该功能所在模块就好。隔离业务,下降耦合度,提升开发效率。经常使用来处理日志记录,性能统计,安全分析等。

    OOP是java的基本编程思想,面向对象编程。针对业务处理过程当中的实体以及属性和行为进行封装,以得到更加清晰的逻辑划分。

   区别:对于某一类物体,其所具备的属性和动做方法可使用oop来进行封装;而对于一个行为片断咱们只能使用aop进行隔离,最经常使用的AOP应用在数据库链接以及事务处理上,实现模式为代理模式和工厂模式。

相关文章
相关标签/搜索