20200708 by 1z
- 请你解释Object如果不重写hashcode()的话,hashcode()是如何计算出来的
Object的hashcode方法是本地方法,是使用c语言或者c++语言实现的,通常是根据“某种策略而形成的..”
hashcode在java层面的底层方法 : public native int hashCode();
对于HashCode的源码分析:
1. java6、7默认是返回随机数
2. java8默认是通过和当前线程有关的一个随机数+三个确定值, 运用Marsaglia’s xorshift scheme随机数算法得到的一个随机数
3. 参考资料: https://juejin.im/entry/5968876df265da6c232898c2
- 请你解释为什么重写了equals还要重写hashcode
首先equals的功能是判断两个对象相等,如果重写了equals相等,则表示两个对象已经相等。
如果不重写hashcode(),则两个对象的hashcode反应出的两个对象的"内存地址"
(虽然在openjdk hashcode的底层源码中并不是内存地址 但是可以这么理解...)不相等。而equals的结论和hashcode结论相悖。
因此重写了equals还要重写hashcode,保证了是同一个对象(属性和内存地址都相同).
- 请你介绍一下map的分类和常见的情况
作为java常用的集合之一,map接口有很多方面可以被问到,因此map的衍生问题也是近几年面试的热点。
个人觉得单看这一个问题没啥用... 因此有一些小扩展
1. map的分类
在java中存在一系列的映射关系,定义了一个接口为 java.util.Map 有着四个实现类为
HashMap
HashTable
LinkedHashMap
TreeMap
2.常见情况
2-1 : HashMap
常用的Map,面试频繁问,根据key的hashcode存储值,具有着很快的访问速度,它的key和value均都可以为null(只能允许 一条记录的key为null),适合单线程下使用,多线程下使用Collections.synchronizedHashMap 或者 ConcurrentHashMap
!!! HashMap值得去好好品味一下源码,不仅仅看一些初始化的容量 负载因子 动态扩容,应该去看看如何插入 删除操作,而且java1.7 java1.8的区别也是面试重点
参考资料 ; https://blog.csdn.net/woshimaxiao1/article/details/83661464
2-2 : HashTable
和hashmap类似,不允许记录的键和值为空
通过synchronized(重量锁)的方式实现对关键方法的线程同步,因为在写入的时候会比较慢(不常用)
2-3 : LinkedHashMap
是HashMap的子类,通过一个链表维护了记录的插入顺序,因此效率较hashmap慢
2-4 : TreeMap
实现了SortMap接口,能够将保存的值按照key排序,默认是升序排序,也可以通过自定义比较器(iterator)从而确定比较信息
SortedMap<Integer,String> map = new TreeMap<>((o1,o2)->{
//升序排列
//return o1 - o2 hello world
//逆序排列
return o2 - o1; //world hello
});
map.put(1,"hello");
map.put(2,"world");
for (Integer key : map.keySet()) {
System.out.print(map.get(key) + " ");
}
总之 : HashMap的一些底层信息一定要去巩固
- 请你讲讲java里面的final关键字是怎么用的?
final用于修饰 类,方法,对象
final 修饰类 : 表示该类不能被继承
final 修饰方法 : 表示该方法不能被重写
final 修饰变量 : 表示该变量只能赋值一次,赋值完不能被修改
* fianl修饰的是一个基本类型数据的时候,这个数据在初始化之后将不能被改变
* final修饰的是一个引用类型的时候(对象)该对象的内存地址不能修改(在堆中固定),
但是这个地址存储的内存信息可以改变
tip :
使用final修饰变量的时候,final修饰的变量会指向一块固定的内存,该内存不能被改变
- 请你谈谈关于Synchronized和lock(ReentractLock)
1.相同点
* 都是用来协调多线程对共享对象,变量的访问
* 都是可重入锁,同一线程可以多次获取同一个锁
* 都保证了可见性和互斥性
2.不同点
* ReentrantLock是显式的获得和释放锁,synchronized是隐式的
* ReentractLock内部可以响应中断
* ReentractLock可以配合condition,实现精确的唤醒(Conditon.await() Condition.signal())
* ReentractLock是 api级别的,synchronized是 jvm级别的
* 底层实现不一样,synchronized是同步阻塞(悲观),lock是同步非阻塞(乐观)
* Lock中可以实现读写锁,使得读读的时候不会被阻塞
* lock可以判断是否成功获取锁,而synchronized不能确定
- 请你介绍一下volatile
1. 当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在局部内存或者其他地方,而是直接把值刷回主内存。当其他内存读取共享变量时,会从主线程中获取该值(不存在从L1或者L2中取值)
L1 : 一级缓存
L2 : 二级缓存
2. Volatile相当于synchronized的一个弱实现,他实现了synchronized的语义却没有锁机制(volatile 使用了轻量级锁 CAS),它确保对volatile字段的更新以可预见的形式告知其他线程。
Java存储模型不对volatile指令的操作做重排序,保证volatile的变量都能按照指令的顺序执行。
3. Volatile类型的变量不会被缓存在寄存器中(寄存器中的数据只有当前线程可以访问),或者其他对CPU不可见的地方,每次都需要充主存中读取对应的数据,这保证每次对变量的修改,其他线程也是可见的,而不是仅仅修改自己线程的局部变量,在happens-before法则中,对一个volatile变量进行写操作后了,此后的任何读操作都可见该次写操作的结果。
4.volatile 关键字 保证了可见性 但是不能保证原子性
- 请你介绍一个Synchronized锁,如果用这个关键字修饰了一个静态方法,锁住了什么? 如果修饰成员方法,锁住了什么?
如果修饰了一个静态方法 => 锁住了当前类 (使用类锁)
如果修饰成员方法 => 锁住了当前对象(使用了对象锁)
- 若对一个类不重写,它的equals() 方法是如何比较的?
如果不重写的话,比较的是引用类型变量指向对象的地址
但是类似String,对equals进行重写之后,比较的就是执行对象的具体内容
- 请解释hashCode 和 equals() 的方法有什么联系?
equals 在不重写的时候比较的是对象地址
hashcode 调用底层的native代码 表现的是‘在内存中的地址’ => 10进制地址
equals相同(表示两个相同的对象) 必须有着相同的hashcode
如果两个对象的hashcode相同,不一定相同
- 请解释java中的概念:什么是构造函数? 什么是构造函数重载? 什么是复制构造函数?
1.构造函数
当新对象被创建的时候,会调用构造函数,每一个类都有着一个构造函数起着初始化的功能。同时,在没有定义构造函数的时候,java编译器会为这个类创建一个默认的构造函数
-------------------------------------------
2.构造函数重载(重载 : 保证重载函数的参数列表一定不同 返回值可以不同)
java中构造函数的重载 和 方法重载很相似。可以为一个类创建多个构造函数。每一个构造函数都必须有它唯一的参数列表。
--------------------------------------------
3.复制构造函数 (c++概念 java并不存在这个概念 )
参考信息 : http://c.biancheng.net/view/151.html