复习日!把国庆以来看过的全部重新看了一遍!
滔博,行!阿水,行!水水水水水水水水水水水水!
算法题:
1.四数之和(力扣每日一题):双循环+双指针+大量剪枝(自己第一次写的是递归回溯,发现去重直接就去不动,而且很慢):
双循环(for i for j)遍历数组来确定第一个和第二个数,双指针left=j+1,right = length - 1,如果num[i] + num[j] +num[left] + num[right]大于目标值则让right--,如果小于目标值则让left++,相等则通过List.add(Array.asList(num[i] + num[j] +num[left] + num[right])添加结果;
去重通过:如果遇到遍历遇到的下个数和上个数相同则continue;如果符合要求添加到res中后左右指针需要移动到不同数字才可继续;
剪枝操作:如果num[i] + num[i+1] + num[i+2]+num[i+3]大于target,因为数组排序过,所以后续都一定大于,可以直接break外层循环;
如果num[i]+num[n-3]+num[n-2]+num[n-1]<target,说明后续都小于target,可以直接continue进入下一轮;
确定前两个数后,如果num[i] + num[j] +num[j+1] + num[j+2] > target,可以break退出内层循环;
如果num[i]+num[j]+num[n-2]+num[n-1]<target,说明后续都小于target,可以直接continue进入下一轮;
双循环(for i for j)遍历数组来确定第一个和第二个数,双指针left=j+1,right = length - 1,如果num[i] + num[j] +num[left] + num[right]大于目标值则让right--,如果小于目标值则让left++,相等则通过List.add(Array.asList(num[i] + num[j] +num[left] + num[right])添加结果;
去重通过:如果遇到遍历遇到的下个数和上个数相同则continue;如果符合要求添加到res中后左右指针需要移动到不同数字才可继续;
剪枝操作:如果num[i] + num[i+1] + num[i+2]+num[i+3]大于target,因为数组排序过,所以后续都一定大于,可以直接break外层循环;
如果num[i]+num[n-3]+num[n-2]+num[n-1]<target,说明后续都小于target,可以直接continue进入下一轮;
确定前两个数后,如果num[i] + num[j] +num[j+1] + num[j+2] > target,可以break退出内层循环;
如果num[i]+num[j]+num[n-2]+num[n-1]<target,说明后续都小于target,可以直接continue进入下一轮;
面试题:
1.代理模式:
静态代理:被代理类代理类实现通用接口,代理类在不改变原有功能的情况下增加更多的逻辑;
JAVA动态代理:
1、定义一个功能接口
2、被代理类继承功能接口并重写方法实现业务逻辑
3、代理类继承InvocationHandler接口,定义一个内部Object类和关于内部类的构造器;重写invoke方法,方法参数是被代理对象proxy、调用方法method、方法参数Object[] args,方法体实现代理类的业务逻辑;
4、Main方法中,新建一个被代理类,新建一个incocationHandler接口的实现类(通过代理类的构造器)并传入被代理类,新建一个功能接口的实现类Proxy(通过proxy.newProxyInstance方法,传入被代理类的类加载器、被代理类实现的接口、刚刚新建的InvocationHandler的实现类);
5、可以通过Proxy对象调用功能方法实现对被代理对象的功能的代理和增强;
核心是通过调用Proxy的静态方法newProxyInstance方法创建的动态代理类,而该方法的核心是传入的InvocationHandler实现类,InvocationHandler是一个接口,每个代理的实例都有一个对应的InvocationHandler实现类,正是这个实现内的Invoke方法决定了怎样处理传过来的方法调用,所以InvocationHandler是实际的执行者;
CGLIB动态代理:通过底层字节码方法实现一个被代理类的子类,并通过方法拦截的方式拦截被代理类的方法调用并顺势织入横向逻辑;
静态代理:被代理类代理类实现通用接口,代理类在不改变原有功能的情况下增加更多的逻辑;
JAVA动态代理:
1、定义一个功能接口
2、被代理类继承功能接口并重写方法实现业务逻辑
3、代理类继承InvocationHandler接口,定义一个内部Object类和关于内部类的构造器;重写invoke方法,方法参数是被代理对象proxy、调用方法method、方法参数Object[] args,方法体实现代理类的业务逻辑;
4、Main方法中,新建一个被代理类,新建一个incocationHandler接口的实现类(通过代理类的构造器)并传入被代理类,新建一个功能接口的实现类Proxy(通过proxy.newProxyInstance方法,传入被代理类的类加载器、被代理类实现的接口、刚刚新建的InvocationHandler的实现类);
5、可以通过Proxy对象调用功能方法实现对被代理对象的功能的代理和增强;
核心是通过调用Proxy的静态方法newProxyInstance方法创建的动态代理类,而该方法的核心是传入的InvocationHandler实现类,InvocationHandler是一个接口,每个代理的实例都有一个对应的InvocationHandler实现类,正是这个实现内的Invoke方法决定了怎样处理传过来的方法调用,所以InvocationHandler是实际的执行者;
CGLIB动态代理:通过底层字节码方法实现一个被代理类的子类,并通过方法拦截的方式拦截被代理类的方法调用并顺势织入横向逻辑;
2.AOP的实现方式:java动态代理和CGLIB动态代理,在使用AOP时Spring会调用一个方法看看被代理类是否实现接口,如果没有接口就用CGLIB,如果有接口就用JAVA动态代理
3.CAS(Compare And Swap):对比和预期值受否相同,相同则进行操作,不同则重新执行对比操作;
隐患:长时间自旋消耗资源、设置一个自旋时限;
ABA问题,设置一个版本号(A1 B1 A2);
隐患:长时间自旋消耗资源、设置一个自旋时限;
ABA问题,设置一个版本号(A1 B1 A2);
4.索引相关:又多又杂,看了很久,总体上有个大概的结构了,面试遇到估计也没之前那么慌了(https://my.oschina.net/tigeriaf/blog/3216265)
5.数据库的IO密集型和CPU密集型以及对应的数据库查询慢排查方式:
IO密集型:数据库仅提供建立的查询插入等操作,复杂的业务逻辑依赖与程序的实现,需要程序与数据库的频繁交互:考虑是不是投影了全表字段、全表查询,表结构、索引结构不佳,内存缓存区过小导致IO次数过多,网络带宽过小等;
CPU密集型:一些复杂的逻辑计算可以在数据库中进行处理,可以依赖与数据库端的存储过程,触发器等功能,减少了程序代码与数据库的交互,减轻访问数据库带来的IO压力,对于装备有高速磁盘阵列的服务器来说,可以实现CPU密集型:过于复杂的语句、存储过程、触发器、自定义函数,锁竞争,高并发;
IO密集型:数据库仅提供建立的查询插入等操作,复杂的业务逻辑依赖与程序的实现,需要程序与数据库的频繁交互:考虑是不是投影了全表字段、全表查询,表结构、索引结构不佳,内存缓存区过小导致IO次数过多,网络带宽过小等;
CPU密集型:一些复杂的逻辑计算可以在数据库中进行处理,可以依赖与数据库端的存储过程,触发器等功能,减少了程序代码与数据库的交互,减轻访问数据库带来的IO压力,对于装备有高速磁盘阵列的服务器来说,可以实现CPU密集型:过于复杂的语句、存储过程、触发器、自定义函数,锁竞争,高并发;
6.手写单例模式:代码就不贴了,普通饿汉懒汉,synchronized加锁懒汉,双重检查懒汉,静态内部类懒汉,全都写了一遍
7.工厂模式(后两个有点抽象,下午看的时候有点晕也没咋看懂,下次找个具体例子再康康吧):
1、简单工厂:一般内部有switch,根据传入参数来判断创建的类型,可以做到在不改变客户端代码的情况下更换和增加新的产品类;但是工厂的职责过重,添加新的产品时简单工厂类就需要修改,违反了开放-封闭原则,不利于系统的拓展和维护;简单工厂的静态方法也使得无法形成继承的等级结构;
2、工厂方法:每种类都有各自的工厂,只需要专注于工厂类即可,工厂方法用来创建具体的产品,在增加新的产品的时候只需要增加对应的工厂即可,创建对象的细节封装在工厂内部,有抽象的工厂父类,所有的工厂都继承父类;但是增加新的产品就需要增加新的工厂类
3、抽象工厂可以实现不止一个接口,一个工厂也可以生产不止一个产品类,增加新的产品族只需要增加新的工厂,但如果增加新的产品类较为复杂
1、简单工厂:一般内部有switch,根据传入参数来判断创建的类型,可以做到在不改变客户端代码的情况下更换和增加新的产品类;但是工厂的职责过重,添加新的产品时简单工厂类就需要修改,违反了开放-封闭原则,不利于系统的拓展和维护;简单工厂的静态方法也使得无法形成继承的等级结构;
2、工厂方法:每种类都有各自的工厂,只需要专注于工厂类即可,工厂方法用来创建具体的产品,在增加新的产品的时候只需要增加对应的工厂即可,创建对象的细节封装在工厂内部,有抽象的工厂父类,所有的工厂都继承父类;但是增加新的产品就需要增加新的工厂类
3、抽象工厂可以实现不止一个接口,一个工厂也可以生产不止一个产品类,增加新的产品族只需要增加新的工厂,但如果增加新的产品类较为复杂
8.HashMap(死循环)、ArrayList线程安全问题及解决:HashMap的死循环,一直没理解这个环是怎么出来的,找了很多帖子,看了很久才终于看懂了;
9.JVM内存模型,GC,对象从新建到销毁全过程:老生常谈,多看多背
10.类加载器、双亲委派;
11.MVCC:当时看觉得巨难,现在再看觉得也算是理解了
特性:
(1)MVCC只支持RC(读取已提交)和RR(可重复读)隔离级别。
(2)MVCC能解决脏读、不可重复读问题,不能解决丢失更新问题和幻读问题。
(3)MVCC是用来解决读写操作之间的阻塞问题。使得在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。
(1)MVCC只支持RC(读取已提交)和RR(可重复读)隔离级别。
(2)MVCC能解决脏读、不可重复读问题,不能解决丢失更新问题和幻读问题。
(3)MVCC是用来解决读写操作之间的阻塞问题。使得在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。
三个重要字段:
DB_TRX_ID(6字节):最近一次更新或插入h或删除改行数据的事务的ID
DB_ROLL_PTR(7字节):指向该行数据的undo log
DB_ROW_ID(6字节): 随着新行插入而单调递增的行ID。(和MVCC关系不大)
快照:(Read View)记录了当前系统中活跃的事务信息,用来做可见性判断
DB_TRX_ID(6字节):最近一次更新或插入h或删除改行数据的事务的ID
DB_ROLL_PTR(7字节):指向该行数据的undo log
DB_ROW_ID(6字节): 随着新行插入而单调递增的行ID。(和MVCC关系不大)
快照:(Read View)记录了当前系统中活跃的事务信息,用来做可见性判断
12.TCP、UDP:区别,三次握手四次挥手、为什么多一次、为什么最后等2MSL、客户端故障怎么办(第N次看了,只求面试的时候能有条理的完整的说出来)
13.公平锁和非公平锁:等锁队列,是否排队
14.内存泄露和内存溢出、如何避免
最后去看了会动态规划的视频,接下来一段时间的算法要集中在动态规划这块了,感觉不论是笔试面试出题的频率都还挺高的,得好好学习一下了;
美团面试近在眼前,国庆收假后中兴和海康应该也差不多开奖了,又要到一个终极忙终极累的阶段了
现在每天学习全靠美食支撑(今天专程骑电动车去买了泸溪河,满足了~)
加油,共勉!
全部评论
(1) 回帖