Java SE面试题

来源:互联网 发布:网络直播怎么兴起的 编辑:程序博客网 时间:2024/06/13 10:08

IO流

  1. 文件切割器

  2. 定义功能,获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示。并不要在运行程序。

  3. 对指定目录进行所有内容的列出(包含子目录中的内容)

多线程

  1. Java 多线程同步的五种方法
    http://www.codeceo.com/article/java-multi-thread-sync.html

  2. 什么是ThreadLocal变量?
    https://my.oschina.net/lichhao/blog/111362

  3. 一个线程运行时发生异常会怎样?
    这是我在一次面试中遇到的一个很刁钻的Java面试题, 简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。

  4. sleep() 和 wait() 有什么区别?
    sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在notfiy方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。对于wait的讲解一定要配合例子代码来说明,才显得自己真明白。

  5. 请阐述一下你对JAVA多线程中“锁”的概念的理解。

  6. 简述synchronized和java.util.concurrent.locks.Lock的异同 ?
    主要相同点:Lock能完成synchronized所实现的所有功能
    主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

  7. 线程的基本概念、线程的基本状态以及状态之间的关系
    线程是进程中某个单一顺序的控制流,是程序执行流的最小单位。线程由线程ID、当前指令指针、寄存器集合和堆栈组成。线程是进程的一个实体,是被系统调度和分配的基本单位,线程与同一进程中的其他线程共享进程的全部资源。
    线程有五种基本状态:新生状态,就绪状态,运行状态,阻塞状态,死亡状态。状态间关系如下图:
    http://www.cnblogs.com/Rosanna/p/3581835.html

  8. 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
    分几种情况:
    1.其他方法前是否加了synchronized关键字,如果没加,则能。
    2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。
    3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
    4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。
    来源: http://blog.csdn.net/nyistzp/article/details/14127547

  9. 同步和异步有何异同,在什么情况下分别使用他们?举例说明。
    同步和异步是相对于多线程来说的。
    同步可防止并发, 主要出于数据安全的考虑。如果数据将在线程间共享,例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,
    那么这些数据就是共享数据,必须进行同步存取。
    异步允许并发,当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,
    在很多情况下采用异步途径往往更有效率。
    来源: http://blog.csdn.net/yuguiyang1990/article/details/15339827

  10. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

基础语法

  1. 给出下面的二叉树先序、中序、后序遍历的序列?
    先序: A B D E G I H C F
    中序: D B G I E H A C F
    后序: D I G H E B F C A

  2. 为什么在重写了equals()方法之后也必须重写hashCode()方法?
    object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
    注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
    (1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
    (2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
    如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象
    (如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址
    hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
    这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,
    当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,
    导致混淆,因此,就也需要重写hashcode()
    来源: http://www.cnblogs.com/happyPawpaw/p/3744971.html

  3. abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
    来源: http://zhoujiyu.blog.51cto.com/675686/428999

  4. 能不能自己写个类,也叫java.lang.String?
    http://blog.csdn.net/tang9140/article/details/42738433

  5. heap和stack有什么区别。

  6. try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
    你中招了……话说当年某天才还开玩笑说用这个例子做笔试题拿去面试的,后来不知道有没有真用。《深入Java虚拟机》里有详细说明,先执行return,把结果缓存起来,然后再执行finally,最终返回缓存的结果。

  7. 下面这条语句一共创建了多少个对象:String s=”a”+”b”+”c”+”d”;
    对于如下代码:
    String s1 = “a”;
    String s2 = s1 + “b”;
    String s3 = “a” + “b”;
    System.out.println(s2 == “ab”);
    System.out.println(s3 == “ab”);
    第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
    题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,
    String s =”a” + “b” + “c” + “d”;
    System.out.println(s== “abcd”);
    最终打印的结果应该为true。
    来源: http://blog.sina.com.cn/s/blog_8f329b7b0101nkh2.html

  8. 数组有没有length()这个方法? String有没有length()这个方法?
    数组没有length()这个方法,有length的属性。String有有length()这个方法

  9. Anonymous Inner Class(匿名内部类)是否可以 extends(继承)其它类,是否可以 implements(实现)interface(接口)?
    1、什么是匿名内部类?
    内部类,存在于另一个类内部的类,而匿名内部类,顾名思义,就是没有名字的内部类。
    2、为什么需要匿名内部类?
    每个inner class都能够各自继承某一实现类(implementation)。因此,inner class不受限于outer class是否已继承自某一实现类。
    如果少了inner class所提供的“继承自多个具象(concrete)或抽象(abstract)类”的能力,设计上和编程上的某些问题会变得十分棘手。
    所以,从某个角度来看inner class,你可以说它是多重继承问题的完整解决方案。interface能够解决其中一部分问题,但inner classes
    才能有效而实际地允许“多重实现继承(multiple implementation)”。也就是说,inner classes实际上允许你继承多个non-interface。
    从这个层面上使用内部类时一般都是通过其父类或继承的接口来进行实例化和初始化的,如3中所示,这时内部类的名字是多余的,所以
    就使用了匿名内部类
    3、怎么实现匿名内部类?
    匿名内部类的实现非常简单,如有一接口
    Interface A{
    public void method();
    }
    这时你可以写一个如下的语句得到接口A的实现类的对象
    A a=new A(){ public void method(){System.out.println(“hehe”);} };
    而其中的{ public void method(){System.out.println(“hehe”);} };就是定义了一个匿名内部类
    4、问题答案
    以上可以看出匿名内部类在实现时必须借助一个类或一个接口,若从这个层次上讲它是可以继承其他类也可以实现接口的,
    但若是通过extends
    或implements
    关键字那是不可能的
    来源: http://blog.csdn.net/fhm727/article/details/5220003

  10. 什么是内部类?Static Nested Class 和 Inner Class 的不同。

  11. 什么是java序列化?如何实现java序列化?或者请解释Serializable接口的作用。
    将一个 java 对象变成字节流的形式传出去或者从一个字节流中恢复成一个 java 对象
    序列化是将对象状态转换为可保持或传输的格式的过程。
    java 中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个字节流中使其可以通过 socket 进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来重构一个相同的对象。一般而言,要使得一个类可以序列化,只需简单实现 java.io.Serializable 接口即可。
    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。 序列化的实现:将需要被序列化的类实现 Serializable 接口,该接口没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream )来构造一个 ObjectOutputStream (对象流)对象,接着,使用 ObjectOutputStream 对象的 writeObject(Object obj) 方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。

  12. 运行时异常与一般异常有何异同
    运行时异常和非运行时异常
    (1)运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
    当出现 RuntimeException 的时候,我们可以不处理。当出现这样的异常时,总是由虚拟机接管。比如:我们从来没有人去处理过 NullPointerException 异常,它就是运行时异常,并且这种异常还是最常见的异常之一。
    出现运行时异常后,如果没有捕获处理这个异常(即没有 catch),系统会把异常一直往上层抛,一直到最上层,如果是多线程就由 Thread.run() 抛出,如果是单线程就被 main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。运行时异常是 Exception 的子类,也有一般异常的特点,是可以被 catch 块处理的。只不过往往我们不对他处理罢了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。
    如果不想终止,则必须捕获所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理。
    (2)非运行时异常是 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。如 IOException、SQLException 等以及用户自定义的 Exception 异常。对于这种异常,JAVA 编译器强制要求我们必需对出现的这些异常进行 catch 并处理,否则程序就不能编译通过。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆 catch 块去处理可能的异常。

  13. Java 的接口和 C++ 的虚类的相同和不同处C++虚类相当与 java 里面的抽象类,与接口的不同之处如下:
    1、一个子类只能继承一个抽象类(虚类),但能实现多个接口;
    2、一个抽象类可以有构造方法,接口没有构造方法;
    3、一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有声明;
    4、一个抽象类可以是 public、private、protected、default,
    接口只有 public;
    5、一个抽象类中的方法可以是 public、private、protected、default,
    接口中的方法只能是 public 和 default.
    相同之处:都不能实例化。
    补充说明:接口是一类特殊的抽象类,是更抽象的抽象类,你可能这样理解。抽象类是一个不完整的类,接口只是定义了一些功能。打个比方,用抽象类和接口分别描述“猪”,抽象类就是在一般的类之前加 abstract ,说:猪能用四肢跑,猪还能怎么怎么,接口的话只能说,猪会跑,用什么跑就是子类的事啦。

  14. swtich 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上
    在 switch(expr1)中,expr1只能是一个整数表达式或者枚举常量(更大字体),整数表达式可以是 int 基本类型或 Integer 包装类型,由于,byte,short,char 都可以隐含转换为 int,所以,这些类型以及这些类型的包装类型也是可以的。显然,long 和 String 类型都不符合 switch 的语法规定,并且不能被隐式转换成 int 类型,所以,它们不能作用于 switch 语句中。Java 7中,switch的参数可以是String类型了

  15. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递
    是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。
    在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。但重要的是要区分参数是如何传递的,这才是该节选的意图。Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数。参数可以是对象引用,而 Java 应用程序是按值传递对象引用的。
    Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型。当作为参数传递给一个方法时,处理这两种类型的方式是相同的。两种类型都是按值传递的;没有一种按引用传递。
    按 值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。按引用传递意味 着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数的值,调用代码中的原始值也随之改变。如果 函数修改了该参数的地址,调用代码中的原始值不会改变.
    当传递给函数的参数不是引用时,传递的都是该值的一个副本(按值传递)。区别在于引用。在 C++ 中当传递给函数的参数是引用时,您传递的就是这个引用,或者内存地址(按引用传递)。在 Java 应用程序中,当对象引用是传递给方法的一个参数时,您传递的是该引用的一个副本(按值传递),而不是引用本身。
    Java 应用程序按值传递参数(引用类型或基本类型),其实都是传递他们的一份拷贝.而不是数据本身.(不是像 C++ 中那样对原始值进行操作。)
    来源: http://xinklabi.iteye.com/blog/1996105

  16. 两个对象值相同 (x.equals(y) == true) ,但却可有不同的 hashcode ,这句话对不对?
    对。
    如果对象要保存在HashSet或HashMap中,它们的equals相等,那么,它们的hashcode值就必须相等。
    如果不是要保存在HashSet或HashMap,则与hashcode没有什么关系了,这时候hashcode不等是可以的,例如arrayList存储的对象就不用实现hashcode,当然,我们没有理由不实现,通常都会去实现的。

集合、容器、泛型等

  1. 集合框架中的泛型有什么优点?
    Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。

  2. 你所知道的集合类都有哪些?主要方法?
    线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。
    Collection
    ├List
    │├LinkedList
    │├ArrayList
    │└Vector
    │ └Stack
    └Set
    Map
    ├Hashtable
    ├HashMap
    └WeakHashMap
    Collection接口
    Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
    所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
    如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
    Object obj = it.next(); // 得到下一个元素
    }
    由Collection接口派生的两个接口是List和Set。
    List接口
    List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
    和下面要提到的Set不同,List允许有相同的元素。
    除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
    实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
    LinkedList类
    LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
    注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(…));
    ArrayList类
    ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
    size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
    每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
    和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
    Vector类
    Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。
    Stack 类
    Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
    Set接口
    Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
    很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
    请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
    Map接口
    请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
    Hashtable类
    Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
    添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
    Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
    使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));
    要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);
    由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
    如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
    Hashtable是同步的。
    HashMap类
    HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
    WeakHashMap类
    WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
    总结
    如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
    如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
    要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
    尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

  3. Comparable和Comparator接口是什么?Comparable和Comparator接口有何区别?
    http://www.cnblogs.com/skywang12345/p/3324788.html

  4. ArrayList 和 Vector 的区别 ,HashMap 和 Hashtable的区别
    这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,,并且其中的数据是允许重复的,这是HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与hashset没有任何关系,但为了说清楚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倍。
    HashMap和Hashtable的区别
    (条理上还需要整理,也是先说相同点,再说不同点)
    HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。
    HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
    HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
    Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
    最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
    Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
    就HashMap与HashTable主要从三方面来说。
    一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
    二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
    三.值:只有HashMap可以让你将空值作为一个表的条目的key或value

网络编程

  1. 使用TCP建立交互方式

  2. 聊天程序(多线程)

  3. 使用UDP建立信息接收端

  4. 使用UDP建立信息发送端