参考资料:
https://blog.csdn.net/m0_37721946/article/details/78405595
https://www.cnblogs.com/Godfunc/p/9193398.html
https://www.cnblogs.com/SaraMoring/p/5687466.html
java数据类型分为:基本数据类型(byte、short、int、long、float、double、char、boolean)和复合数据类型(类);
对于基本数据类型,(= =)用于比较值的大小;
对于复合数据类型,(==)用于比较他们在内存中的存放地址;
String类重写了父类Object的equals方法,equals方法比较的是两个字符串是否相等;
栈:普通变量、基本数据类型的变量值 和 对象的引用变量,都存放在栈中;
堆:new创建的对象和数组,存放在堆中。在堆中分配的内存,由Java虚拟机的自动垃圾回收器GC来管理;
常量池:基本数据类型的常量值、复合数据类型的常量值 和 其他符号引用,都存放在常量池中;
String类的 “+” 操作
String str1 = "ab";
String str2 = "abc";
String str3 = str1+"c";
System.out.println(str3 == str2);
/*
str1 与 "c" 进行字符串连接时,
底层是通过StringBuffer 进行 append,
生成一个StringBuffer对象,然后通过toString()方法,
将StringBuffer对象转为String,
此时会产生一个新的堆内存地址,str3指向这个新的内存地址。
*/
几种String对象的创建方式
String s1 = "china";
String ss1 = new String("china");
/*
通过new产生一个字符串(假设为“china”)时,
会先去常量池中查找是否已经有了“china”对象,
如果没有则在常量池中创建一个此字符串对象,
然后堆中再创建一个常量池中此”china”对象的拷贝对象。
*/
String的 intern()方法
String s0= "kvill";
String s1=new String("kvill");
String s2=new String("kvill");
System.out.println( s0==s1 );
s1.intern();
s2=s2.intern(); //把常量池中"kvill"的引用赋给s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
/*
输出结果:
false
false
true
true
String的 intern()方法就是扩充常量池的 一个方法;
当一个String实例str调用intern()方法时,
Java 查找常量池中是否有相同Unicode的字符串常量,
如果有,则返回其的引用,如果没有,
则在常量池中增加一个Unicode等于str的字符串并返回它的引用
*/
1、用户向服务器发送请求,请求被SpringMVC的前端控制器DispatcherServlet截获。
2、DispatcherServlet对请求的URL(统一资源定位符)进行解析,得到URI(请求资源标识符),然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象,包括Handler对象以及Handler对象对应的拦截器,这些对象都会被封装到一个HandlerExecutionChain对象当中返回。
3、DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。HandlerAdapter的设计符合面向对象中的单一职责原则,代码结构清晰,便于维护,最为重要的是,代码的可复制性高。HandlerAdapter会被用于处理多种Handler,调用Handler实际处理请求的方法。
4、 提取请求中的模型数据,开始执行Handler(Controller)。在填充Handler的入参过程中,根据配置,spring将帮助做一些额外的工作,包括:
消息转换:将请求的消息,如json、xml等数据转换成一个对象,将对象转换为指定的响应信息。
数据转换:对请求消息进行数据转换,如String转换成Integer、Double等。
数据格式化:对请求的消息进行数据格式化,如将字符串转换为格式化数字或格式化日期等。
数据验证:验证数据的有效性如长度、格式等,验证结果存储到BindingResult或Error中。
5、Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象,ModelAndView对象中应该包含视图名或视图模型。
6、根据返回的ModelAndView对象,选择一个合适的ViewResolver(视图解析器)返回给DispatcherServlet。
7、ViewResolver结合Model和View来渲染视图。
8、将视图渲染结果返回给客户端。
以上8个步骤,DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等对象协同工作,完成SpringMVC请求—>响应的整个工作流程,这些对象完成的工作对于开发者来说都是不可见的,开发者并不需要关心这些对象是如何工作的,开发者,只需要在Handler(Controller)当中完成对请求的业务处理。
for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数
for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
// 二分查找普通循环实现
public static int biSearch(int[] array, int a) {
int lo = 0;
int hi = array.length - 1;
int mid;
while (lo <= hi) {
mid = (lo + hi) / 2;
if (array[mid] == a) {
return mid;
} else if (array[mid] < a) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return -1;
}
主要存储引擎:MyISAM、InnoDB、MEMORY和MERGE
MyISAM:它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表。
InnoDB:InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。
MEMORY:使用存在内存中的内容来创建表。每个MEMORY表实际对应一个磁盘文件,格式是.frm。MEMORY类型的表访问非常快,因为它到数据是放在内存中的,并且默认使用HASH索引,但是一旦服务器关闭,表中的数据就会丢失,但表还会继续存在。
List中存储的数据是有顺序的,并且值允许重复;
Map中存储的数据是无序的,它的键是不允许重复的,但是值是允许重复的;
Set中存储的数据是无顺序的,并且不允许重复,但它的实现类能对集合中的对象按照特定的方式排序,例如 TreeSet 类,可以按照默认顺序,也可以通过实现 Java.util.Comparator< Type >接口来自定义排序方式。
List接口有三个实现类:
1.1 LinkedList
基于链表实现,链表内存是散列的,增删快,查找慢;
1.2 ArrayList
基于数组实现,非线程安全,效率高,增删慢,查找快;
1.3 Vector
基于数组实现,线程安全,效率低,增删慢,查找慢;
Map接口有四个实现类:
2.1 HashMap
基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null 键;
2.2 HashTable
线程安全,低效,不支持 null 值和 null 键;
2.3 LinkedHashMap
是 HashMap 的一个子类,保存了记录的插入顺序;
2.4 SortMap 接口
TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序
Set接口有两个实现类:
3.1 HashSet
底层是由 Hash Map 实现,不允许集合中有重复的值,使用该方式时需要重写 equals()和 hash Code()方法;
3.2 LinkedHashSet
继承于 HashSet,同时又基于 LinkedHashMap 来进行实现,底层使用的是 LinkedHashMap
3.3 TreeSet
一种基于TreeMap的实现,TreeSet 是一个有序的集合,性能比HashSet差,但是我们在需要排序的时候可以用
Servlet生命周期分为三个阶段:
1:初始化阶段,调用init()方法
2:响应客户请求阶段,调用service()方法
3:终止阶段,调用destory()方法
参考资料:https://www.cnblogs.com/zhaoyan001/p/6365064.html
java的基本数据类型有八种:
1)四种整数类型(byte、short、int、long): byte:8 位,用于表示最小数据单位,如文件中数据,-128~127 short:16 位,很少用,-32768 ~ 32767 int:32 位、最常用,-231-1~231 (21 亿) long:64 位、次常用 注意事项: int i=5; // 5 叫直接量(或字面量),即 直接写出的常数。 整数字面量默认都为 int 类型,所以在定义的 long 型数据后面加 L或 l。 小于 32 位数的变量,都按 int 结果计算。 强转符比数学运算符优先级高。见常量与变量中的例子。
2)两种浮点数类型(float、double): float:32 位,后缀 F 或 f,1 位符号位,8 位指数,23 位有效尾数。 double:64 位,最常用,后缀 D 或 d,1 位符号位,11 位指数,52 位有效尾 注意事项: 二 进 制 浮 点 数 : 1010100010=101010001.02=10101000.10210(2次方)=1010100.010211(3次方)= . 10101000102^1010(10次方) 尾数: . 1010100010 指数:1010 基数:2 浮点数字面量默认都为 double 类型,所以在定义的 float 型数据后面加F 或 f;double 类型可不写后缀,但在小数计算中一定要写 D 或 X.X float 的精度没有 long 高,有效位数(尾数)短。 float 的范围大于 long 指数可以很大。 浮点数是不精确的,不能对浮点数进行精确比较。
3)一种字符类型(char): char:16 位,是整数类型,用单引号括起来的 1 个字符(可以是一个中文字符),使用 Unicode 码代表字符,0~2^16-1(65535) 。 注意事项: 不能为 0个字符。 转义字符:\n 换行 \r 回车 \t Tab 字符 " 双引号 \ 表示一个\ 两字符 char 中间用“+”连接,内部先把字符转成 int 类型,再进行加法运算,char 本质就是个数!二进制的,显示的时候,经过“处理”显示为字符。
4)一种布尔类型(boolean):true 真 和 false 假。
5)类型转换: char–> 自动转换:byte–>short–>int–>long–>float–>double 强制转换:①会损失精度,产生误差,小数点以后的数字全部舍弃。②容易超过取值范围。
6)记忆:8位:Byte(字节型) 16位:short(短整型)、char(字符型) 32位:int(整型)、float(单精度型/浮点型) 64位:long(长整型)、double(双精度型) 最后一个:boolean(理论上占1bit,实际上视情况而定)
try{
System.out.println("try");
return 0;
} finally {
System.out.println("finally");
return 1;
}
//执行结果:
//try
//finally
//1
try{
System.out.println("try");
return 0;
} finally {
System.out.println("finally");
}
//执行结果:
//try
//finally
//0
String a = "abcde";
/** 从最后一个字符开始,倒叙一个字母一个字母的输出 */
for (int i = a.length() - 1; i >= 0; i--) {
System.out.print(a.charAt(i));
}
基于注解的方式配置spring声明式事务,用@Transactional注解声明服务层方法,其中:
propagation属性可以设置事务传播行为,默认取值为 REQUIRED(被调用事务方法 和 调用事务方法 使用同一个事务)。也可以设置为REQUIRES_NEW(被调用事务方法使用新事务,调用事务方法先挂起,等 被调用事务方法执行完,再继续 调用事务方法 的事务)
isolation属性可以设置事务隔离级别,最常用取值为READ_COMMITTED(其他取值READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE)
单例模式(singleton)
有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降。如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式就被称为单例模式。
简单工厂(StaticFactory Method)
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
代理模式(Proxy)
代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。
命令模式(Command)
某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到执行该方法时才可以确定。(在某些编程语言如Ruby、Perl里,允许传入一个代码块作为参数。但Jara暂时还不支持代码块作为参数)。在Java中,传入该方法的是一个对象,该对象通常是某个接口的匿名实现类的实例,该接口通常被称为命令接口,这种设计方式也被称为命令模式。
参考资料:
https://www.cnblogs.com/guodongdidi/p/6953217.html
Integer是int的包装类,int则是java的一种基本数据类型;
Integer变量必须实例化后才能使用,而int变量不需要;
Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值;
Integer的默认值是null,int的默认值是0;
关于Integer和int的比较
1、由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
2、Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
3、非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
4、对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
对于第4条的原因:
java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);,而java API中对Integer类型的valueOf的定义如下:
public static Integer valueOf(int i){
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
参考资料:https://blog.csdn.net/qidong7/article/details/52205332
多例,可以通过修改redis.conf文件修改配置pidfile和port这两个参数的值,实现在不同端口号启动不同的实例。(这两个实例之间还能通过slaveof命令实现主从配置)
默认情况下是单例的(初始化ioc容器就创建),可以通过注解@scope进行设置(如果设置为多例,只有使用到该bean是才会创建)
参考资料:https://blog.csdn.net/aluomaidi/article/details/52460844#commentBox
事务原子性和事务一致性区别
举个栗子吧:
转账:张三给李四转账100元。那数据库假设需要 张三扣100,李四加100,记录一条流水。
如果流水没记录成功,那整体回滚,张三也没转账成功,李四也没多钱。这就是原子性的体现。
而张三必须扣100,李四必须加100,这个就是一致性了,如果因为某些逻辑原因,导致张三扣了100,流水记录100转账,而李四只加了60。然后这3条操作都成功了,那原子性就符合了,但是一致性就不符合了~~~
1、在控制器中不使用实例变量
2、将控制器的作用域从单例改为原型,即在spring配置文件Controller中声明 scope=“prototype”,每次都创建新的controller
3、在Controller中使用ThreadLocal变量
多线程就是指一个进程中同时有多个执行线程正在执行。
高并发是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问或者socket端口集中性收到大量请求(例如:12306的抢票情况;天猫双十一活动)。
高并发可以由多线程实现,但是多线程不代表就是高并发,要实现系统的高并发有多种方式,多线程只是其中的一种方式。
异常分为两大类:Error,Exception。父类都是Throwable
Error:程序发生错误,导致程序终止,捕捉不到。发生在运行时期。
情景1:误写代码,比如:少了一个字母;少了一个分号。报错:找不到XX;添加“;”。
情景2:误删代码,比如:删了A类;删了b方法。报错:找不到A类;找不到b方法。
Exception又分为Runtime Exception(运行时异常)、Cheked Exception(可检查异常,也有人叫普通异常、可恢复异常)。
Runtime Exception:每行代码都可能会出现异常,没必要时不用捕捉,通过系统自行检测。常见:空指针,数组越界,类转换异常,非法参数异常等。
Checked Exception:编译时,如果不进行捕捉,直接报错。常见:ClassNotFoundException、NoSuchMethodException、IOException。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的。
参考资料:https://blog.csdn.net/u011480603/article/details/75332435/
参考资料:https://mp.csdn.net/mdeditor/83594449#
缓存穿透
什么是缓存穿透?
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。【感觉应该用的不多吧】
缓存雪崩
什么是缓存雪崩?
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)
FIFO ,first in first out ,最先进入缓存的数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去。
LFU , Less Frequently Used ,一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。
LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
Spring Cloud Eureka负责服务的注册于发现
Spring Cloud Ribbon服务消费者调用服务生产者,客户端负载均衡器由
Spring Cloud Feign使得Eureka和Ribbon的使用更为简单
Spring Cloud Hystrix断路器,防止对某一故障服务持续进行访问
Spring Cloud Config方便服务配置文件统一管理,实时更新,分布式配置中心组件
Spring Cloud Zuul通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能
30.消息队列的应用场景?
1.异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。
2.应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。
3.流量削锋
秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
4.日志处理
(1)Kafka:接收用户日志的消息队列。
(2)Logstash:做日志解析,统一成JSON输出给Elasticsearch。
(3)Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,通过index组织数据,兼具强大的搜索和统计功能。
(4)Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK stack的重要原因。
5.消息通讯
客户端A和客户端B使用同一队列,进行消息通讯。