java面试题精简版

一、单例模式

1. 什么是单例模式

因程序需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

2. 单例模式的特点

1. 单例模式只能有一个实例。

2. 单例类必须创建自己的唯一实例。

3. 单例类必须向其他对象提供这一实例。

3. 单例模式VS静态类

在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。

1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。

2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。

3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。

4. 单例模式的实现

1. 懒汉模式

public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){

    }
    public static SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}

如上,通过提供一个静态的对象instance,利用private权限的构造方法和getInstance()方法来给予访问者一个单例。

缺点是,没有考虑到线程安全,可能存在多个访问者同时访问,并同时构造了多个对象的问题。之所以叫做懒汉模式,主要是因为此种方法可以非常明显的lazy loading。

针对懒汉模式线程不安全的问题,我们自然想到了,在getInstance()方法前加锁,于是就有了第二种实现。

2. 线程安全的懒汉模式

public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){

    }
    public static synchronized SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}
然而并发其实是一种特殊情况,大多时候这个锁占用的额外资源都浪费了,这种打补丁方式写出来的结构效率很低

二、String和StringBuilder、StringBuffer的区别?

Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

三、Java集合框架的基础接口有哪些?

Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。 
List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。 
Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。 

四、Java的hashmap实现原理

1.HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。因为未实现synchronized,所以是非线程安全的;


从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

put:根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

get:HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。

HashMap的resize:当hashmap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。

hash碰撞:拉链法,jdk1.8后,当碰撞个数达到8以后,链表变为红黑树,常用解决hash冲突的方法还有开放地址法、多重散列法(再哈希法)、 公共溢出区法,都无可避免的多次冲突或堆积的解决或者消费了大量的时间,所以,选择链地址法是相对而言最合适的。


五、hashmap和hashtable有什么区别

1.HashMap是非线程安全的,HashTable是线程安全的;

2.HashMap的键和值都允许有null值存在,而HashTable不允许;

3.因为线程安全问题,hashmap的效率要比hashtable的要高;

4.hashtable是同步的,而hashmap不是,因此,hashmap更适用于单线程环境中,而hashtable适用于多线程环境。

ps:一般不建议使用hashtable,hashtable是遗留类,内部实现很多没有优化和冗余,即使在多线程环境下,也有currenthashmap替代;

六、HashMap与TreeMap的区别:

相同点:

HashMap非线程安全,TreeMap非线程安全

都继承了AbstractMap

  不同点:

HashMap:基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()(可以重写hashCode()和equals()),为了优化HashMap空间的使用,您可以调优初始容量和负载因子。

TreeMap:基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。

HashMap :适用于在Map中插入、删除和定位元素。Treemap:适用于按自然顺序或自定义顺序遍历键(key)。

HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在需要排序的Map时候才用TreeMap。

HashMap的结果是没有排序的。TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。在Map中插入、删除和定位元素,HashMap是最好的选择。TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。

七、String和StringBuilder、StringBuffer的区别?

Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

八、Java中的final关键字有哪些用法?

(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

九、如何实现字符串反转?

public static String reverseString(String s){
    if(s.isEmpty()) return s;
    System.out.println(s.substring(1));
    System.out.println(s.charAt(0));
    return reverseString(s.substring(1))+s.charAt(0);
}







猜你喜欢

转载自blog.csdn.net/evo_steven/article/details/80926072