基础篇
1
、
Java
语言有哪些特点
1
、简单易学、有丰富的类库
2
、面向对象(
Java
最重要的特性,让程序耦合度更低,内聚性更高)
2
、面向对象和面向过程的区别
面向过程
:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,然后在使用的时候一
一调用则可。性能较高,所以单片机、嵌入式开发等一般采用面向过程开发
面向对象
:是把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,
而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有
封装、继承、多态
的特
性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要
低。
标识符的命名规则。
标识符的含义:
是指在程序中,我们自己定义的内容,譬如,类的名字,方法名称以及变量名称等 等,都是标识符。
命名规则:(硬性要求)
标识符可以包含英文字母,
0-9
的数字,
$
以及
_
标识符不能以数字开头 标 识符不是关键字
命名规范:(非硬性要求)
类名规范:首字符大写,后面每个单词首字母大写(大驼峰式)。 变量名规范:首字母小写,后面每个单词首字母大写(小驼峰式)。 方法名规范:同变量名。
重载和重写的区别
重写
(Override)
从字面上看,重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子
类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,
参数列表,返回类型
(
除过子类中方法的返回值是父类中方法返回值的子类时
)
都相同的情况下, 对
方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
public class
Father
{
public static
void
main
(
String
[]
args
) {
// TODO Auto-generated method stub
Son s
=
new
Son
();
s
.
sayHello
();
}
public
void
sayHello
() {
System
.
out
.
println
(
"Hello"
);
}
}
class
Son
extends
Father
{
@Override
public
void
sayHello
() {
// TODO Auto-generated method stub
System
.
out
.
println
(
"hello by "
);
}
}
重写 总结:
1.
发生在父类与子类之间
2.
方法名,参数列表,返回类型(除过子类中方法的返回类型
是父类中返回类型的子类)必须相同
3.
访问修饰符的限制一定要大于被重写方法的访问修饰符
(
public>protected>default>private) 4.
重写方法一定不能抛出新的检查异常或者比被重写方法申
明更加宽泛的检查型异常
重载(
Overload
)
在一个类中,同名的方法如果有不同的参数列表(
参数类型不同、参数个数不同甚至是参数顺序不
同
)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但
不能通过返回类型是
否相同来判断重载
。
public class
Father
{
public static
void
main
(
String
[]
args
) {
// TODO Auto-generated method stub
Father s
=
new
Father
();
s
.
sayHello
();
s
.
sayHello
(
"wintershii"
);
}
public
void
sayHello
() {
System
.
out
.
println
(
"Hello"
);
}
public
void
sayHello
(
String
name
) {
System
.
out
.
println
(
"Hello"
+
" "
+
name
);
}
}
重载 总结:
1.
重载
Overload
是一个类中多态性的一种表现
2.
重载要求同名方法的参数列表不同
(
参
数类型,参数个数甚至是参数顺序
) 3.
重载的时候,返回值类型可以相同也可以不相同。无法以返回
型别作为重载函数的区分标准
equals
与
==
的区别
==
:
==
比较的是变量
(
栈
)
内存中存放的对象的
(
堆
)
内存地址,用来判断两个对象的地址是否相同,即是
否是指相同一个对象。比较的是真正意义上的指针操作。
1
、比较的是操作符两端的操作数是否是同一个对象。
2
、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。 3
、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为
true
,如:
int a=10
与
long b=10L
与
double c=10.0
都是相同的(为
true
),因为他们都指向地
址为
10
的堆。
equals
:
equals
用来比较的是两个对象的内容是否相等,由于所有的类都是继承自
java.lang.Object
类的,所
以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是
Object
类中的方法,而
Object
中的
equals
方法返回的却是
==
的判断。
总结:
所有比较是否相等时,都是用
equals
并且在对常量相比较时,把常量写在前面,因为使用
object
的
equals object
可能为
null
则空指针
在阿里的代码规范中只使用
equals
,阿里插件默认会识别,并可以快速修改,推荐安装阿里插件来 排查老代码使用“==”
,替换成
equals
Hashcode
的作用
java
的集合有两类,一类是
List
,还有一类是
Set
。前者有序可重复,后者无序不重复。当我们在
set 中插入的时候怎么判断是否已经存在该元素呢,可以通过equals
方法。但是如果元素太多,用这样的方法就会比较满。
于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域。
hashCode
方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当
集合要添加新的元素时,先调用这个元素的
hashCode
方法,就一下子能定位到它应该放置的物理
位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如
果这个位置上已经有元素了,就调用它的
equals
方法与新元素进行比较,相同的话就不存了,不相
同就散列其它的地址。这样一来实际调用
equals
方法的次数就大大降低了,几乎只需要一两次。
String
、
String StringBuffffer
和
StringBuilder
的区别是什
么
?
String
是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个
fifinal
类型的
字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对
String
的操作都会生成
新的
String
对象。
private final
char
value
[];
每次
+
操作 : 隐式在堆上
new
了一个跟原字符串相同的
StringBuilder
对象,再调用
append
方法 拼
接
+
后面的字符。
StringBuffffer
和
StringBuilder
他们两都继承了
AbstractStringBuilder
抽象类,从
AbstractStringBuilder
抽象类中我们可以看到
char
[]
value
;
他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用
StringBuffffer
和
StringBuilder
来进行操作。 另外
StringBuffffer
对方法加了同步锁或者对调用的方法加了同步锁,所
以是线程安全的。
StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
ArrayList
和
linkedList
的区别
Array
(数组)是基于索引
(index)
的数据结构,它使用索引在数组中搜索和读取数据是很快的。
Array
获取数据的时间复杂度是
O(1),
但是要删除数据却是开销很大,因为这需要重排数组中的所有
数据
, (
因为删除数据以后
,
需要把后面所有的数据前移
)
缺点
:
数组初始化必须指定初始化的长度
,
否则报错
例如
:
int
[]
a
=
new
int
[
4
];
//
推介使用
int[]
这种方式初始化
int
c
[]
=
{
23
,
43
,
56
,
78
};
//
长度:
4
,索引范围:
[0,3]
List—
是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承
Collection
。
List
有两个重要的实现类:
ArrayList
和
LinkedList
ArrayList:
可以看作是能够自动增长容量的数组
ArrayList
的
toArray
方法返回一个数组
ArrayList
的
asList
方法返回一个列表
ArrayList
底层的实现是
Array,
数组扩容实现
LinkList
是一个双链表
,
在添加和删除元素时具有比
ArrayList
更好的性能
.
但在
get
与
set
方面弱于
ArrayList.
当然
,
这些对比都是指数据量很大或者操作很频繁。
12
、
HashMap
和
HashTable
的区别
1
、两者父类不同
HashMap
是继承自
AbstractMap
类,而
Hashtable
是继承自
Dictionary
类。不过它们都实现了同时
实现了
map
、
Cloneable
(可复制)、
Serializable
(可序列化)这三个接口。
2
、对外提供的接口不同
Hashtable
比
HashMap
多提供了
elments()
和
contains()
两个方法。
elments()
方法继承自
Hashtable
的父类
Dictionnary
。
elements()
方法用于返回此
Hashtable
中的
value
的枚举。
contains()
方法判断该
Hashtable
是否包含传入的
value
。它的作用与
containsValue()
一致。事实
上,
contansValue()
就只是调用了一下
contains()
方法。
3
、对
null
的支持不同
Hashtable
:
key
和
value
都不能为
null
。
HashMap
:
key
可以为
null
,但是这样的
key
只能有一个,因为必须保证
key
的唯一性;可以有多个
key
值对应的
value
为
null
。
4
、安全性不同
HashMap
是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。
Hashtable
是线程安全的,它的每个方法上都有
synchronized
关键字,因此可直接用于多线程中。
虽然
HashMap
是线程不安全的,但是它的效率远远高于
Hashtable
,这样设计是合理的,因为大部
分的使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的
ConcurrentHashMap
。
ConcurrentHashMap
虽然也是线程安全的,但是它的效率比
Hashtable
要高好多倍。因为
ConcurrentHashMap
使用了分段锁,并不对整个数据进行锁定。
5
、初始容量大小和每次扩充容量大小不同
6
、计算
hash
值的方法不同
13
、
Collection
包结构,与
Collections
的区别
Collection
是集合类的上级接口,子接口有
Set
、
List
、
LinkedList
、
ArrayList
、
Vector
、
Stack
、
Set
;
Collections
是集合类的一个帮助类, 它包含有各种有关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于Java
的
Collection
框架。
Java
的四种引用,强弱软虚
强引用
强引用是平常中使用最多的引用,强引用在程序内存不足(
OOM
)的时候也不会被回收,使用
方式:
String
str
=
new
String
(
"str"
);
System
.
out
.
println
(
str
);
软引用
软引用在程序内存不足时,会被回收,使用方式:
//
注意:
wrf
这个引用也是强引用,它是指向
SoftReference
这个对象的,
//
这里的软引用指的是指向
new String("str")
的引用,也就是
SoftReference
类中
T
SoftReference
<
String
>
wrf
=
new
SoftReference
<
String
>
(
new
String
(
"str"
));
可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,
JVM
就会回收早先创建
的对象。
弱引用
弱引用就是只要
JVM
垃圾回收器发现了它,就会将之回收,使用方式:
WeakReference
<
String
>
wrf
=
new
WeakReference
<
String
>
(
str
);
可用场景:
Java
源码中的
java.util.WeakHashMap
中的
key
就是使用弱引用,我的理解就是,
一旦我不需要某个引用,
JVM
会自动帮我处理它,这样我就不需要做其它操作。
虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入
ReferenceQueue
中。注意
哦,其它引用是被
JVM
回收后才被传入
ReferenceQueue
中的。由于这个机制,所以虚引用大多
被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有
ReferenceQueue
,
使用例子:
PhantomReference
<
String
>
prf
=
new
PhantomReference
<
String
>
(
new
String
(
"str"
),
new
ReferenceQueue
<>
());
可用场景: 对象销毁前的一些操作,比如说资源释放等。
Object.finalize()
虽然也可以做这
类动作,但是这个方式即不安全又低效
上诉所说的几类引用,都是指对象本身的引用,而不是指
Reference
的四个子类的引用
(SoftReference
等
)
。
泛型常用特点
泛型是
Java SE 1.5
之后的特性, 《
Java
核心技术》中对泛型的定义是:
“
泛型
”
意味着编写的代码可以被不同类型的对象所重用。
“
泛型
”
,顾名思义,
“
泛指的类型
”
。我们提供了泛指的概念,但具体执行的时候却可以有具体的规则来约束,比如我们用的非常多的ArrayList
就是个泛型类,
ArrayList
作为集合可以存放各种元素,如
Integer, String
,自定义的各种类型等,但在我们使用的时候通过具体的规则来约束,如我们可以约 束集合中只存放Integer
类型的元素,如
List
<
Integer
>
iniData
=
new
ArrayList
<>
()
使用泛型的好处?
以集合来举例,使用泛型的好处是我们不必因为添加元素类型的不同而定义不同类型的集合,如整
型集合类,浮点型集合类,字符串集合类,我们可以定义一个集合来存放整型、浮点型,字符串型
数据,而这并不是最重要的,因为我们只要把底层存储设置了
Object
即可,添加的数据全部都可向
上转型为
Object
。 更重要的是我们可以通过规则按照自己的想法控制存储的数据类型
Java
创建对象有几种方式?
java
中提供了以下四种创建对象的方式
:
new
创建新对象
通过反射机制
采用
clone
机制
通过序列化机制
17
、有没有可能两个不相等的对象有相同的
hashcode
有可能
.
在产生
hash
冲突时
,
两个不相等的对象就会有相同的
hashcode
值
.
当
hash
冲突产生时
,
一般
有以下几种方式来处理
:
拉链法
:
每个哈希表节点都有一个
next
指针
,
多个哈希表节点可以用
next
指针构成一个单向链
表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储
.
开放定址法
:
一旦发生了冲突
,
就去寻找下一个空的散列地址
,
只要散列表足够大
,
空的散列地址总
能找到
,
并将记录存入
再哈希
:
又叫双哈希法
,
有多个不同的
Hash
函数
.
当发生冲突时
,
使用第二个
,
第三个
….
等哈希函数
计算地址
,
直到无冲突
.
18
、深拷贝和浅拷贝的区别是什么
?
浅拷贝
:
被复制对象的所有变量都含有与原来的对象相同的值
,
而所有的对其他对象的引用仍然指
向原来的对象
.
换言之
,
浅拷贝仅仅复制所考虑的对象
,
而不复制它所引用的对象
.
深拷贝
:
被复制对象的所有变量都含有与原来的对象相同的值
.
而那些引用其他对象的变量将指向
被复制过的新对象
.
而不再是原有的那些被引用的对象
.
换言之
.
深拷贝把要复制的对象所引用的
对象都复制了一遍
.
fifinal
有哪些用法
?
fifinal
也是很多面试喜欢问的地方
,
但我觉得这个问题很无聊
,
通常能回答下以下
5
点就不错了
:
被
fifinal
修饰的类不可以被继承
被
fifinal
修饰的方法不可以被重写
被
fifinal
修饰的变量不可以被改变
.
如果修饰引用
,
那么表示引用不可变
,
引用指向的内容可变
.
被
fifinal
修饰的方法
,JVM
会尝试将其内联
,
以提高运行效率
被
fifinal
修饰的常量
,
在编译阶段会存入常量池中
.
除此之外
,
编译器对
fifinal
域要遵守的两个重排序规则更好
:
在构造函数内对一个
fifinal
域的写入
,
与随后把这个被构造对象的引用赋值给一个引用变量
,
这两个操作之间不能重排序 初次读一个包含fifinal
域的对象的引用
,
与随后初次读这个
fifinal
域
,
这两个操作之间不能重排序
OOM
你遇到过哪些情况,
SOF
你遇到过哪些情况
OOM
:
1
,
OutOfMemoryError
异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生
OutOfMemoryError(OOM)
异常的
可能。
Java Heap
溢出:
一般的异常信息:
java.lang.OutOfMemoryError:Java heap spacess
。
java
堆用于存储对象实例,我们只要不断的创建对象,并且保证
GC Roots
到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。
仅供
出现这种异常,一般手段是先通过内存映像分析工具(
如
Eclipse Memory Analyzer)
对
dump
出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory
Leak)
还是内存溢出
(Memory Overflflow)
。
如果是内存泄漏,可进一步通过工具查看泄漏对象到
GCRoots
的引用链。于是就能找到泄漏对象是
通过怎样的路径与
GC Roots
相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数
(-Xmx
与
-Xms)
的设置是否适当。
2
,虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出
StackOverflflowError
异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出
OutOfMemoryError
异常
这里需要注意当栈的大小越大可分配的线程数就越少。
3
,运行时常量池溢出
异常信息:
java.lang.OutOfMemoryError:PermGenspace
如果要向运行时常量池中添加内容,最简单的做法就是使用
String.intern()
这个
Native
方法。该方法
的作用是:如果池中已经包含一个等于此
String
的字符串,则返回代表池中这个字符串的
String
对
象;否则,将此
String
对象包含的字符串添加到常量池中,并且返回此
String
对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize
和
-XX:MaxPermSize
限制方法区的大小,从而间接限制其中常量池的容量。
4
,方法区溢出
方法区用于存放
Class
的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。也有可能是方法区中保存的class
对象没有被及时回收掉或者
class
信息占用的内存超过了我们配置。
异常信息:
java.lang.OutOfMemoryError:PermGenspace
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻
的。在经常动态生成大量
Class
的应用中,要特别注意这点。
SOF
(堆栈溢出
StackOverflflow
):
StackOverflflowError
的定义:当应用程序递归太深而发生堆栈溢出时,抛出该错误。
因为栈一般默认为
1-2m
,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m
而导致溢出。
栈溢出的原因:递归调用,大量循环或死循环,全局变量是否过多,数组、
List
、
map
数据过大。
29
、
Java IO
与
NIO
的区别(补充)
NIO
即
New IO
,这个库是在
JDK1.4
中才引入的。
NIO
和
IO
有相同的作用和目的,但实现方式不同,
NIO
主要用到的是块,所以
NIO
的效率要比
IO
高很多。在
Java API
中提供了两套
NIO
,一套是针对标
准输入输出
NIO
,另一套就是网络编程
NIO
。
30
、
java
反射的作用于原理
1
、定义:
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,
都能够调用它的任意一个方法。在
java
中,只要给定类的名字,就可以通过反射机制来获得类的所
有信息。
这种动态获取的信息以及动态调用对象的方法的功能称为
Java
语言的反射机制。
2
、哪里会用到反射机制?
jdbc
就是典型的反射
Class
.
forName
(
'com.mysql.jdbc.Driver.class'
);
//
加载
MySQL
的驱动类
这就是反射。如hibernate
,
struts
等框架使用反射实现的。
3
、反射的实现方式:
第一步:获取
Class
对象,有
4
中方法:
1
)
Class.forName(“
类的路径
”)
;
2
)类名
.class 3
)对象
名
.getClass() 4
)基本类型的包装类,可以调用包装类的
Type
属性来获得该包装类的
Class
对象
4
、实现
Java
反射的类:
1
)
Class
:表示正在运行的
Java
应用程序中的类和接口 注意: 所有获取对象的信息都需要
Class
类
来实现。
2
)
Field
:提供有关类和接口的属性信息,以及对它的动态访问权限。
3
)
Constructor
:
提供关于类的单个构造方法的信息以及它的访问权限
4
)
Method
:提供类或接口中某个方法的信息
5
、反射机制的优缺点:
优点:
1
)能够运行时动态获取类的实例,提高灵活性;
2
)与动态编译结合
缺点:
1
)使用反射
性能较低,需要解析字节码,将内存中的对象进行解析。 解决方案:
1
、通过
setAccessible(true)
关闭
JDK
的安全检查来提升反射速度;
2
、多次创建一个类的实例时,有缓存会快很多
3
、
ReflflectASM
工具类,通过字节码生成的方式加快反射速度
2
)相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
说说
List,Set,Map
三者的区别?
List(
对付顺序的好帮手
)
:
List
接口存储一组不唯一(可以有多个元素引用相同的对象),有序
的对象
Set(
注重独一无二的性质
):
不允许重复的集合。不会有多个元素引用相同的对象。
Map(
用
Key
来搜索的专家
):
使用键值对存储。
Map
会维护与
Key
有关联的值。两个
Key
可以引
用相同的对象,但
Key
不能重复,典型的
Key
是
String
类型,但也可以是任何对象。
36
、用过
ArrayList
吗?说一下它有什么特点?
只要是搞
Java
的肯定都会回答
“
用过
”
。所以,回答题目的后半部分
——ArrayList
的特点。可以从这
几个方面去回答:
Java
集合框架中的一种存放相同类型的元素数据,是一种变长的集合类,基于定长数组实现,当加
入数据达到一定程度后,会实行自动扩容,即扩大数组大小。
底层是使用数组实现,添加元素。
如果
add(o)
,添加到的是数组的尾部,如果要增加的数据量很大,应该使用
ensureCapacity()
方法,该方法的作用是预先设置
ArrayList
的大小,这样可以大大提高初始化速度。
如果使用
add(int,o)
,添加到某个位置,那么可能会挪动大量的数组元素,并且可能会触发扩
容机制。
高并发的情况下,线程不安全。多个线程同时操作
ArrayList
,会引发不可预知的异常或错误。
ArrayList
实现了
Cloneable
接口,标识着它可以被复制。注意:
ArrayList
里面的
clone()
复制其实
是浅复制。
37
、有数组了为什么还要
搞个
ArrayList
呢?
通常我们在使用的时候,如果在不明确要插入多少数据的情况下,普通数组就很尴尬了,因为你不
知道需要初始化数组大小为多少,而
ArrayList
可以使用默认的大小,当元素个数到达一定程度
后,会自动扩容。
可以这么来理解:我们常说的数组是定死的数组,
ArrayList
却是动态数组。
fail-fast
机制是
Java
集合(
Collection
)中的一种错误机制。当多个线程对同一个集合的内容进行
操作时,就可能会产生
fail-fast
事件。
例如:当某一个线程
A
通过
iterator
去遍历某集合的过程中,若该集合的内容被其他线程所改变
了,那么线程
A
访问集合时,就会抛出
ConcurrentModifificationException
异常,产生
fail-fast
事
件。这里的操作主要是指
add
、
remove
和
clear
,对集合元素个数进行修改。
解决办法:建议使用
“java.util.concurrent
包下的类
”
去取代
“java.util
包下的类
”
。
可以这么理解:在遍历之前,把
modCount
记下来
expectModCount
,后面
expectModCount
去
和
modCount
进行比较,如果不相等了,证明已并发了,被修改了,于是抛出
ConcurrentModifificationException
异常。
说说
Hashtable
与
HashMap
的区别
本来不想这么写标题的,但是无奈,面试官都喜欢这么问
HashMap
。
1.
出生的版本不一样,
Hashtable
出生于
Java
发布的第一版本
JDK 1.0
,
HashMap
出生于
JDK
1.2
。
2.
都实现了
Map
、
Cloneable
、
Serializable
(当前
JDK
版本
1.8
)。
3. HashMap
继承的是
AbstractMap
,并且
AbstractMap
也实现了
Map
接口。
Hashtable
继承
Dictionary
。
4. Hashtable
中大部分
public
修饰普通方法都是
synchronized
字段修饰的,是线程安全的,
HashMap
是非线程安全的。
5. Hashtable
的
key
不能为
null
,
value
也不能为
null
,这个可以从
Hashtable
源码中的
put
方
法看到,判断如果
value
为
null
就直接抛出空指针异常,在
put
方法中计算
key
的
hash
值之
前并没有判断
key
为
null
的情况,那说明,这时候如果
key
为空,照样会抛出空指针异常。
6. HashMap
的
key
和
value
都可以为
null
。在计算
hash
值的时候,有判断,如果
key==null
,则其
hash=0
;至于
value
是否为
null
,根本没有判断过。
7. Hashtable
直接使用对象的
hash
值。
hash
值是
JDK
根据对象的地址或者字符串或者数字算出
来的
int
类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时
间的,效率很低。
HashMap
为了提高计算效率,将哈希表的大小固定为了
2
的幂,这样在取
模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
8. Hashtable
、
HashMap
都使用了
Iterator
。而由于历史原因,
Hashtable
还使用了
Enumeration
的方式。
9.
默认情况下,初始容量不同,
Hashtable
的初始长度是
11
,之后每次扩充容量变为之前的
2n+1
(
n
为上一次的长度)而
HashMap
的初始长度为
16
,之后每次扩充变为原来的两倍。
另外在
Hashtable
源码注释中有这么一句话:
Hashtable is synchronized. If a thread-safe implementation is not needed, it is
recommended to use HashMap in place of Hashtable . If a thread-safe highly
concurrent implementation is desired, then it is recommended to use
ConcurrentHashMap in place of Hashtable.
大致意思:
Hashtable
是线程安全,推荐使用
HashMap
代替
Hashtable
;如果需要线程安全高并
发的话,推荐使用
ConcurrentHashMap
代替
Hashtable
。
这个回答完了,面试官可能会继续问:
HashMap
是线程不安全的,那么在需要线程安全的情况下
还要考虑性能,有什么解决方式?
这里最好的选择就是
ConcurrentHashMap
了,但面试官肯定会叫你继续说一下
ConcurrentHashMap
数据结构以及底层原理等。
41
、
HashMap
的长度为什么是
2
的
N
次方呢?
为了能让
HashMap
存数据和取数据的效率高,尽可能地减少
hash
值的碰撞,也就是说尽量把数
据能均匀的分配,每个链表或者红黑树长度尽量相等。
我们首先可能会想到
%
取模的操作来实现。
下面是回答的重点哟:
取余(%)操作中如果除数是 2 的幂次,则等价于与其除数减一的与(&)操作(也就是说
hash % length == hash &(length - 1) 的前提是 length 是 2 的 n 次方)。并且,采用二进
制位操作 & ,相对于 % 能够提高运算效率。
这就是为什么
HashMap
的长度需要
2
的
N
次方了。
怎么处理
Java
异常的
try-catch-fifinally
try
块负责监控可能出现异常的代码
catch
块负责捕获可能出现的异常,并进行处理
fifinally
块负责清理各种资源,不管是否出现异常都会执行
其中
try
块是必须的,
catch
和
fifinally
至少存在一个标准异常处理流程