面试专题
Java体系
stream流
-
大白话理念 :
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等 - ==集合接口分为 串行流stream() 和 并行流parallelStream() ==
-
主要分为 中间操作和最终操作
-
中间操作:
- 过滤类型 : ==filter(过滤),distinct(去重),limit(限流)==,skip(跳过)
- 映射 : ==map==,flatMap
-
最终操作 :
- 查找 : ==allMatch,anyMatch==,noneMathch,findFirst,findAny
- 归约 : ==reduce==
- 收集 : ==counting(总数), maxBy(最大值), minBy(最小值), summingInt, summingLong, summingDouble(求和), averageInt, averageLong,== ==averageDouble(平均数),== summarizingInt, summarizingLong, summarizingDouble(一次性查询元素个数、总和、最大值、最小值和平均值),==joining(字符串拼接), groupingBy(分组),== partitioningBy(分区)
-
中间操作:
lambda和接口
接口的默认方法 :
- 默认方法允许我们在接口里添加新的方法,而不会破坏实现这个接口的已有类的兼容性,也就是说不会强迫实现接口的类实现默认方法
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供一个默认的方法实现,所有这个接口的
实现类都会通过继承得倒这个方法(如果有需要也可以重写这个方法) - 默认方法是可以在接口中写执行体的。主要作用:
- 1.接口升级,可以避免改变其他实现类。
- 2.函数拼接
public interface Animal {
default void fly() {
System.out.println("birds can fly...");
}
default void swim() {
System.out.println("fishes can swim......");
}
}
public class Bird implements Animal {
}
public class TestMain {
public static void main(String[] args) {
Bird bird = new Bird();
bird.fly();
Fish fish = new Fishe();
fish.swim();
}
}
接口的静态方法 :
- 因为静态方法不可以实例化,在接口中也是一样的
- 所以在接口中定义静态方法的作用就是静态方法的作用:
- 不需要实例化,直接使用,节省内存空间
- 注意:接口中静态方法和类中静态方法一样,只能通过接口.静态方法名的方式调用
public interface AnimalFactory {
static Animal create(Supplier<Animal> supplier) {
return supplier.get();
}
}
public class TestAnimalFactory {
public static void main(String[] args) {
// 生产一只鸟
Animal bird = AnimalFactory.create(Bird::new);
bird.fly();
// 生产一条鱼
Animal fish = AnimalFactory.create(Fishe::new);
fish.swim();
}
}
函数式接口
- 每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
- JDK 1.8 之前已有的函数式接口:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
- JDK 1.8 新增加的函数接口:
- java.util.function
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
Lambda 表达式
- Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
- ambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
执行结果:
$ javac Java8Tester.java
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
Lambda 作用域 :
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
springboot 框架
springboot 1.x 和 2.x 的区别
- 2.x 至少需要 JDK 8 的支持
- 2.x 增加响应式 Spring 编程支持
- 2.x 增加 HTTP/2 支持
- 部分配置有改动
Spring Boot @RestController和@controller有什么区别?
- @RestController注解相当于@ResponseBody + @Controller合在一起的作用,返回的是json或文本数据。
- @Controller 可以配合视图解析器进行路由控制
Spring Boot 的配置文件有哪几种格式?它们有什么区别?
- .properties 和 .yml,它们的区别主要是书写格式不同。
Spring Boot 自动配置原理是什么?
- 注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
Spring Boot 有哪几种读取配置的方式?
- Spring Boot 可以通过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量
Mybatis 框架
#{}和${}的区别是什么?
-
{}是预编译处理,${}是字符串替换。
- Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
- Mybatis在处理
{}替换成变量的值。
- 使用#{}可以有效的防止SQL注入,提高系统安全性。
当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
- 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
- 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系。
模糊查询like语句该怎么写?
- like concat('%', #{name}, '%') 有效防止SQL注入
如何执行批量插入?
- 使用<foreach>
如何获取自动生成的(主)键值?
- <inset usegeneratedkeys=”true” keyproperty=”id”>
在mapper中如何传递多个参数?
- 在映射文件中使用#{0},#{1}代表传递进来的第几个参数
- 使用@param注解:来命名参数
- 使用Map集合作为参数来装载
接口绑定有几种实现方式,分别是怎么实现的?
- 接口绑定有两种实现方式:
- 通过注解绑定,在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定
- 通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.
Mysql
mysql优化
- ==字段类型选择尽可能小(占用存储空间少)、尽可能定长(占用存储空间固定)、尽可能使用整数。==
- ==索引 首先应考虑在 where 及 order by 涉及的列上建立索引。==
- 少用(不用)多表操作(子查询,联合查询),而是将复杂的SQL拆分多次执行。如果查询很原子(很小),会增加查询缓存的利用率。
- 分页查询优化 在子查询中只查询数据的id并分页 然后根据子查询的id在进行查询完整数据
- ==使用join 代替 子查询==
- ==对查询单条是 添加limit 1==
- ==不要在列上进行运算==
- 不使用NOT IN和<>操作 NOT IN可以NOT EXISTS代替,id<>3则可使用id>3 or id<3来代替。
- 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
select id from t where num=10 or Name = 'admin'
#可以这样查询:
select id from t where num = 10
union all
select id from t where Name = 'admin'
- ==exists 代替 in==
- 分页时每页条数过大时 要先分页再JOIN
- ==不要使用 select *==
- ==如何避免死锁==
- 注意程序的逻辑
根本的原因是程序逻辑的顺序,最常见的是交差更新 Transaction 1: 更新表A -> 更新表B Transaction 2: 更新表B -> 更新表A 这类问题要从程序上避免,所有的更新需要按照一定的顺序
- 保持事务的轻量
- 提高运行的速度
- 尽量快提交事务,减少持有锁的时间
- 设置锁等待超时参数,我们可以通过 innodb_lock_wait_timeout 设置合理的等待超时阈值,特别是在一些高并发的业务中,我们可以尽量将该值设置得小一些,避免大量事务等待,占用系统资源,造成严重的性能开销。
redis
常用数据类型
- String(字符串):Redis最基本的数据类型,一个键对应一个值,一个键值最大存储512MB
- Hash(哈希):hash是一个键值对的集合,是一个String类型的field和value的映射表,适合用于存储对象.可用作简易的消息队列
- List(列表):是redis的简单的字符串列表,按插入顺序排序
- Set(集合):是String字符串类型的无序集合,也不可重复
- ZSet(sorted set 有序集合)是String类型的有序集合,也不可重复。有序集合中的每个元素都需要指定一个分数,根据分数对元素进行升序排序。可以做排行榜
Redis应用场景,能做什么
- 数据缓存(最常用)
- 会话session管理器(最常用)
- 消息队列(支付)
- 活动排行榜或计数
- 发布,订阅消息(消息通知)
- 商品列表,评论列表
缓存穿透
- 描述:缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大
- 解决方案:
- 接口层增加校验,如使用布隆过滤器封装全部id ,查询时 先去redis查询,如果没有,再去查看布隆过滤器中是否存在. 如果不存在直接返回 存在再到数据库中进行查询;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿
- 描述:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
- 解决方案
- 设置热点数据永远不过期
- 加互斥锁
缓存雪崩
- 描述:缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
- 解决方案
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 设置热点数据永远不过期。
布隆过滤器
Memcache与Redis的区别都有哪些?
- 存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,redis可以持久化其数据
- 数据支持类型 memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 ,提供list,set,zset,hash等数据结构的存储
- redis的速度比memcached快很多
-Redis支持数据的备份,即master-slave模式的数据备份。
单线程的redis为什么这么快
- 纯内存操作
- 单线程操作,避免了频繁的上下文切换
- 采用了非阻塞I/O多路复用机制
如果redis没有设置expire,他是否默认永不过期?
- Redis无论有没有设置expire,他都会遵循redis的配置好的删除机制,在配置文件里设置:
redis最大内存不足"时,数据清除策略,默认为"volatile-lru"。- volatile-lru ->对"过期集合"中的数据采取LRU(近期最少使用)算法.如果对key使用"expire"指令指定了过期时间,那么此key将会被添加到"过期集合"中。将已经过期/LRU的数据优先移除.如果"过期集合"中全部移除仍不能满足内存需求,将OOM.
- allkeys-lru ->对所有的数据,采用LRU算法
- volatile-random ->对"过期集合"中的数据采取"随即选取"算法,并移除选中的K-V,直到"内存足够"为止. 如果如果"过期集合"中全部移除全部移除仍不能满足,将OOM
- allkeys-random ->对所有的数据,采取"随机选取"算法,并移除选中的K-V,直到"内存足够"为止
- volatile-ttl ->对"过期集合"中的数据采取TTL算法(最小存活时间),移除即将过期的数据.
- noeviction ->不做任何干扰操作,直接返回OOM异常
Redis 部署方式
- 单机
- 哨兵
- 集群
分布式锁
- 加锁 : 使用SETNX, 只在键 key 不存在的情况下, 将键 key 的值设置为 value 。
若键 key 已经存在, 则 SETNX 命令不做任何动作。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。 - 解锁 : 使用lua脚本 避免死锁
Redis的持久化
- Redis持久有两种方式:快照(RDB),仅附加文件(AOF)
Redis 发布订阅
- Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
- Redis 客户端可以订阅任意数量的频道。
linux
常用命令
- ls : 展示文件夹内内容
- cd : 切换到目录
- tree : 显示树形的层级目录结构,非原生命令,需要安装tree
- cp : 复制文件
- rm : 删除文件
- mv : 移动文件
- pwd : 显示当前所在实际路径
- tar : 压缩解压
- mkdir : 创建目录
- rmdir : 删除目录 , -p 递归删除目录
- ps : 显示运行的进程信息
- kill : 终止进程 , -9 强制中断一个进程的进行
- crontab : 启动linux定时任务的服务
- free : 显示Linux系统中空闲的、已用的物理内存及swap内存,及被内核使用的buffer
- top : Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器
- chmod : 配置 文件权限 ,-R:进行递归的持续更改,即连同子目录下的所有文件都会更改
- useradd : 建立用户账号
- vi/vim : 使用vi编辑器
- cat : 连接文件或标准输入并打印。这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用。
- tail : 将文件写到标准输出, -f 可以方便的查阅正在改变的日志文件
- ifconfig : 用来查看和配置网络设备
- whereis : 只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)
- grep : 该命令常用于分析一行的信息,若当中有我们所需要的信息,就将该行显示出来,该命令通常与管道命令一起使用,用于对一些命令的输出进行筛选加工等等,比如可以加在ps, tail, cat后面
查看端口
- ==netstat== -tunlp | grep 8888
netstat命令各个参数说明如下:
-t : 指明显示TCP端口
-a : 显示所有的套接字
-u : 指明显示UDP端口
-l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的程序)
-p : 显示进程标识符和程序名称,每一个套接字/端口都属于一个程序。
-n : 不进行DNS轮询,显示IP(可以加速操作)
- lsof -i:80 : 查看进程占用哪些文件的
- fuser 22/tcp -v : fuser命令和lsof正好相反,是查看某个文件被哪个进程占用的。
网友评论