1.多线程
- 进程是程序的最小单位,线程是资源调度的最小单位
- Process 进程
- Thread 线程
- 创建线程的三种方式
- 线程的状态
- synchronized和lock的区别
Java中的sleep与wait区别
sleep- 让当前线程休眠指定时间。
- 休眠时间的准确性依赖于系统时钟和CPU调度机制。
- 不释放已获取的锁资源,如果sleep方法在同步上下文中调用,那么其他线程是无法进入到当前同步块或者同步方法中的。
- 可通过调用interrupt()方法来唤醒休眠线程。
wait
- 让当前线程进入等待状态,当别的其他线程调用notify()或者notifyAll()方法时,当前线程进入就绪状态
- wait方法必须在同步上下文中调用,例如:同步方法块或者同步方法中,这也就意味着如果你想要调用wait方法,前提是必须获取对象上的锁资源
- 当wait方法调用时,当前线程将会释放已获取的对象锁资源,并进入等待队列,其他线程就可以尝试获取对象上的锁资源。 - sleep vs wait
2.反射
-
什么是反射?
- Java 反射,就是在运行状态中。
获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
获取任意对象的属性,并且能改变对象的属性
调用任意对象的方法
判断任意一个对象所属的类
实例化任意一个类的对象
- Java 反射,就是在运行状态中。
-
获取获取Class类实例的三种方式?
获取class方法 (类 对象 Class) -
对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object类型的对象,而我不知道你具体是什么类,用这种方法
Person p1 = new Person();
Class c1 = p1.getClass();
- 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
这说明任何一个类都有一个隐含的静态成员变量 class
Class c2 = Person.class;
- 通过 Class 对象的 forName() 静态方法来获取,用的最多,但可能抛出 ClassNotFoundException 异常
Class c3 = Class.forName(“reflex.Person”);
- 测试反射分析性能问题
package com.liang.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//分析性能问题
public class Test10 {
//普通方式调用
public static void test01(){
User user = new User();
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次的时间为:"+(endTime-startTime+"ms"));
}
//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次的时间为:"+(endTime-startTime+"ms"));
}
//反射方式调用(暴力反射) 关闭监测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);//关闭监测
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("关闭监测执行10亿次的时间为:"+(endTime-startTime+"ms"));
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {//测试让程序跑起来哦
test01();
test02();
test03();
}
}
运行结果:
D:\JavaEnvironment\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Study software\IntelliJ IDEA 2020.3\lib\idea_rt.jar=58240:D:\Study software\IntelliJ IDEA 2020.3\bin" -Dfile.encoding=UTF-8 -classpath D:\JavaEnvironment\jdk1.8.0_152\jre\lib\charsets.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\deploy.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\dnsns.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\jaccess.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\localedata.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\nashorn.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunec.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\ext\zipfs.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\javaws.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jce.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jfr.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jfxswt.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\jsse.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\management-agent.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\plugin.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\resources.jar;D:\JavaEnvironment\jdk1.8.0_152\jre\lib\rt.jar;D:\IdeaWorkSpace2\kuangshen\out\production\reflect com.liang.reflection.Test10
普通方式执行10亿次的时间为:4ms
反射方式执行10亿次的时间为:3118ms
关闭监测执行10亿次的时间为:1552ms
Process finished with exit code 0
3.集合
-
Java中有几种常用的数据结构,主要分为Collection和map两个主要接口(接口只提供方法,并不提供实现),而程序中最终使用的数据结构是继承自这些接口的数据结构类
java数据结构
1、List;2、Vector;3、ArrayList;4、LinkedList;5、Set;6、HashSet;7、LinkedHashSet;8、SortedSet;9、Map;10、HashMap。 -
数组( Array )
属于引用类型,使用一定要开辟空间(实例化),要不然会报空指针 NullPointerException 异常,数组在内存中是连续存储的下标从0开始。
缺点:删除和插入元素麻烦,需要移动原来元素,耗费时间,只能存放特定的数据类型的值。 -
List(接口)
- List: 是 Collection接口的子接口,ArrayList是list的实现类,list中的元素是有序列且可重复的被称为序列。
- list是一个接口,不能实例化,只能用他的具体类来实例化。
- list基本上都是以Array为基础。
-
ArrayList、Vector、LinkedList区别
-
ArrayList:基于数组的数据结构,索引搜索和读取数据很快,但是删除数据开销大,需要重排数组中的所有数据。
-
ArrayList:可以动态夸张和收缩,声明ArrayList对象不需要指定它的大小。
-
ArrayList:可以储存不同类型的对象(包括null),数组只能存放特定数据类型的值。
-
LinkedList:是一个双链表结构,增删快。
-
Vector :基于数组(array)的list,线程同步(sychronized)的《线程安全》,这也是Vector 和ArrayList的重要区别,但是效率低下
-
-
Vector、ArrayList和LinkedList使用各种情况
- 多数情况下,性能最好的是ArrayList,但是当集合元素需要频繁插入或者删除时候linkedlist比较适合,但是他们三个性能没有数组好,vector是线程同步。
-
list总结
- 如果能确定数组长度,元素类型固定,尽量用数组来代替list;
- 如果没有频繁的删除操作,又不用考虑多线程问题,优先选择ArrayList;
- 如果需要考虑安全多线程工作,优先使用vector
- 如果有频繁的增删操作,优先使用 LinkedList
- 如果你什么都不知道,用ArrayList就没错了
-
Set(接口)
- set:是在hashmap基础上实现的,list则是array实现这是set和list的根本区别。(储存方式:k-v键值对)
- HashSet:存储方式是把hashmap中的key作为set的对应存储项
- LinkedHashSet : HashSet的一个子类,一个链表。
- SortedSet:有序的Set,通过SortedMap来实现的。
-
Map(接口)
- Map :把键对象和值对象进行关联的容器,map容器不允许重复。
- HashMap :基于哈希表map接口实现。允许使用null值和null健 ,HashMap 是非线程安全的, Hashtable是线程安全的。
- 无序
- 键值对(k-v)
- jdk1.8前数组+链表 jdk1.8后数据数组+链表+红黑树(效率高)
边界值大于8并且数组长度大于64链表转化为红黑树
数据结构:就是存储数据的一种方式 - HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(首先遍历链表,存在即覆盖,否则新增,hashmap中链表最少性能才会越好)
- 哈希冲突:如果两个不同的元素,通过哈希函数得出的实际存储地址相同怎么办?也就是说,当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞
- JDK1.8在JDK1.7的基础上针对增加了红黑树来进行优化。即当链表超过8时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。
-
Hashtable: Hashtable的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null 。
TreeMap:TreeMap则是对键按序存放 。
几个常用类的区别
1.ArrayList: 元素单个,效率高,多用于查询
2.Vector: 元素单个,线程安全,多用于查询
3.LinkedList:元素单个,多用于插入和删除
4.HashMap: 元素成对,线程不安全,元素可为空
5.HashTable: 元素成对,线程安全,元素不可为空
4.springMVC的执行流程
SpringMVC流程:
01、用户发送出请求到前端控制器DispatcherServlet。
02、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
03、HandlerMapping找到具体的控制器(可查找xml配置或注解配置),生成处理器对象的执行链(如果有),再一起返回给DispatcherServlet。
04、DispatcherServlet调用HandlerAdapter(处理器适配器)。
05、HandlerAdapter经过适配调用具体的处理器(controller)。
06、Controller执行完成返回ModelAndView对象。
07、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
08、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
09、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中。
11、DispatcherServlet响应用户。
5.springboot自动装配原理
启动器
-
spring-boot-dependencies:核心依赖在父工程中!
-
我们在写或者引入springboot依赖的时候不需要置指定版本,就因为有这些版本仓库
-
启动器:说白了就是springboot的启动场景,比如spring-boot-starter-web,他就会帮我们导入所有的依赖!springboot会将所有的功能场景都变成一个启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- 我们要使用什么功能只需要找到对应的启动器即可 starter
主程序
//@SpringBootApplication:标注这个类是一个springboot的应用
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
//将springboot应用启动
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
- 点开@SpringBootApplication注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan 扫描类以及类下面的包
@SpringBootConfiguration: springboot的配置
@Configuration:spring配置类
@Component:说明这也是一个spring的组件
@EnableAutoConfiguration: 自动配置
@AutoConfigurationPackage: 自动配置包
@Import(AutoConfigurationPackages.Registrar.class): 自动配置 包注册
@Import({AutoConfigurationImportSelector.class}): 自动导入选择
- 获取所有的配置
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
- 获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
META-INF/spring.factories:自动配置的核心文件
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。
6. String和StringBuilder、StringBuffer的区别?
答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。
7.Linux常用命令
-
pwd 当前所在的工作目录路径
-
cd 进入当前 输入的文件
-
cd … 返回上一个目录
-
ls 查看当前目录内的内容
-
cat 查看当前文件的内容 例如:cat lwh.txt
-
cp 将文件从当前目录复制到另外一个目录
-
mv 移动文件或者重命名文件
-
mkdir 创建一个新目录
-
rm 删除目录以及其中的内容
-
du 检查文件或目录占用的空间(磁盘使用情况)
-
kill 终止进程
-
ping 检查与服务器的连接状态
-
more 输出文档所有的内容,分页输出,空格浏览下一屏,q退出
-
less 用法和more相同,只是通过PgUp、PgOn键来控制
-
tail 用于显示文件后几号,使用频繁
-
tail -10 nginx.conf 查看nginx.conf的最后10行
-
tail –f nginx.conf 动态查看日志,方便查看日志新增的信息
-
ctrl+c 结束查看
-
tar -xvf 解压
-
rm -rf dump.rdb 删除文件
-
shutdown 关闭服务
-
flushdb 清除数据库
-
ps -ef|grep redis 查看当前服务的进程
-
vim 编辑脚本文件
-
i 编辑 模式
-
esc命令 模式
-
选到英文 输入法,!wq保存退出
-
nginx常用命令
- ps -aux|grep nginx 查看Nginx所在位置
- mast process 后面一般是nginx 的安装目录
- ps aux | grep nginx 查看master的端口号
- netstat -anput | grep nginx Nginx监听的端口
- sudo ./nginx -s reload 进入nginx对应的sbin目录,重启 nginx
- sudo systemctl status nginx 查看nginx状态
8.int的包装类
int的包装类就是Integer
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
}
当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf
int值 -128到127
如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1f2的结果是true,而f3f4的结果是false
八种基本数据类型外,其他都是引用数据类型
int 是基本数据类型,默认值为0,不需要进行实例化,基本数据类型不是类,不是类,不能new
integer(等) 是引用数据类型,是int的封装类型,默认值为null,创建该类型需要进行实例化。
基本数据类型是可以用“==”进行比较,而引用数据类型则不可以,一般是通过equals方法来实现比较。
9.Java中 == 和 equals 的区别
- java后台判断用equals:“1”.equals(u.getServiceType()),== 比较的是地址值,前端比较用===
1.比较java基本类型
比较基本类型只能用==,不能用"equals",这里的==比较的是两个基本类型的值.
2.比较String
String类重写了equals()方法,对比是字符串包含的内容是否相同。
- "=="比较的是内存地址,"equals"比较的是值
java中的数据类型可以分为两类:
- 基本数据类型
byte,short,char,int,long,float,double,boolean
基本数据类型之间的比较需要用双等号(==),因为他们比较的是值
对于对象引用类型: ==比较的是对象的内存地址。
对于基本数据类型: ==比较的是值。
10. mysql
- concat 拼接字符串
SELECT CONCAT(dname,"--梁伟浩--",db_source) as 测试concat拼接字符串 FROM dept
- LENGTH(str):返回字符串str的长度
- ABS(X):返回X的绝对值
- NOW() 返回当前日期和时间
- CURTIME() 返回当前时间
- CURDATE() 返回当前日期
- MONTH(“2022-08-12”) 返回当前时间月份
- HOUR(“2032-8-12 15:30:56”) 返回当前时间小时值
- MySQL控制流函数:
- CASE WHEN[test1] THEN [result1]…ELSE [default] END如果testN是真,则返回resultN,否则返回default
- CASE [test] WHEN[val1] THEN [result]…ELSE [default]END 如果test和valN相等,则返回resultN,否则返回default
- IF(test,t,f) 如果test是真,返回t;否则返回f
- IFNULL(arg1,arg2) 如果arg1不是空,返回arg1,否则返回arg2
- NULLIF(arg1,arg2) 如果arg1=arg2返回NULL;否则返回arg1
- mysql存储过程
- 事先经过编译存储在数据库中的一段sql语句集合。简单来说就是一组封装好的sql语句,类似与java的函数。
-
mysql默认引擎innodb
-
myisam是默认表类型不是事务安全的;innodb支持事务
-
myisam不支持外键;innodb支持外键
-
- mysql创建索引和索引的类型
- 创建索引
alter table [表名] add index [索引名]
(已存在表上创建索引) - 创建索引
create index [索引名] ON [表名]
- 创建索引
- 创建普通索引
CREATE INDEX index_name
ON table_name (column_name);
- 创建唯一索引
CREATE UNIQUE INDEX index_name
ON table_name (column_name);
- 组合索引
(名为 “PersonIndex”,在 Person 表的 LastName 列)
CREATE INDEX PersonIndex
ON Person (LastName, FirstName);
11.sql优化
- 避免使用 select * 会导致查出多余的字段,需要什么就查什么
- 一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
- exists 代替 in
- 连续查询的可以用between and 代替 in 和 not in
- 少使用select * 会查出所有字段浪费资源, 查询对应字段即可
- 考虑在 where 及 order by 涉及的列上建立索引,避免全表示扫描
- 导致引擎放弃使用索引而进行全表扫描
- 迷糊查询LIKE双百分号无法使用到索引
- 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
- 用union all代替union
union关键字后,可以获取排重后的数据。
union all关键字,可以获取所有数据,包含重复的数据。
反例:
(select * from user where id=1)
union
(select * from user where id=2);
排重的过程需要遍历、排序和比较,它更耗时,更消耗cpu资源。
所以如果能用union all的时候,尽量不用union。
正例:
(select * from user where id=1)
union all
(select * from user where id=2);
除非是有些特殊的场景,比如union all之后,结果集中出现了重复数据,而业务场景中是不允许产生重复数据的,这时可以使用union。
12.重载与重写的区别
重载(Overloading) | 重写 (Overriding) |
---|---|
方法名相同 | 方法名相同 |
参数列表不同 | 参数列表相同 |
访问修饰符可以相同也可以不相同 | 不能比父类访问权限低 |
返回值类型可以相同也可以不同 | 返回值类型必须相同 |
发生在本类 | 发生在父类与子类(继承) |
13.mybatis的#和$的区别
1、#{}是预编译处理,MyBatis在处理#{ }时,它会将sql中的#{ }替换为?,可以防止sql注入,然后调用PreparedStatement的set方法来赋值,传入字符串后,会在值两边加上单引号,如上面的值 “4,44,514”就会变成“ ‘4,44,514’ ”;
2、$()是字符串替换 ,在处理是字符串替换 ,MyBatis在处理时 , 它会将sql中的{}是字符串替换,在处理{ }是字符串替换, MyBatis在处理{ }时,它会将sql中的是字符串替换,在处理是字符串替换,MyBatis在处理时,它会将sql中的{ }替换为变量的值,传入的数据不会加两边加上单引号。
注意:使用${ }会导致sql注入,不利于系统的安全性!SQL注入:就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。常见的有匿名登录(在登录框输入恶意的字符串)、借助异常获取数据库信息等
3 . #方式能够很大程度防止sql注入
4. $方式无法防止Sql注入
5.一般用于传入数据和表名
6.一般能用#的就别用$.
MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDER BY,你可以这样来使用:
ORDER BY ${columnName}
这里MyBatis不会修改或转义字符串。
重要:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。
7、#{} 和 ${} 的实例:假设传入参数为 1
(1)开始
1)#{}:select * from t_user where uid=#{uid}
(2)然后
1)#{}:select * from t_user where uid= ?
2)${}:select * from t_user where uid= ‘1’
(3)最后
1)#{}:select * from t_user where uid= ‘1’
2)${}:select * from t_user where uid= ‘1’
14. GET和POST区别
Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。 但是这种做法也不时绝对的,大部分人的做法也是按照上面的说法来的,但是也可以在get请求加上 request body,给 post请求带上 URL 参数。
Get请求提交的url中的数据最多只能是2048字节,这个限制是浏览器或者服务器给添加的,http协议并没有对url长度进行限制,目的是为了保证服务器和浏览器能够正常运行,防止有人恶意发送请求。Post请求则没有大小限制。
Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
Get执行效率却比Post方法好。Get是form提交的默认方法。
GET产生一个TCP数据包;POST产生两个TCP数据包。
15. mybatis一级缓存和二级缓存的区别
- mybatis默认就是一级缓存,Sqlsession级别的缓存,进行查操作的时候把数据存在缓存中,如果下次还是正常查询同样的数据就会从缓存拿,如果进行了增删改操作就会把缓存的数据清除
- 存储结构 map<key, value>
- Mybatis的一级缓存是一个SqlSession级别的缓存,只能访问自己的一级缓存数据,而二级缓存是Mapper级别的缓存,是跨SqlSession的,不同的SqlSession是可以共享缓存数据的
- 二级缓存配置
- 需要在Mybatis的配置文件中标签中配置二级缓存:
<settings>
<setting name="cacheEnabled" value="true"/> <!--Mybatis的二级缓存配置-->
</settings>
- Mybatis的二级缓存的范围是mapper级别的,因此我们mapper如果想要使用二级缓存,还需要在对应的映射文件中配置标签
<mapper namespace="com.snow.xml.SnowOracle">
<cache></cache> <!--Mybatis的二级缓存配置-->
</mapper>
Mybatis的二级缓存的范围是mapper级别的,因此我们mapper如果想要使用二级缓存,还需要在对应的映射文件中配置标签
16. HashMap和HashTable的区别
区别 :
- HashMap⽅法没有synchronized修饰,线程⾮安全,HashTable线程安全;
- HashMap允许key和value为null,⽽HashTable不允许
- 因为hashmap会计算key的hash值,null的hash值是0
- hashtable会调用hashcode,如果为null为报空指针
底层实现:
数组+链表实现,jdk8开始链表⾼度到8、数组⻓度超过64,链表转变为红⿊树,元素以内部类Node节点存在
- 计算key的hash值,⼆次hash然后对数组⻓度取模,对应到数组下标,
- 如果没有产⽣hash冲突(下标位置没有元素),则直接创建Node存⼊数组,
- 如果产⽣hash冲突,先进⾏equal⽐较,相同则取代该元素,不同,则判断链表⾼度插⼊链表,链
表⾼度达到8,并且数组⻓度到64则转变为红⿊树,⻓度低于6则将红⿊树转回链表 - key为null,存在下标0的位置
17.Session、Cookie和Token的主要区别?
什么是cookie
cookie是由Web服务器保存在用户浏览器上的小文件(key-value格式),包含用户相关的信息。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户身份。
什么是session
session是依赖Cookie实现的。session是服务器端对象
session 是浏览器和服务器会话过程中,服务器分配的一块储存空间。服务器默认为浏览器在cookie中设置 sessionid,浏览器在向服务器请求过程中传输 cookie 包含 sessionid ,服务器根据 sessionid 获取出会话中存储的信息,然后确定会话的身份信息。
cookie与session区别
存储位置与安全性:cookie数据存放在客户端上,安全性较差,session数据放在服务器上,安全性相对更高;
存储空间:单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie,session无此限制
占用服务器资源:session一定时间内保存在服务器上,当访问增多,占用服务器性能,考虑到服务器性能方面,应当使用cookie
什么是Token
Token的引入:Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位
session与token区别
session机制存在服务器压力增大,CSRF跨站伪造请求攻击,扩展性不强等问题;
session存储在服务器端,token存储在客户端
token提供认证和授权功能,作为身份认证,token安全性比session好;
session这种会话存储方式方式只适用于客户端代码和服务端代码运行在同一台服务器上,token适用于项目级的前后端分离(前后端代码运行在不同的服务器下)
18.什么是http,http与HTTPS的区别
19.抽象类和接口的区别
- 抽象类可以提供成员方法的实现细节,而接口中只能存在抽象方法。(不能实例化,只能被继承)
- 抽象类中的成员变量可以是各种类型的,⽽接⼝中的成员变量只能是public static final类型的。
- 抽象类只能继承⼀个,接⼝可以实现多个
- 抽象类用abstract关键字修饰的类称为抽象类
- 接口用interface关键字修饰
- 如果要多继承就必须使用接口实现多实现,因为类只能单继承
使⽤场景:当你关注⼀个事物的本质的时候,⽤抽象类;当你关注⼀个操作的时候,⽤接⼝。
20. TCP(传输控制协议)和IP(网际协议),三次握手四次挥手
tcp的三次握手和四次挥手?
一个TCP连接由一个4元组构成,分别是两个IP地址和两个端口号。一个TCP连接通常分为三个阶段:连接、数据传输、退出(关闭)。
通过三次握手建立一个链接,通过四次挥手来关闭一个连接。
三次握手的本质是确认通信双方收发数据的能力
四次挥手的目的是关闭一个连接
21.Java构造函数
创建一个Employee类和EmployeeText类
package com.macro.mall.controller.system;
public class Employee {
String name;
int age;
String work;
double salary;
//Employee构造器
public Employee(String name){
this.name = name;
}
//设置age的值
public void empage(int empage){
age = empage;
}
//设置work的值
public void empwork(String empwork){
work = empwork;
}
//设置salary的值
public void empsalart(double empsalart){
salary = empsalart;
}
//打印信息
void printEmployee(){
System.out.println("我的名字是"+name);
System.out.println("我的年龄是"+age);
System.out.println("我的工作是"+work);
System.out.println("我的薪资是"+salary);
}
}
package com.macro.mall.controller.system;
public class EmployeeTest {
public static void main(String[] args) {
Employee employee = new Employee("大帅哥");//new Employee构造方法 返回了一整个对象
employee.empsalart(10000);
employee.empwork("it");
employee.empage(24);
employee.printEmployee();
}
}
输出结果
我的名字是大帅哥
我的年龄是24
我的工作是it
我的薪资是10000.0
Process finished with exit code 0
22.访问修饰符
public > protected > default 不写(默认) > private
23.数据库主从测试
备份实现原理:
- 当数据库主机的数据发生变化时,会将修改的数据写进二进制日志文件中.
- 从库中通过IO线程,读取主库的二进制日志文件,获取之后,将数据保存到中继(临时存储)日志中.
- 从库开启sql线程,之后读取中继日志中的数据,之后将数据同步到从库中.
主从架构测试:
4. 修改主库的数据,从库会跟着同步数据.
5. 如果修改从库数据,则主从的关系将会终止.
24.Servlet
Servlet不是线程安全的,多线程并发的读写会导致数据不同步的问题。
Servlet接口中有哪些方法及Servlet生命周期探秘
在Java Web程序中,Servlet主要负责接收用户请求HttpServletRequest,在doGet(),doPost()中做相应的处理,并将回应HttpServletResponse反馈给用户。Servlet可以设置初始化参数,供Servlet内部使用。
Servlet接口定义了5个方法,其中前三个方法与Servlet生命周期相关:
void init(ServletConfig config) throws ServletException
void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
void destory()
java.lang.String getServletInfo()
ServletConfig getServletConfig()
生命周期
Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;
请求到达时调用Servlet的service()方法,service()方法会根据需要调用与请求对应的doGet或doPost等方法;
当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。
init方法和destory方法只会执行一次,service方法客户端每次请求Servlet都会执行。Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中,这样就不需要每次处理客户端的请求都要初始化与销毁资源。