转发和重定向的区别
Java Web
转发和重定向的区别
- 转发是服务器行为,重定向是客户端行为
- 转发速度快,重定向速度慢
- 转发的是同一次请求,重定向是两次不同的请求
- 转发不会执行转发后的代码,重定向会执行重定向之后的代码
- 转发地址栏没有变化,重定向地址栏有变化
- 转发必须在同一台服务器下完成,重定向可以不同的服务器下完成
在servlet中调用转发,重定向语句:
request.getRequestDispatcher(“new.jsp”).forward(request,response); //转发
response.sendRedirect(“new.jsp”); //重定向
转发过程
客户浏览器发送http请求,web服务器接收请求,调用内部的一个方法在容器内完成请求处理和转发动作,将目标资源发送给客户;此处,转发的路径必须是同一个web服务器下的url,不能转向其他的web路径上去,中间传递的是自己的容器内request。在客户浏览器路径显示的仍然是其第一次访问的路径,客户端并不能感受到服务器做了转发。转发行为是浏览器只做了一次请求。
重定向过程
客户浏览器发送http请求,web服务器接收后发送302状态码及新的location给客户端浏览器,客户浏览器根据302响应会自动再次发送一个新的http请求,请求url是新的location地址,服务器根据此请求寻找资源发送给客户。此处的location可以是任意的url,既然是新的请求,也就没有request的传递概念了。在客户浏览器地址栏显示的是重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器至少做了两次请求访问的,即两次request。
HTTP
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议
HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件,图片,查询结果等)
HTTP是属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。
HTTP协议工作与客户端-服务器架构上。浏览器作为HTTP客户端通过URL向HTTP服务器(WEB服务器)发送请求。WEB服务器根据接收到的请求后,向客户端发送响应消息。
Java SE
varchar和char的区别
- 定长和变长
char表示固定长度,varchar表示可变长度。char如果插入的长度小于定义长度时,使用空格填充;varchar小于定义长度时,还是按实际长度存储,插入多长就存多长。
因为char长度固定,char的存取速度要比varchar快的多,方便程序的存储与查找;但是char也为此付出空间的代价,长度固定,会占据更多的空间,以空间换取时间效率。varchar刚好相反,以时间换空间。 - 存储的容量不同
varchar(M)和char(M),M都表示字符数。varchar的最大长度是65536个字节(utf编码下字符长度为21845),不同的编码所对应的最大可存储的字符数不同。char最多可存储255个字符,不同的编码所对应的最大可用字节数不同。
ArrayList的底层原理
ArrayList的底层数据结构就是一个数组,数组元素类型为Object类型,对ArrayList的所有操作底层都是基于数组的。
ArrayList的线程安全
对ArrayList进行添加元素的操作的时候是分两个步骤完成的,即第一步现在object[size]的位置上存放需要添加的元素;第二步将size的值增加1。由于整个过程在多线程的环境下不能保证具有原子性,因此ArrayList在多线程环境下是线程不安全的。
如果非要在多线程环境使用ArrayList,就需要保证他的线程安全性,通常两种解决办法。
- 使用synchronized关键字
- 使用Collections类中的静态方法synchronizedList()对ArrayList调用即可
ArrayList扩容机制
自己查看ArrayList类的add()方法去,数组扩容按照当前容量的1.5倍进行扩容。
HashMap 和 HashTable
HashMap
HashMap是基于哈希表的map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null键和null值,此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap的底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
将键值对传递给put()时,它调用健对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储对象。当获取对象时,通过对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会存在链表的下一个节点中。HashMap在每个链表节点中存储键值对对象。当两个不同的键对象的hashcode相同时会发生什么?他们会存储在同一个bucket位置的链表中。
HashTable
和HashMap一样,HashTable也是一个散列列表,他存储的内容是键值对映射。HashTable继承于DIctionary类,实现了Map、Cloneable、Serializable接口。HashTable的函数都是同步的,这意味着他是线程安全的。他的key,value都不能为null。
HashTable容器使用synchronized来保证线程安全,但在多线程环境下HashTable的效率非常低。因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,就可能会进入阻塞或轮询状态。这就导致所有访问HashTable的线程都必须竞争同一把锁。
ConcurrentHashMap
底层采用分段的数组+链表实现,线程安全的。
ConcurrentHashMap采用锁分段技术,首先将数据分成一段一段的存储,然后给每段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他数据段的数据也能被其他线程访问。
HashMap 和 HashTable 的区别
- HashTable是线程安全的,而HashMap则非线程安全
- HashMap可以使用 null 作为 key ,而 HashTable 则不允许 null 最为 key
- HashMap 是对 Map 接口的实现,而HashTable实现了Map接口和DIctionary抽象类
- HashMap的初始容量为16,HashTable的初始容量为11,两者的填充因子默认都是0.75
- 两者计算hash的方法不同
字符串的拼接方式
- 使用“+”拼接字符串
- 使用String类中的方法concat方法
- StringBuffer
就是为了解决大量拼接字符串时产生很多中间对象而提供的一个类,提供append和add方法,可以将字符串添加到已有的序列的末尾或指定位置,本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized关键字,保证了线程的安全但是需要付出性能的代价。 - StringBuilder
StringBuilder和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。 - StringUtils.join
equals方法的四个特性
- 自反性(reflexive)
对于任何非null的引用值x,x.equals(x)必须返回True - 对称性
对于任何非null的引用值x,y,当且仅当y.equals(x)返回True时,x.equals(y) 返回True - 传递性
对于任何非null的引用值x,y,z,如果x.equals(y)返回True,y.equals(z)返回True,则x.equals(z)返回True - 一致性
对于任何非null的引用值x,y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回True,或者一致的返回False
Exception和Error
Exception和Error都继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出或者捕获,他是异常处理机制的基本类型。
Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复,是程序本身可以处理的异常。遇到这类异常,应该尽可能处理异常,使程序恢复运行。
Error(错误)是系统中的错误,程序员不能改变和处理的,实在程序编译时出现的错误,只能通过修改程序才能修正。一般值与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
Exception又分为CheckedException和UnCheckedException。
CheckedException(编译时异常):需要使用try–catch显示的捕获,对于可恢复的异常使用
UnCheckedException(RuntimeException运行时异常):不需要捕获
数据库
数据库索引
缺点
- 虽然索引大大提高了查询速度,同时会降低更新表的速度。如insert,update,delete。因为更新表示,不仅要保存数据,还要保存索引。
- 建立索引会占用磁盘空间的索引文件。
注意事项
- 索引不会包含有null值的列。
只要列中包含null值都将不会被包含在索引中,复合索引中只要有一列含有null值,那么这一列对于此复合索引就是无效的。所以在数据库设计时不要让字段默认值为null。 - 使用短索引。
对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个char(255)的列,如果在前10或20个字符串内,多数值是唯一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘I/O操作。 - 索引列排序
查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库的默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。 - like语句
一般情况下不推荐使用like操作,如果非使用不可,如何使用也是个问题。like "%aaa%"不会使用索引,而like "aaa%"可以使用索引。 - 不要再列上进行运算,这将导致索引失效而进行全表扫描
- 不适用not in 和 <> 操作
联合索引
对多个字段同时建立索引(有顺序,ABC和ACB是两个完全不同的联合索引)。以联合索引(a,b,c)为例:
- 此联合索引相当于建立了a、ab、abc三个索引。
- 覆盖(动词)索引。
同样的有联合索引(a,b,c),如果有如下的sql: select a,b,c from table where a=xxx and b = xxx。那么MySQL可以直接通过遍历索引取得数据,而无需读表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。 - 索引列越多,通过索引筛选出的数据越少。
有1000W条数据的表,有如下sql:select * from table where a = 1 and b =2 and c = 3,假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W*10%=100w 条数据,然后再回表从100w条数据中找到符合b=2 and c= 3的数据,然后再排序,再分页;如果是复合索引,通过索引筛选出1000w *10% *10% *10%=1w,然后再排序、分页,哪个更高效,一眼便知。
- 单个索引需要注意的事项,组合索引全部通用。比如索引列不要参与计算啊、or的两侧要么都索引列,要么都不是索引列啊、模糊匹配的时候%不要在头部啦等等
- 最左匹配原则。(A,B,C) 这样3列,mysql会首先匹配A,然后再B,C。如果用(B,C)这样的数据来检索的话,就会找不到A使得索引失效。如果使用(A,C)这样的数据来检索的话,就会先找到所有A的值然后匹配C,此时联合索引是失效的。
- 把最常用的,筛选数据最多的字段放在左侧
IN 和 EXISTS 的区别
- in() 适合B表比A表数据少的情况
- exists()适合B表比A表数据多的情况
- 当A表数据和B表数据一样情况下,in和exists的效率差不多
- 使用 not in 和 not exists ,则直接使用 not exits,因为 not in 会全表扫描,不走索引,not exists会走索引。
select * from A where id in( select id from B )
sql语句先执行子查询,即查询B表,再查A表。in()只执行一次,它查出B表中的所有id字段并缓存起来(内存中),之后检查A表的id是否与B表中的id相等,如果相等则将A表的记录加入结果集中,直到遍历完A表的所有记录。最多可能遍历 A.length * B.length 次(内存中)。
select a.* from A a where exists ( select 1 from B b where a.id=b.id )
先对where 前的主查询(A表)进行查询,然后用主查询的结果一个一个的代入exists的查询进行判断,如果为真则输出
当前这一条主查询的结果,否则不输出。 会执行 A.length 次判断A表中的id是否与B表中的id相等(查询数据库)。
行转列、列转行
图一:
图二:
行转列 case when 方式:
SELECT
user_name,
max( CASE `subject` WHEN '语文' THEN score ELSE 0 END ) AS '语文',
max( CASE `subject` WHEN '数学' THEN score ELSE 0 END ) AS '数学',
max( CASE `subject` WHEN '英语' THEN score ELSE 0 END ) AS '英语',
max( CASE `subject` WHEN '生物' THEN score ELSE 0 END ) AS '生物'
FROM
student_scores
GROUP BY
user_name;
多线程
多线程间的通信方式
- 使用synchronized关键字同步实现多线程间的通信
这种方式,本质上是“共享内存”的概念。多个线程需要访问同一个共享变量,谁拿到了锁(获得访问权限),谁就可以执行。 - 线程while轮询
- wait/notify 机制(通知/等待)
spring
spring bean生命周期
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
DipatcherServlet作用
- 客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将请求转交给DispatcherServlet.
- DipatcherServlet接收到这个请求之后将根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandlerMapping的配置找到处理请求的处理器(Handler)。
- DispatcherServlet根据HandlerMapping找到对应的Handler,将处理权交给Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用。
- Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。
- Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver将逻辑视图转化为真正的视图View。
- Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。
对Spring中IOC和AOP的理解
IOC:控制反转;DI:依赖注入。是同一种概念,利用了工厂模式。spring中创建被调用者的工作不再由调用者来完成,而是由spring容器管理,因此称为控制反转。
1. 让spring来管理对象的创建及管理对象
2. 在spring容器启动的时候,spring会把配置文件中配置的bean都初始化好
3. 需要调用的时候,容器把已经初始化好的bean分配给需要调用这些bean的类。
4. spring以动态灵活的方式来管理对象,注入的方式有两种,设置注入和依赖注入
AOP:面向切面编程。完善依赖注入,主要表现为:
1. 面向切面编程提供声明式事务管理
2. spring支持用户自定义切面
面向切面编程(AOP)是面向对象编程(OOP)的补充,面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行分解成各个切面。AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,OOP是静态的抽象,AOP是动态的抽象。