《天净沙·我·蘑菇街总结》 双非菜鸡奇葩,面试项目框架,java java,卑微学子去哪?


2020-03-18 蘑菇街一面 是凉凉了 但是很高兴的是面试官真的很耐心 自己也发现了很多的问题和不足

AOP

动态代理的两种方法

Proxy + InvocationHandler

被代理对象接口

1
2
3
4
public interface UserService {
public void addUser(User user);
public User getUser(int id);
}

被代理类接口实现

1
2
3
4
5
6
7
8
9
10
11
public class UserServiceImpl implements UserService {
public void addUser(User user) {
System.out.println("add user into database.");
}
public User getUser(int id) {
User user = new User();
user.setId(id);
System.out.println("getUser from database.");
return user;
}
}

代理(中间)类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ProxyUtil implements InvocationHandler {
private Object target; // 被代理的对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do sth before....");
Object result = method.invoke(target, args);
System.out.println("do sth after....");

return result;
}
ProxyUtil(Object target){
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}

测试实现

1
2
3
4
5
6
7
8
9
10
11
public class ProxyTest {
public static void main(String[] args){
Object proxyedObject = new UserServiceImpl(); // 被代理的对象
ProxyUtil proxyUtils = new ProxyUtil(proxyedObject);
// 生成代理对象,对被代理对象的这些接口进行代理:UserServiceImpl.class.getInterfaces()
UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
UserServiceImpl.class.getInterfaces(), proxyUtils);
proxyObject.getUser(1);
proxyObject.addUser(new User());
}
}

核心则在于被代理的对象必须要有含有自己方法的接口,才可以使用Proxy+InvocationHandler,同时,代理类中主要是通过调用被重写的invoke()方法。

CGlib

Cglib代理的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CGProxy implements MethodInterceptor{
private Object target; // 被代理对象
public CGProxy(Object target){
this.target = target;
}
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("do sth before....");
Object result = proxy.invokeSuper(arg0, arg2);
System.out.println("do sth after....");
return result;
}
public Object getProxyObject() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass()); // 设置父类
// 设置回调
enhancer.setCallback(this); // 在调用父类方法时,回调 this.intercept()
// 创建代理对象
return enhancer.create();
}
}

测试结果

1
2
3
4
5
6
7
8
9
public class CGProxyTest {
public static void main(String[] args){
Object proxyedObject = new UserServiceImpl(); // 被代理的对象
CGProxy cgProxy = new CGProxy(proxyedObject);
UserService proxyObject = (UserService) cgProxy.getProxyObject();
proxyObject.getUser(1);
proxyObject.addUser(new User());
}
}

与Proxy+InvocationHandler不同,Cglib是利用enhancer.create创造了一个代理对象,而这个大力对象的父类就是被代理类,利用enhancer.setSuperclass()指定,同时利用enhancer.setCallback()回调被重写的intercept,完成对方法的包裹

数据库索引

聚合索引 和 非聚合索引

聚合索引是 数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
非聚合索引是 该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引
言外之意,索引是有序的,如果存储数据和索引一样有序就是聚合索引,非聚合索引就是数据不是和索引本身一样有序

聚簇索引和非聚簇索引

数据和索引放在一起就是聚簇索引
地址和索引在一起,数据是通过地址再关联在一起是非聚簇索引

主索引,辅助索引

聚合索引状态下

就是根据主键值建立的索引就是主索引
辅助索引又叫二级索引,他的索引存主键的值

非聚合索引状态下

非聚簇索引的主索引和辅助索引的叶子节点的data都是存储的数据的物理地址,也就是说索引和数据并不是存储在一起的,数据的顺序和索引的顺序并没有任何关系,也就是索引顺序与数据物理排列顺序无关。

InnoDB

插入缓存

  1. 正常情况下正常的插入速度很快
  2. 但是如果使用UUID的话,需要离散的先访问非聚集索引索引页,判断非索引也是否在缓冲池中,若在,则直接插入,若没在则放入Insert Buffer中,在Insert Buffer 中合并后插入到索引页,提高了插入性能

    索引失效问题

组合索引

如果组合索引是abc那么用where a或者where a,b 或者where abc都可以,但是如果用 where b这样子的跳跃会导致索引失效

前导模糊系列

例:(like ‘%XX’或者like ‘%XX%’)

or失效

使用 or 的时候会导致索引失效,可以改用union

判断null

索引是b+树的排序结构,如果是null的话,导致节点不知道应该放在哪里

where 子句中使用!=或<>操作符

in 和 not in 也要慎用,

select id from t where num in(1,2,3)

三范式

原文博客

属性原子性

行可以唯一识别性

数据信息不冲突

BCNF

  1. 非主属性依赖主属性
  2. 主属性也要依赖其它的非主属性
  3. 非主属性不能被依赖

    HashMap

    HashMap根据hashcode查找到对应位置的方法

    原帖地址
  4. 拿到 key 的 hashCode 值
  5. 将 hashCode 的高位参与运算,重新计算 hash 值
  6. 将计算出来的 hash 值与 (table.length - 1) 进行 & 运算

    原因解释如下

    首先拿到key的hashcode值,如果是并利用 hashcode ^ table.length (table.length是2次幂)得到hash值,如果是与运算的话,假如table是初始化的16位,那么高位是无法进行运算的,结果值也就是被hashcode值的后四位所决定
    之后用hash值 进行 hash & (table.length)

    线程池的拒绝策略

    CallerRunsPolicy

    满了以后用调用线程池的线程去执行他

    DiscardPolicy

    直接拒绝,啥也不干

    AbortPolicy

    报个错,然后直接拒绝,啥也不干

    DiscardOldestPolicy

    抛出最先加入使用的线程