计算机综合面试题总结

本科二年级上半学期的时候开始Java编程,到现在也5年了,但是一直也没有做过关于Java的大型项目,因此感觉进步也不是很大。现在要找工作了,需要重新把Java和JVM,计算机操作系统,数据结构和算法,计算机网络,计算机组成原理,数据库原理等的基础知识点总结一下,本来是要总结在纸质本子上面的,但是效率太低了,因此就索性再写篇博客吧。

一. Java SE基础知识点总结

1. Java的8种基本数据相关的知识点。

数据类型 位数 默认值 包装类
long 64 0L Long
int   32 0 Integer
short 16 (short)0 Short
byte 8 (byte)0 Byte
char 16 \u0000 Character
float 32 0.0f Float
double 64 0.0d Double
boolean false Boolean

 

 

 

 

 

 

 

 

 

 

解析:需要说明的是\u开头的是一个Unicode码的字符,每一个\u0000都代表了一个空格''。

2. switch能否用string做参数?

解析:switch的参数不可以是string。switch是用"="进行比较,而string没有"="的概念,只有strcmp。[JDK 1.7版本及以后已经支持]

3. equals与==的区别。

解析:简单理解,基本数据类型之间的比较,应该使用==,因为它们比较的是值。引用数据类型在用==进行比较的时候,比较的是它们在堆内存中的存放地址,而equals比较的是堆内存中存放的内容。[Object类中提供的equals()默认是比较地址的]

4. Object有哪些公用方法?

方法名称 类型 描述
public Object() 构造方法 构造方法
public boolean equals(Object obj) 普通方法 对象比较
public int hashCode() 普通方法 取得Hash码
public String toString() 普通方法 对象打印时调用

 

 

 

 

 

 

解析:Object类提供的hashCode()默认实现确实保证每个对象的hash码不同(在对象的内存地址基础上经过特定算法返回一个hash码)。

5. Java的四种引用"强软弱虚"以及用到的场景 [7][8][9][10]。

解析:现在首先思考一个问题,为什么引用还要分类呢?答案:合理的使用引用可以帮助垃圾回收器更好的管理Java内存。

  • 强引用(Strong Reference):如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。[使用最普遍的引用]
  • 软引用(Soft Reference):如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。[通常用来实现内存敏感的高速缓存]
  • 弱引用(Weak Reference):只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。[通常用于Debug、内存监视工具等程序中]
  • 虚引用(Phantom Reference):如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。[通常用来跟踪对象被垃圾回收的活动]

6. hashCode的作用 [11]。

解析:Java中的集合有2类,分别是List,Set。现在我们考虑Set,即元素不能重复。但是,如何判断元素是否重复呢?容易想到的方法就是Object.equals()。现在会遇到一个问题,那就是当元素很多时,后添加到集合中的元素比较的次数就非常多。这样hashCode就派上用场了,它返回一个对象存储的物理地址(实际可能并不是这样的,总之是对象唯一的标识符)。当Set要添加新元素时,首先调用这个元素的hashCode(),就能定位到它应该放置的位置。如果这个位置上没有元素,它就可以直接存储在这个位置上;如果这个位置上已经有元素了,
就调用它的equals()与新元素进行比较,如果相同就不存储了,否则就散列其它的地址存储。这样一来实际调用equals()的次数就大大降低了,几乎只需要一两次。因此,如果两个对象相同,那么它们的hashCode值一定要相同;如果两个对象的hashCode相同,那么它们并不一定相同。    

7. ArrayList、LinkedList、Vector的区别 [12]。

解析:

  • ArrayList和Vector可以看作数组,而LinkedList可以看作链表;
  • ArrayList属于新的操作类,异步处理,非线程安全,只能使用Iterator,foreach输出;
  • Vector属于旧的操作类,同步处理,线程安全,只能使用Iterator,foreach,Enumeration输出。

8. String、StringBuffer与StringBuilder的区别 [13]。

解析:

  • String:字符串常量;
  • StringBuffer:字符串变量,线程安全的操作类;
  • StringBuilder:字符串变量,非线程安全的操作类。

9. Map、Set、List、Queue、Stack等的相关知识点。

解析:类集对于Java开发极其重要,务必精通类集。Java中的类集相当于C++中的STL和Boost等类库。

接口或类 描述
Collection     是存放一组单值的最大父接口,在新的开发标准中已经很少直接使用此接口进行操作了。
List 是Collection接口的子接口,对其进行了大量的扩充,里面的内容是允许重复的。
Set 是Collection接口的子接口,没有对其进行扩充,里面的内容是不允许重复的。
Map 是存放一对值的最大父接口,即接口中的每个元素都是一对的,以key—>value的形式保存。
Iterator 集合的输出接口,用于输出集合中的内容,只能进行从前到后的单向输出。
ListIterator 是Iterator的子接口,可以进行由前向后或由后向前的双向输出。
Enumeration  是最早的输出接口,用于输出指定集合中的内容。
SortedSet 单指的排序接口,实现此接口的集合类,里面的内容可以使用比较器排序。
SortedMap 存放一对值的排序接口,实现此接口的集合类,里面的内容按照key排序,使用比较器排序。
Queue 队列接口,此接口的子类可以实现队列操作。
Map.Entry Map的内部接口,每个Map.Entry对象都保存着一对key->value的内容,每个Map接口中都保存有多个Map.Entry接口实例。
HashMap 无序存放的,是新的操作类,key不允许重复。
Hashtable 无序存放的,是旧的操作类,key不允许重复。
TreeMap 可以排序的Map集合,按集合中的key排序,key不允许重复。
WeakHashMap 若引用的Map集合,当集合中的某些内容不再使用时清除掉无用的数据,使用gc进行回收。
IdentityHashMap                                                               key可以重复的Map集合。
Stack k可以完成先进后出的操作。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

10. HashMap和Hashtable的区别。

解析:

比较点 HashMap Hashtable
        推出时间         JDK 1.2之后推出,属于新的操作类 JDK 1.0时推出,属于旧的操作类
性能 采用异步处理方式,性能更高 使用同步处理方式,性能较低
线程安全 属于非线程安全的操作类 属于线程安全的操作类
空键

允许将key设置为null

不允许将key设置为null,否则将出现Null Pointer Exception

 

 

 

 

 

 

 

 

11. HashMap和ConcurrentHashMap的区别及源码分析。

解析:

  • HashMap因为采用的是异步处理方式,所以是非安全的操作类。
  • ConcurrentHashMap在多线程中是安全的操作类,那与Hashtable的区别是什么呢?Hashtable采用的是单个锁,而它采用的是多个锁,在并发控制方面比Hashtable更加的优化和高效。
  • 至于HashMap和ConcurrentHashMap的源码分析,推荐博客 [14][15]。

12. TreeMap、HashMap、LinkedHashMap的区别 [16]。

解析:java.util.Map接口有四个实现类,分别是HashMap,Hashtable,LinkedHashMap和TreeMap。

  • TreeMap:可以按照自然顺序或自定义顺序都key进行排序。
  • HashMap:在Map中插入、删除和定位元素,它是最好的选择。
  • LinkedHashMap:它是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现。

13. Collection与Collections的区别。

解析:

  • Collection是个java.util下的接口,它是各种集合结构(Set,List,Map等)的父接口。
  • Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。

14. 在try catch finally中,如果try里有return,那么finally还会执行么 [17]?

解析:

  • 任何执行try或者catch中的return语句之前,都会先执行finally语句;
  • 如果finally中有return语句,那么程序就return了。因此,finally中的return是一定会被return的;
  • 在finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中的返回值。

15. Excption与Error等相关的知识点。Out Of Memory(OOM)你遇到过哪些情况,Stack Over Flow(SOF)你遇到过哪些情况。

解析:

计算机综合面试题总结_第1张图片

  • 异常的最大父类是Throwable,它包含2个子类,分别是Exception,Error。Exception表示程序处理的异常,而Error表示JVM错误,一般不由程序开发人员处理;
  • Out Of Memory(OOM):引起OOM主要有2个原因,分别是内存泄漏和内存溢出(即堆溢出和栈溢出) [19][20]。
  • Stack Over Flow(SOF):(堆)栈溢出主要发生在递归的调用中 [21]。  
  • OOM和SOF:递归调用可以导致栈溢出,不断创建对象可以导致堆溢出。

16. Java面向对象的三个特征与含义。

解析:

  • 封装性:它是将类的一些敏感信息隐藏在类的类部,不让外界直接访问到,但是可以通过getter()和setter()间接访问。
  • 继承性:子类通过一种方式来接受父类所有的公有的,受保护的成员变量和成员方法。
  • 多态性:程序在运行的过程中,同一种类型在不同的条件下表现不同的结果,这种不定状态的表现形式称为多态性。

17. Override和Overload的含义和区别 [22]。

解析:方法的重写(Override)和重载(Overload)是Java多态性的不同表现。

  • 区别:重写(Override)是父类与子类之间多态性的一种表现,而重载(Overload)是一个类中多态性的一种表现。如果在子类中定义某方法与其
  • 重写(Override):父类有相同的名称和参数,我们说该方法被重写 (Override)。子类的对象使用这个方法时,将调用子类中的定义,而父类中的定义如同被屏蔽了。
  • 重载(Overload):如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型或有不同的参数次序,则称为方法的重载(Overload)。

18. interface与abstract类的区别。

解析:

区别点 抽象类 接口
定义 包含一个抽象方法的类 抽象方法和全局常量的集合
组成 构造方法,抽象方法,普通方法,常量,变量 常量,抽象方法
使用 子类继承抽象类(extends) 子类实现接口(implements)
关系 抽象类可以实现多个接口 接口不能继承抽象类,但是允许继承多个接口
常见设计模型 模板设计 工厂设计,代理设计
对象 都通过对象的多态性产生实例化对象
局限 抽象类有单继承的局限 接口没有此局限
实际 作为一个模板 作为一个标准或表示一种能力
选择 如果抽象类和接口都可以使用,则优先使用接口,避免单继承的局限
特殊 一个抽象类中可以包含多个接口,一个接口中可以包含多个抽象类

 

 

 

 

 

 

 

 

 

 

 

 

19. Static class 与non static class的区别 [23]。

解析:在Java中可以有静态实例变量、静态方法、静态块,当然也可以有静态类,但是用static不能修饰顶级类,只能修饰内部类。
静态内部类和非静态内部类究竟有什么区别呢?

  • 内部静态类不需要有指向外部类的引用,因为用static声明的静态内部类变成了外部类,但是非静态内部类需要持有对外部类的引用;
  • 非静态内部类能够访问外部类的静态和非静态成员(数据成员和成员函数),但是静态内部类只能访问外部类的静态成员(数据成员和成员函数);
  • 非静态内部类不能脱离外部类实体被创建,但是非静态内部类可以访问外部类的数据和函数,因为它就在外部类里面;
  • 如果在方法中定义的内部类要想访问方法中的参数,那么必须在参数前加上final关键字。

20. Java多态的实现原理 [24]。

解析:
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

21. 实现多线程的两种方法:Thread与Runable。

解析:

  • Thread类:它是在java.lang包中定义的,一个类只要继承了Thread类,此类就称为多线程实现类。在Thread子类中,必须明确地覆写Thread类中的run(),因为此方法为线程的主体。
  • Runable接口:在Java中也可以通过实现Runnable接口的方式实现多线程,Runnable接口中只定义了一个抽象方法:public void run()。
  • 区别:通过public class Thread extends Object implements Runnable发现,Thread类也是Runnable接口的子类,但是在Thread类中并没有完全地实现Runnable接口中的run(),而是调用的Runnable接口中的run()。因此,如果通过继承Thread类实现多线程,那么必须覆写run()。实际上,Runnable接口相对于Thread类来说更适合多个相同程序代码的线程去处理同一资源的情况。

22. 线程同步的方法:sychronized、lock、reentrantLock、Atomic等 [25][26]。

解析:synchronized是jvm虚拟机的关键字,在java.util.concurrent.locks命名空间中还有一个Lock接口,和Lock接口的实现类ReentrantLock(可重入锁)。

  • 如果资源竞争不是很激烈,偶尔会有同步的情形,那么synchronized是非常合适的选择;
  • ReentrantLock提供了多样化的同步,比如有可以被Interrupt的同步(synchronized的同步是不能被Interrupt的)等。如果资源竞争不是很激烈,那么它的性能稍微比synchronized差点儿。但是,当资源竞争很激烈的时候,synchronized是非常合适的选择;
  • 与ReentrantLock的情况类似,如果资源竞争不是很激烈,那么它的性能稍微比synchronized差点儿。但是,当资源竞争很激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是,它也有一个缺点,那就是只能同步一个值。

23. 锁的等级:对象锁(方法锁)、类锁等相关知识点 [27]。

解析:简单来说,就是两个锁的粒度不同。对象锁的粒度是对象,而类锁的对象是类。

  • 对象锁就是在一个类的方法前面加synchronized关键字(对象的方法);
  • 类锁就是在一个类的方法前面加synchronized static关键字(类的方法)。

24. 写出生产者和消费者模式 [28]。

解析:

问题过程:生产者用于将消息放入缓冲区,而消费者用于从缓冲区中取出消息。问题出现在当缓冲区已经满了,而此时生产者还想向其中放入一个新的数据项的情形,其解决方法是让生产者此时进行休眠,等待消费者从缓冲区中取走了一个或者多个数据项后再去唤醒它。同样地,当缓冲区已经空了,而消费者还想去取数据项,此时可以让消费者进行休眠,等待生产者放入一个或者多个数据项时再唤醒它。  问题本质:生产者和消费者模式考察的知识点是Java中的多线程,解决的问题是两个进程如何共享同一个资源(缓冲区)。

(1)PublicResource.java如下所示:

 1 /**
 2  * 生产者线程,功能是向缓冲区中放入数据项
 3  */
 4 public class ProducerThread implements Runnable {
 5     private PublicResource resource;
 6 
 7     public ProducerThread(PublicResource resource) {
 8         this.resource = resource;
 9     }
10 
11     @Override
12     public void run() {
13         for (int i = 0; i < 10; i++) {
14             try {
15                 Thread.sleep((long) (Math.random() * 1000));
16             } catch (InterruptedException e) {
17                 e.printStackTrace();
18             }
19             resource.increase();
20         }
21     }
22 }
View Code

(2)ProducerThread.java如下所示:

 1 /**
 2  * 生产者线程,功能是向缓冲区中放入数据项
 3  */
 4 public class ProducerThread implements Runnable {
 5     private PublicResource resource;
 6 
 7     public ProducerThread(PublicResource resource) {
 8         this.resource = resource;
 9     }
10 
11     @Override
12     public void run() {
13         for (int i = 0; i < 10; i++) {
14             try {
15                 Thread.sleep((long) (Math.random() * 1000));
16             } catch (InterruptedException e) {
17                 e.printStackTrace();
18             }
19             resource.increase();
20         }
21     }
22 }
View Code

(3)ConsumerThread.java如下所示:

 1 public class ConsumerThread implements Runnable {
 2     public PublicResource resource;
 3 
 4     public ConsumerThread (PublicResource resource) {
 5         this.resource = resource;
 6     }
 7 
 8     @Override
 9     public void run() {
10         for (int i = 0; i < 10; i++) {
11             try {
12                 Thread.sleep((long) (Math.random() * 1000));
13             } catch (InterruptedException e) {
14                 e.printStackTrace();
15             }
16             resource.decrease();
17         }
18     }
19 }
View Code

(4)ProducerConsumerTest如下所示:

 1 public class ProducerConsumerTest {
 2     public static void main(String[] args) {
 3         PublicResource resource = new PublicResource();
 4 
 5         new Thread(new ProducerThread(resource)).start();
 6         new Thread(new ConsumerThread(resource)).start();
 7         new Thread(new ProducerThread(resource)).start();
 8         new Thread(new ConsumerThread(resource)).start();
 9         new Thread(new ProducerThread(resource)).start();
10         new Thread(new ConsumerThread(resource)).start();
11     }
12 }
View Code

25. ThreadLocal的作用 []。

解析:

ThreadLocal并不是一个Thread,而是一个Thread的局部变量。它的作用是为每个使用该变量的线程都提供一个变量的副本,每个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每个线程都完全拥有该变量一样。[public class java.lang.ThreadLocal<T> extends Object]

26. ThreadPool的作用 [29]。

解析:

ThreadPool顾名思义就是线程的容器,和数据库中连接池的概念类似。它的作用是减少创建和销毁线程的额外开销,并且最大程度地利用线程。Java中自带的线程池类:public class java.util.concurrent.ThreadPoolExecutor extends AbstractExecutorService。

27. Concurrent中的ArrayBlockingQueue、CountDownLatch等 [31]。

解析:

  • ArrayBlockingQueue:阻塞队列是指一个指定长度的队列,如果队列满了,那么添加新元素的操作会被阻塞等待,直到有空位为止。同样,当队列为空的时候,请求队列元素的操作同样会被阻塞等待,直到有可用元素为止。[public class java.util.concurrent.ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable]
  • CountDownLatch:可以看作是一个倒数计数的锁,当计数为0时触发特定的事件,这样我们就可以让主线程等待子线程的结束。[public class java.util.concurrent.CountDownLatch extends Object]

28. wait()和sleep()的区别。

解析:

  • sleep()来自Thread类,而wait()来自Object类;
  • sleep()必须捕获异常,而wait()不需要捕获异常;
  • sleep()不释放同步锁,而wait()释放同步锁。

29. Java NIO与IO的区别 [32]。

解析:

  • Java NIO是面向块的,而Java IO是面向流的;
  • Java NIO是非阻塞的I/O,而Java IO是阻塞的I/O。

30. 反射机制相关的知识点。

解析:通过反射机制可以取得一个类所继承的父类,实现的接口,类中的全部构造方法,全部的普通方法和全部的属性。

31. List<String>类型的变量能否直接赋值给List<Object>类型的变量 [33]。

解析:不能。因为泛型中不存在继承关系。

32. Java 1.8的新特性 [34]。

33. 23种设计模式。

34. JNI的使用。

 

二. JVM基础知识点总结

1. 内存模型以及分区,需要详细到每个区放什么。

2. 堆里面的分区:Eden,survival from to,老年代,各自的特点。

3. 对象创建方法,对象的内存分配,对象的访问定位。

4. GC的两种判定方法:引用计数与引用链。

5. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?

6. GC收集器有哪些?CMS收集器与G1收集器的特点。

7. Minor GC与Full GC分别在什么时候发生?

8. 几种常用的内存调试工具:jmap、jstack、jconsole。

9. 类加载的五个过程:加载、验证、准备、解析、初始化。

10. 双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。

11. 分派:静态分派与动态分派。

 

三. 操作系统总结

1. 进程和线程的区别。

2. 死锁的必要条件,怎么处理死锁。

3. Window内存管理方式:段存储,页存储,段页存储。

4. 进程的几种状态。

5. IPC几种通信方式。

6. 什么是虚拟内存。

7. 虚拟地址、逻辑地址、线性地址、物理地址的区别。

 

四. 计算机网络总结

1. OSI与TCP/IP各层的结构与功能,都有哪些协议。

2. TCP与UDP的区别。

3. TCP报文结构。

4. TCP的三次握手与四次挥手过程,各个状态名称与含义,TIMEWAIT的作用。

5. TCP拥塞控制。

6. TCP滑动窗口与回退N针协议。

7. Http的报文结构。

8. Http的状态码含义。

9. Http request的几种类型。

10. Http1.1和Http1.0的区别。

11. Http怎么处理长连接。

12. Cookie与Session的作用于原理。

13. 电脑上访问一个网页,整个过程是怎么样的:DNS、HTTP、TCP、OSPF、IP、ARP。

14. Ping的整个过程。ICMP报文是什么。

15. C/S模式下使用socket通信,几个关键函数。

16. IP地址分类。

17. 路由器与交换机区别。

 

五. 数据结构和算法

1. 链表与数组。

2. 队列和栈,出栈与入栈。

3. 链表的删除、插入、反向。

4. 字符串操作。

5. Hash表的hash函数,冲突解决方法有哪些。

6. 各种排序:冒泡、选择、插入、希尔、归并、快排、堆排、桶排、基数的原理、平均时间复杂度、最坏时间复杂度、空间复杂度、是否稳定。

7. 快排的partition函数与归并的Merge函数。

8. 对冒泡与快排的改进。

9. 二分查找,与变种二分查找。

10. 二叉树、B+树、AVL树、红黑树、哈夫曼树。

11. 二叉树的前中后续遍历:递归与非递归写法,层序遍历算法。

12. 图的BFS与DFS算法,最小生成树prim算法与最短路径Dijkstra算法。

13. KMP算法。

14. 排列组合问题。

15. 动态规划、贪心算法、分治算法。

16. 大数据处理:类似10亿条数据找出最大的1000个数等。

 

六. 计算机组成原理总结

 

七. 数据库原理总结

 

参考文献:

[1] 面试心得与总结:http://www.nowcoder.com/discuss/3043

[2] 《Java开发实战经典》

[3] 《Java编程思想》

[4] 《Java核心技术》(第8版)

[5] 《Java编程思想》

[6] 常见的数据库基础面试题大全:http://www.it165.net/database/html/201111/1146.html

[7] Java的四种引用:http://www.cnblogs.com/ymind/archive/2012/05/04/2483590.html

[8] Java中弱引用和软引用的区别以及虚引用和强引用介绍:http://www.jb51.net/article/49085.htm

[9] Java中四种引用(强、软、弱、虚):http://www.xuebuyuan.com/1772186.html

[10] 强引用、弱引用、软引用、虚引用:http://my.oschina.net/ydsakyclguozi/blog/404389

[11] 关于hashCode()的作用:http://my.oschina.net/91jason/blog/306996

[12] ArrayList、LinkedList、Vector区别与用法:http://www.cnblogs.com/mgod/archive/2007/08/05/.html

[13] StringBuffer与StringBuilder区别:http://zhidao.baidu.com/link?url=OgV7TUaFr8h4wGP1uI7xA0Z2nB6S9ZdEw5rPFFFFtF-VX3Q_WEWchmISKeowomV2R5YQiMqW4Zb1astS-at5R_

[14] ConcurrentHashMap:http://www.cnblogs.com/yydcdut/p/3959815.html

[15] HashMap与ConcurrentHashMap的区别:http://blog.csdn.net/xuefeng0707/article/details/40834595

[16] HashMap,LinkedMap,TreeMap的区别:http://blog.sina.com.cn/s/blog_5ded2e5b01011pn5.html

[17] 有return的情况下try catch finally的执行顺序:http://blog.csdn.net/kavensu/article/details/8067850

[18] 深入理解java异常处理机制:http://blog.csdn.net/hguisu/article/details/6155636

[19] 关于OOM的那些事儿:http://www.cnblogs.com/gaojing/archive/2012/10/30/2844938.html

[20] Java常见内存溢出(OOM)解决方案:http://www.tuicool.com/articles/myuQ7b

[21] Java内存溢出示例(堆溢出、栈溢出):http://www.jb51.net/article/49485.htm

[22] Overload与Override的区别:http://www.cnblogs.com/whgw/archive/2011/10/01/2197083.html

你可能感兴趣的:(计算机综合面试题总结)