Java经典的十道面试题及答案分享

时间:2018-11-14
本文章向大家介绍Java经典的十道面试题及答案分享,需要的朋友可以参考一下
41.Iterator、ListIterator 和 Enumeration的区别?
迭代器是一种设计模式,
它是一个对象,
它可以遍历并选择序列中的对象,
而开发人员不需要了解
该序列的底层结构。
迭代器通常被称为“轻量级”对象,
因为创建它的代价小。
Java中的Iterator功能比较简单,
并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。
第一次调用Iterator的next()方法时,
它返回序列的第一个元素。
注意:iterator()方法是java.lang.Iterable接口
被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,
为List设计的ListIterator具有更多的功能,
它可以从两个方向遍历List,
也可以从List中插入和删除元素。
-------------
ListIterator的特点:
它的父类接口是Iterator,
名称是系列表迭代器,
允许程序员按任一方向遍历列表、
迭代期间修改列表,
并获得迭代器在列表中的当前位置。
ListIterator没有当前元素,
它的光标位置始终位于调用previous()
所返回的元素和调用next()
所返回的元素之间。
长度为n的列表的迭代器有n+1个
可能的指针位置。
------------------
Enumeration的特点:
API中是这样描述的,
它主要是和Vector结合配套使用。
另外此接口的功能与Iterator接口的功能是重复的,
此外,Iterator接口添加了
一个可选的移除操作,
并且使用较短的方法名。
新的实现应该优先
考虑使用Iterator接口
而不是Enumeration接口。
-----------------------
java中的集合类都提供了
返回Iterator的方法,
就是迭代器,
它和Enumeration的主要区别
其实就是Iterator可以删除元素,
但是Enumration却不能。
42.Java 中 Set 与 List 有什么不同?
1、Set 不允许重复,List允许重复 
2、Set 没有顺序,List有顺序 

List接口对Collection进行了简单的扩充,
它的具体实现类常用的有ArrayList和LinkedList。
你可以将任何东西放到一个List容器中,
并在需要时从中取出。
ArrayList从其命名中可以看出
它是一种类似数组的形式进行存储,
因此它的随机访问速度极快,
而LinkedList的内部实现是链表,
它适合于在链表中间
需要频繁进行插入和删除操作。
在具体应用时可以根据需要自由选择。
前面说的Iterator只能对容器进行向前遍历,
而ListIterator则继承了Iterator的思想,
并提供了对List进行双向遍历的方法。 

Set接口也是Collection的一种扩展,
而与List不同的时,
在Set中的对象元素不能重复,
也就是说你不能把同样的东西
两次放入同一个Set容器中。
它的常用具体实现有HashSet和TreeSet类。
HashSet能快速定位一个元素,
但是你放到HashSet中的对象
需要实现hashCode()方法,
它使用了前面说过的哈希码的算法。
而TreeSet则将放入
其中的元素按序存放,
这就要求你放入
其中的对象是可排序的,
这就用到了集合框架提供的
另外两个实用类Comparable和Comparator。
一个类是可排序的,
它就应该实现Comparable接口。
有时多个类具有相同的排序算法,
那就不需要在每分别重复定义
相同的排序算法,
只要实现Comparator接口即可。
集合框架中还有两个很实用的公用类:
Collections和Arrays。
Collections提供了对一个Collection容器
进行诸如排序、复制、查找和填充等
一些非常有用的方法,
Arrays则是对一个数组进行类似的操作。 
43.arraylist 与 vector 的区别?
ArrayList与Vector的区别,
这主要包括两个方面:. 
1.同步性:
Vector是线程安全的,
也就是说是它的方法之间是线程同步的,
而ArrayList是线程序不安全的,
它的方法之间是线程不同步的。
如果只有一个线程会访问到集合,
那最好是使用ArrayList,
因为它不考虑线程安全,
效率会高些;
如果有多个线程会访问到集合,
那最好是使用Vector,
因为不需要我们自己
再去考虑和编写线程安全的代码。

备注:
对于Vector&ArrayList、Hashtable&HashMap,
要记住线程安全的问题,
记住Vector与Hashtable是旧的,
是java一诞生就提供了的,
它们是线程安全的,
ArrayList与HashMap是java2时才提供的,
它们是线程不安全的。

2.数据增长:
ArrayList与Vector
都有一个初始的容量大小,
当存储进它们里面的元素的个数超过了容量时,
就需要增加ArrayList与Vector的存储空间,
每次要增加存储空间时,
不是只增加一个存储单元,
而是增加多个存储单元,
每次增加的存储单元的个数
在内存空间利用与程序效率之间
要取得一定的平衡。
Vector默认增长为原来两倍,
而ArrayList的增长策略
在文档中没有明确规定
从源代码看到的是增长为原来的1.5倍。
ArrayList与Vector都可以设置初始的空间大小,
Vector还可以设置增长的空间大小,
而ArrayList没有提供
设置增长空间的方法。
总结:
即Vector增长原来的一倍,
ArrayList增加原来的0.5倍。
44.什么类实现了List接口?
List接口的实现类中
最经常使用最重要的就是这三个:
ArrayList、
Vector、
LinkedList。

1.三个都直接实现了AbstractList这个抽象类
2,ArrayList和Vector都实现了
RandomAccess接口。
而LinkedList没有。
这是什么意思呢?              
在JDK 中,
RandomAccess接口是一个空接口,
所以它没有实际意义。就是一个标记,
标记这个类支持高速随机訪问,
所以,arrayList和 vector是支持随机訪问的,
可是LinkedList不支持持         
3.serializbale接口表明他们都支持序列化。
45.什么类实现了Set接口?
HashSet
LinkedHashSet
TreeSet

HashSet是使用哈希表(hash table)实现的,
其中的元素是无序的。
HashSet的
add、
remove、
contains方法 
的时间复杂度为常量O(1)。
--------------------
TreeSet使用树形结构
算法书中的红黑树red-black tree
实现的。
TreeSet中的元素是可排序的,
但add、remove和contains方法的时间
复杂度为O(log(n))。
TreeSet还提供了
first()、
last()、
headSet()、
tailSet()等
方法来操作排序后的集合。
-----------------------
LinkedHashSet介于HashSet和TreeSet之间。
它基于一个由链表实现的哈希表,
保留了元素插入顺序。
LinkedHashSet中基本方法的
时间复杂度为O(1)。
46.如何保证一个集合线程安全?
Java提供了不同层面的线程安全支持。
在传统集合框架内部,
除了Hashtable等同步容器,
还提供了所谓的同步包装器(Synchronized Wrapper),
可以调用Collections工具类提供的包装方法,
来获取一个同步的包装容器,
例如Collections.synchronizedMap()。
但是它们都是利用非常粗粒度的同步方式,
在高并发情况下的性能比较低下。
另外,更加普遍的选择是利用并发包(java.util.concurrent)
提供的线程安全容器类:
各种并发容器,
比如ConcurrentHashMap、
CopyOnWriteArrayList。
各种线程安全队列(Queue/Deque),
比如ArrayBlockingQueue、
SynchronousQueue。
各种有序容器的线程安全版本等。
具体保证线程安全的方式,
包括有从简单的synchronized方式,
到基于更加精细化的,
比如基于分离锁实现的ConcurrentHashMap等并发实现等。
具体选择要看开发的场景需求,
总体来说,
并发包内提供的容器通用场景,
远远优于早期的简单同步实现。

为什么需要ConcurrentHashMap
首先,Hashtable本身比较低效,
因为它的实现基本就是
将put、get、size等方法
简单粗暴地加上“synchronized”。
这就导致了所有并发操作都要竞争同一把锁,
一个线程在进行同步操作时,
其它线程只能等待,
大大降低了并发操作的性能。
47.是否可以往 TreeSet 或者 HashSet 中添加 null 元素?
1.TreeSet 是二差树实现的,
Treeset中的数据是自动排好序的,
不允许放入null值 

2.HashSet 是哈希表实现的,
HashSet中的数据是无序的,
可以放入null,
但只能放入一个null,
两者中的值都不能重复,
就如数据库中唯一约束 

3.HashSet要求放入的对象
必须实现HashCode()方法,
放入的对象,是以hashcode码作为标识的,
而具有相同内容的String对象,
hashcode是一样,
所以放入的内容不能重复。
但是同一个类的对象可以放入不同的实例
48.hashCode() 和 equals() 方法的重要性?如何在Java中使用它们?
Java中的HashMap使用
hashCode()和equals()方法
来确定键值对的索引,
当根据键获取值的时候
也会用到这两个方法。 
如果没有正确的实现这两个方法,
两个不同的键可能会有相同的hash值,
因此可能会被集合认为是相等的。 
而且,这两个方法也用来发现重复元素,
所以这两个方法的实现对HashMap的
精确性和正确性是至关重要的。

同一个对象(没有发生过修改)
无论何时调用hashCode(),
得到的返回值必须一样。
hashCode()返回值相等,
对象不一定相等,
通过hashCode()和equals()
必须能唯一确定一个对象。
一旦重写了equals(),
就必须重写hashCode()。
而且hashCode()生成哈希值的依据应该是
equals()中用来比较是否相等的字段。
如果两个由equals()规定相等的对象
生成的hashCode不等,
对于HashMap来说,
他们可能分别映射到不同位置,
没有调用equals()比较是否相等的机会,
两个实际上相等的对象可能被插入到不同位置,
出现错误。
其他一些基于哈希方法的集合类
可能也会有这个问题。
----------------
怎么判断两个对象是相同的?
使用等号== 判断两个对象是否相同,
这种是严格的相同,
即内存中的同一个对象
 Object的equal方法就是使用==判断两个对象是否相同
------------
集合set要求元素是唯一的,怎么实现?
要实现元素的唯一,
需要在往集合set中添加元素时,
判断集合set是否存在相同的元素,
如果存在,则不添加,反之。
那么怎么确定两个元素是否相同,
1.如果是使用等号==判断两个元素是否相同,
即默认使用Object的equals的方法。
2.如果没有使用等号==判断两个元素是否相同,
而是按照某种业务规则判断两个元素是否相同,
即重写了Object的equals的方法。
----------------------
当重写equals方法,必须重写hashCode方法吗?
不是必须的,
得看具体的情况
当equals方法返回的结果和使用等号
比较的结果是一致的时候,
是没有必要重写hashCode方法。
当用等号比较对象,
只有是内存中同一个对象实例,
才会返回true,
当然调用其hashCode()方法
肯定返回相同的值,
这满足了满足了hashCode的约束条件,
所以不用重写hashCode()方法。

当equals方法返回的结果
和使用等号比较的结果是不一致的时候,
就需要重写hashCode方法。
当重写后的equals方法
不认为只有是在内存中同一个对象实例,
才返回true,
如果不重新hashCode方法()
Object的hashCode()方法 是对内存地址的映射,
hashCode方法返回的值肯定是不同的,
这违背了hashCode的约束条件,
所以必须要重新hashCode方法,
并满足对hashCode的约束条件。
49.array 和 arraylist 的区别?
两者间的区别:
Array 的容量是固定的,
ArrayList 的容量是根据需求自动扩展
ArrayList 提供了 添加、插入或移除
 某一范围元素的方法
而 Array 中,
只能一次获取或设置一个元素值
用Synchronized方法可以
很容易地创建ArrayList的同步版本
而 Array 将一直保持
它知道用户实现同步为止
 
array 数组的用法
type [] name = new type [size];
注意:size不能省略,type前后要一致
缺点:在数据间插入数据是


ArrayList 动态数组的用法
是 Array 的复杂版本
 动态的增加和减少元素,
实现 ICollection 和 IList 接口
灵活的设置数组大小
50.如何将一个字符串转换为arraylist?
string 转 ArrayList
先将字符串按照某个字符切割,转为string数组
然后用Arrays的asList方法,将数组转为List
public class test1 {
    public static void main(String[] args)  {
        //string 转 ArrayList
        String str1 = "a,b,c";
        ArrayList<String> list = 
        new ArrayList<String>(Arrays.asList(str1.split(",")));
        System.out.println(list);
    }
}

ArrayList 转 string
public class test1 {
    public static void main(String[] args)  {
        //ArrayList 转 string
        ArrayList<String> list = new ArrayList<String>();

        list.add("a");
        list.add("b");
        list.add("c");

        System.out.println(list);//[a, b, c]

        String list_str = StringUtils.join(list,",");

        System.out.println(list_str);//a,b,c
    }
}