Java名企高频率面试题及答案 精心整理(一)

请尊重个人劳动成果,转载注明出处,谢谢!

1 . 九种基本数据类型的大小,以及他们的封装类。    变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。

内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。

Java的两大数据类型:

(一).内置数据类型(基本数据类型)

  1 六种数字类型 ( byte, short, int, long, float, double) + void

          8 16 32 64 32 64 位

  2 一种字符类型 char

          16位Unicode字符

  3 一种布尔型 boolean

          1位

(二).引用数据类型

  引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型。变量一旦声明后,类型就不能被改变了。

  对象、数组都是引用数据类型,所有引用类型的默认值都是null。

基本数据类型只能按值传递,而封装类按引用传递。

  Void无返回值类型,作为伪类型对应类的对象,也被认为是 基本数据类型

2 . Switch能否用string做参数?

   1 . 在jdk 7 之前,switch 只能支持 byte、short、char、int 这几个基本数据类型和其对应的封装类型。switch后面的括号里面只能放int类型的值,但由于byte,short,char类型,它们会 自动 转换为int类型(精精度小的向大的转化),所以它们也支持。

对于精度比int大的类型,long、float、double,不会自动转换成int。要想使用就得加强转如(int)long。 另外boolean类型不参与转换,任何类型不能转换为boolean型.

2 .我们也可以用枚举类型实现switch可传入string参数

public enum En{ a,b,c } (String[] args) { En t = En.a; function(t); } (En type){ switch (type) { case a: System.err.println("a"); break; case b: System.err.println("b"); break; case c: System.err.println("c"); break; default: break; } }

运行结果:

技术分享

3 . jdk7之后java加入了switch对string的支持,就不用枚举来实现啦!

(String[] args) { String s = "a"; switch (s) { case "a": System.err.println("a"); break; case "b": System.err.println("b"); break; case "c": System.err.println("c"); break; default: break; } }

运行结果:

技术分享

3.equals与==的区别
  • == 是一个运算符。
    equals则是string对象的方法。

  • java中 值类型 是存储在内存中的栈中。
    而引用类型在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中。所以字符串的内容相同,引用地址不一定相同,有可能创建了多个对象。

  • ==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。

  • equals将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。即堆中的内容是否相同。==比较的是2个对象的地址(栈中),而equals比较的是2个对象的内容(堆中)。所以当equals为true时,==不一定为true。

  • 4 .Hashcode的作用 关于HashCode的官方文档定义:

    hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。 hashCode 的常规协定是: 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
    如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
    以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
    实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)
    当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

  • 以上这段官方文档的定义,我们可以抽出成以下几个关键点:

    1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;

    2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;

    3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;

    4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

    再归纳一下就是hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。

  • 总的来说,Java中的集合(Collection)有两类,一类 是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一 次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它 就要调用1000次equals方法。这显然会大大降低效率。
    于是,Java采用了哈希表的原理。哈希算法也称为散列算法,是 将数据依特定算法直接指定到一个地址上。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以 直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散 列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
    所以,Java对于eqauls方法和hashCode方法是这样规定的:
    1、如果两个对象相同,那么它们的hashCode值一定要相同;
    2、如果两个对象的hashCode相同,它们并不一定相同

  • 5 .Java的四种引用,强弱软虚,用到的场景

    从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

    1 . 强引用(StrongReference)

    强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。只要你给强引用对象s赋空值null,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用。

    2 . 弱引用(WeakReference)

    弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象(s),不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象,弱引用非常适合存储元数据。另一个使用弱引用的例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。 如下代码创建弱引用: Counter counter = new Counter(); // strong reference - line 1 WeakReference<Counter> weakCounter = new WeakReference<Counter>(counter); //weak reference counter = null; // now Counter object is eligible for garbage collection

    3 . 软引用(SoftReference)

    如果一个对象(如 s)只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 可以使用如下代码创建软引用: Counter prime = new Counter(); // prime holds a strong reference SoftReference soft = new SoftReference(prime) ; //soft reference variable has SoftReference to Counter Object created at line 2 prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory

    4 . 虚引用(PhantomReference)

    “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
    虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用 必须 和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

    如下代码创建虚引用:

    DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference – line 3 PhantomReference phantom = new PhantomReference(digit); // phantom reference to object created at line 3 digit = null;

    弱引用、软引用可以和一个引用队列(ReferenceQueue)联合使用,如果其所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

    在创建任何弱引用、软引用和虚引用的过程中你可以通过如下代码提供引用队列ReferenceQueue。

    ReferenceQueue refQueue = new ReferenceQueue(); //reference will be stored in this queue for cleanup DigitalCounter digit = new DigitalCounter(); PhantomReference<DigitalCounter> phantom = new PhantomReference<DigitalCounter>(digit, refQueue); 6 .Object有哪些公用方法?

    官方文档

    技术分享


    技术分享

    7 .ArrayList、LinkedList、Vector的区别

    首先我们来看一下继承关系:

    技术分享

  • 我们可以看出ArrayList、LinkedList、Vector都实现了List的接口。
    接下来分别看一下三个数据结构的官方文档说明。
  • public class ArrayList extends AbstractList
    implements List, RandomAccess, Cloneable, Serializable

    List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)

    每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。

    在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。

    List list = Collections.synchronizedList(new ArrayList(...));

    此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

  • 然后是LinkedList
  • public class LinkedList extends AbstractSequentialList
    implements List, Deque, Cloneable, Serializable

    List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

    此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

    所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

    注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:

    List list = Collections.synchronizedList(new LinkedList(…));

    此类的 iterator 和 listIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

    注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

  • 最后是Vector
  • public class Vector extends AbstractList
    implements List, RandomAccess, Cloneable, Serializable

    Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

    每个向量会试图通过维护 capacity 和 capacityIncrement 来优化存储管理。capacity 始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement 的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。

    由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。

    注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。

    从 Java 2 平台 v1.2 开始,此类改进为可以实现 List 接口,使它成为 Java Collections Framework 的成员。与新 collection 实现不同,Vector 是同步的。

  • 区别

    ArrayList 本质上是一个可改变大小的数组.当元素加入时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问.元素顺序存储 ,随机访问很快,删除非头尾元素慢,新增元素慢而且费资源 ,较适用于无频繁增删的情况 ,比数组效率低,如果不是需要可变数组,可考虑使用数组 ,非线程安全.

    LinkedList 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList. 适用于 :没有大规模的随机读取,大量的增加/删除操作.随机访问很慢,增删操作很快,不耗费多余资源 ,允许null元素,非线程安全.

    Vector (类似于ArrayList)但其是同步的,开销就比ArrayList要大。如果你的程序本身是线程安全的,那么使用ArrayList是更好的选择。
    Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.

  • 8.String、StringBuffer与StringBuilder的区别

    先看官方文档:

  • String
  • public final class String extends Object
    implements Serializable, Comparable, CharSequence
    String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。

    字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。例如:

    String str = "abc"

    等效于:

    char data[] = {‘a‘, ‘b‘, ‘c‘}; String str = new String(data);

    下面给出了一些如何使用字符串的更多示例:

    System.out.println("abc"); String cde = "cde"; System.out.println("abc" + cde); String c = "abc".substring(2,3); String d = cde.substring(1, 2);

    String 类包括的方法可用于检查序列的单个字符、比较字符串、搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写。大小写映射基于 Character 类指定的 Unicode 标准版。

    Java 语言提供对字符串串联符号(”+”)以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。有关字符串串联和转换的更多信息,请参阅 Gosling、Joy 和 Steele 合著的 The Java Language Specification。

    除非另行说明,否则将 null 参数传递给此类中的构造方法或方法将抛出 NullPointerException。

    String 表示一个 UTF-16 格式的字符串,其中的增补字符 由代理项对 表示(有关详细信息,请参阅 Character 类中的 Unicode 字符表示形式)。索引值是指 char 代码单元,因此增补字符在 String 中占用两个位置。

    String 类提供处理 Unicode 代码点(即字符)和 Unicode 代码单元(即 char 值)的方法。

  • StringBuffer
  • public final class StringBuffer extends Object
    implements Serializable, CharSequence

    线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。

    可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

    StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。

    例如,如果 z 引用一个当前内容为 “start” 的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含 “startle”,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含 “starlet”。

    通常,如果 sb 引用 StringBuilder 的一个实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。

    当发生与源序列有关的操作(如源序列中的追加或插入操作)时,该类只在执行此操作的字符串缓冲区上而不是在源上实现同步。

    每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从 JDK 5 开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。

  • StringBuilder
  • public final class StringBuilder extends Object
    implements Serializable, CharSequence

    一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。

    在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串生成器中。append 方法始终将这些字符添加到生成器的末端;而 insert 方法则在指定的点添加字符。

    将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。

    1 .上String、StringBuffer与StringBuilder的原代码中的部分代码

    java.io.Serializable, Comparable<String>, CharSequence { value[]; /** * Allocates a new [email protected] String} so that it represents the sequence of * characters currently contained in the character array argument. The * contents of the character array are copied; subsequent modification of * the character array does not affect the newly created string. * * @param value * The initial value of the string */ public String(String original) { this.value = Arrays.copyOf(value, value.length); // 把传入构造函数original字符串切分成字符数组并赋给value[]; } } .io.Serializable, CharSequence { /** * Constructs a string buffer initialized to the contents of the * specified string. The initial capacity of the string buffer is * <code>16</code> plus the length of the string argument. * * @param str the initial contents of the buffer. * @exception NullPointerException if <code>str</code> is <code>null</code> */ public StringBuffer(String str) { super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组 append(str); //将str切分成字符序列并加入到value[]中 } public synchronized StringBuffer append(String str) { super.append(str); return this; } } .io.Serializable, CharSequence { /** * Constructs a string builder initialized to the contents of the * specified string. The initial capacity of the string builder is * <code>16</code> plus the length of the string argument. * * @param str the initial contents of the buffer. * @throws NullPointerException if <code>str</code> is <code>null</code> */ public StringBuilder(String str) { super(str.length() + 16); append(str); } public StringBuilder append(String str) { super.append(str); return this; } } Appendable, CharSequence { /** * The value is used for character storage. */ char[] value; /** * Creates an AbstractStringBuilder of the specified capacity. */ AbstractStringBuilder(int capacity) { value = new char[capacity]; } public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } }

    2 .我们可以看出在String中构造器传入的字符串对象被切分成字符序列,存放在其私有final的类变量value[]中。
    new String(“java”)使得value[]={‘j’,’a’,’v’,’a’},之后这个String对象中的value[]再也不能改变了,只能读,所以是线程安全的。

    StringBuffer和StringBuilder都继承了AbstractStringBuilder 这个抽象类,在他们的构造函数中传入字符串,会调用父类AbstractStringBuilder中对应的构造函数和append函数,父类构造函数新建一个长度为(字符串长度+末尾附加16个空元素)的数组然后通过父类append函数把字符串转换成字符数组value[]中(这里的char[] value 没有final,StringBuffer和StringBuilder中的字符串是可变的)。以后通过append()方法将新字符串加入value[]末尾。这样也就改变了value[]的内容和大小了。

    StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
    而StringBuilder不是线程安全的。

    String s = "ja" +"va"

    String s1="ja"; StringBuffer sb=new StringBuffer("va"); sb.append(s1);

    String s1="ja"; String s2 = "va"; String s = s1 +s2

    在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的”+”连接操作效率最高。
    时间上 ①<③

    StringBuffer对象的append效率要高于两个String对象的”+”连接操作。
    时间上 ②<③

    一般来说 执行时间上从快到慢 StringBuilder、StringBuffer、String

    非多线程操作字符串缓冲区建议使用 StringBuilder。

  • 抽象类和接口
  • 抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;

    而接口中只是对方法的申明和常量的定义。

    9 .Map、Set、List、Queue、Stack的特点与用法
  • Map
  • 键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。

    某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。

    Map中元素,可以将key序列、value序列单独抽取出来。
    使用keySet()抽取key序列,将map中的所有keys生成一个Set。
    使用values()抽取value序列,将map中的所有values生成一个Collection。
    为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。

  • Set
  • 一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。

    不可随机访问包含的元素

    只能用Iterator实现单向遍历

    Set 没有同步方法

  • List
  • 可随机访问包含的元素
    元素是有序的
    可在任意位置增、删元素
    不管访问多少次,元素位置不变
    允许重复元素
    用Iterator实现单向遍历,也可用ListIterator实现双向遍历

  • Queue
  • 先进先出

    Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。 如果要使用前端而不移出该元素,使用element()或者peek()方法。
    值得注意的是LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。

    Queue 实现通常不允许插入 null 元素,尽管某些实现(如 LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。

  • Stack
  • 后进先出

    Stack继承自Vector(可增长的对象数组),也是同步的
    它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法。

  • 用法
  • 如果涉及到堆栈、队列等操作,应该考虑用List;

    对于需要快速插入,删除元素,应该使用LinkedList;

    如果需要快速随机访问元素,应该使用ArrayList。

    如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高

    10 .HashMap和HashTable的区别
  • HashTable
  • Hashtable继承于Dictionary字典,实现Map接口

    键、值都不能是空对象

    多次访问,映射元素的顺序相同

    线程安全

    hash算法 ,Hashtable则直接利用key本身的hash码来做验证

    数据遍历的方式 Iterator (支持fast-fail)和 Enumeration (不支持fast-fail)

    缺省初始长度为11,内部都为抽象方法,需要 它的实现类一一作自己的实现

    备注:程序在对 collection 进行迭代时,某个线程对该 collection 在结构上对其做了修改,这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。

  • HashMap
  • HashMap继承于AbstractMap抽象类

    键和值都可以是空对象

    多次访问,映射元素的顺序可能不同

    非线程安全
    HashMap可以通过下面的语句进行同步:
    Map m = Collections.synchronizeMap(hashMap);

    检测是否含有key时,HashMap内部需要将key的hash码重新计算一边再检测

    数据遍历的方式 Iterator (支持fast-fail)

    缺省初始长度为16,其内部已经实现了Map所需 要做的大部分工作, 它的子类只需要实现它的少量方法