-
聊一聊 java中的编译器和解释器? Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。
-
java数据类型
整数类型(byte,short,int,long)
浮点类型(float,double)
字符型(char)
布尔型(boolean)
-
switch 是否能作用在 byte 上,是否能作用在 long 上,是否 能作用在 String 上? 在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。从 Java5 开始,Java 中引入了枚举类型,expr 也可以是 enum 类型,从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版 本中都是不可以的
-
用最有效率的方法计算 2 22 = 8?为什么? 2<<3 数据是以二进制的形式存储和处理的。位运算是直接对二进制位进行操作,乘法运算通常会涉及到更复杂的硬件电路和算法,需要多个时钟周期来完成。而位运算只需要简单的移位操作,在硬件层面可以更快地执行,因此在性能上更具优势.
-
访问修饰符 public,private,protected,以及不写(默认)时的 区别? private 当前类 default 同包 protected 同包子类 public 都可以
-
final 有什么用?
被final修饰的类不可以被继承 被final修饰的方法不可以被重写 被final修饰的变量不可以被改变
-
成员变量与局部变量的区别有哪些?
- 作用域,成员变量:针对整个类有效。局部变量:只在某个范围内有效。
- 存储位置,成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。
- 生命周期,成员变量:随着对象的创建而存在,随着对象的消失而消失。局部变量:当方法调用完,或者语句结束后,就自动释放。
- 初始值,成员变量:有默认初始值。局部变量:没有默认初始值,使用前必须赋值。
-
若一个类没有声明构造方法, 程序能正确执行吗?为什么?
可以执行,默认会有无参构造方法的 -
== 和 equals 的区别是什么?
- 基本数据类型==比较的是值,引用数据类型 == 比较的是内存地址
- 类没有覆盖equals() 方法。则通过 equals() 比较该类的两个对象时, 等价于通过“==”比较这两个对象,也就是比较地址,如果重写了 equal比较的是对象里的数据
-
hashCode 与 equals (重要) hashcode相等对象不一定相等,equals相等,一定相等,因为 hashset,我们是根据hashcode快速定位到索引,然后根据 equals去比较数据,提高效率
-
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是 引用传递?
-
基本数据类型,一个方法不能修改一个基本数据类型的参数
-
引用作为参数就不一样,对象引用及其他的拷贝同时 引用同一个对象。
-
-
字符串常量池?在 jvm哪个位置?
- new好的对象不会放在常量池。因为字符串在开发过程经常用到,为了提升性能和减少内存消耗,而设计的一个特殊内存区域
- 存储位置和版本变化,1.7 及前是方法去的永久带,1.8 元空间(堆)
-
String s = new String("aaa")创建了几个对象? 1 个或者 2 个,为什么?就是在于常量池是否有对象,有就只在堆创建对象,如果没有,在常量迟创建对象
-
String 可变吗? 不可变,被 final修饰了,
-
String s1 = "aa"; String s2 = "bb"; String s3 = s1 + s2;
- 常量池原本没有
"aa"和"bb"的情况:总共创建了 4 个对象,分别是常量池中的"aa"、"bb"对象,堆中的StringBuilder对象以及拼接结果"aabb"的String对象。
- 常量池原本没有
-
在使用 HashMap 的时候,用 String 做 key 有什么好处? 首先是因为 String被 final修饰,不可变。
String类是不可变的,一旦创建,其值不能被修改,哈希值缓存:String类会缓存其哈希值。当第一次调用String对象的hashCode()方法时,计算得到的哈希值会被存储起来。后续再次调用该方法时,直接返回缓存的哈希值,避免了重复计算,提高了HashMap操作的效率 -
简单说说自动装箱与拆箱? 装箱 int-》integer 拆箱integer->int Integer a= 127 与 Integer b = 127相等吗 对于对象引用类型:==比较的是对象的内存地址。 对于基本数据类型:==比较的是值。如果整型字面量的值在-128到127之间,那么自动装箱时不会new 新的Integer 对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false
-
ArrayList 和 LinkedList 的区别是什么? list从数据结构是数组,数组就有索引,查找比较快,但是新增比较慢,LinkedList用的是链表,查询慢,插入快,只需要指向头尾指针,但是内存占用比较大,首位引用
-
ArrayList的扩容机制? 如果没有指定大小,默认为 10,当容量不足是,>=默认的个数的时候就开始扩容,也就是添加地 11 个的时候开始扩容,按照 1.5 倍开始扩容,复制数组,原来的数组释放,添加元素,添加到末尾。 如何优化数组? 减少扩容,根据自己的业务设置相对合理的默认大小
-
for循环和 迭代器区别?
for循环适用于简单的正向或反向遍历,尤其是在性能要求较高的场景;而ListIterator则适用于需要双向遍历和在遍历过程中修改列表结构的场景。 当调用list.remove(fruit)时,集合的modCount会增加,但迭代器的expectedModCount不会更新。当再次调用迭代器的next()方法时,就会发现expectedModCount和modCount不相等,从而抛出ConcurrentModificationException异常。
解决方法
如果需要在遍历过程中修改列表结构,可以使用迭代器本身的 remove() 方法,因为该方法会同时更新 expectedModCount 和 modCount,保证两者的一致性。
21. transient什么时候使用?作用是什么?
不希望该字段被序列化和反序列化,也是对数据的保护,不希望该数据存储到文件和网络传输,还有就是如果这个字段占用资源很大,在序列化的时候会占用很大资源
22. HashSet如何检查重复?HashSet是如何保证数据不可重复的?
首先 hashSet底层就是 map,其实就是现比较 hashCode相同,那就比较 equals,然后 put(值,present)
23. 说一下 HashMap 的实现原理?
当调用 put(key, value) 方法插入键值对时,主要步骤如下:
-
计算键的哈希值:通过
hash()方法计算键的哈希值。 -
定位数组位置:根据哈希值和数组长度,通过
(n - 1) & hash计算出键值对应该存储的数组位置。 -
检查数组位置:
- 如果该位置为空,直接创建一个新的节点放入该位置。
- 如果该位置已经有节点:
- 首先判断第一个节点的键是否与要插入的键相等,如果相等则直接覆盖其值。
- 如果不相等,判断该节点是链表节点还是红黑树节点。
- 如果是链表节点,遍历链表,查找是否存在相同的键,如果存在则覆盖其值;如果不存在,则将新节点插入到链表尾部。当链表长度达到 8 且数组长度达到 64 时,将链表转换为红黑树。
- 如果是红黑树节点,调用红黑树的插入方法插入新节点。
检查是否需要扩容:插入新节点后,如果
size超过threshold,则进行扩容操作。
-
为什么 hashmap使用尾插法,不用头插法 头插法在多线程的时候容易形成链表环路,因为在扩容的时候,A-》B->C 会变成 C->B->A 顺序已经变了,a线程认为是C->B->A,B线程认为是 A->B->C
juejin.cn/post/723600… -
hashmap什么时候开始扩容? 有个公式,mashmap默认是 16*负载因子是 0.75 也就是 12 当put第 13 个的时候就开始扩容
-
h = key.hashCode()) ^ (h >>> 16) 为啥要? 把键的原始哈希码和其右移 16 位后的结果进行异或(
^)运算,得到一个新的哈希值,目的就是为了减少hash碰撞, -
简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的:
- 使用链地址法(使用散列表)来链接拥有相同hash值的数据;
- 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
- 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快
-
简单说一说红黑树? blog.csdn.net/weixin_4478…
-
平衡二叉树
- 每个节点最多只有两个子节点
- 每个节点的值比它的左子树所有的节点大,比它的右子树所有节点小
- 每个节点左子树的高度与右子树高度之差的绝对值不超过1
(左左:右旋解决
左右:先左旋再右旋
右右:左旋解决
右左:先右旋再左旋)
-
红黑树规则
- 每个节点不是黑色就是红色
- 根节点为黑色
- 红色节点的父节点和子节点不能为红色
- 所有的叶子节点都是黑色(如果一个节点是红色的,则它的两个子节点都是黑色的,下面都有隐藏的空的黑色叶子节点(NIL 节点)
- 每个节点到叶子节点的每个路径黑色节点的个数都相等 先变色,在旋转 自己理解就是新增的时候就是红色,然后是否变色,需要先将所有如果子节点是空就假装补成黑色,然后计算数目然后旋转
-
谈一谈ConcurrentHashMap 在 1.7 和 1.8?
-
1.7 使用的是分段锁,每个断就是每个**Segment**,**继承ReentrantLock锁**ConcurrentHashMap底层是由两层嵌套数组来实现的,- ConcurrentHashMap对象中有一个属性segments, 类型为Segment[]; Segement对象中有一个属性table,类型为HashEntry.当调用ConcurrentHashMap的put方法时,先根据key计算出对应的Segment[]的数组下表j。确定好当前key,value应该插入到哪个Segment对象中,如果Segments[j]数组为空,则利用自旋锁方式在j位置生成一个Segment对象。 -
1,8 使用的cas和synchronized关键字,数组+链表+红黑树,
-
之后调用Segment对象的put方法。Segment对象的put方法会先加锁,然后根据key计算出对应的hashEntry[]的数组下表i,然后将key和value封装为HashEntry对象放入该位置,此过程和JDK7中的HashMap的put方法一样,然后解锁。
- hashmap 1.7用的分段锁 1.8 后续改为 cas+sync 为什么?
- 减少锁的粒度和开销- Java 1.7 分段锁:在 Java 1.7 里,HashMap 采用分段锁(Segment)机制,将整个数据结构分成多个段(Segment),每个段相当于一个小的 HashMap,不同的段可以被不同的线程同时访问,以此来提高并发性能。然而,这种方式需要额外维护多个 Segment 对象,并且在计算分段时也有一定的开销。Java 1.8 CAS + synchronized:Java 1.8 去掉了分段锁,采用 CAS + synchronized 机制。当多个线程对不同的桶进行操作时,不会产生锁竞争,因为每个桶可以独立进行操作。并且,synchronized 在 JDK 1.6 之后进行了大量优化,引入了偏向锁、轻量级锁等,在锁竞争不激烈时,加锁和解锁的成本较低。CAS 操作在无锁状态下可以高效地完成一些更新操作,避免了锁的使用,进一步提高了性能。
- 数据结构不一样,一个是数组+链表,一个是数组+链表+红黑树
- Collection 和 Collections 有什么区别? Collection是一个集合接口,顶级接口,set和 list都继承于它,Collections只是个工具类,提供了一些类似排序
- 双亲委派模型简单说一下?
- 启动类加载器(Bootstrap ClassLoader) :最顶层的类加载器,由 C++ 实现,负责加载 Java 的核心类库,如
java.lang包下的类。
- 启动类加载器(Bootstrap ClassLoader) :最顶层的类加载器,由 C++ 实现,负责加载 Java 的核心类库,如
- 扩展类加载器(Extension ClassLoader) :由 Java 代码实现,负责加载 Java 的扩展类库,通常是
jre/lib/ext目录下的类。 - 应用程序类加载器(Application ClassLoader) :也由 Java 代码实现,负责加载用户类路径(
classpath)上的类,一般来说,用户自己编写的类都是由该类加载器加载。 - 自定义类加载器:用户可以根据需求自定义类加载器,继承自
java.lang.ClassLoader类。
-
简单说一说异常的分类,结构? Throwable,有两个重要的子类:Exception(异常) 和 Error(错误) Error这些问题一般是由系统本身或底层资源耗尽等原因导致的,程序通常无法通过代码来处理这些错误,####
OutOfMemoryError,####StackOverflowErrorexception类表示程序可以捕获和处理的异常情况