所学知识点归纳梳理(二)

SpringCloud

1.什么是SpringCloud

     是提供了微服务的一站式的解决方案,是一个项目集合,提供了很多子项目,为项目提供了各个方面的解决方案

2.什么是微服务

     就是把单体结构系统按照里单一职责,拆分成不同的细小服务,服务与服务之间进行通信,然后对外提供接口,采用是轻量级的http协议,去中心化

 

SpringBoot

1.流行的原因

       归根结底,其核心思想就是约定优于配置。按照默认约定,spring boot 会把一切尽可能配置好,这样对于开发者来说就省去了很多配置,倘若默认配置不符合自己的应用场景,也可以自己配置,覆盖默认配置。

2.如何搭建

3.application.properties的作用

4.static和templates文件夹

[email protected]和@Controller

[email protected]包含了扫描注解和springboot配置

 

Dubbo

1.注册中心,服务提供方,服务消费方

2.端口号20880 zookeeper端口号2188

3.为什么推荐使用zookeeper

4.dubbo原理 相当于一个高速的中转站

原理:有一个注册中心 负责接收服务提供方的服务 和给服务消费方提供地址,让服务消费方调用服务提供方。并且给出返回值 在服务消费方调用服务提供方时 传递的参数必须序列化

5.po实现序列化

6.搭建:注册中心地址,暴露的服务提供方地址,协议端口,应用工程名

 

过滤器

过滤器的创建和初始化会在工程启动的时候就进行完成,并且过滤器是单例多线程的,可以被多次访问,但是只会创建一次。

手动实现过滤器

1、web.xml中配置过滤器,<filter></filter>这样才能在项目一启动就完成初始化

2.实现Filter接口,实现方法,并访问页面查看输出结果

所学知识点归纳梳理(二)

web.xml中/*和/在Filter和Servlet中的区别

/*在过滤器中,所有请求都会拦截,后台程序都执行,/不会拦截动态资源,所以不支持

/*在Servlet中,所以请求都会拦截,后台程序都执行,/不会拦截动态资源,也就是jsp请求,不会访问后台程序

当应用中存在多个Filter时,其执行顺序与其注册顺序一致

当servlet和filter的执行顺序

FirstFilterInit

SecondFilterInit

init

FirstFilterdoFilter111

SecondFilterdoFilter111

servlet访问

SecondFilterdoFilter222

FirstFilterdoFilter222

filter随项目启动,当访问servlet时,servlet初始化,然后请求前过滤器按顺序启动,请求后过滤器按顺序是否

多个过滤器执行顺序:先启后停

 

拦截器

1. 实现HandlerInterceptor接口)

实现preHandle()、postHandle()、afterCompletion()

所学知识点归纳梳理(二)

多个拦截器的执行顺序和过滤器一样,先启后停

Springmvc允许多个拦截器并存,如果两个拦截器配置了相同的拦截URL,主要看第一个是否放行,放行后,可以执行第二个拦截器,再看第二个请求是否放行,放行就执行后台方法,如果第一个拦截器不放行,第二个拦截器也不会执行,后台方法也不会执行

 

Spring

核心:AOP和IOC

AOP:面向切面编程,扩展功能不是通过修改源代码实现

IOC:控制反转,把对象的创建交给spring进行配置

DI:依赖注入,向类里面的属性中设置值

关系:依赖注入不能单独存在,需要在ioc基础之上完成操作,也就是说得创建了对象才能设置属性,不然无法设置。

核心配置文件:applicationContext.xml

创建对象的四个注解

在类上使用@Component是用注解方式创建该类(实例化对象)

Web的@controller

Service的@Servcie

Dao的@Repository

1.配置监听器

2.配置spring配置文件,上下文参数,用于被监听器监听

配置事务

1.配置事务管理器,知道对哪个数据库进行事务管理

2.开启事务注解

3.使用@Transactional

 

SpringMVC

前端控制器,处理器映射器,处理器适配器,视图解析器

所学知识点归纳梳理(二)

Restful风格的url

所学知识点归纳梳理(二)

Springmvc执行原理

第一步:发送请求到前端控制器(DispatchServlet)

第二步:前端控制器请求HandlerMapping查找Handler(可以根据xml配置,或者注解配置)

第三步:处理器映射器HandlerMapping向前端控制器返回Handler

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器去执行Handler

第六步:Handler执行完给适配器返回ModerlAndView

第七步:处理器适配器向前端控制器返回ModerlAndView(ModerlAndView是springmvc的底层对象)

第八步:前端控制器控制视图解析器去进行视图解析(根据逻辑视图名解析成真正的物料视图,jsp,freemarker…)

第九步:视图解析器向前端控制器返回view

第十步:前端控制器将视图进行渲染

第十一步:前端控制器向用户反映结果

所学知识点归纳梳理(二)

1.前端控制器 DispatchServlet(不需要开发)

作用:接收请求,响应结果,相当于转发器

2.处理器映射器 HandlerMapping(不需要开发)

作用:根据请求的url,查找Handler

3.处理器适配器 HandlerAdapter(不需要开发)

作用:去执行Handler

4.处理器Handler(需要开发)

5.视图解析器(不需要开发)

作用:进行视图解析,将逻辑视图名解析成真正的视图

6.view视图对象(需要开发)

 

Web.xml配置前端控制器,读取配置springmvc.xml

springmvc.xml中配置视图解析器,处理器适配器,处理器映射器

 

SSM框架整合

Dao层

使用mybatis框架。创建SqlMapConfig.xml。里面的数据源都交给spring管理,但是这个文件必须存在,否则会报错

创建一个applicationContext-dao.xml

1.配置数据源

2.需要让spring容器管理SqlsessionFactory,单例存在。

3.把mapper的代理对象放到spring容器中。使用扫描包的方式加载mapper的代理对象。

Service层

1.事务管理

2.需要把service实现类对象放到spring容器中管理。

表现层

1.配置注解驱动

2.配置视图解析器

3.需要扫描controller

Web.xml

1.spring容器的配置

2.Springmvc前端控制器的配置

3.Post乱码过滤器

所学知识点归纳梳理(二)

所学知识点归纳梳理(二)

问题:为什么不只配一个扫描包,而在dao,service,controller中都进行配置?

解答:在web.xml中配置了一个spring容器,这会创建一个spring的父容器,又创建了一个springmvc的前端控制器,是在spring容器中创建了一个springmvc的子容器,springmvc子容器中放着的是controller,spring容器中放着的是dao和service,子容器可以访问父容器的对象,但是父容器不可以访问子容器,如果配置了全局扫描,那么dao,service,controller就都会在spring容器中存在,springmvc中前段控制器就没有这个对象,访问时会报404错误。

注意:这是spring和springmvc整合时候需要注意的问题,如果只用springmvc,可以只扫描一次,dao,service,controller都会在springmvc容器中存在,不会出现问题

@ResponseBody注解的使用

@ResponseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需要注意的呢,在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。

 

@RequestMapping("/login")   
@ResponseBody   
public User login(User user){     
    return user;   
}
User字段:userName pwd   
那么在前台接收到的数据为:'{"userName":"xxx","pwd":"xxx"}'
效果等同于如下代码:

@RequestMapping("/login")   
public void login(User user, HttpServletResponse response){     
    response.getWriter.write(JSONObject.fromObject(user).toString());   
}

@RequestBody注解的使用

 通过@RequestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然,也可以将其分别绑定到对应的字符串上。

例如说以下情况:
$.ajax({         
    url:"/login",         
    type:"POST",         
    data:'{"userName":"admin","pwd","admin123"}',         
    content-type:"application/json charset=utf-8",         
    success:function(data){           
        alert("request success ! ");         
    }     
});
@RequestMapping("/login")     
public void login(@requestBody String userName,@requestBody String pwd){       
    System.out.println(userName+" :"+pwd);     
}

@ResponseBody注解是添加在方法上,@RequestBody是添加在形参上


Redis

五种数据类型

String Hash Set Zset List

String可以存储简单的字符串或者图片二进制类的

Hash是一个String集合。特别适合存储对象。

hset myhash field1 value

List是一个两端链表结构的集合

存:Lpush list1 “11”

取:Lrange list1 0 -1

删:lpop

Set是String类型的无序集合:无序且唯一,不可重复

添加:Sdd 查看:smembers 删除:srem

Zset

有序集合,意味有序集合里面的元素是排好序的,也满足唯一性和确定性。

每一个元素都关联着一个分值,按照分值从大到小顺序进行排列

Zdd添加 zrange查询

所学知识点归纳梳理(二)

 

持久化:RDB和AOF

所学知识点归纳梳理(二)

Appendonly.aof文件和dump.rdb文件是可以共存的,在redis启动的时候会先检查appendonly.aof,然后再检查dump.rdb

相同的数据集,aof文件要大于rdb文件,恢复速度慢于rdb

rdb存的是真实数据,aof存的是命令操作

Rdb可以手动save或者bgsave或者根据配置文件自动进行保存操作,aof需要开启配置文件

SAVE保存是阻塞主进程,客户端无法连接redis,等SAVE完成后,主进程才开始工作,客户端可以连接

BGSAVE是fork一个save的子进程,在执行save过程中,不影响主进程,客户端可以正常链接redis,等子进程fork执行save完成后,通知主进程,子进程关闭。

 

集群分片

所学知识点归纳梳理(二)

 

Redis集群的转向

由于集群无中心节点的特性,请求会发给任意节点

主节点只会处理自己负责的槽位命令,其他的槽位的命令请求,该主节点会返回客户端一个错误,客户端根据错误中包含的地址和端口,向正确的负责主节点发起请求

 

基本的Redis的容灾策略:

1 采用master-slave方式

2 为了得到好的读写性能,master不做任何的持久化

3 slave同时开启Snapshot和AOF来进行持久化,保证数据的安全性

4 当master挂掉后,修改slave为master

5 恢复原master数据,修改原先master为slave,启动slave

6 若master与slave都挂掉后,调用命令通过aof和snapshot进行恢复 恢复时要先确保恢复文件都正确了,才能启动主库;也可以先启动slave,将master与slave对调

哨兵的作用

1.监控:监控主从是否正常

2.通知:出现问题时,可以通知相关人员

3.故障迁移:自动主从切换

4.统一的配置管理:连接者询问sentinel取得主从的地址 Raft算法核心: 可视图

 

如何配置redis的主从复制

从只能读,不能写

无法同步的情况:

    1.主服务器未开启

    2.关闭两台机器防火墙

    3.设置bind0.0.0.0

    4.配置主服务器密码

 

如何配置集群模式:

1.先是搭建节点并启动

port 7000 //端口7000,7002,7003

#bind 本机ip //默认ip为127.0.0.1,在3.2之后要注释掉 daemonize yes //redis后台运行

pidfile /var/run/redis_7000.pid //pidfile文件对应7000,7001,7002

cluster-enabled yes //开启集群 把注释#去掉

cluster-config-file nodes_7000.conf //集群的配置 配置文件首次启动自动生成 7000,7001,7002

cluster-node-timeout 15000 //请求超时默认15秒,可自行设置

appendonly yes //aof日志开启 有需要就开启,它会每次写操作都记录一条日志

2.使用redis-trib.rb将各个节点搭建成一个集群

/redis/bin/redis-trib.rb create --replicas 1 192.168.159.130:7001 192.168.159.130:7002 192.168.159.130:7003 192.168.159.130:7004 192.168.159.130:7005 192.168.159.130:7006

所学知识点归纳梳理(二)

集群验证

所学知识点归纳梳理(二)

数据存入了节点为7001的7046的槽位里

登录7002的节点,进行查询

所学知识点归纳梳理(二)

这里,代表集群搭建完成!!!

注意:当你存放的时候是在哪个节点,就只有哪个节点有数据,其他的是没有数据的

redis集群数据的和槽位的分配

在redis官方给出的集群方案中,数据的分配是按照槽位来进行分配的,每一个数据的键被哈希函数映射到一个槽位,redis-3.0.0规定一共有16384个槽位,当然这个可以根据用户的喜好进行配置。当用户put或者是get一个数据的时候,首先会查找这个数据对应的槽位是多少,然后查找对应的节点,然后才把数据放入这个节点。这样就做到了把数据均匀的分配到集群中的每一个节点上,从而做到了每一个节点的负载均衡,充分发挥了集群的威力。

 

Redis和Memcache区别

1.Redis只使用单核,而Memcache使用多核;即Redis属于单线程操作,而Memcache属于多线程操作。也就是说在多个用户请求时,Redis是处理完一个请求后在处理另一个请求,而Memcache可以同时处理多个请求。

2.多数据结构:Redis不仅仅支持简单的K/V类型的数据,同时还提供list、set、hash等数据结构的存储。

3.数据安全性:Redis和Memcache都是将数据存储在内存中,都属于内存数据库。Memcache属于纯内存数据库,服务器宕机或重启后,数据不可恢复;Redis服务器宕机或重启后可以恢复,因为Redis可以做持久化,将内存数据定期保存到磁盘中,而且提供了两种持久策略,默认支持的是RDM持久化,还有需要手动开启的AOF持久化。而Memcache仅仅将数据存储在内存中。

4.数据备份:Redis支持数据备份,即需要开启master-slave策略

5.过期策略:Memcache在set时就指定了过期时间;而Redis可以通过expire设定key的过期时间

6.内存回收:Memcache有内存回收机制,就是当程序里给它设定的内存大小,一旦存储的数据超过该分配的内存大小的时候,就会去自动回收,也就是释放,不然就会出现内存溢出的情况,因为Memcache的数据都是存储在内存中的,但是Redis不会,因为Redis可以将数据持久化到磁盘上

 

Mysql

DDL、DML、DQL、TCL、DCL代表了什么

having后面可以使用分组函数

where后面不可以使用分组函数

重点:若一条sql语句中根据group by子句,那么select关键字后面只能跟参与分组的字段和分组函数。

比如:select max(sal),name,job from emp group by job;

这里的name字段就没有意义,因为name并不是分组的字段,或者函数,而sal是分组函数,job是分组字段。

并且这种类型的sql在mysql不报错,但是在oracle上的报错的

SQl执行顺序

from 从某张表中检索数据

where 经过某些条件进行过滤

group by 然后分组

having 分组不满意后再过滤

select 查询出来结果

order by 排序输出结果

 

内连接:a,b两表连接时,把两表能够完全匹配的几条数据查询出来(只显示两表关联条件匹配的数据,也就是满足on的条件的数据)

外连接:a,b两表连接时,除了这两表能完全匹配的数据之外,把a表或b表的数据无条件的全部查询出来

左外连接:是指把左表的数据全部拿出来,而右表只会显示符合搜索条件的记录,右表记录不足的地方均为NULL.

右外连接:是指把右表的数据全部拿出来,而左表只会显示符合搜索条件的记录,左表记录不足的地方均为NULL.

外连接的查询结果条数 >= 内连接的查询结果条数

union 可以合并集合(相加)

要把两个查询结果集合并,必须两个查询结果集的字段个数一致。

union 自带去重的效果

union all 不带去重的效果

在删除父表中数据的时候,级联删除子表的数据 on delete cascade

1.删除外键约束

2.添加外键约束

alter table tableName drop 外键 字段

在更新父表中数据的时候,级联更新子表的数据 on update cascade

存储引擎

MyISAM表最合适大量的数据读而少量数据更新的混合操作,更适合使用压缩的只读表

InnoDB支持事务,支持崩溃后自动恢复

MEMORY存储引擎适合存储非永久需要的数据

隔离性有隔离级别(4个)

读未提交(read uncommited)

事务A未提交的数据,事务B可以读到,这里读取到的数据可以叫做"脏数据"。这种隔离级别是最低级别的,数据库默认的隔离级别一般都是高于该隔离级别。

读已提交(read commited)

事务A提交的数据,事务B才能读取到。这种隔离级别可以避免脏数据。这种隔离级别会导致"不可重复读取"

Oracle默认的隔离级别就是读已提交

可重复读(repetable read)

事务A提交的数据,事务B读取不到,事务B是可重复读取数据的Mysql默认的隔离级别就是可重复读

串行化(serializable)

事务A在操作数据库中数据的时候,事务B只能排队等待。这种事务隔离级别一般很少使用,用户体验不好

 

如何创建,删除索引

创建:create index 索引名 on 表名(字段名);

删除:drop index 索引名 on 表名;

 

数据库三范式

1.要求有主键,并且要求每个字段是原子性不可再分

不可再分就比如一个字段存了电话和邮箱,完全可以拆分成两个字段

2.是建立在第一范式上,要求所有非主键字段完全依赖主键,不能产生部分依赖

(尽量别使用联合主键)

3.建立在第二范式基础上,非主键字段不能传递依赖于主键字段(不要产生传递依赖)

简述项目中优化sql语句执行效率的方法

1.尽量选择较小的列

2.将where中用的比较频繁的字段建立索引

3.select子句中避免使用“*”

4.避免在索引列上使用计算,not,in和<>等操作

5.当只需要一行数据的时候使用limit 1

6.保证表单数据不超过200W,并对表进行适时分割

 

Oracle

分页

Rownum是oracle特有的一个特性,可以理解为在每条数据后都有一个隐藏的字段,字段 为rownum,存着的是当前数据的行数(处于第几行),可以使用rownum进行排名或者分页处理,但是在不使用子查询的情况下一条sql语句分页的时候只能使用<,=符号,使用>,=就没有效果,必须配合子查询

显示6到10条记录

select a1.*,rownum from (select * from emp) a1 where rownum <=10 and rownum >=6;//这种写法是不行的,oracle中的rownum只能使用一次

select * from (select a1.*,rownum rn from (select * from emp) a1 where rownum <=10) where rn>6;

由内往外推,先取到表中前十条,再取到表中6-10条

带参的存储过程procedure

所学知识点归纳梳理(二)

触发器trigger

所学知识点归纳梳理(二)

Count有两种用法

1.count(*) 查询数据的总条数

2.count(字段) 这种情况下,忽略空值的数据

所有的组函数都会忽略空值的数据

 

删除数据的三种方式Drop、delete、truncate

Truncate删除数据的效率比delete高,但是慎用,因为会自动commit,删除就无法回滚了。

 

Mybatis

Mybatis和Hibernate本质区别和应用场景

Hibernate:是一个标准的ORM框架(对象关系映射)。入门门槛较高,不需要程序写sql,sql语句自动生成,对sql语句进行优化、修改比较困难。

应用场景:

适用于需求变化不多的中小型项目,比如:后台管理系统,erp…

Mybatis:专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis也可以实现映射(输出映射,输入映射)

应用场景:

适用与需求变化较多的项目,比如:互联网项目

Mybatis核心对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。

延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

本质:先查询简单的sql(最好单表),再根据需求去查询关联表,降低数据库压力。

ResultMap可以实现延迟加载

ResultType不可以实现延迟加载

一级缓存原理

第一次发起查询id为1的用户信息,先去缓存中找是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

如果Sqlsession去执行commit操作(cud),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询id为1的用户信息,先去缓存中找是否有id为1的用户信息,如果有,直接从缓存中获取用户信息。

二级缓存原理

首先开启mybatis的二级缓存,默认关闭

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级存储中。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取。

sqlSession3去执行相同mapper下sql,执行commit提交,清空该mapper下的二级缓存区域的数据。

区别:

一级缓存是SqlSession级别的缓存

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个Sqlsession可以共用二级缓存,二级缓存是跨SqlSession的。

 

设计模式

手写单例、装饰者、静态代理设计模式

静态代理类与装饰者类的相同点:

1.都要实现与目标类相同的接口

2.都要声明目标对象作为成员变量

3.都可以在不修改目标类的前提下,增强目标类的方法

静态代理类与装饰者类的区别:

1.使用目的不同

装饰者设计的使用目的:是为了增强目标对象

静态代理设计的使用目的:是为了保护和隐藏目标对象

2.对于目标对象的获取方式不同

装饰者类中的目标对象的获取:通过带参构造器传入

静态代理中的目标对象的获取:因为是为了保护和隐藏对象,所以不会用传入的方式,而是在无参的构造器中直接创建

3.功能增强的实现者不同

装饰者设计模式中存在装饰者基类,其并不真正的实现增强,而是由具体的装饰者进行功能增强,所以存在一个"装饰者链"的概念

静态代理设计模式中一般不存在父子类的关系,具体的增强,就是由代理类完成,无需子类完成,所以不存在"链"的概念

double d1,d2,d3 = 0.123;

这里的d1,d2,d3指的是什么?

d1 = 0.0

d2 = 0.0

d3 = 0.123

并不是d1,d2,d3都是0.123,因为只有d3赋值了,d1,d2还没有初始化值。

i++ 先赋值,然后再自增

++i 先自增,后赋值

 

重写和重载

重载:有相同的方法名,但参数不同的多个方法

重写:重写方法必须和被重写方法具有相同方法名称、参数列表和返回类型且不能比原有方法更低的访问权限

 

内存剖析

类是放在方法区中,局部变量放在栈空间中,new出的对象放在堆空间中

Static修饰的静态变量和字符串常量存放在数据段中

基本数据类型占用两块内存,引用数据类型占用一块内存

test.chang1(date); 调用chang1时,把date的值copy给了chang1方法中的形参i,体现了Java的值传递的特点,会在栈内存中开辟一个名为i的内存空间

注意:方法执行完成后,栈内存会随之消失,但是堆内存不一定,但是在垃圾回收机制回收后,堆内存也就消失。

class Child extends Father{

     public String name; 

     Child(){

        super(100); 调用的是基类的有参的构造方法

        this(100); 调用的是本类的有参的构造方法

   }

   Child(int i){

       System.out.println("----");

   }

}

Child c = new Child();

内存解析:

在栈中有个c变量,指向了Child对象,堆中有个内存名为name,还有个Father内存,Father内存中存放着Father对象的成员。

因为继承了Father对象,那么子类中必有Father对象的成员(变量和方法)。

 

static和this关键字

super调用的是所继承父类的引用。this是当前类的引用。

final关键字

1 final的变量的值不能够被改变

2 final的方法不能够被重写

3 fianl的类不能够被继承

继承中的构造方法

1 子类的构造的过程中必须调用其基类的构造方法

2 子类可以在自己的构造方法中使用super(参数列表)调用基类的构造方法

如果调用super,必须写在子类构造方法的第一行

3 如果子类的构造方法中没有显式地调用基类构造方法,则系统默认调用基类无参数的构造方法

4 如果子类的构造方法中既没有显式地调用基类的构造方法,而基类中又没有无参的构造方法,则编译出错,因为子类会隐式的调用super()

多态

多态的存在有三个必要的条件

1.要有继承

2.要有重写

3.父类引用指向子类对象

个人内存理解

如果C c = new Cat("1"); 那么堆空间就有一个Cat对象,里面放着一个变量,变量的值是1。

如果C继承了A类,那么堆空间就有一个父类对象A,里面放着一个变量,变量的值是1。

抽象类的特性

1 用abstract关键字来修饰一个类时,这个类叫做抽象类;用abstract来修饰一个方法时,该方法叫做抽象方法。

2 含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被重写。

3 抽象类不能被实例化。

4 抽象方法只需声明,而不需要实现。

接口的特性

1.接口可以多重实现;就是可以多实现

2.接口中声明的属性默认为public static final的,也只能是public static final的

如果不写public static final,默认的就是public static final

3.接口中只能定义抽象方法,而且这些方法默认为public的,也只能是public的

4.接口可以继承其他接口,并添加新的属性和抽象方法

5.虽然接口中的方法没用abstract关键字,但是接口中的方法都是抽象的。也可以有abstract关键字

6.多个类可以实现同一个接口

7.一个类可以实现多个接口

8.接口可以继承接口,类是用来实现接口

 

String s1 = "hello";

String s2 = "world";

String s3 = "hello";

s1 == s3 true

内存解析:

栈中存在s1的变量,hello属于字符串常量,所以存在于数据区

s1指向了数据区的hello

这里需要注意的一点,编译器的优化,如果在数据区存在这个字符串常量,那么就算再次声明,也不会新增一个hello的常量,而是沿用之前的。所以s1 == s3是相等的,因为两个引用指向了一个对象,地址值是一致的。

s1 = new String("hello");

s2 = new String("hello");

s1 == s2 false

因为new了两次,地址值肯定不会相等

s1.equals(s2) true 比较的是值

不可变的String字符串分析

String s1 = new String("hello");

栈空间有个变量s1,指向堆内存的一块内存,内存中存着hello

String s2 = new String("world");

栈空间有个变量s2,指向堆内存的一块内存,内存中存着world

s1 +=s2;

因为String是不可变字符序列,所以是重新在堆内存开辟一个内存,把s1和s2copy到这块内存中,然后s1指向这个内存。

 

多线程

线程的创建和启动

1.继承Thread类或者实现Runnable接口

2.只有使用start()才是启动线程,调用run()只是普通的方法调用

并不是执行了start()就一定会运行,它会处于等待状态,等待CPU有时间后来执行

Sleep()休眠当前线程

Jon()合并某个线程

Yield()让出当前线程,给其它线程执行

线程的优先级

默认5,范围1-10

线程同步

Synchronized关键字

解决死锁的问题

把锁的粒度加大,不要对某个成员进行锁,这个对类进行锁

 

线程安全的类是哪些

statck:堆栈类,先进后出

hashtable:就比hashmap多了个线程安全

StringBuffer是线程安全,而StringBuilder是线程不安全的

之前的线程不安全:

List<String> list1 = new ArrayList<String>();// 线程不安全

现在使用B,线程安全的:

List<String> list2 = Collections.synchronizedList(new ArrayList<String>())

Mybatis返回主键id

<selectKey resultType="java.lang.Long" order="AFTER" keyProperty="productId">

SELECT LAST_INSERT_ID()

</selectKey>

Spring的事务

TransactionManager类

TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。

TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

 

线程是阻塞IO的,NIO是非阻塞IO

Java NIO和IO之间最大的区别是IO是面向流(Stream)的,NIO是面向块(buffer)的

解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法。 答:通过new关键字和构造器创建的对象放在堆空间。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其他进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。

1

String str = new String("hello");

上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而”hello”这个字面量放在静态区。

Switch可以是byte、short、char、int、枚举、String、但是不能为long

构造器(constructor)是否可被重写(override)? 答:构造器不能被继承,因此不能被重写,但可以被重载。

是否可以继承String类? 答:String 类是final类,不可以被继承。

Springmvc的文件上传

MultipartFile实例,CommonsMultipartResolver类

SpringMVC上传文件时,需要配置MultipartResolver处理器

设置文件最大的大小