Java 基础重要面试题(一)
1. Java 创建对象的方式有哪几种?
常见主要有以下 5 种:
使用 new 关键字(调用构造方法)
使用 Class 类的 newInstance 方法(调用构造方法)
使用 Constructor 类的 newInstance 方法(调用构造方法)
使用 clone 方法(没有调用构造方法)
使用反序列化(没有调用构造方法)
2. abstract 的方法是否可同时是 static 、native 的,或者是 synchronized 的?为什么?
abstract 是抽象的(指方法只有声明没有实现,实现是子类干的事情)
static 是一种属于类而不属于对象的关键字;声明 abstract 的方法说明需要子类进行重写,如果同时声明 static 和 abstract ,用类名调用一个抽象方法这违背了抽象的意义。
native 和 abstract 本身就是冲突的,虽然它们都是方法的声明,但前者把方法的实现交给了操作系统(底层函数实现),后者把方法的实现交给了子类实现,如果可以同时使用,那么到底由谁来实现呢?
synchronized 是针对具体操作而言的,才有同步的意义,如果像 abstract 一样只有声明,那么就不存在同步的意义了。
3. Java1.8 之前为什么方法内部类和匿名内部类访问局部变量和形参时必须加 final ?
在 Java1.8 之前的版本中,对于普通局部变量或形参的作用域是方法内,当方法结束时,局部变量或形参也要随之消失;但当有匿名内部类或方法内部类时,这里面的逻辑还没有执行完,仍然需要这个局部变量或形参,那么就需要一些手段来持有这个局部变量或形参。
所以 Java 在编译匿名内部类或方法内部类时就有一个规定来解决生命周期问题。即如果访问的外部类方法的局部变量值在编译期能确定则直接在匿名内部类或方法内部类中创建一个常量副本,如果访问的外部类方法的局部变量值无法在编译期确定则通过构造器传参的方式来对副本进行初始化赋值。
由此说明在匿名内部类或方法内部类中访问的外部类方法的局部变量或形参时内部类自己的一份副本,和外部类方法的局部变量或形参不是一份,所以如果在匿名内部类或方法内部类对变量做修改就一定会导致数据不一致性(外部类方法的参数不会跟着被修改,引用类型仅是引用,指修改不存在问题)。为了杜绝数据不一致性导致其他问题,Java 就要求使用 final 来保障,所以必须是 final 的。
从 Java 1.8 版本开始可以不加 final 了,系统会默认添加,Java 将这个功能称为 Effectively final 。
4. ArrayList 的动态扩容机制是如何自动增加的,简单流程。
当在 ArrayList 中增加一个对象时,Java 会去检查 ArrayList 以确保已存在的数组中有足够的空间来存储这个对象(默认为 10,最大容量为 int 上限,减 8 是为了容错);如果没有足够的空间,就会新建一个长度更长的数组(原来的 1.5 倍),旧的数据就会使用 Arrays.copyOf() 方法被复制到新的数组中,现有的数组引用指向了新的数组。
5. Iterator 和 ListIterator 的区别。
ListIterator 有 add() 方法,可以向 List 中添加对象,而 Iterator 不能。
ListIterator 和 Iterator 都有 hasNext() 方法和 next() 方法,可以实现顺序向后遍历,但是 ListIterator 还有 hasPrevious() 方法 和 previous() 方法,可以实现逆向(顺序向前)遍历, Iterator 则没有此方法。
ListIterator 可以定位当前的索引位置,通过 nextIndex() 方法和 previousIndex() 方法可以实现,Iterator 没有此功能。
都可实现删除对象,但 ListIterator 还可以实现修改,通过 set() 方法;Iterator 仅能遍历,不能修改。
ListIterator 是 Iterator 的子接口。
注意:容器类提供的迭代器都会在迭代中间进行结构性检测,如果容器发生了结构性变化,就会抛出 ConcurrentModificationException ,所以不能在迭代中间直接调用容器类提供的 add()、remove() 方法,如需添加和删除,应调用迭代器的相关方法。
使用迭代器的 remove() 方法前必须先调用迭代器的 next() 方法,且不允许调用一次 next() 方法后调用多次 remove() 方法。
6. 为什么使用 for...each 时调用 List 的 remove() 方法会抛出 ConcurrentModificationException 异常?
首先 Java 提供了一个 Iterable 接口返回一个迭代器,常用的 Collection<E> 、List<E>、Set<E> 等都实现了这个接口,该接口的 iterator() 方法返回一个标准的 Iterator 实现,实现 Iterable 接口允许对象成为 fro...each 语句的目标来遍历底层集合序列,因此使用 for...each 方式遍历列表在编译后实质是迭代器的形式实现。之所以会抛出 ConcurrentModificationException 异常,需要看最常见的 ArrayList 中 iterator() 方法的实现。
大家好,我是一名正在学习 Java 的程序员,博客内容首发更新在公众号 `推荐学 java`,功夫不负有心人,每天保持学习,掌握一项技能其实用不了多长时间,加油!
版权声明: 本文为 InfoQ 作者【逆锋起笔】的原创文章。
原文链接:【http://xie.infoq.cn/article/478af45ab1afb525008463873】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论