欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

拓展面试

程序员文章站 2022-03-02 22:08:38
...

JDK基础面试题

问:String是最基本的数据类型吗?
参考答案:
  不是,Java基本数据类型只有8种,byte、int、char、long、float、double、boolean和short。

问:Collection 和 Collections的区别?
参考答案:

​ Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
  Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

问:String对象的intern()是指什么?
参考答案:
intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回. 比如 String s1=“aa”; String s2=s1.intern(); System.out.print(s1==s2);//返回true

问:final、finally、finalize的区别是什么?
参考答案:
  final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。

问:error和exception有什么区别?
参考答案:
  error通常表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出、不可能指望程序能处理这样的情况;
exception通常表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况;

问:运行时异常与受检异常有何异同?
参考答案:
运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生;
受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。

问:列出一些你常见的运行时异常?
参考答案:

ArithmeticException(算术异常)

ClassCastException (类转换异常)

IllegalArgumentException (非法参数异常)

IndexOutOfBoundsException (下标越界异常)

NullPointerException (空指针异常)

SecurityException (安全异常)

问:static都有哪些用法?
参考答案:

静态变量
静态方法
静态块,多用于初始化
静态内部类.
静态导向,即import static.import static是在JDK 1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名.资源名,可以直接使用资源名,比如:

import static java.lang.Math.sin;
public class Test{
public static void main(String[] args){
//System.out.println(Math.sin(20));传统做法
System.out.println(sin(20)); // 静态导包
}
}

Java面向对象面试题

问:抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?为什么?
参考答案:
都不能。
抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的;
本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的;
synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的;

问:java.lang.Class类的newInstance方法和 java.lang.reflect.Constructor类的newInstance方法有什么区别?
参考答案:
• Class类的newInstance只能触发无参数的构造方法创建对象,而构造器类的newInstance能触发有参数或者任意参数的构造方法来创建对象。
• Class类的newInstance需要其构造方法是public的或者对调用方法可见的,而构造器类的newInstance可以在特定环境下调用私有构造方法来创建对象。
• Class类的newInstance抛出类构造函数的异常,而构造器类的newInstance包装了一个InvocationTargetException异常。
说明:Class类本质上调用了反射包Constructor中无参数的newInstance方法,捕获了InvocationTargetException,将构造器本身的异常抛出

问:Java 中的final关键字有哪些用法?
参考答案:
(1)修饰类:表示该类不能被继承;
(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)

问:关键字swtich 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?
参考答案:
在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

问:面向对象的特征有哪些?
参考答案:
  1.抽象:
  抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
  2.继承:
  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。
  3.封装:
  封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
  4. 多态性:
  多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。

问:代码中如何实现多态?
参考答案:
 实现多态主要有以下三种方式:

接口实现
继承父类重写方法
同一类中进行方法重载

问:overload和overide的区别?
参考答案:

Override 可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是 对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意 以下的几点:
覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
覆盖的方法的返回值必须和被覆盖的方法的返回值一致;
覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
被覆盖的方法不能为 private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
Overload 可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点: 5. 在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是 fun(int,float),但是不能为 fun(int,int)); 5. 不能通过访问权限、返回类型、抛出的异常进行重载; 5. 方法的异常类型和数目不会对重载造成影响; 5. 对于继承来说,如果某一方法在父类中是访问权限是 private,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。
总之,重写(Override) 是父类与子类之间多态性的一种表现,重载 (Overload) 是一个类中多态性的一种表现。很重要的一点就是,Overloaded 的方法是可以改变返回值的类型。
另外:在Java语言规范中,一个方法的特征仅包括方法的名字,参数的数目和种类,而不包括方法的返回类型,参数的名字以及所抛出来的异常。在Java编译 器检查方法的重载时,会根据这些条件判断两个方法是否是重载方法。但在Java编译器检查方法的置换时,则会进一步检查两个方法(分处超类型和子类型)的 返还类型和抛出的异常是否相同。

问:使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
参考答案:
使用 final 关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改 变的。例如,对于如下语句:
final StringBuffer a=new StringBuffer(“immutable”);
执行如下语句将报告编译期错误:
a=new StringBuffer("");
但是,执行如下语句则可以通过编译:
a.append(" broken!");
有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
public void method(final StringBuffer param){
}
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:
param.append(“a”);

问:静态变量和实例变量的区别?
参考答案:
在语法定义上的区别:静态变量前要加 static 关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。 总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
例如,对于下面的程序,无论创建多少个实例对象,永远都只分配了一个 staticVar 变量,并且每创建一个实例对象,这个 staticVar就会加1;但是,每创建一个实例对象,就会分配一个 instanceVar,即可能分配多个 instanceVar,并且每个 instanceVar 的值都只自加了 1 次。

public class VariantTest{

public static int staticVar = 0;
    
public int instanceVar = 0;
    
public VariantTest(){
	staticVar++;
	instanceVar++;
	System.out.println("staticVar=" + staticVar + ",instanceVar=" + instanceVar);
}
}

问:&和&&的区别?
参考答案:
&运算符有两种用法:(1)按位与;(2)逻辑与。
&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

问:父类的静态方法能否被子类重写?
参考答案:
不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏,调用的方法为定义的类所有的静态方法。

问:什么是不可变对象?
参考答案:
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。

问:能否创建一个包含可变对象的不可变对象?
参考答案:
可以,不过需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。
最常见的例子就是对象中包含一个String对象的引用.

Java进阶面试题

问:为什么反射的性能较差?有没有什么方法可以让他变快?
参考答案: java反射要解析字节码,将内存中的对象进行解析,包括了一些动态类型,JVM难以优化,而且在调用时还需要拼接参数,执行步骤也更多。因此,反射操作的效率要更低

​ 常用的改进性能方法主要有:

m.setAccessible(true);
由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的;

用缓存将反射得到的元数据保存起来;
利用一些高性能的反射库,如ReflectASM ReflectASM 使用字节码生成的方式实现了更为高效的反射机制。执行时会生成一个存取类来 set/get 字段,访问方法或创建实例。一看到 ASM 就能领悟到 ReflectASM 会用字节码生成的方式,而不是依赖于 Java 本身的反射机制来实现的,所以它更快,并且避免了访问原始类型因自动装箱而产生的问题。
问:java当中的四种引用分别指什么?
参考答案:

强引用,软引用,弱引用,虚引用。不同的引用类型主要体现在GC上:

强引用,如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象;

软引用,在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收;

弱引用,具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象;

虚引用,顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收;

问:为什么要有不同的引用类型?
参考答案:

Java语言有时需要我们适当的控制对象被回收的时机,因此就诞生了不同的引用类型,可以说不同的引用类型实则是对GC回收时机不可控的妥协。比如说以下应用场景:

利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题;

问:虚拟机是如何实现多态的?
参考答案:

动态绑定技术(dynamic binding),执行期间判断所引用对象的实际类型,根据实际类型调用对应的方法.

问:静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?
参考答案:

Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化

问:内部类的作用
参考答案:

内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。在单个外围类当中,可以让多个内部类以不同的方式实现同一接口,或者继承同一个类.

创建内部类对象的时刻不依赖于外部类对象的创建.内部类并没有令人疑惑的”is-a”关系,它就像是一个独立的实体。内部类提供了更好的封装,除了该外围类,其他类都不能访问

问:3*0.1==0.3返回值是什么
参考答案:

false,因为有些浮点数不能完全精确的表示出来。

问:a=a+b与a+=b有什么区别吗?
参考答案:

+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换。

举个例子,如: byte a = 23; byte b = 22; b = a + b;//编译出错 而 b += a; // 编译OK

问:int 和Integer谁占用的内存更多?
参考答案:

Integer 对象会占用更多的内存。Integer是一个对象,需要存储对象的元数据。但是int是一个原始类型的数据,所以占用的空间更少;

问:JVM、JRE、JDK及 JIT 之间有什么不同
参考答案:

java 虚拟机 (JVM),是实现java语言平*立性的基础,可以理解伪代码字节码,提供对多个平台的良好支持,在用户和操作系统之间建立了一层枢纽。

java 运行时环境 (JRE),是JVM 的一个超集。JVM 对于一个平台或者操作系统是明确的,而 JRE 确实一个一般的概念,他代表了完整的运行时环境。在 jre 文件夹中的jar 文件和可执行文件都会变成运行时的一部分。事实上,运行时 JRE 变成了 JVM。所以对于一般情况时候使用 JRE,对于明确的操作系统来说使用 JVM。当你下载了 JRE 的时候,也就自动下载了 JVM。

java 开发工具箱 (JDK),java 开发工具箱指的是编写一个 java 应用所需要的所有 jar 文件和可执行文件。事实上,JRE 是 JDK 的一部分。如果你下载了 JDK,你会看到一个名叫 JRE 的文件夹在里面。JDK 中要被牢记的 jar 文件就是 tools.jar,它包含了用于执行 java 文档的类还有用于类签名的 jar 包。

即时编译器 (JIT),即时编译器是种特殊的编译器,它通过有效的把字节码变成机器码来提高 JVM 的效率。JIT 这种功效很特殊,因为他把检测到的相似的字节码编译成单一运行的机器码,从而节省了 CPU 的使用。这和其他的字节码编译器不同,因为他是运行时编译(从字节码到机器码)而不是在程序运行之前。正是因为这些,动态编译这个词汇才和 JIT 有那么紧密的关系。

问:Java 泛型类在什么时候确定类型?
参考答案:

在编译期间确定变量类型。类型擦除。

问:引用类型是占用几个字节?
参考答案:

hotspot在64位平台上,占8个字节,在32位平台上占4个字节。

问:JDK主流版本的差异
参考答案:

Java 5(2004年发行),影响很大的一个版本;

1、泛型。
2、Metadata,元数据,描述数据的数据。
3、自动装箱和拆箱,也就是基本数据类型(如 int)和它的包装类型(如 Integer)自动帮你转换(其实背后是相关的方法帮你做了转换工作)。
4、枚举。
5、可变参数,一个函数可以传入数量不固定的参数值。
6、增强版的 for 循环。
7、改进了 Java 的内存模型,提供了 java.util.concurrent 并发包。
Java 6(2006年发行),这个版本的 Java 更多是对之前版本功能的优化,增强了用户的可用性和修复了一些漏洞,

1、提供动态语言支持。
2、提供编译 API,即 Java 程序可以调用 Java 编译器的 API。
3、Swing 库的一些改进。
4、JVM 的优化。
5、微型 HTTP 服务器 API ;
Java 7(2011年发行)

1、放宽 switch 的使用,可以在 switch 中使用字符串;
2、try-resource-with 语句,帮助自动化管理资源,如打开文件,对文件操作结束后,JVM 可以自动帮我们关闭文件资源,当然前提是你要用 try-resource-with 语句。
3、加入了类型推断功能,比如你之前版本使用泛型类型时这样写 ArrayList userList= new ArrayList();,这个版本只需要这样写 ArrayList userList= new ArrayList<>();,也即是后面一个尖括号内的类型,JVM 帮我们自动类型判断补全了。
4、简化了可变参数的使用。
5、支持二进制整数,在硬件开发中,二进制数更常用,方便人查看。
6、支持带下划线的数值,如 int a = 100000000;,0 太多不便于人阅读,这个版本支持这样写 int a = 100_000_000,这样就对数值一目了然了吧。
7、异常处理支持多个 catch 语句。
8、NIO 库的一些改进,增加多重文件的支持、文件原始数据和符号链接。
Java 8(2014年发行)

1、Lambda 表达式,简化代码
2、注解功能的增强。重复注解和注解扩展,现在几乎可以为任何东西添加注解:局部变量、泛型类、父类与接口的实现,就连方法的异常也能添加注解。
3、新的时间和日期 API,在这之前 Java 的时间和日期库被投票为最难用的 API 之一,所以这个版本就改进了。
4、JavaFX,一种用在桌面开发领域的技术(也是和其他公司竞争,这个让我们拭目以待吧)。
5、静态链接 JNI 程序库(这个做安卓开发的同学应该熟悉)。
6、接口默认方法和静态方法
7、函数式接口
8、方法引用
9、java.util.stream
10、HashMap的底层实现有变化
11、JVM内存管理方面,由元空间代替了永久代。
Java 9 (2017年发行)

1、模块化(这点也是向其他语言学习的,如 JavaScript)。
2、Java Shell(这点也是向其他语言学习的,如 Python),在这之前总有人说 Java 太麻烦,写个 Hello Word 都要新建个类,有时候测试个几行的代码都不方便,Java Shell 推出后,Java 开发者不用眼馋其他语言的 Shell 了;
3、即时编译功能的增强。
4、XML Catalogs,XML 文件自动校验。
Java 10(2018年发行)

1、局部变量的类型推断 var关键字
2、GC改进和内存管理 并行全垃圾回收器 G1
3、垃圾回收器接口
4、线程-局部变量管控
5、合并 JDK 多个代码仓库到一个单独的储存库中
Java 11(2018年发行)

1、本地变量类型推断
2、字符串加强
3、集合加强
4、Stream 加强
Optional 加强
5、InputStream 加强
6、HTTP Client API
7、化繁为简,一个命令编译运行源代码
问:final有哪些用法
参考答案:

被final修饰的类不可以被继承
被final修饰的方法不可以被重写
被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
被final修饰的方法,JVM会尝试将其内联,以提高运行效率
被final修饰的常量,在编译阶段会存入常量池中.

Map类面试题

问:说说List,Set,Map三者的区别?
参考答案:

List(对付顺序的好帮手):List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象;
Set(注重独一无二的性质):不允许重复的集合。不会有多个元素引用相同的对象;
Map(用Key来搜索的专家):使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。

问:poll()方法和remove()方法区别?
参考答案:

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

问:为何Map接口不继承Collection接口?
参考答案:

尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。

如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。

问:我们能否使用任何类作为Map的key?
参考答案:

我们可以使用任何类作为Map的key,然而在使用它们之前,需要考虑以下几点:

(1)如果类重写了equals()方法,它也应该重写hashCode()方法。

(2)类的所有实例需要遵循与equals()和hashCode()相关的规则。请参考之前提到的这些规则。

(3)如果一个类没有使用equals(),你不应该在hashCode()中使用它。

(4)用户自定义key类的最佳实践是使之为不可变的,这样,hashCode()值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashCode()和equals()在未来不会改变,这样就会解决与可变相关的问题了。

比如,我有一个类MyKey,在HashMap中使用它。

//传递给MyKey的name参数被用于equals()和hashCode()中 MyKey key = new MyKey(‘Pankaj’); //assume hashCode=1234 myHashMap.put(key, ‘Value’); // 以下的代码会改变key的hashCode()和equals()值 key.setName(‘Amit’); //assume new hashCode=7890 //下面会返回null,因为HashMap会尝试查找存储同样索引的key,而key已被改变了,匹配失败,返回null myHashMap.get(new MyKey(‘Pankaj’)); 那就是为何String和Integer被作为HashMap的key大量使用。

问:Map接口提供了哪些不同的集合视图?
参考答案:

Map接口提供三个集合视图:

(1)Set keyset():返回map中包含的所有key的一个Set视图。集合是受map支持的,map的变化会在集合中反映出来,反之亦然。当一个迭代器正在遍历一个集合时,若map被修改了(除迭代器自身的移除操作以外),迭代器的结果会变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除对应的映射。它不支持add和addAll操作。

(2)Collection values():返回一个map中包含的所有value的一个Collection视图。这个collection受map支持的,map的变化会在collection中反映出来,反之亦然。当一个迭代器正在遍历一个collection时,若map被修改了(除迭代器自身的移除操作以外),迭代器的结果会变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除对应的映射。它不支持add和addAll操作。

(3)Set<Map.Entry<K,V>> entrySet():返回一个map钟包含的所有映射的一个集合视图。这个集合受map支持的,map的变化会在collection中反映出来,反之亦然。当一个迭代器正在遍历一个集合时,若map被修改了(除迭代器自身的移除操作,以及对迭代器返回的entry进行setValue外),迭代器的结果会变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除对应的映射。它不支持add和addAll操作。

问:如何决定选用HashMap还是TreeMap?
参考答案:

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。

问:TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?
参考答案:

TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。

问:HashMap ,LinkedHashMap ,TreeMap,WeakHashMap,ConcurrentHashMap,IdentityHashMap的区别?
参考答案:

HashMap基于散列表来的实现,即使用hashCode()进行快速查询元素的位置,显著提高性能。插入和查询“键值对”的开销是固定的。可以通过设置容量和负载因子,以调整容器的性能。
LinkedHashMap, 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入的次序,只比HashMap慢一点。而在迭代访问时反而更快,因为它使用链表维护内部次序。
TreeMap, 是基于红黑树的实现。实现了SortedMap,SortedMap 可以确保键处于排序状态。所以查看“键”和“键值对”时,所有得到的结果都是经过排序的,次序由Comparable或Comparator决定。SortedMap拥有其他额外的功能,如:Comparator comparator()返回当前Map使用的Comparator或者null. T firstKey() 返回Map的第一个键,T lastKey() 返回最后一个键。SortedMap headMap(toKey),生成一个键小于toKey的Map子集。SortedMap tailMap(fromKey) 也是生成一个子集。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树
WeakHashMap表示弱键映射,允许释放映射所指向的对象。这是为了解决某类特殊问题而设计的,如果映射之外没有引用指向某个“键”,则“键”可以被垃圾收集器回收。
ConcurrentHashMap一种线程安全的Map,它不涉及同步加锁。
IdentityHashMap使用==代替equals() 对“键”进行比较的散列映射。专为解决特殊问题而设计。
问:HashMap 和 HashSet区别是什么?
参考答案:

如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了clone()、writeObject()、readObject()是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。

HashMap HashSet
实现了Map接口 实现Set接口
存储键值对 仅存储对象
调用 put()向map中添加元素 调用 add()方法向Set中添加元素
HashMap使用键(Key)计算Hashcode HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,
问:HashSet如何检查重复?
参考答案:

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。

问:WeakHashMap与HashMap的区别是什么?
参考答案:

WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。

List与Set类面试题

问:数组(Array)和列表(ArrayList)的差别是什么?
参考答案:

Array可以容纳基本类型和对象,而ArrayList只能容纳对象;

Array 是静态的,一旦创建就无法更改它的大小,ArrayList 是Java集合框架类的一员,可以称它为一个动态数组。

问:ArrayList和Vector有何异同点?
参考答案:

相同点:
(1)两者都是基于索引的,内部由一个数组支持。

(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。

(3)ArrayList和Vector的迭代器实现都是fail-fast的。

(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。

不同点:
(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。

(2)ArrayList比Vector快,它因为有同步,不会过载。

(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。

问:EnumSet是什么?
参考答案:

java.util.EnumSet是使用枚举类型的集合实现。当集合创建时,枚举集合中的所有元素必须来自单个指定的枚举类型,可以是显示的或隐示的。EnumSet是不同步的,不允许值为null的元素。

问:Java集合类中的Iterator和ListIterator的区别?
参考答案:

iterator()方法在set和list接口中都有定义,但是ListIterator()仅存在于list接口中(或实现类中);
ListIterator有add()方法,可以向List中添加对象,而Iterator不能;
ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以;
ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能;
都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改;

集合类进阶面试题

问:Java集合框架是什么?说出一些集合框架的优点?
参考答案:

每种编程语言中都有集合。集合框架的部分优点如下:
(1)使用核心集合类降低开发成本,而非实现我们自己的集合类。
(2)随着使用经过严格测试的集合框架类,代码质量会得到提高。
(3)通过使用JDK附带的集合类,可以降低代码维护成本。
(4)复用性和可操作性。

问:集合框架中的泛型有什么优点?
参考答案:

Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。

泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。

问:Java集合框架的基础接口有哪些?
参考答案:

Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。

Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。

List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。

Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。

一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。

问:为何Collection不从Cloneable和Serializable接口继承?
参考答案:

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

问:Iterator是什么?
参考答案:

Iterator接口提供遍历任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者在迭代过程中移除元素。

问:迭代器的优点
参考答案:

如果用的是for循环,就用集合自带的remove(),而这样就改变了集合的Size()循环的时候会出错。但如果把集合放入迭代器,既iterator迭代可以遍历并选择集合中的每个对象而不改变集合的结构,而把集合放入迭代器,用迭代器的remove()就不会出现问题

问:Enumeration和Iterator接口的区别?
参考答案:

Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,因为其他线程不能够修改正在被iterator遍历的集合里面的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。

问:Iterater和ListIterator之间有什么区别?
参考答案:

(1)我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List。

(2)Iterator只可以向前遍历,而LIstIterator可以双向遍历。

(3)ListIterator从Iterator接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

问:哪些集合类是线程安全的?
参考答案:

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使用。Java1.5并发API包括一些集合类,允许迭代时修改,因为它们都工作在集合的克隆上,所以它们在多线程环境中是安全的。

问:并发集合类是什么?
参考答案:

Java1.5并发包(java.util.concurrent)包含线程安全集合类,允许在迭代时修改集合。迭代器被设计为fail-fast的,会抛出ConcurrentModificationException。一部分类为:CopyOnWriteArrayList、 ConcurrentHashMap、CopyOnWriteArraySet。

问:Collections类是什么?
参考答案:

Java.util.Collections是一个工具类仅包含静态方法,它们操作或返回集合。它包含操作集合的多态算法,返回一个由指定集合支持的新集合和其它一些内容。这个类包含集合框架算法的方法,比如折半搜索、排序、混编和逆序等。

问:如何保证线程安全又效率高?
参考答案:

Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

ConcurrentHashMap将整个Map分为N个segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认N为16。

问:怎么确保一个集合不能被修改?
参考答案:

(1)Java中提供final关键字,对基本类型进行修饰,当第一次初始化后,该变量就不可被修改 (2)Collections`工具类中的UnmodifiableList(不可修改的List、Map、Set等)

问:Comparator和Comparable的区别?
参考答案:

相同点
都是用于比较两个对象“顺序”的接口

都可以使用Collections.sort()方法来对对象集合进行排序

不同点
Comparable位于java.lang包下,而Comparator则位于java.util包下

Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序

总结
使用Comparable接口来实现对象之间的比较时,可以使这个类型(设为A)实现Comparable接口,并可以使用Collections.sort()方法来对A类型的List进行排序,之后可以通过a1.comparaTo(a2)来比较两个对象;

当使用Comparator接口来实现对象之间的比较时,只需要创建一个实现Comparator接口的比较器(设为AComparator),并将其传给Collections.sort()方法即可对A类型的List进行排序,之后也可以通过调用比较器AComparator.compare(a1, a2)来比较两个对象。

可以说一个是自己完成比较,一个是外部程序实现比较的差别而已。

用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,这时候只要(也只有)使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。

两种方式,各有各的特点:使用Comparable方式比较时,我们将比较的规则写入了比较的类型中,其特点是高内聚。但如果哪天这个规则需要修改,那么我们必须修改这个类型的源代码。如果使用Comparator方式比较,那么我们不需要修改比较的类,其特点是易维护,但需要自定义一个比较器,后续比较规则的修改,仅仅是改这个比较器中的代码即可。

问:为何Collection不从Cloneable和Serializable接口继承?
参考答案:

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

问:哪些集合类提供对元素的随机访问?
参考答案:

ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。

问:与Java集合框架相关的有哪些最好的实践?
参考答案:

(1)根据需要选择正确的集合类型。比如,如果指定了大小,我们会选用Array而非ArrayList。如果我们想根据插入顺序遍历一个Map,我们需要使用TreeMap。如果我们不想重复,我们应该使用Set。

(2)一些集合类允许指定初始容量,所以如果我们能够估计到存储元素的数量,我们可以使用它,就避免了重新哈希或大小调整。

(3)基于接口编程,而非基于实现编程,它允许我们后来轻易地改变实现。

(4)总是使用类型安全的泛型,避免在运行时出现ClassCastException。

(5)使用JDK提供的不可变类作为Map的key,可以避免自己实现hashCode()和equals()。

(6)尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性。

上一篇: Full GC

下一篇: JAVA面试题(2)