Java 核心

  • 解释一下 OOP 的概念

    1. 抽象

    在面向对象的概念中,所有对象都是由类来描述,但是反过来,并不是所有类都是用来描述对象的。如果一个类中没有包含足够信息来描绘一个具体的对象,这样的类就是抽象类。

    1. 继承

    继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
    有些编程语言支持多重继承,即一个子类别可以同时有多个父类别,比如C++编程语言;而在有些编程语言中,一个子类别只能继承自一个父类别,比如Java编程语言,这时可以利用接口来实现与多重继承相似的效果。
    现今面向对象程式设计技巧中,继承并非以继承类别的“行为”为主,而是继承类别的“型态”,使得元件的型态一致。另外在设计模式中提到一个守则,“多用合成,少用继承”,此守则也是用来处理继承无法在执行期动态扩充行为的遗憾。

    1. 封装

    从字面上理解就是包装的意思,是指利用抽象数据类型,将数据和关于数据的操作封装起来,使其成为一个不可分割的独立实体。数据将会被保护在抽象数据类型的内部,仅能够通过暴露在表面的操作(public方法,比如setter和getter)来与这个对象进行交流和交互。用户不知道对象的内部细节,但是通过该对象提供的接口来访问对象。其好处是:减少耦合,方便地在未来修改调整自己,更加有把握地(精确地)控制成员,隐藏信息,实现细节。

    1. 多态

    使用相同的消息,使得类作出不同的反应(继承为我们使用多态打下了基础)。Java实现多态有三个必要条件:继承、重写、向上转型。

  • 面向对象的三个基本元素和五个原则
  1. 三个基本元素:

    封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。

    继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。

    多态: 多态性是指允许不同类的对象对同一消息作出 响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

  1. 五个基本原则:

    单一职责原则(Single-Resposibility Principle):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。

    开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。

    Liskov替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。

    依赖倒置原则(Dependecy-Inversion Principle):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。

    接口隔离原则(Interface-Segregation Principle):使用多个小的专门的接口,而不要使用一个大的总接口。

  • 抽象类和接口的区别?Link

    • 抽象类是一个可同时包含具体方法和抽象方法(方法未被实现)的类。抽象方法必须被该抽象类的子类实现。抽象类是可以继承的。
    • 接口像是描述类的一张蓝图或者说是类的一种契约,它包含了许多空方法,这代表着它的所有的子类都应该拥有共同点。它的子类应该提供这些空方法的具体实现。一 个类需要用 implements 来实现接口,接口可以用 extends 来继承其他接口。

    • 在设计层级理解他们的不同:

    1. 抽象的层次不同:抽象类对类的整体(包括属性,行为)都可以进行抽象,接口对类的局部进行抽象,具体来说接口仅仅是对类的行为进行抽象。
    2. 跨域不同:抽象类是 从各种子类中提取相似的部分,然后泛化成抽象类,子类可以继承这样的抽象类。 实现接口是 不存在is-a的关系的类们,你不可以称同样可以飞行的飞机和鸟为同一个抽象类,但是他们可以有同样的接口fly-able。抽象类的父类和派生类在概念上一致,接口的原生类和派生类在仅仅在局部行为上一致。
    3. 设计层次不同:抽象类是从一堆在底层的子类们来进行抽象提取,从下往上,从而产生抽象类;接口是在直接定义的高度来声明的,然后从这个高度上往下实现此接口。抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
  • 序列化是什么?如何实现它?

    • 序列化是一种将对象转换为字节流的过程,目的是为了将该对象存储到内存中,等后面再次构建该对象时可以获取到该对象先前的状态及数据信息。Java中,有两种方式可以实现序列化,既可以实现Serializable接口,也可以实现Parcelable接口。然而,在Android中,我们不应该使用Serializable接口。因为Serializable接口使用了反射机制,这个过程相对缓慢,而且往往会产生出很多临时对象,这样可能会触发垃圾回收器频繁地进行垃圾回收。相比而言,Parcelable接口比Serializable接口效率更高,性能方面要高出10x多倍。
  • 什么是单例?

  • 什么是匿名内部类?

    普通的类可以自然地实例化他自己,相反地,内部类是这样的类:
    一定要绑定上一个外部类才能进行实例化的类

    内部类提供了一种模拟车和车轮的机制,车是外部类,车轮是内部类

    而匿名内部类也就是没有名字的内部类,因为没有名字,所以只能使用一次。

    使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

    详情可见:

    http://www.cnblogs.com/nerxious/archive/2013/01/25/2876489.html

  • 对字符串进行 ==equals() 操作时有什么区别?

    == 比较两个字符串的地址,初学者很经常拿来比较其内容,将会导致出现不等的情况。
    equals()是String这个类重写的一个方法,平常的类的equals()也仅仅是比较两个变量的地址,而String类的equals()重写后,将依次比较其串中的字符。

  • hashCode()equals() 何时使用?

    一般是在想要人性化地(而不是计算机式地,比较地址那样)比较两个对象的时候,我们需要使用这两个方法,或者说我们要重写这两个方法,而且有如下的原则:

    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请参考:http://blog.csdn.net/fenglibing/article/details/8905007

  • Java 中的 final, finallyfinalize?

    1. final: 修饰变量,方法,类;
      修饰变量时表明这对象的值不可变,你不能为这个变量赋一个新的值,或者这样说:对基本类型而言,你不能改变其数值,对于引用,你不能将其指向一个新的引用(而引用自身是可以改变的)。
      修饰方法时表明我们希望把这个方法锁定,以防止任何继承类修改它的含义,这样会确保在继承中,我们的final方法的行为不会改变,并且不会被覆盖。使用final方法的另一个考虑是效率问题:在Java早期的时候,遇到final方法,编译器会将此方法调用转为内嵌调用,如此一来以减小方法调用产生的开销。
      修饰类的时候表明你不打算继承该类,而且也不允许别人这样做。
    2. finally:是异常处理中进行收场处理的代码块,比如关闭一个数据库连接,清理一些资源占用的问题。不管有没有异常被捕获,finally子句中的代码都会被执行。
    3. finalize:finalize出现的原因在于: 我们一定需要进行清理动作。Java没有用于释放对象的,如同C++里的delete调用,的方法,而是使用垃圾回收器(GC)帮助我们释放空间。当垃圾回收器准备释放对象占用的存储空间的时候,将首先调用其finalize()方法。
  • 什么是内存泄露,Java 是如何处理它的?

    总的来说是:保留下来却永远不再使用的对象引用
    它包括:

    1. 静态集合类引起内存泄漏,
    2. 当集合里面的对象属性被修改后,再调用remove()方法时不起作用,
    3. 监听器,
    4. 各种连接
    5. 内部类和外部模块的引用
    6. 单例模式
      详情可见:http://note.youdao.com/noteshare?id=376631d4640128dc55646c8e577cc3ab

      而关于Java 如何处理它,我还不能给出准确答案,应该是使用各种检测泄漏的工具检测到之后,使用GC处理它们

  • 垃圾收集器是什么?它是如何工作的

    • 所有的对象实例都在JVM管理的堆区域分配内存

      只要对象被引用,JVM就会认为它还存活于进程中。

      一旦对象不再被引用,就不能被应用程序所访问,

      垃圾收集器将删除它并重新声明未使用的内存。

  • 比较 ArraysArrayLists

    Arrays:一个包含许多和操纵数组有关方法的类,比如排序和查找,它继承自Object类。

    ArraysList:是一个容器,它可以实现数组的大小可变,方便地增加和删除元素。它实现了List接口的类。

    详细的笔记在:http://note.youdao.com/noteshare?id=659562b6beba34649d769d09cd2e0533

  • 比较 HashSetTreeSet

    TreeSet是基于二叉树实现的,其中的数据是自动排序好的。不允许放入null值。

    HashSet是基于Hash表实现的,其中的数据是无序的,允许放入null值。

    详细的笔记在:http://note.youdao.com/noteshare?id=4a3e44e90105d9906eb308317bc816bb

  • Java 中的类型转换。

    1. 基本数据的类型转换

      img
      在Java中,整数类型(byte/short/int/long)中,对于未声明数据类型的整形,其默认类型为int型。在浮点类型(float/double)中,对于未声明数据类型的浮点型,默认为double型。而当我们将一个数值范围较小的类型赋给一个数值范围较大的数值型变量,jvm在编译过程中会将此数值的类型进行自动提升。在数值类型的自动类型提升过程中,数值精度至少不应该降低(整型保持不变,float->double精度将变高)。而当我们需要将数值范围较大的数值类型赋给数值范围较小的数值类型变量时,我们需要手动地转换,称为强制类型转换。

      2.引用数据类型的类型转换

      由于继承和向上转型,子类向父类的转换是很自然的。但是当把父类转换为子类时,强制类型转换会在运行时检查父类的真实类型:如果引用的父类对象的真实身份是子类类型,只不过这个子类类型的对象经历了向上转型变成父类对象,这个时候我们再向下转回来子类,是可以的;而如果真的是父类的类型,就会抛出ClassCastException的异常。

      详细的笔记在:http://note.youdao.com/noteshare?id=7e85432e5d781ef8ad86ddabfc964885

  • 方法重载和重写的区别



    Overloading and Overriding

    • 重载发生在编译时,重写发生在运行时。重载方法调用与其定义的绑定发生在编译时,但是重写方法调用与其定义的绑定在运行时发生。
    • 静态方法可以重载,意味着一个类可以有多个同名的静态方法。静态方法不能被重写,即使在子类中声明了一个相同的静态方法,它与父类的相同方法无关。
    • 最基本的区别是重载是在同一个类中完成的,重写父类需要有子类。重写是给父类的继承方法一个具体的实现。
    • 静态绑定用于方法重载,动态绑定用于方法重写。性能:重载比重写更有效率,原因是方法重写的绑定是在运行时完成的。
    • 私有方法和用 final 修饰的方法可以重载但不可重写。这意味着一个类可以有多个同名的 private/final 方法,子类不能重写父类的 private/final 方法。
    • 方法重载的情况下不关心返回值类型, 它可以相同,也可以不同。但是方法重写的情况下可以有多个具体的返回值类型。
    • 方法重载时参数列表必须不同,方法重写时参数列表必须相同。
  • 什么是访问修饰符?它们能做什么?

    诸如public,protected,private这几个关键词叫做访问修饰符。

    其作用是控制它所定义的域或者方法的访问权。

  • 接口可以继承另一个接口吗?

    可以,通过继承,你可以

    1. 在接口中添加新的方法声明

    2. 还可以通过继承,在新的接口中组合数个接口,这时只需要用逗号将接口名一一分开来即可。

  • Java 中 static 关键字是什么意思?

    static是Java里的非访问修饰符,它可以用来创建类方法和类变量。

    当修饰一个变量的时候,此变量就成了独立于对象的静态变量,无论一个类实例化了多少个对象,这个类只有一份这个静态变量的拷贝,所以static修饰的变量,即静态变量,也被叫做类变量。一个局部变量不能被声明为static变量。

    当修饰一个方法的时候,此方法就成了独立于对象的静态方法,静态方法不能使用类的非静态变量,因为静态方法和静态变量先于非静态的其他成员初始化,静态方法先出来,然后才是非静态的,所以明白这个顺序很重要。静态方法从参数列表得到数据,然后计算这些数据。

    有些人认为static方法不是面向对象的,因为它们确实具有全局函数的语义;使用static方法时,由于不存在this,所以不是通过「向对象发送消息」的方式来完成的。当代码中出现很多static方法时,你应该反思自己的设计的,然而,static确有其用。

    总结一下,当static:

    1. 修饰方法时,此方法能够不用在初始化对象的前提下直接调用,即,可以直接通过类名.static方法()这样来访问。

    2. 修饰代码块时,这部分代码块只会被执行一次。

    3. 修饰变量时,这个变量只在内存中有一个副本。

  • Java 中静态方法可以被重写吗?

    严格来说,不存在静态方法的重写,当一个子类继承父类时,写同样的方法时,只是将父类的静态方法隐藏。

    [静态方法能否被重写](http://xm-king.iteye.com/blog/745787)

    所谓静态,就是在运行时,虚拟机已经认定此方法属于哪个类。
    专业术语有严格的含义,用语要准确.”重写”只能适用于实例方法.不能用于静态方法.对于静态方法,只能隐藏.
    静态方法的调用不需要实例化.. 不实例化也就不能用多态了,也就没有所谓的父类引用指向子类实例.因为不能实例化 也就没有机会去指向子类的实例。所以也就不存在多态了。

  • 什么是多态?什么是继承?

    • 多态是:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

    允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)

    • 继承是:子类继承父类的特征和行为,使得子类具有父类的各种属性和方法。
  • Integerint 之间的区别

    1. Integer是int提供的封装类,而int是Java的基本数据类型;
      Integer默认值是null,而int默认值是0;
      声明为Integer的变量需要实例化,而声明为int的变量不需要实例化;
      Integer是对象,用一个引用指向这个对象,而int是基本类型,直接存储数值
    2. 两个new 出来的Integer总是不一样的。当使用==时,发现其内存地址不同,所以进行==返回false
    3. 两个不是new出来的Integer,而是诸如Integer i = x,如果x的范围在-128~127,因为如下语句:
      Integer i5 = 127;//java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);
      调用了Integer.valueOf (), 这会将127缓存,下次我们写:Integer i6 = 127;时,i6指向缓存中的同一个对象。所以此时i5==i6是true;
      当x范围在-128~127之外,没有缓存存在,即使他们包裹的数值相等,他们也不能使用==的到true。
    4. int和Integer的比较,无论Integer是否使用new,其值和将和平常的预料的一样:Integer自动拆箱,然后和int比较数值.这里就不会被内存地址的不同所影响,该相等时就相等。
  • Java 中的对象是否会以引用传递或者值传递?详细说明。

    Java中的对象总是以值传递的。

    1. private void init(MyClass objVar) {
    2. objVar = new MyClass();
    3. }
    4. public void test() {
    5. MyClass obj = null;
    6. init(obj);
    7. //在调用init方法后,obj仍旧指向空
    8. //这是因为,obj是值传递,而不是引用传递。
    9. }
  • 什么是 ThreadPoolExecutor? Link

    中文翻译请参考这里:http://note.youdao.com/noteshare?id=84c9bc5c8b01defcb9db9ca963bf6f67

  • 本地变量、实例变量以及类变量之间的区别?

    本地变量就是局部变量,它在方法或者代码块里被声明并使用,其内存中的位置是栈里,没有默认初始化值,生命周期很短。

    实例变量是没有被static修饰的成员变量,它属于一个类的一个实例。每次new一个实例,这样的变量也同时new一遍,其位置在堆区中,有默认初始化的值,生命周期和它所在的实例一样长。

    类变量,又称静态变量,它是被static修饰的成员变量,它属于一个类,被所有实例共享。每次new一个实例,这样的变量并不会被new一遍,其内存位置在方法区内。可以通过类名直接访问。有默认的初始化值,生命周期很长。

  • 什么是反射? Link

  • 在 Java 中什么是强引用、软引用、弱引用以及虚引用?

    强引用:不会被GC轻易清理,只要引用存在,垃圾回收器永远不会回收。

    Object obj = new Object();

    软引用: 非必须引用,内存溢出之前进行回收

    1. Object obj = new Object();
    2. SoftReference<Object> sf = new SoftReference<Object>(obj);
    3. obj = null;
    4. sf.get();//有时候会返回null

    弱引用: 第二次垃圾回收时回收

    1. Object obj = new Object();
    2. WeakReference<Object> wf = new WeakReference<Object>(obj);
    3. obj = null;
    4. wf.get();//有时候会返回null
    5. wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

    虚引用: 垃圾回收时回收,无法通过引用取到对象值

    1. Object obj = new Object();
    2. PhantomReference<Object> pf = new PhantomReference<Object>(obj);
    3. obj=null;
    4. pf.get();//永远返回null
    5. pf.isEnQueued();//返回是否从内存中已经删除

    具体笔记可参考:http://note.youdao.com/noteshare?id=d52d8dd97e8dc162ddc90ff72a5c6001

    以及:http://note.youdao.com/noteshare?id=a9b2cebfbeb8fb33093ca318db58b389

  • 什么是依赖注入?能说几个依赖注入的库么?你使用过哪些?

  • 关键字synchronized的作用是什么?

    synchronized通过修饰一个方法或者代码块,从而产生一个同步对象锁,以及对应 的同步代码块。每当有线程要对该同步代码块进行访问时,线程就会首先尝试获取这个对 象的锁,并在成功获取到对象锁后,对这个同步代码块进行正常的访问。在同步代码块的 访问过程中,线程会一直持有这个对象锁,直到同步代码块访问完毕,然后才会释放。

    在上述线程持有同步锁并进行同步代码块访问过程中,其它线程将无法获得该对象锁,也 无法访问该同步代码,这些线程都会被阻塞直到上述线程访问完毕

  • 为什么说String不可变的?

    1. java.lang.String类型在实现时,其内部成员变量全部使用final来修饰,保证成员变量的引用值只能通过构造函数来修改;

    2. java.lang.String类型在实现时,在外部某个地方,可能修改一个String实例的内部存储值的函数实现中,在这个地方的调用返回时,一律构造新的String对象或者新的byte数组或者char数组,给赋值符号的左边变量(左值);

    详细请看:http://note.youdao.com/noteshare?id=747f5d269dd7e177153e4f099f095876

  • 修饰符transientvolatile的作用是什么?

  1. volatile

    易式修饰符

    出现它的原因是,java的多线程中存在两个或多个线程时间间隔很短地访问共享成员变量(指被多个线程共享的变量),在每个线程自己的工作内存中,可能对这个变量进行修改,但是没有及时将工作内存中的变量(对原本的共享成员变量的一份拷贝)写回共享成员变量,此时,当另外一个线程进行读取时,将无法得到最新的此变量,导致进程的工作不能正确进行。

    于是出现了volatile,带有volatile修饰的变量,就是当其在某个线程自己的工作内存中发生改变时,会被强制地,写回公共成员变量所在的公共内存处。

    如此便保证了,所有线程对这个变量的访问都是能得到此变量最新状态的访问。

  2. transient

    transient是一个类型修饰符,仅仅能用来修饰字段(变量)。在此字段所在的对象进行序列化的时候,这个字段不会被序列化。

    其他没有transient修饰的变量将会被序列化,然后进行传输,或者存储到本地磁盘,transient变量就在这个过程里丢失了

  • finalize()方法的作用是什么?

    它是继承自Object类的方法,当垃圾回收器认为一个对象没有用的时候,会调用此对象的finalize()方法,释放对象在堆中占用的内存。在finalize里,我们告诉了垃圾回收器应该做的事情。

  • 异常捕获中的 try{}finally{} 块儿是如何工作的?

    进行完try后,在try的return之前,总是会执行finally语句,进行善后处理。之后再进行return。

  • 对象的实例化和初始化之间的区别是什么?

    实例化可以理解为做出一个蛋糕:

    Cake ca = new Cake();

    初始化可以理解为,每个蛋糕都要有它自己的奶油:

    ca.iceCream = "香草味奶油";

    实例化着重于动作,强调==对象==从无到右的创建过程,而初始化着重于状态,强调==对象的特征==从无到有的过程。

  • 静态块何时运行?

    初始化的顺序大致是这样的:

    1. 为类中的静态变量分配好空间(同时给变量以默认值)
    2. 如果创建了类的对象,那么为类的实例变量分配地址空间(同时给变量以默认值)

    就在这两步之间,发生了静态块的运行

  • 解释一下 Java 中的泛型?

  • StringBufferStringBuilder 的区别在哪里?

    StringBuffer、StringBuilder和String一样,也用来代表字符串。String类是不可变类,任何对String的改变都 会引发新的String对象的生成;StringBuffer和StringBuilder则是可变类

    先说一下,以集合为例,HashTable是线程安全的,很多方法都是synchronized方法,而HashMap不是线程安全的,但其在单线程程序中的性能比HashTable要高。StringBuffer和StringBuilder类的区别也是如此,他们的原理和操作基本相同,区别在于StringBufferd支持并发操作,线性安全的,适 合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用。新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。

  • StringBuilder 是怎么避免不可变字符串分配的问题?

  • 什么是自动装箱和拆箱?

简单一点说,装箱就是 自动将基本数据类型转换为包装器类型;拆箱就是 自动将包装器类型转换为基本数据类型

自动装箱(autoboxing):

一般来说,这是我们创建一个类的实例时,我们会这样:

  1. MyClass a = new MyClass();

但是当我们新建一些支持自动装箱和拆箱的数据类型的时侯(比如Integer,基本数据类型的包装类),我们可以这样:

  1. Integer i = 100;

执行这句语句,将会被编译器执行为:

  1. Integer i = Integer.valueOf(100);

这就是自动装箱

自动拆箱(unboxing):

将对象中的基本数据从对象中自动取出

比如:

  1. Integer i = 10;//autoboxing
  2. int c = i;//unboxing
  • 枚举和迭代器有什么区别?
  1. 函数接口不同:
    Enumeration只有2个函数接口。通过Enumeration,我们只能读取集合的数据,而不能对数据进行修改。
    Iterator只有3个函数接口。Iterator除了能读取集合的数据之外,也能数据进行删除操作。
  2. Iterator支持fail-fast机制,而Enumeration不支持。
    Enumeration 是JDK 1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。
    而Iterator 是JDK 1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
  • Java 中 fail-fastfail-safe 的区别?

  • 什么是 Java 优先级队列?

  • 什么是设计模式 Link