1. int转long
long val = 24 * 60 * 60 * 1000 * 1000l;
long val1 = 24 * 60 * 60 * 1000 * 1000;
System.out.println(Integer.MAX_VALUE);
System.out.println(Long.MAX_VALUE);
System.out.println(Long.MIN_VALUE);
System.out.println(24 * 60 * 60 * 1000);
System.out.println(val);
System.out.println(val1);
System.out.println(val / 60 / 60 / 1000 / 1000);
// 1 这里最后结果是0的原因是计算过程中产生的是int再转的long
System.out.println(val1 / 60 / 60 / 1000 / 1000);
2. 引用传递
另外:对于打印char[] :
char类型的数组就相当于一个字符串。
char类型的数组就相当于一个字符串。
因为输出流System.out是PrintStream对象,PrintStream有多个重载的println方法,其中一个就是public void println(char[] x),直接打印字符数组的话,不像int[]等其他数组,它会直接调用这个方法来打印,因而可以打印出数组内容,而不是地址。
3. ConcurrentHashMap的put方法简述
自己的答案:
- 先判断当前容器是否被初始化,如果没有初始化则先初始化
- 然后先根据key使用(key & (len - 1))的方式计算出所在位置
- 如果该位置为空,则直接使用CAS插入即可,然后判断此时是否达到了阈值,是则进行扩容。
- 如果该位置不为空,则需要先加锁
- 然后先通过hash进行比较,相等再通过equals比较是否相等,相等则直接替换值,如果不相等再看该节点是链表节点还是红黑树节点,然后按照对应数据结构的遍历方式依次遍历,直到相等后替换值或者找不到则插入对应的数据结构,插入之前如果是链表则需要考虑是否满足条件(元素size大于64,链表长度大于8),是则转换为红黑树。
- 看是否达到了阈值,达到了就扩容
根据源码补充的答案:
1. 先对key使用扰动方法((key.hashCode() ^ (key.hashCode() >>> 16)) & 0x7fffffff)得到hash值
2. 判断是否为null,是则进行初始化(初始化过程中只能让一个线程初始化)
3. 然后根据hash值通过hash & (len - 1)得到所在位置
4. 判断所在位置是否为空,为空直接使用CAS插入
5. 如果不为空,则先加锁(Synchronzied),加锁范围限制在通过hash找到的节点上
6. 先判断f节点是链表节点还是红黑树节点,是链表节点的话,通过for循环一个节点一个节点的判断(先判断hash,再判断是否相等),最后找到了节点的话,就替换值,如果没有找到,则在尾部插入
7. 是红黑树的话,则依照红黑树的遍历方式遍历最后插入或替换
8. 此时同步块结束,开始判断如果是链表,看是否满足条件(转化为红黑树的条件),满足则转换。
9. 此时增加size,看是否达到阈值,达到则扩容
4. 有三个bean A、B、C;其中,A依赖于B,B依赖于C,C又依赖于A,这样能否正常启动?
Spring中循环依赖场景有:
(1)构造器的循环依赖
(2)field属性的循环依赖
其中,构造器的循环依赖问题无法解决,只能拋出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。
https://blog.csdn.net/xx897115293/article/details/108635440
https://www.cnblogs.com/jajian/p/10241932.html
https://zhuanlan.zhihu.com/p/62382615
https://segmentfault.com/a/1190000019286083
5. spring bean 如何保持线程安全,为什么?
对于有状态的bean(比如Model和View),就需要自行保证线程安全,最浅显的解决办法就是将有状态的bean的作用域由“singleton”改为“prototype”。
也可以采用ThreadLocal解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。
https://www.cnblogs.com/frankcui/p/14341795.html
6. 现有集合ArrayList list = new ArrayList(); 利用反射机制在这个泛型为Integer的ArrayList中存放一个String类型的对象。
先解释:
可以使用反射技术,来获得add方法,这样的话就可以随便存了,sun规定的泛型只是将错误上升到编译时期,在运行时期没有泛型的。直接使用反射得到add方法就不会再有泛型的限制了
代码:
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<Integer>(); //定义Integer泛型
String str = "abc";
Method[] method=list.getClass().getMethods();//取得list的所有方法
System.out.println(method.length);
for(int i=0;i<method.length;i++){
System.out.println(method[i]);//遍历打印list的方法
}
//通过反射来执行list的第一个方法,第一个是list对象,代表该对象的方法,第二个是方法参数
method[0].invoke(list, str);
System.out.println(list.size());
for(int i=0;i<list.size();i++)
System.out.println(list.get(i));
}
7. 描述一下HotSpot JVM的内存结构,说明每块区域对应的功能。
自己的答案:
内存结构主要分为两部分:
1. 一部分是线程私有区域:栈、程序计数器、本地栈
2. 一部分是线程共享区域:堆、方法区
堆:主要用来存储对象
方法区:主要存储加载的类的元数据信息
程序计数器:指向执行的字节码的行号,主要用来实现跳转等功能。
栈:里面包含栈帧,每个栈帧用来表述运行的方法,栈帧中包含该方法的局部变量表、操作数栈等
本地栈:和栈功能类似,不过主要用来表述本地的方法
其他答案:
https://blog.csdn.net/chen772209/article/details/107045647/
8. 运行一段代码会抛出什么异常:
private void recursive() {
recursize();
}
StackOverFlowError
原因:栈的大小是有限的,当申请的栈帧过多时,会抛出此异常
线程请求的栈深度大于虚拟机所允许的深度
9. 列出一些常用的jvm参数,并描述每个参数的作用
-Xss 线程栈大小
-Xmn 初始堆大小
-Xmx 最大堆大小
-XX:NewSize设置年轻代大小
-XX:NewRatio 设置年轻代和老年代的比值,一般是3,代表年轻代是年老代的1/3
-XX:SurvivorRatio 年轻代中Eden和两个Survivor区的比值
-XX:+PrintGCDetails 打印虚拟机的具体细节
-XX:+HeapDumpOnOutOfMemoryError 如果内存溢出就把堆中的信息打印出来
10. 以下哪个对象不可能包含SQL语句
1. 触发器
2. 函数
3. 索引
4. 视图
这个暂时不太清楚。。
11. 下面关于DNS说法错误的是:
1. DNS的作用是域名和IP地址的互相映射
2. DNS协议运行在UDP协议之上
3. DNS协议端口为53
4. DNS的默认缓存时间为1小时
这个目前感觉是4,缓存时间到底指的是游览器的还是其他的
12. 编程实现:给定两个字符串,确定其中一个字符串重排列后是否可以包含另一个字符串?
例如:
给出字符串:ac以及abc, 对字符串重新排列生成acb包含ac,返回true
自己的解决办法:
1. 先判断哪个字符长,短的将其所有字符存入map并计数
2. 然后遍历长的,一一判断每个字符,对map进行消除
3. 判断map是否为空,为空则代表true
13. 实现LRU
https://blog.csdn.net/CodingNO1/article/details/107365282
class LRUCache {
// 缓存容量
private int cap = 0;
// 已经存储的数据个数
private int size = 0;
// 用来提高查找效率的工具
private ConcurrentHashMap<Integer, Node> cache = new ConcurrentHashMap<>();
// 维护一个首尾节点 作为一个标记位,实际不存值
private Node head, tail;
public LRUCache(int cap) {
// 对容量进行初始化
this.size = 0;
this.cap = cap;
// 对首尾节点初始化
head = new Node();
tail = new Node();
// 构建一个双向链表
head.next = tail;
tail.pre = head;
}
/**
* 每次取数据后加节点移到最前面
* @param key
* @return
*/
public int get(int key) {
Node node = cache.get(key);
// 没有则返回-1
if (node == null) {
return -1;
}
// 移动
moveToHead(node);
return node.val;
}
/**
* 存入数据,且将这个数据置于首部
* @param key
* @param value
*/
public void put(int key, int value) {
// 先看是否存在这个key ,存在就直接移动到首部即可
Node node = cache.get(key);
if (node != null) {
// 更新对应的value
node.val = value;
// 移到首部
moveToHead(node);
} else { // 不存在则需要加入key
// 先根据key,value构建节点
Node newNode = new Node();
newNode.setKey(key);
newNode.setVal(value);
// 先判断size是不是小于cap
if (size == cap) {
// 等于cap则先将尾部的remove,再添加
Node tail = popTail();
cache.remove(tail.key);
size --;
}
// 将节点加入cache
cache.put(key, newNode);
// 加入以head为头,tail为尾的双向链表,head与tail都是空的节点
addNode(newNode);
// size 增加
size ++;
}
}
/**
* 将节点移到头部
* @param node
*/
private void moveToHead(Node node) {
// 先删除节点
removeNode(node);
// 再添加就到了首部了(head的后面一个)
addNode(node);
}
/**
* 添加到链表中
* 这里就已经相当于添加到头部了,head和tail只是用来做标记的一个空节点
* @param node
*/
private void addNode(Node node) {
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
}
/**
* 从链表中移除
* @param node
*/
private void removeNode(Node node){
// 获取当前节点的前后节点
Node pre = node.pre;
Node next = node.next;
// 前后节点连接
pre.next = next;
next.pre = pre;
}
/**
* 移除尾部节点
* @return
*/
private Node popTail() {
// 先取到尾部节点
Node res = tail.pre;
// 调用移除方法即可
removeNode(res);
return res;
}
}
/**
* 双向链表
* @author
*
*/
class Node {
// 下一个节点
Node next;
// 上一个节点
Node pre;
// 键
int key;
// 值
int val;
public Node() {
}
public Node(int val) {
this.val = val;
}
/**get/set方法**/
}
14. 怎么理解高内聚和低耦合
https://zhidao.baidu.com/question/369384616486815764.html
https://blog.csdn.net/villainy13579/article/details/93507954?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
15. 输出所有1到999中不包含5的整数
private void print5() {
for (int i = 0; i <= 9; i ++) {
if (i == 5) {
continue;
}
for (int j = 0; j <= 9; j ++) {
if (j == 5) {
continue;
}
for (int k = 0; k <= 9; k ++) {
// 先排除0
if (i == 0 && j == 0 && k == 0) {
continue;
}
if (k == 5) {
continue;
}
// 打印剩余数字
System.out.println(i * 100 + j * 10 + k);
}
}
}
}
16. 申请到一个C类地址,若要分为8个子网,其子网掩码为
将一个C类IP网络分为8个子网,其子网掩码为255.255.255.224
https://zhidao.baidu.com/question/142371759.html
17. 若用一个一维数组表示一个深度为5,节点个数为10的二叉树,数组长度最少为多少
31
18. 从500万个数中选出最大的30个数,时间复杂度最低的是:
1. 快速排序
2. 选择排序
3. 归并排序
4. 堆排序
自己的答案:4
19. 下列聚合函数中不忽略null的是
1. SUM(列名)
2. MAX(列名)
3. COUNT(* )
4. AVG(列名)
count(*)是不忽略的
在聚合函数中遇到空值时,除了COUNT(*)外,都跳过空值而去处理非空值。
比如count(列名)也会忽略空值
20. 输入arr, 找出其中最小的k个数
// 使用api中的优先队列作为堆
public int[] getLeastNumbers(int[] arr, int k) {
if (k <= 0 || arr == null || arr.length == 0) {
return new int[0];
}
// 默认最小堆 可以传参使其变为最大堆
// 这题如果使用最小堆的话,需要建一个数量为arr.length的堆 而最大堆只需要建立一个k大小的堆
PriorityQueue<Integer> heap = new PriorityQueue<Integer>((a, b) -> b - a);
for (int i = 0; i < arr.length; i ++) {
if (i < k) {
heap.add(arr[i]);
} else {
// 比较堆顶元素和数组元素的大小
if (arr[i] < heap.peek()) {
heap.poll();
heap.add(arr[i]);
}
}
}
// 最后将k个元素加入结果集即可
int[] res = new int[k];
for (int i = 0; i < k; i ++) {
res[i] = heap.poll();
}
return res;
}