1. 不使用任何优化,直接访问数据库,如何优化?
编辑
优化思路 :
-
Redo、Undo Log 的开销:数据库的事务日志(Redo和Undo日志)在事务处理过程中会产生大量的I/O操作。如果数据库没有优化,事务的回滚或提交会产生很大的性能开销。为了优化,可以减少事务的提交频率,或者批量处理多条SQL语句。
编辑 -
批处理(Batch Processing) :将多个SQL语句合并成一个批处理,减少与数据库的交互次数。这可以大幅降低网络延迟及数据库I/O负担。
编辑 -
连接池(Connection Pool) :数据库连接的创建和销毁是非常耗时的,使用连接池可以重用现有的连接,避免频繁的连接和断开数据库。常见的连接池有HikariCP、C3P0等。
编辑 -
缓存 :使用缓存技术减少对数据库的频繁访问,例如通过Redis等内存数据库缓存热点数据。
编辑
2. 行锁锁的是索引,如果一个表没有主键怎么加锁?
如果表没有主键,InnoDB会选择使用一行记录的最左边的唯一索引作为锁定对象。如果表没有任何唯一索引,InnoDB将使用一个隐式的"隐藏主键"(实际上是一个基于行数据的聚簇索引)来执行锁定操作。由于没有显式的主键或唯一索引,锁定可能会影响性能,因此推荐给每个表添加主键或唯一索引。
3. HashMap 为什么不直接用红黑树,还要用链表?
-
性能考虑 :HashMap的设计目的是在平均情况下提供O(1)的查找速度,而链表作为HashMap中"碰撞"处理的方式,在大多数情况下非常高效。红黑树的查找操作复杂度为O(log n),相比之下,链表的查找复杂度虽然为O(n),但是对于大多数情况下的哈希冲突,它足够快。
-
空间和复杂度 :红黑树需要更多的内存空间来保存平衡信息。链表结构简单,占用内存较少且实现起来更容易,适合哈希表的冲突处理。
-
链表转红黑树:如果哈希冲突过于严重,Java 8中的HashMap会在链表长度超过一定阈值时(如8个元素)转为红黑树,从而提高性能。
4. 红黑树的特性?各种操作的时间复杂度?最多旋转几次达到平衡?
红黑树的特性 :
-
每个节点是红色或黑色。
-
根节点为黑色。
-
所有叶子节点(NIL节点)为黑色。
-
如果一个红色节点有子节点,它的两个子节点都为黑色(即不允许有两个连续的红色节点)。
-
从任意节点到其子孙节点的路径上,黑色节点的数量是相同的(称为黑色高度)。
时间复杂度 :
- 插入、删除、查找的时间复杂度均为O(log n),这是因为红黑树保证了树的高度是对数级别。
最多旋转次数 :
- 插入和删除操作在最坏情况下最多需要进行2次旋转来保持红黑树的平衡。
5. finally 中 return 会发生什么?
-
在
finally块中使用return会导致它覆盖try或catch块中的return,并且finally中的return会在方法执行完成之前先执行。 -
注意 :如果在
finally中使用return,方法会返回finally中的值,而不是try或catch中的值。这是因为finally块中的return会先执行,且覆盖了之前的return。
6. 如何破坏双亲委派?
双亲委派模型是Java类加载机制的核心原则,即加载器委托给父类加载器加载类。要破坏双亲委派模型,可以通过自定义类加载器来实现:
-
在自定义类加载器中,重写
findClass()方法,直接加载类文件而不委托给父类加载器。 -
通过设置
setParent()方法或其他方式改变父加载器,或者将父加载器设置为null。
7. 浏览器中用户信息和密码的安全有没有考虑过?
在浏览器中存储用户信息和密码时,需要考虑以下安全措施:
-
加密存储 :密码应在客户端或服务器端加密存储。常用的加密算法包括AES、SHA等。
-
SSL/TLS :使用HTTPS协议(即SSL/TLS)加密用户和服务器之间的通信,防止信息被中间人攻击窃取。
-
JWT :使用JWT(JSON Web Token)来确保身份验证的安全性,并确保令牌的有效期。
-
输入验证 :对密码和用户信息进行严格的输入验证,防止SQL注入等攻击。
8. 两个线程如何交替打印A和B?
可以使用Object的wait()和notify()方法来实现线程交替打印:
```java
class PrintAB {
private static final Object lock = new Object();
private static boolean printA = true;
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
while (true) {
synchronized (lock) {
while (!printA) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("A");
printA = false;
lock.notify();
}
}
});
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
while (printA) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("B");
printA = true;
lock.notify();
}
}
});
threadA.start();
threadB.start();
}
}
```
9. 100万个数,数写一篇某电商大厂某里的场景面试相关的文章,并回答以下问题:类型是int,给你8核CPU,8G内存,如何求数组的和?
可以利用多线程来并行计算数组的和。由于有8个核心,可以将数组分成8个部分,每个核心计算一部分的和,然后将各部分的结果相加:
-
切分数组成8个部分。
-
每个线程计算一个部分的和。
-
汇总各线程的结果。
代码示例 :
```java
public class SumArray {
public static void main(String[] args) {
int[] arr = new int[1000000];
// 填充数组
for (int i = 0; i < arr.length; i++) {
arr[i] = i + 1;
}
int numThreads = 8;
int chunkSize = arr.length / numThreads;
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future> results = new ArrayList<>();
// 分配任务给线程
for (int i = 0; i < numThreads; i++) {
int start = i * chunkSize;
int end = (i == numThreads - 1) ? arr.length : (i + 1) * chunkSize;
results.add(executor.submit(() -> {
int sum = 0;
for (int j = start; j < end; j++) {
sum += arr[j];
}
return sum;
}));
}
// 汇总结果
int totalSum = 0;
for (Future result : results) {
try {
totalSum += result.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("Total Sum: " + totalSum);
executor.shutdown();
}
}
```
10. 很多个不重复的数,假设用的数组来装,我希望你实现一个方法,每次调用这个方法给我随机返回100个不重复,记住是随机的。
可以通过Collections.shuffle()方法来随机打乱数组,然后取前100个数:
```java
import java.util.*;
public class RandomSelector {
private List list;
public RandomSelector(List numbers) {
this.list = new ArrayList<>(numbers);
}
public List getRandom100() {
Collections.shuffle(list);
return list.subList(0, 100);
}
public static void main(String[] args) {
List numbers = new ArrayList<>();