1.1.3
什么是并发:所谓并发操作是指在同一时间可能有多个用户对同一数据进行读写操作.
并发问题的瓶颈在哪儿,读和写。
怎么解决并发:
所谓并发是指大量用户同一时刻读写同一条数据,那关键点就在于读和写。
> 首先从读的角度来解决:
1、读写分离。
2、配置缓存
3、配置集群
> 从写的角度:
1、从项目的情况来选择合适的队列。
2、配置集群
在解决并发的时候可以举一个形象的例子来阐述什么是并发:比如我们去乘坐地铁,如果不排队是不是就很挤很乱,效率就低啊,那如果排队之后一个一个来效率是不是就提高了,这样就解决了大并发的问题。
那么怎么实现这个队列呢,这就是我们需要考虑的问题,在 java 代码中我们使用的 list集合是不是就是一个队列,循环遍历一个一个取出。而在 redis 中还有一种数据结构就是 list结构,这就是一个队列结构。当然我们也可以将 MQ 作为一个方案,MQ 本来就是处理队列的。怎么取舍,就需要考虑到 redis 和 MQ 的优缺点,然后看需求了。
如果简单点,就用以上回答.
> 详细点见下:
1、确认服务器硬件是否足够支持当前的容量。
普通的 P4 服务器一般最多能支持每天 10 万独立 IP,如果访问量比这个还要大,那么必须首先配置一台更高性能的专用服务器才能解决问题,否则怎么优化都不可能彻底解决性能问题。
2、优化数据库访问。
前台实现完全的静态化,可以完全不用访问数据库。不过对于频繁更新的网站,静态化往往不能满足某些功能。缓存技术是另一个解决方案,将动态数据存储到缓存文件中,动态网页直接调用这些文件,而不必再访问数据库。如果确实无法避免对数据库的访问,那么可以尝试优化数据库的查询SQL.避免使用 Select *from 这样的语句,每次查询只返回自己需要的结果,避免短时间内的大量 SQL 查询。
3、禁止外部的盗链。
外部网站的图片或者文件盗链往往会带来大量的负载压力,因此应该严格限制外部对于自身的图片或者文件盗链。好在目前可以简单地通过 refer 来控制盗链,Apache 自己就可以通过配置来禁止盗链,IIS 也有一些第三方的 ISAPI 可以实现同样的功能。或者使用非技术手段来解决,比如在图片上增加水印。
4、控制大文件的下载。
大文件的下载会占用很大的流量,并且对于非 SCSI 硬盘来说,大量文件下载会消耗 CPU,使得网站响应能力下降。因此,尽量不要提供超过 2M 的大文件下载,如果需要提供,可以将大文件放在另外一台服务器上。
5、使用不同主机分流主要流量
将文件放在不同的主机上,提供不同的镜像供用户下载。比如如果觉得 RSS 文件占用流量大,那么使用 FeedBurner 或者 FeedSky 等服务将 RSS 输出放在其他主机上,这样别人访问的流量压力就大多集中在 FeedBurner 的主机上,RSS 就不占用太多资源了。
6、使用流量分析统计软件。
在网站上安装一个流量分析统计软件,可以即时知道哪些地方耗费了大量流量,哪些页面需要再进行优化。因此,解决流量问题还需要进行精确的统计分析才可以。推荐使用的流量分析统计软件是Google Analytics(Google 分析)。
7、HTML 静态化
效率最高、消耗最小的就是纯静态化的 html 页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统 CMS。信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的 CMS 是必不可少的。
8、图片服务器分离
对于 Web 服务器来说,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,使用独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。在应用服务器和图片服务器上,可以进行不同的配置优化。
9、数据库集群和库表散列
我们在应用程序中按照业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户 ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。
1.1.4. 集群
其实从上面并发的地铁例子,还可以牵扯出另外一个方案,集群。所谓集群其实就相当于我多开了几个收银柜台。那么比如我开放了 10 个进票口,不管我之后可能几个进票口可能出现问题,只要还有一个在正常工作就可以。
1.1.5. 分布式
> 分布式架构,我们必须对比传统架构才能彰显其优势。
①最为明显的一点,在传统的架构中,如果某个功能需要进行维护,那么我们必须停掉整个服务,这对于公司的运营会造成损失。分布式系统在核心功能模块使用单独服务器,维护部分模块不影响用户的其他操作。
②在海量数据处理方面,传统架构显得比较乏力;分布式系统架构采用服务器集群,使用负载均衡,海量数据处理游刃有余!
③在性能(检索)以及维护方面,分布式系统架构也有较为明显的优势。而所谓的分布式,其实也可以用地铁来举例子。地铁进票口和门口的包裹检查,这是两个系统,进票口和包裹检查这是两个模块,互不干,这个就是分布式。两个系统做各自的事情,但是事情是有联系的,组合起来就是一个完整的流程。而且任何一方如果需要维护,并不影响另外一方的运行。
1.1.6. 多线程
1.1.6.1.线程池
创建线程是比较耗费资源的,这时就有了线程池的引入。
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。
线程池的概念类似连接池。
线程池能够带来三个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性
1.1.6.2.java 多线程解决方案
synchronized 关键字主要解决多线程共享数据同步问题,ThreadLocal 主要解决多线程中数据因并发产生不一致问题,二者都用于解决多线程并发访问。
二者的本质区别:
synchronized 是利用锁的机制,使变量或代码在某一时刻只能被一个线程访问。而
ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的共享。而 synchronized 正好相反,它用于在多个线程间共享通信时能够数据共享。synchronized 用于线程间数据共享,而 ThreadLocal 用于线程间数据隔离。
1.1.6.3.什么叫线程死锁?
两个或者多个线程之间相互等待使用对方的锁,导致线程都无法执行,叫做线程死锁。
1.1.6.4.请描述一下进程与线程的区别?
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
1.1.6.5.现在有 T1、T2、T3 三个线程,如何保证 T2 在 T1 执行完后执行,T3 在 T2 执行完后执行?
在 JDK1.5 中,提供了 Condition 对象控制线程状态,可以有针对性的控制线程的等待和唤醒Condition 对象可以创建多个,每个 Condition 可以控制不同类型的线程。这样就能实现让指定类型线程等待或唤醒!
例如:我们可以创建两个 Condition,一个控制生成线程,一个专门控制消费线程!
1.1.6.6.wait 和 sleep 方法有什么区别:
wait 需要使用 notify 或 notifyAll 唤醒,而 sleep 方法它时间到会自动醒。
wait 需要放在同步代码块中。sleep 可以在同步中也可以不在同步中。
wait 在等待的时候会释放锁,sleep 不会释放锁。
wait 可以指定时间也可以不指定时间,但 sleep 必须指定时间。
1.1.7. 权限
一般企业中权限控制有哪几种方式:
1、创建文件夹,将需要控制的相应文件放在该文件夹下,通过判断文件夹名称来控制权限
2、通过 url 地址进行控制,在用户表中添加一列,在这列中添加可以访问的所有路径
3、配置文件管理权限,通过针对不同的角色创建不同的文件,在文件中添加用户可以访问的路径,然后通过配置过滤器来设置权限
4、利用 Spring Security 或者 shiro 等框架实现权限管理
Shiro:
支持认证跨一个或多个数据源(LDAP,JDBC,kerberos 身份等)
执行授权,基于角色的细粒度的权限控制。
增强的缓存的支持。
支持 web 或者非 web 环境,可以在任何单点登录(SSO)或集群分布式会话中使用。
主要功能是:认证,授权,会话管理和加密。
Spring Security:
这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。在 Spring Framework 基础上,Spring Security 充分利用了依赖注入(DI,Dependency Injection)和面向切面技术。
1.1.8. 网站并发量
Nginx 的并发量大致在 3-5 万。
1.1.9. 订单 ID 是怎么生成的?
订单 ID 首先是不能重复的,鉴于此,我们可以使用商品 ID+当前时间戳,或者通过userId+当前时间戳通过 MD5 加密的方式,来获取一个唯一的订单号,主要还是看需求,并保证订单 ID 的唯一性。
1.1.10. 如果用户一直向购物车添加商品怎么办?并且他添加一次你查询一次数据库?互联网上用户那么多,这样会对数据库造成很大压力你怎么办?
配置读写分离,讲数据库分成读库和写库,当查询数据的时候从读库中进行查询,并且可以添加缓存,利用缓存来提高读取效率,增删改操作就使用写库。这样就能很大程度上缓解数据库的压力。
1.1.11. 项目是分布式的,系统与系统之前如何进行通讯。
通信协议和技术有很多,如 web service,EJB,jms,httpclient.
1.2. 框架面试问题
1.2.1. Servlet
通过判断客户端发送的请求是 post 还是 get 方法来确定执行的是 doGet 还是 doPost 方法
Servlet 生命周期:
1、创建时间:当这个 Servlet 程序被外界第一次访问的时候 tomcat 服务器内部会自动的创建这个 Servlet 对象,并调用 init 方法对这个 Servlet 进行初始化
2、服务的时间:当 Servlet 创建初始化完成之后,就开始一直在提供服务,用户通过浏览器只要访问这个 Servlet 程序,那么就一定会调用这个 Servlet 的 service 方法。每次访问都会调用 service 方法。Servlet 中的 service 方法是专门提供出来给用户服务的。
3、销毁的时间:当这个 Servlet 从项目中被移除,或者 tomcat 服务器正常关闭(需要手动的去使用 stoptomcat),才会调用。
1.2.2. Struts
Struts 是一个按 MVC 设计的 web 层的框架,其实它就是一个更大的 servlet,这个 servlet 是
ActionServlet 或者 ActionServlet 的子类,说到底就是把 servlet 给封装了。
Struts 执行流程:
1)客户端提起一个请求
2)这个请求经过一系列的过滤器直到最后一个过滤器 FilterDispatcher(Filter)
3)接着 FilterDispatcher 被调用,FilterDispatcher 询问 ActionMapper 来决定这个请是否需要调用某个 Action
4)如果 ActionMapper 决定需要调用某个 Action,FilterDispatcher 把请求的处理交给ActionProxy
5)ActionProxy 通过 Configuration Manager 询问框架的配置文件,找到需要调用的Action 类
6)ActionProxy 创建一个 ActionInvocation 的实例。
7)ActionInvocation 实例使用命名模式来调用,在调用 Action 的过程前后,涉及到相关拦截器(Intercepter)的调用。
8)一旦 Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果
优点:
1、实现 MVC 模式,结构清晰,开发者只关注业务逻辑的实现。
2、Strust 的标签库提供了丰富的标签可以使用,大大提高了开发效率
3、页面导航:通过一个配置文件,即可把握整个系统各部分之间的联系。
4、提供了 exception 处理机制
5、数据库连接池管理
6、支持 I18N
缺点:
1、Struts 对每一个用户的请求都会创建一个 action,它只允许一个实例去处理所有的请求。
2、测试不方便,struts 的每一个 action 都和 web 层耦合在一起,它的测试依赖于 web 容
器,单元测试也很难,不过有一个 Junit 的扩展工具 Struts TestCase 可以实现单元测试。
3、对Servlet依赖性过强,Structs处理action都必须依赖ServletRequest和ServletResponse,摆脱不了 servlet 容器。
Struts 和 springmvc 的区别:
1、springMVC 的入口是 Servlet,Struts2 的入口是 Filter,两者的实现机制不同
2、SpringMVC 是基于方法设计,传递参数是通过方法形参接收,其实现方式是单例设计模式(也可以改为多例,推荐使用单例),
Struts2 是基于类设计,传递参数是通过类的属性,只能是多例实现,性能上 springMVC更高一些
4、传递参数方面,Struts2 是用类的属性来接收,也就是在多个方法间共享,而 springMVC是基于方法的,多个方法间数据无法共享
5、Struts 是类级别的拦截,一个类对应一个 request 上下文,而 springmvc 是方法级别的拦截,一个方法对应一个 request 上下文,而一个方法又对应一个 url,因此从架构上来说springmvc 更容易实现 restful。
1.2.3. Springmvc
Springmvc 执行流程:
1. 用户向服务器发送请求,请求被 Spring 前端控制 Servelt ---DispatcherServlet 捕获;
2. DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回;
3. DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(...)方法)
4. 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。 在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些
额外的工作:
HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对
象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult或 Error 中
5. Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;
6. 根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring 容器中的 ViewResolver)返回给 DispatcherServlet ;
7. ViewResolver 结合 Model 和 View,来渲染视图
8. 将渲染结果返回给客户端。
1.2.4. Hibernate
优点:
对象/关系数据库映射(ORM),使用时只需要操作对象,完全面向对象的思想。
封装了 jdbc 的底层代码,不需要和 jdbc api 打交道即可直接访问数据库,在大型项目中开发效率高。
封装了很多可操作数据库的方法,不需要写 sql 语句。
提供一二级缓存。
缺点:
执行效率低,很多 sql 语句都是自动生成的,会有冗余语句,优化负责,更消耗内存。
多表和复杂数据的操作比较麻烦。
更适合单一对象的操作,不适合批量操作。
和 mybatis 的比较:
1、MyBatis 容易掌握,而 Hibernate 门槛较高。
2、Mybatis 基于原生的 jdbc,运行速度比 hinernate 更高。
3、针对高级查询,Mybatis 需要手动编写 SQL 语句,以及 ResultMap。而 Hibernate 有良好的映射机制,开发者无需关心 SQL 的生成与结果映射,可以更专注于业务流程。
4、Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
1.2.5. Spring
Spring IOC:
所谓控制反转,举个例子:
遵循 mvc 的开发原则,我们在 web 层创建业务层对象时如下:
UserService userService=new UserService ();
但是这样写死的话不利于扩展和维护。而为了利于扩展功能,因此我们会写一个接口比如 userService,然后再通过父类引用子类对象的多态原则使程序有更好的扩展性:
UserService userService=new UserServiceImpl().但这种写法还是耦合性太高,如果有一天我要更换实现类,还是需要修改原代码,这样就违法了 OCP 原则(open-close 原则,该原则建议不要修改原代码,可以通过添加新的代码,实现新的功能,即对修改关闭,对扩展开放)。
那么为了实现 service 层和 web 层的解耦,我们就使用了工厂方式:
但是如此一来,工厂和实现类又耦合了,那么是否可以使工厂和实现类解耦呢。可以通过反射和配置文件:
而 IOC 能做到什么呢,就是 service 不再利用 web 层去获取,而是通过 spring 容器创建bean 对象来自动的为我们注入。这样的话整个过程就反过来了,以前是 web 层去 new 一个service,现在是 service 主动被设置到 web 层中去了,这就是反转控制的由来。
通过 IOC,我们就可以在不修改任何代码的情况下,就可以进行实现类的切换,当然前提还是必须得写一个 service 的实现类。这样我们只需要在配置文件配置一下就 ok 了,而不需要再一个个的写工厂来获取了。
这就是 IOC 为我们带来的模块的松耦合和应用的便利性。
DI 注入:
依赖注入,在 spring 框架创建 bean 对象时,动态的动态的将依赖对象注入到 bean 组件。
举例:HelloServiceImpl 内部需要依赖 info 属性
传统方法和依赖注入的区别:
IOC 和 DI 的区别:
IOC 控制反转指的是对象的创建权,被反转到了 spring 容器来管理。DI 依赖注入,指spring 容器在创建对象的过程中将对象依赖的属性通过配置文件进行注入。表面是先有控制反转,再有依赖注入。实际是一个事件,两种不同时期的称呼。DI 实际上也可以理解为就是实现 IOC 的方式。就比如将一个 B 对象注入到另外一个 A对象中,即实现了控制反转(不再在 A 对象中创建 B 对象,而是通过配置文件主动配置),也实现了 DI 注入(将 B 对象作为 A 对象的属性,利用配置文件注入到 A 对象中)。
Spring AOP:
AOP 的意思就是面向切面编程,AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码。实际上就是利用动态代理对目标对象中的方法进行增强JDK 的动态代理要求被代理对象必须实现接口。Cglib 则不需要。AOP 的底层实际上是实现了 JDK 和 CGLIB 两种代理机制。如果目标对象实现了接口,那么优先使用 JDK 的动态代理,如果没有实现任何接口,就会使用 CGLIB 库生成目标对象的子类作为代理对象。
1.3. 数据库面试问题
1.3.1. Mysql 的数据库引擎
Innodb 引擎
Innodb 引擎提供了对数据库 ACID 事务的支持,并且实现了 SQL 标准的四种隔离级别,关于数据库事务与其隔离级别的内容请见数据库事务与其隔离级别这篇文章。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于 MySQL 后台的完整数据库系统,MySQL 运行时 Innodb 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持 FULLTEXT 类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE 时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用 Innodb 引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个 SQL 语句时 MySQL 不能确定要扫描的范围,InnoDB 表同样会锁全表。
MyIASM 引擎
MyIASM 是 MySQL 默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和 Innodb 不同,MyIASM 中存储了表的行数,于是 SELECT COUNT(*)FROM TABLE 时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么 MyIASM 也是很好的选择。两种引擎的选择大尺寸的数据集趋向于选择 InnoDB 引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB 可以利用事务日志进行数据恢复,这会比较快。主键查询在 InnoDB 引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题,关于这个问题我会在下文中讲到。大批的 INSERT 语句(在每个 INSERT 语句中写入多行,批量插入)在 MyISAM 下会快一些,但是 UPDATE 语句在 InnoDB 下则会更快一些,尤其是在并发量大的时候。
1.3.2. SQL 优化
1. 对操作符的优化 尽量不采用不利用索引的操作符
如:in ,not in , is null, is not null,<> like 等
2. select count(*) from table;这样不带任何条件的 count 会引起全表扫描,并且没有任何
业务意义,是一定要杜绝的。
3. 使用“临时表”暂存中间结果
部分 UPDATE、SELECT 语句 写得很复杂(经常嵌套多级子查询)——可以考虑适当拆成几步,先生成一些临时数据表,再进行关联操作。简化 SQL 语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在 tempdb 中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。
4. 排序
避免使用耗费资源的操作,带有 DISTINCT,UNION,MINUS,INTERSECT,ORDER BY 的 SQL语句会启动 SQL 引擎 执行,耗费资源的排序(SORT)功能. DISTINCT 需要一次排序操作,而其他的至少需要执行两次排序
1.3.3. 如何快速向表中插入 100 万条数据
1、Mysql 中有 LOAD DATA INFILE 语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。
2、批量插入
1.4. java 基础
1.4.1. Error 和 Exception 的区别
Error:表示 Java 程序中存在的那些错误现象,而这些错误现象统一使用 Error 进行描述。在我们的程序中,如果发生 Error 问题,我们不会去处理这样的问题,而是让程序的定义者自己去处理自己程序中的错误。针对 Error 我们不给出程序的针对性处理。
Exception:表示 Java 程序中存在的异常问题。而不是错误问题。而这些异常问题,在程序中通过
判断等形式是可以检测并且预防的。而针对这些异常问题,程序员在写代码的时候一旦发生必须给出有效的解决方案。
1.4.2. 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么方法可以通知 jvm 进行垃圾回收?
答:对象刚创建,GC 就开始监控这个对象的地址、大小以及使用情况。通常,GC 采用有向图的方式记录和管理(heap)堆中所有对象。通过这种方式确定哪些对象时可达的,哪些是不可达的,当确定一些对象不可达,GC 就有责任回收这些内存空间。可以。手动执行 System.gc(),通知 GC 运行,但是 java 语言规范并不保证 gc 一定运行。
1.4.3. 抽象类和接口的区别?什么时候用抽象类,什么时候用接口?
答:
含有 abstract 修饰符的类就是抽象类,抽象类不能创建实例对象。含有抽象方法的类必须是抽象类,抽象类中可以有非抽象方法。
子类在继承抽象类时,必须重写抽象类的所有抽象方法。不能有抽象构造方法和抽象静态方法。如果子类没有实现抽象类中的所有抽象方法,那么子类也必须是抽象的。
接口是抽象类的一种特例,接口中的方法都是抽象方法,接口中的变量都要用 public static final 修饰。
两者的区别:
1.抽象类中可以有构造方法,接口中没有构造方法
2.抽象类中有普通的成员变量,接口中没有普通成员变量。
3.抽象类中可以有非抽象方法,接口中不能有非抽象方法。
4.抽象类中抽象方法的访问类型可以是 public ,protected,但是接口中只能是 public
5.抽象类中可以有静态方法,接口中不能有静态方法。
6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,
但是接口中必须是 public static fianl。
7.一个类可以实现多个接口,但是只能继承一个抽象类
应用上的不同:
接口更多的是在系统架构设计方法上发挥作用,主要用于定义模块之间的通信契约和扩展功能。
而抽象类在代码实现方面发挥作用,可以实现代码的重用。比如我们项目中三层开发中的 dao 层和 service 层,会创建一个接口,然后定义一个接口类,主要是考虑到多实现。
在 mybatis 的时候,我们操作数据库,会封装一些方法到一个抽象类中,然后由 service 层来继承这个抽象类,就可以复用里面的方法,方便开发。
1.4.4. 是否可以继承 String 类?
不可以,因为 String 类被 final 修饰。
1.4.5. String 和 StringBuffer 的区别?
1.String 数值不可变,StringBuffer 字符串长度可变。
2.String 覆盖了 equals 和 hashCode 方法,StringBuffer 没有覆盖 equals 和 hashCode 方法。
1.4.6. final、finalize 和 finally 的区别
final 用于声明属性,方法和类的
finally 是异常处理语句结够的一部分,表示总是执行。
finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法
1.4.7. 多线程有几种实现方法?同步有几种实现方法?
多线程有两种实现方法:继承 Thread 类和实现 Runnable 接口。
1.4.8. 简述一下线程的几种可用状态;
线程在执行过程中,可以处于下面几种状态:
1、就绪(Runnable):线程准备运行,不一定立马就能开始执行。
2、运行中(Running):进程正在执行线程的代码。
3、等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。,
4、睡眠中(Sleeping):线程被强制睡眠。
5、I/O 阻塞(BlockedonI/O):等待 I/O 操作完成。
6、同步阻塞(BlockedonSynchronization):等待获取锁。
7、死亡(Dead):线程完成了执行。
1.4.9. 什么是 java 序列化,如何实现 java 序列化?或者请解释 serializable 的作用.
序列化:就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现 Serializable 接口,然后使用一个输出(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象,接着使用 ObjectOutputStream 对象的writeObject(Object obj)方 法就 可以 将参数 为 obj 的 对象 写出(即 保存其 状态), 要恢 复的话则 用输 入流ObjectInputStream。
1.4.10. session 共享的几种方式?
1.写客户端 cookie 的方式
当用户登陆成功以后,把网站域名、用户名、密码、token、session 有效时间全部采用 cookie的形式写入到客户端的 cookie 里面,如果用户从一台 Web 服务器跨越到另一台服务器,我们的程序主动去检测客户端的 cookie信息,进行判断,然后提供对应的服务。当然,如果 cookie 过期或者无效,自然就不让用户继续服务了。当然,这种方法的弊端就不言而喻了,比如客户端禁用了cookie 或者 cookie 被黑客窃取了呢?
2.服务器之间 Session 数据同步的方式
假设 Web 服务器 A 是所有用户登陆的服务器,那么当用户验证登陆一下,session 数据就会写到 A 服务器里,那么就可以自己写脚本或者守护进程来自动把 session 数据同步到其他 Web 服务器,那么当用户跳转到其他服务器的时候,那么 session 数据是一致的,自然就能够直接进行服务无须再次登陆了。缺点是,可能会速度慢,不稳定。如果是单向同步的话,登陆服务器出现问题,那么其他服务器也无法服务,当然也可以考虑双向同步的问题。
3.利用 NFS 共享 Session 数据的方式
大致就是有一台公共的 NFS 服务器(Network File Server)做共享服务器,所有的 Web 服务器登陆的时候把 session 数据写到这台服务器上,那么所有的 session 数据其实都是保存在这台 NFS 服务器上的,不论用户访问哪台 Web 服务器,都要来这台服务器获取 session 数据,那么就能够实现共享 session 数据了。缺点是依赖性太强,如果 NFS 服务器挂掉了,那么大家都无法工作了。当然,可以考虑多台 NFS 服务器同步的形式。
4. 利用 Mysql 数据库共享 Session 数据的方式(我们可以使用 Redis)
这个方式与 NFS 的方式类似,也是采用一台 Mysql 服务器做共享服务器,把所有的 session的数据保存到 Mysql 服务器上,所有 Web 服务器都来这台 Mysql 服务器来获取 Session 数据。缺点也是依赖性太强,Mysql无法工作了影响所有的 Web 服务器。当然,可以考虑多台 Mysql 数据库来共享 session,使用同步 Mysql 数据的方式。
5.使用硬件设备
这个算是比较成熟的解决方案了,使用类似 BIG-IP 的负载设备来实现资源共享,那么就能够又稳定又合理的的共享 Session 了。目前很多门户网站采用这种方式。缺点很明显了,就是要收费了,硬件设备肯定需要购买成本的,不过对于专业或者大型应用来讲,是比较合理并且值得的
1.4.11. Ajax 同步与异步的区别
异步传输方式是用的最多的也是默认的方式,他避免了服务器检索给用户带来的时间延迟。在异步传输时候,它只是在后面悄悄进行着,用户仍旧可以做他做的事情,不会给用户任何的等待的感觉。在传输的数据量较大的时候,服务器检索的时间就更长了,但是用户却不知道,用户仍旧专注于页面上面的操作,根本就不知道服务器都干了些什么,就给用户良好的体验。同步传输方式却相反,他就好像是刚刚加载页面的那一刻一样,当发出了同步请求之后,浏览器就在等待,等待服务器检索完毕,返回结果。此时,鼠标会变成等待的形状,提醒我们的用户请求还没有相应,您什么也不能做,我们的用户就什么也干不成,能够做的一件事就是——等待……虽然用户已经习惯了等待整改页面的加载,虽然在 ajax 里面同步请求的时间一般不会大于整个页面加载的时间,但是你要知道什么都不做只是在那里被动等待是多么痛苦的一件事情。所以,这个同步请求要慎重使用……
1.4.12. 简单描述一下 List、Map 和 Set 的区别;
1.Set 接口:有两个实现类(HashSet(底层由 HashMap 实现),LinkedHashSet)
2.List 接口:有三个实现类(LinkedList:基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢;
ArrayList:非线程安全的,效率高;基于数组;便于索引,但不便于插入删除
Vector:基于线程安全的,效率低;基于数组; )
3.Map 接口:有三个实现类(HashMap,HashTable,LinkedHashMap,TreeMap)
HashMap:非线程安全,高效,支持 null;
HashTable:线程安全,低效,不支持 null;
LinkedHashMap:非线程安全,是 HashMap 的一个子类,保存了记录的插入顺序
TreeMap:该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序
1.4.13. JAVA 中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
覆盖是 override 方法-重写 重载是 overload按照教科书上的定义,重载就是具有相同函数名,返回类型可以不同,参数个数、顺序、类型不同的函数。我的理解是重载是发生在两个或者是更多的函数具有相同的名字的情况下。
重写就是覆盖父类的方法,和父类有相同返回类型,参数,甚至是抛出的异常,重写方法不能为 private,运用中最典型的就是对接口方法的覆盖。
什么是并发:所谓并发操作是指在同一时间可能有多个用户对同一数据进行读写操作.
并发问题的瓶颈在哪儿,读和写。
怎么解决并发:
所谓并发是指大量用户同一时刻读写同一条数据,那关键点就在于读和写。
> 首先从读的角度来解决:
1、读写分离。
2、配置缓存
3、配置集群
> 从写的角度:
1、从项目的情况来选择合适的队列。
2、配置集群
在解决并发的时候可以举一个形象的例子来阐述什么是并发:比如我们去乘坐地铁,如果不排队是不是就很挤很乱,效率就低啊,那如果排队之后一个一个来效率是不是就提高了,这样就解决了大并发的问题。
那么怎么实现这个队列呢,这就是我们需要考虑的问题,在 java 代码中我们使用的 list集合是不是就是一个队列,循环遍历一个一个取出。而在 redis 中还有一种数据结构就是 list结构,这就是一个队列结构。当然我们也可以将 MQ 作为一个方案,MQ 本来就是处理队列的。怎么取舍,就需要考虑到 redis 和 MQ 的优缺点,然后看需求了。
如果简单点,就用以上回答.
> 详细点见下:
1、确认服务器硬件是否足够支持当前的容量。
普通的 P4 服务器一般最多能支持每天 10 万独立 IP,如果访问量比这个还要大,那么必须首先配置一台更高性能的专用服务器才能解决问题,否则怎么优化都不可能彻底解决性能问题。
2、优化数据库访问。
前台实现完全的静态化,可以完全不用访问数据库。不过对于频繁更新的网站,静态化往往不能满足某些功能。缓存技术是另一个解决方案,将动态数据存储到缓存文件中,动态网页直接调用这些文件,而不必再访问数据库。如果确实无法避免对数据库的访问,那么可以尝试优化数据库的查询SQL.避免使用 Select *from 这样的语句,每次查询只返回自己需要的结果,避免短时间内的大量 SQL 查询。
3、禁止外部的盗链。
外部网站的图片或者文件盗链往往会带来大量的负载压力,因此应该严格限制外部对于自身的图片或者文件盗链。好在目前可以简单地通过 refer 来控制盗链,Apache 自己就可以通过配置来禁止盗链,IIS 也有一些第三方的 ISAPI 可以实现同样的功能。或者使用非技术手段来解决,比如在图片上增加水印。
4、控制大文件的下载。
大文件的下载会占用很大的流量,并且对于非 SCSI 硬盘来说,大量文件下载会消耗 CPU,使得网站响应能力下降。因此,尽量不要提供超过 2M 的大文件下载,如果需要提供,可以将大文件放在另外一台服务器上。
5、使用不同主机分流主要流量
将文件放在不同的主机上,提供不同的镜像供用户下载。比如如果觉得 RSS 文件占用流量大,那么使用 FeedBurner 或者 FeedSky 等服务将 RSS 输出放在其他主机上,这样别人访问的流量压力就大多集中在 FeedBurner 的主机上,RSS 就不占用太多资源了。
6、使用流量分析统计软件。
在网站上安装一个流量分析统计软件,可以即时知道哪些地方耗费了大量流量,哪些页面需要再进行优化。因此,解决流量问题还需要进行精确的统计分析才可以。推荐使用的流量分析统计软件是Google Analytics(Google 分析)。
7、HTML 静态化
效率最高、消耗最小的就是纯静态化的 html 页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统 CMS。信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的 CMS 是必不可少的。
8、图片服务器分离
对于 Web 服务器来说,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,使用独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。在应用服务器和图片服务器上,可以进行不同的配置优化。
9、数据库集群和库表散列
我们在应用程序中按照业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户 ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。
1.1.4. 集群
其实从上面并发的地铁例子,还可以牵扯出另外一个方案,集群。所谓集群其实就相当于我多开了几个收银柜台。那么比如我开放了 10 个进票口,不管我之后可能几个进票口可能出现问题,只要还有一个在正常工作就可以。
1.1.5. 分布式
> 分布式架构,我们必须对比传统架构才能彰显其优势。
①最为明显的一点,在传统的架构中,如果某个功能需要进行维护,那么我们必须停掉整个服务,这对于公司的运营会造成损失。分布式系统在核心功能模块使用单独服务器,维护部分模块不影响用户的其他操作。
②在海量数据处理方面,传统架构显得比较乏力;分布式系统架构采用服务器集群,使用负载均衡,海量数据处理游刃有余!
③在性能(检索)以及维护方面,分布式系统架构也有较为明显的优势。而所谓的分布式,其实也可以用地铁来举例子。地铁进票口和门口的包裹检查,这是两个系统,进票口和包裹检查这是两个模块,互不干,这个就是分布式。两个系统做各自的事情,但是事情是有联系的,组合起来就是一个完整的流程。而且任何一方如果需要维护,并不影响另外一方的运行。
1.1.6. 多线程
1.1.6.1.线程池
创建线程是比较耗费资源的,这时就有了线程池的引入。
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。
线程池的概念类似连接池。
线程池能够带来三个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性
1.1.6.2.java 多线程解决方案
synchronized 关键字主要解决多线程共享数据同步问题,ThreadLocal 主要解决多线程中数据因并发产生不一致问题,二者都用于解决多线程并发访问。
二者的本质区别:
synchronized 是利用锁的机制,使变量或代码在某一时刻只能被一个线程访问。而
ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的共享。而 synchronized 正好相反,它用于在多个线程间共享通信时能够数据共享。synchronized 用于线程间数据共享,而 ThreadLocal 用于线程间数据隔离。
1.1.6.3.什么叫线程死锁?
两个或者多个线程之间相互等待使用对方的锁,导致线程都无法执行,叫做线程死锁。
1.1.6.4.请描述一下进程与线程的区别?
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
1.1.6.5.现在有 T1、T2、T3 三个线程,如何保证 T2 在 T1 执行完后执行,T3 在 T2 执行完后执行?
在 JDK1.5 中,提供了 Condition 对象控制线程状态,可以有针对性的控制线程的等待和唤醒Condition 对象可以创建多个,每个 Condition 可以控制不同类型的线程。这样就能实现让指定类型线程等待或唤醒!
例如:我们可以创建两个 Condition,一个控制生成线程,一个专门控制消费线程!
1.1.6.6.wait 和 sleep 方法有什么区别:
wait 需要使用 notify 或 notifyAll 唤醒,而 sleep 方法它时间到会自动醒。
wait 需要放在同步代码块中。sleep 可以在同步中也可以不在同步中。
wait 在等待的时候会释放锁,sleep 不会释放锁。
wait 可以指定时间也可以不指定时间,但 sleep 必须指定时间。
1.1.7. 权限
一般企业中权限控制有哪几种方式:
1、创建文件夹,将需要控制的相应文件放在该文件夹下,通过判断文件夹名称来控制权限
2、通过 url 地址进行控制,在用户表中添加一列,在这列中添加可以访问的所有路径
3、配置文件管理权限,通过针对不同的角色创建不同的文件,在文件中添加用户可以访问的路径,然后通过配置过滤器来设置权限
4、利用 Spring Security 或者 shiro 等框架实现权限管理
Shiro:
支持认证跨一个或多个数据源(LDAP,JDBC,kerberos 身份等)
执行授权,基于角色的细粒度的权限控制。
增强的缓存的支持。
支持 web 或者非 web 环境,可以在任何单点登录(SSO)或集群分布式会话中使用。
主要功能是:认证,授权,会话管理和加密。
Spring Security:
这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。在 Spring Framework 基础上,Spring Security 充分利用了依赖注入(DI,Dependency Injection)和面向切面技术。
1.1.8. 网站并发量
Nginx 的并发量大致在 3-5 万。
1.1.9. 订单 ID 是怎么生成的?
订单 ID 首先是不能重复的,鉴于此,我们可以使用商品 ID+当前时间戳,或者通过userId+当前时间戳通过 MD5 加密的方式,来获取一个唯一的订单号,主要还是看需求,并保证订单 ID 的唯一性。
1.1.10. 如果用户一直向购物车添加商品怎么办?并且他添加一次你查询一次数据库?互联网上用户那么多,这样会对数据库造成很大压力你怎么办?
配置读写分离,讲数据库分成读库和写库,当查询数据的时候从读库中进行查询,并且可以添加缓存,利用缓存来提高读取效率,增删改操作就使用写库。这样就能很大程度上缓解数据库的压力。
1.1.11. 项目是分布式的,系统与系统之前如何进行通讯。
通信协议和技术有很多,如 web service,EJB,jms,httpclient.
1.2. 框架面试问题
1.2.1. Servlet
通过判断客户端发送的请求是 post 还是 get 方法来确定执行的是 doGet 还是 doPost 方法
Servlet 生命周期:
1、创建时间:当这个 Servlet 程序被外界第一次访问的时候 tomcat 服务器内部会自动的创建这个 Servlet 对象,并调用 init 方法对这个 Servlet 进行初始化
2、服务的时间:当 Servlet 创建初始化完成之后,就开始一直在提供服务,用户通过浏览器只要访问这个 Servlet 程序,那么就一定会调用这个 Servlet 的 service 方法。每次访问都会调用 service 方法。Servlet 中的 service 方法是专门提供出来给用户服务的。
3、销毁的时间:当这个 Servlet 从项目中被移除,或者 tomcat 服务器正常关闭(需要手动的去使用 stoptomcat),才会调用。
1.2.2. Struts
Struts 是一个按 MVC 设计的 web 层的框架,其实它就是一个更大的 servlet,这个 servlet 是
ActionServlet 或者 ActionServlet 的子类,说到底就是把 servlet 给封装了。
Struts 执行流程:
1)客户端提起一个请求
2)这个请求经过一系列的过滤器直到最后一个过滤器 FilterDispatcher(Filter)
3)接着 FilterDispatcher 被调用,FilterDispatcher 询问 ActionMapper 来决定这个请是否需要调用某个 Action
4)如果 ActionMapper 决定需要调用某个 Action,FilterDispatcher 把请求的处理交给ActionProxy
5)ActionProxy 通过 Configuration Manager 询问框架的配置文件,找到需要调用的Action 类
6)ActionProxy 创建一个 ActionInvocation 的实例。
7)ActionInvocation 实例使用命名模式来调用,在调用 Action 的过程前后,涉及到相关拦截器(Intercepter)的调用。
8)一旦 Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果
优点:
1、实现 MVC 模式,结构清晰,开发者只关注业务逻辑的实现。
2、Strust 的标签库提供了丰富的标签可以使用,大大提高了开发效率
3、页面导航:通过一个配置文件,即可把握整个系统各部分之间的联系。
4、提供了 exception 处理机制
5、数据库连接池管理
6、支持 I18N
缺点:
1、Struts 对每一个用户的请求都会创建一个 action,它只允许一个实例去处理所有的请求。
2、测试不方便,struts 的每一个 action 都和 web 层耦合在一起,它的测试依赖于 web 容
器,单元测试也很难,不过有一个 Junit 的扩展工具 Struts TestCase 可以实现单元测试。
3、对Servlet依赖性过强,Structs处理action都必须依赖ServletRequest和ServletResponse,摆脱不了 servlet 容器。
Struts 和 springmvc 的区别:
1、springMVC 的入口是 Servlet,Struts2 的入口是 Filter,两者的实现机制不同
2、SpringMVC 是基于方法设计,传递参数是通过方法形参接收,其实现方式是单例设计模式(也可以改为多例,推荐使用单例),
Struts2 是基于类设计,传递参数是通过类的属性,只能是多例实现,性能上 springMVC更高一些
4、传递参数方面,Struts2 是用类的属性来接收,也就是在多个方法间共享,而 springMVC是基于方法的,多个方法间数据无法共享
5、Struts 是类级别的拦截,一个类对应一个 request 上下文,而 springmvc 是方法级别的拦截,一个方法对应一个 request 上下文,而一个方法又对应一个 url,因此从架构上来说springmvc 更容易实现 restful。
1.2.3. Springmvc
Springmvc 执行流程:
1. 用户向服务器发送请求,请求被 Spring 前端控制 Servelt ---DispatcherServlet 捕获;
2. DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回;
3. DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(...)方法)
4. 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。 在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些
额外的工作:
HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对
象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult或 Error 中
5. Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;
6. 根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring 容器中的 ViewResolver)返回给 DispatcherServlet ;
7. ViewResolver 结合 Model 和 View,来渲染视图
8. 将渲染结果返回给客户端。
1.2.4. Hibernate
优点:
对象/关系数据库映射(ORM),使用时只需要操作对象,完全面向对象的思想。
封装了 jdbc 的底层代码,不需要和 jdbc api 打交道即可直接访问数据库,在大型项目中开发效率高。
封装了很多可操作数据库的方法,不需要写 sql 语句。
提供一二级缓存。
缺点:
执行效率低,很多 sql 语句都是自动生成的,会有冗余语句,优化负责,更消耗内存。
多表和复杂数据的操作比较麻烦。
更适合单一对象的操作,不适合批量操作。
和 mybatis 的比较:
1、MyBatis 容易掌握,而 Hibernate 门槛较高。
2、Mybatis 基于原生的 jdbc,运行速度比 hinernate 更高。
3、针对高级查询,Mybatis 需要手动编写 SQL 语句,以及 ResultMap。而 Hibernate 有良好的映射机制,开发者无需关心 SQL 的生成与结果映射,可以更专注于业务流程。
4、Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
1.2.5. Spring
Spring IOC:
所谓控制反转,举个例子:
遵循 mvc 的开发原则,我们在 web 层创建业务层对象时如下:
UserService userService=new UserService ();
但是这样写死的话不利于扩展和维护。而为了利于扩展功能,因此我们会写一个接口比如 userService,然后再通过父类引用子类对象的多态原则使程序有更好的扩展性:
UserService userService=new UserServiceImpl().但这种写法还是耦合性太高,如果有一天我要更换实现类,还是需要修改原代码,这样就违法了 OCP 原则(open-close 原则,该原则建议不要修改原代码,可以通过添加新的代码,实现新的功能,即对修改关闭,对扩展开放)。
那么为了实现 service 层和 web 层的解耦,我们就使用了工厂方式:
但是如此一来,工厂和实现类又耦合了,那么是否可以使工厂和实现类解耦呢。可以通过反射和配置文件:
而 IOC 能做到什么呢,就是 service 不再利用 web 层去获取,而是通过 spring 容器创建bean 对象来自动的为我们注入。这样的话整个过程就反过来了,以前是 web 层去 new 一个service,现在是 service 主动被设置到 web 层中去了,这就是反转控制的由来。
通过 IOC,我们就可以在不修改任何代码的情况下,就可以进行实现类的切换,当然前提还是必须得写一个 service 的实现类。这样我们只需要在配置文件配置一下就 ok 了,而不需要再一个个的写工厂来获取了。
这就是 IOC 为我们带来的模块的松耦合和应用的便利性。
DI 注入:
依赖注入,在 spring 框架创建 bean 对象时,动态的动态的将依赖对象注入到 bean 组件。
举例:HelloServiceImpl 内部需要依赖 info 属性
传统方法和依赖注入的区别:
IOC 和 DI 的区别:
IOC 控制反转指的是对象的创建权,被反转到了 spring 容器来管理。DI 依赖注入,指spring 容器在创建对象的过程中将对象依赖的属性通过配置文件进行注入。表面是先有控制反转,再有依赖注入。实际是一个事件,两种不同时期的称呼。DI 实际上也可以理解为就是实现 IOC 的方式。就比如将一个 B 对象注入到另外一个 A对象中,即实现了控制反转(不再在 A 对象中创建 B 对象,而是通过配置文件主动配置),也实现了 DI 注入(将 B 对象作为 A 对象的属性,利用配置文件注入到 A 对象中)。
Spring AOP:
AOP 的意思就是面向切面编程,AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码。实际上就是利用动态代理对目标对象中的方法进行增强JDK 的动态代理要求被代理对象必须实现接口。Cglib 则不需要。AOP 的底层实际上是实现了 JDK 和 CGLIB 两种代理机制。如果目标对象实现了接口,那么优先使用 JDK 的动态代理,如果没有实现任何接口,就会使用 CGLIB 库生成目标对象的子类作为代理对象。
1.3. 数据库面试问题
1.3.1. Mysql 的数据库引擎
Innodb 引擎
Innodb 引擎提供了对数据库 ACID 事务的支持,并且实现了 SQL 标准的四种隔离级别,关于数据库事务与其隔离级别的内容请见数据库事务与其隔离级别这篇文章。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于 MySQL 后台的完整数据库系统,MySQL 运行时 Innodb 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持 FULLTEXT 类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE 时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用 Innodb 引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个 SQL 语句时 MySQL 不能确定要扫描的范围,InnoDB 表同样会锁全表。
MyIASM 引擎
MyIASM 是 MySQL 默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和 Innodb 不同,MyIASM 中存储了表的行数,于是 SELECT COUNT(*)FROM TABLE 时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么 MyIASM 也是很好的选择。两种引擎的选择大尺寸的数据集趋向于选择 InnoDB 引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB 可以利用事务日志进行数据恢复,这会比较快。主键查询在 InnoDB 引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题,关于这个问题我会在下文中讲到。大批的 INSERT 语句(在每个 INSERT 语句中写入多行,批量插入)在 MyISAM 下会快一些,但是 UPDATE 语句在 InnoDB 下则会更快一些,尤其是在并发量大的时候。
1.3.2. SQL 优化
1. 对操作符的优化 尽量不采用不利用索引的操作符
如:in ,not in , is null, is not null,<> like 等
2. select count(*) from table;这样不带任何条件的 count 会引起全表扫描,并且没有任何
业务意义,是一定要杜绝的。
3. 使用“临时表”暂存中间结果
部分 UPDATE、SELECT 语句 写得很复杂(经常嵌套多级子查询)——可以考虑适当拆成几步,先生成一些临时数据表,再进行关联操作。简化 SQL 语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在 tempdb 中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。
4. 排序
避免使用耗费资源的操作,带有 DISTINCT,UNION,MINUS,INTERSECT,ORDER BY 的 SQL语句会启动 SQL 引擎 执行,耗费资源的排序(SORT)功能. DISTINCT 需要一次排序操作,而其他的至少需要执行两次排序
1.3.3. 如何快速向表中插入 100 万条数据
1、Mysql 中有 LOAD DATA INFILE 语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。
2、批量插入
1.4. java 基础
1.4.1. Error 和 Exception 的区别
Error:表示 Java 程序中存在的那些错误现象,而这些错误现象统一使用 Error 进行描述。在我们的程序中,如果发生 Error 问题,我们不会去处理这样的问题,而是让程序的定义者自己去处理自己程序中的错误。针对 Error 我们不给出程序的针对性处理。
Exception:表示 Java 程序中存在的异常问题。而不是错误问题。而这些异常问题,在程序中通过
判断等形式是可以检测并且预防的。而针对这些异常问题,程序员在写代码的时候一旦发生必须给出有效的解决方案。
1.4.2. 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么方法可以通知 jvm 进行垃圾回收?
答:对象刚创建,GC 就开始监控这个对象的地址、大小以及使用情况。通常,GC 采用有向图的方式记录和管理(heap)堆中所有对象。通过这种方式确定哪些对象时可达的,哪些是不可达的,当确定一些对象不可达,GC 就有责任回收这些内存空间。可以。手动执行 System.gc(),通知 GC 运行,但是 java 语言规范并不保证 gc 一定运行。
1.4.3. 抽象类和接口的区别?什么时候用抽象类,什么时候用接口?
答:
含有 abstract 修饰符的类就是抽象类,抽象类不能创建实例对象。含有抽象方法的类必须是抽象类,抽象类中可以有非抽象方法。
子类在继承抽象类时,必须重写抽象类的所有抽象方法。不能有抽象构造方法和抽象静态方法。如果子类没有实现抽象类中的所有抽象方法,那么子类也必须是抽象的。
接口是抽象类的一种特例,接口中的方法都是抽象方法,接口中的变量都要用 public static final 修饰。
两者的区别:
1.抽象类中可以有构造方法,接口中没有构造方法
2.抽象类中有普通的成员变量,接口中没有普通成员变量。
3.抽象类中可以有非抽象方法,接口中不能有非抽象方法。
4.抽象类中抽象方法的访问类型可以是 public ,protected,但是接口中只能是 public
5.抽象类中可以有静态方法,接口中不能有静态方法。
6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,
但是接口中必须是 public static fianl。
7.一个类可以实现多个接口,但是只能继承一个抽象类
应用上的不同:
接口更多的是在系统架构设计方法上发挥作用,主要用于定义模块之间的通信契约和扩展功能。
而抽象类在代码实现方面发挥作用,可以实现代码的重用。比如我们项目中三层开发中的 dao 层和 service 层,会创建一个接口,然后定义一个接口类,主要是考虑到多实现。
在 mybatis 的时候,我们操作数据库,会封装一些方法到一个抽象类中,然后由 service 层来继承这个抽象类,就可以复用里面的方法,方便开发。
1.4.4. 是否可以继承 String 类?
不可以,因为 String 类被 final 修饰。
1.4.5. String 和 StringBuffer 的区别?
1.String 数值不可变,StringBuffer 字符串长度可变。
2.String 覆盖了 equals 和 hashCode 方法,StringBuffer 没有覆盖 equals 和 hashCode 方法。
1.4.6. final、finalize 和 finally 的区别
final 用于声明属性,方法和类的
finally 是异常处理语句结够的一部分,表示总是执行。
finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法
1.4.7. 多线程有几种实现方法?同步有几种实现方法?
多线程有两种实现方法:继承 Thread 类和实现 Runnable 接口。
1.4.8. 简述一下线程的几种可用状态;
线程在执行过程中,可以处于下面几种状态:
1、就绪(Runnable):线程准备运行,不一定立马就能开始执行。
2、运行中(Running):进程正在执行线程的代码。
3、等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。,
4、睡眠中(Sleeping):线程被强制睡眠。
5、I/O 阻塞(BlockedonI/O):等待 I/O 操作完成。
6、同步阻塞(BlockedonSynchronization):等待获取锁。
7、死亡(Dead):线程完成了执行。
1.4.9. 什么是 java 序列化,如何实现 java 序列化?或者请解释 serializable 的作用.
序列化:就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现 Serializable 接口,然后使用一个输出(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象,接着使用 ObjectOutputStream 对象的writeObject(Object obj)方 法就 可以 将参数 为 obj 的 对象 写出(即 保存其 状态), 要恢 复的话则 用输 入流ObjectInputStream。
1.4.10. session 共享的几种方式?
1.写客户端 cookie 的方式
当用户登陆成功以后,把网站域名、用户名、密码、token、session 有效时间全部采用 cookie的形式写入到客户端的 cookie 里面,如果用户从一台 Web 服务器跨越到另一台服务器,我们的程序主动去检测客户端的 cookie信息,进行判断,然后提供对应的服务。当然,如果 cookie 过期或者无效,自然就不让用户继续服务了。当然,这种方法的弊端就不言而喻了,比如客户端禁用了cookie 或者 cookie 被黑客窃取了呢?
2.服务器之间 Session 数据同步的方式
假设 Web 服务器 A 是所有用户登陆的服务器,那么当用户验证登陆一下,session 数据就会写到 A 服务器里,那么就可以自己写脚本或者守护进程来自动把 session 数据同步到其他 Web 服务器,那么当用户跳转到其他服务器的时候,那么 session 数据是一致的,自然就能够直接进行服务无须再次登陆了。缺点是,可能会速度慢,不稳定。如果是单向同步的话,登陆服务器出现问题,那么其他服务器也无法服务,当然也可以考虑双向同步的问题。
3.利用 NFS 共享 Session 数据的方式
大致就是有一台公共的 NFS 服务器(Network File Server)做共享服务器,所有的 Web 服务器登陆的时候把 session 数据写到这台服务器上,那么所有的 session 数据其实都是保存在这台 NFS 服务器上的,不论用户访问哪台 Web 服务器,都要来这台服务器获取 session 数据,那么就能够实现共享 session 数据了。缺点是依赖性太强,如果 NFS 服务器挂掉了,那么大家都无法工作了。当然,可以考虑多台 NFS 服务器同步的形式。
4. 利用 Mysql 数据库共享 Session 数据的方式(我们可以使用 Redis)
这个方式与 NFS 的方式类似,也是采用一台 Mysql 服务器做共享服务器,把所有的 session的数据保存到 Mysql 服务器上,所有 Web 服务器都来这台 Mysql 服务器来获取 Session 数据。缺点也是依赖性太强,Mysql无法工作了影响所有的 Web 服务器。当然,可以考虑多台 Mysql 数据库来共享 session,使用同步 Mysql 数据的方式。
5.使用硬件设备
这个算是比较成熟的解决方案了,使用类似 BIG-IP 的负载设备来实现资源共享,那么就能够又稳定又合理的的共享 Session 了。目前很多门户网站采用这种方式。缺点很明显了,就是要收费了,硬件设备肯定需要购买成本的,不过对于专业或者大型应用来讲,是比较合理并且值得的
1.4.11. Ajax 同步与异步的区别
异步传输方式是用的最多的也是默认的方式,他避免了服务器检索给用户带来的时间延迟。在异步传输时候,它只是在后面悄悄进行着,用户仍旧可以做他做的事情,不会给用户任何的等待的感觉。在传输的数据量较大的时候,服务器检索的时间就更长了,但是用户却不知道,用户仍旧专注于页面上面的操作,根本就不知道服务器都干了些什么,就给用户良好的体验。同步传输方式却相反,他就好像是刚刚加载页面的那一刻一样,当发出了同步请求之后,浏览器就在等待,等待服务器检索完毕,返回结果。此时,鼠标会变成等待的形状,提醒我们的用户请求还没有相应,您什么也不能做,我们的用户就什么也干不成,能够做的一件事就是——等待……虽然用户已经习惯了等待整改页面的加载,虽然在 ajax 里面同步请求的时间一般不会大于整个页面加载的时间,但是你要知道什么都不做只是在那里被动等待是多么痛苦的一件事情。所以,这个同步请求要慎重使用……
1.4.12. 简单描述一下 List、Map 和 Set 的区别;
1.Set 接口:有两个实现类(HashSet(底层由 HashMap 实现),LinkedHashSet)
2.List 接口:有三个实现类(LinkedList:基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢;
ArrayList:非线程安全的,效率高;基于数组;便于索引,但不便于插入删除
Vector:基于线程安全的,效率低;基于数组; )
3.Map 接口:有三个实现类(HashMap,HashTable,LinkedHashMap,TreeMap)
HashMap:非线程安全,高效,支持 null;
HashTable:线程安全,低效,不支持 null;
LinkedHashMap:非线程安全,是 HashMap 的一个子类,保存了记录的插入顺序
TreeMap:该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序
1.4.13. JAVA 中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
覆盖是 override 方法-重写 重载是 overload按照教科书上的定义,重载就是具有相同函数名,返回类型可以不同,参数个数、顺序、类型不同的函数。我的理解是重载是发生在两个或者是更多的函数具有相同的名字的情况下。
重写就是覆盖父类的方法,和父类有相同返回类型,参数,甚至是抛出的异常,重写方法不能为 private,运用中最典型的就是对接口方法的覆盖。