2019-2020年_Java中级开发/高级开发_超凡的面试心经(三)
发布日期:2021-06-29 16:48:16 浏览次数:2 分类:技术文章

本文共 16354 字,大约阅读时间需要 54 分钟。

          2019-2020年_Java中级开发/高级开发_超凡的面试心经(三)

 

                                                                                                                            作者:田超凡

版权所有,转载请注明原作者,仿冒侵权必究法律责任

 

1、HashMap的实现原理?

答:在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到数组中的位置来储存Entry对象。当对象的hashcode相同时,它们在数组中的位置相同,“碰撞”会发生。因为HashMap使用线性链表存储对象,这个Entry会存储在线性链表中。将会调用key的eqauls()方法比较决定是采用覆盖行为(返回 true),还是产生 Entry 链(返回 false),新加入的放在链头,最先加入的放在链尾。

2、HashMap与HashTable的区别?

答:HashMap可以接受null键值和值,而Hashtable则不能。Hashtable是线程安全的,通过synchronized实现线程同步。而HashMap是非线程安全的,但是速度比Hashtable快。

3、ArrayList与LinkedList的区别?

答:两者都实现的是List接口,不同之处在于:

-- ArrayList是基于动态数组实现的,LinkedList是基于链表的数据结构。

-- get访问List内部任意元素时,ArrayList的性能要比LinkedList性能好。LinkedList中的get方法是要按照顺序从列表的一端开始检查,直到另一端

-- 对于新增和删除操作LinkedList要强于ArrayList,因为ArrayList要移动数据

附加:

       LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。

       注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:

List list = Collections.synchronizedList(new LinkedList(…));

4、如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?

答:HashMap默认的负载因子大小为0.75,也就是说,当一个map填满了75%的空间的时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的数组,来重新调整map的大小,并将原来的对象放入新的数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

5、ConcurrentHashMap和Hashtable的区别?

答:它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。

6、Collection 和 Collections的区别?

答:Collection是集合类的上级接口,继承与他的接口主要有Set 和List.

Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

7、简单描述java集合类?

答:java集合类都继承于Collection接口,主要有三种:List、Set和Map。

-- List有子类LinkedList、ArrayList和Vector

ArrayList: 元素单个,效率高,多用于查询 。

Vector: 元素单个,线程安全,多用于查询。

LinkedList: 元素单个,多用于插入和删除

-- Set有子类HashSet、SortedSet和TreeSet

-- Map有子类HashMap、HashTable、LinkedHashMap和WeakHashMap

HashMap: 元素成对,元素可为空。

HashTable: 元素成对,线程安全,元素不可为空。

LinkedHashMap:它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。

WeakHashMap: 是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被。

8、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

答:分几种情况:

-- 其他方法前是否加了synchronized关键字,如果没加,则能。

-- 如果这个方法内部调用了wait,则可以进入其他synchronized方法。

-- 如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。

9、sleep() 和 wait() 有什么区别?

答:sleep就是当前线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。

wait是指在一个已经进入了同步锁的线程内,让自己让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是处于wait状态的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在当前线程中,如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁),值得注意的是,notify方法只是随机通知一个wait状态的线程,notifyAll方法可以通知所有wait该锁的线程。

例子:

  1. public static void main(String[] args) { 
  2.     new Thread(new Thread1()).start(); 
  3.  
  4.     try { 
  5.     Thread.sleep(10); 
  6.     } catch (InterruptedException e) { 
  7.     e.printStackTrace(); 
  8.     } 
  9.  
  10.     new Thread(new Thread2()).start(); 
  11.  
  12.     try { 
  13.     Thread.sleep(10); 
  14.     } catch (InterruptedException e) { 
  15.     e.printStackTrace(); 
  16.     } 
  17.  
  18.     new Thread(new Thread3()).start(); 
  19.  
  20. private static class Thread1 implements Runnable { 
  21. @Override 
  22. public void run() { 
  23. // 由于这里的Thread1和下面的Thread2内部run方法要用同一对象作为监视器,我们这里不能用this,因为在Thread2里面的this和这个Thread1this不是同一个对象。我们用MultiThread.class这个字节码对象,当前虚拟机里引用这个变量时,指向的都是同一个对象。 
  24. synchronized (T.class) { 
  25.     System.out.println("enter thread1..."); 
  26.     System.out.println("thread1 is waiting"); 
  27.     try { 
  28.         // 释放锁有两种方式,第一种方式是程序自然离开监视器的范围,也就是离开了synchronized关键字管辖的代码范围,另一种方式就是在synchronized关键字管辖的代码内部调用监视器对象的wait方法。这里,使用wait方法释放锁。 
  29.         T.class.wait(); 
  30.     } catch (InterruptedException e) { 
  31.         e.printStackTrace(); 
  32.     } 
  33.     System.out.println("thread1 is going on..."); 
  34.     System.out.println("thread1 is being over!"); 
  35.  
  36.  
  37. private static class Thread2 implements Runnable { 
  38. @Override 
  39. public void run() { 
  40. // 由于这里的Thread1和下面的Thread2内部run方法要用同一对象作为监视器,我们这里不能用this,因为在Thread2里面的this和这个Thread1this不是同一个对象。我们用MultiThread.class这个字节码对象,当前虚拟机里引用这个变量时,指向的都是同一个对象。 
  41. synchronized (T.class) { 
  42.     System.out.println("enter thread2..."); 
  43.     System.out.println("thread2 is waiting"); 
  44.     try { 
  45.         // 释放锁有两种方式,第一种方式是程序自然离开监视器的范围,也就是离开了synchronized关键字管辖的代码范围,另一种方式就是在synchronized关键字管辖的代码内部调用监视器对象的wait方法。这里,使用wait方法释放锁。 
  46.         T.class.wait(); 
  47.     } catch (InterruptedException e) { 
  48.         e.printStackTrace(); 
  49.     } 
  50.     System.out.println("thread2 is going on..."); 
  51.     System.out.println("thread2 is being over!"); 
  52.  
  53.  
  54. private static class Thread3 implements Runnable{ 
  55. @Override 
  56. public void run() { 
  57. synchronized (T.class) { 
  58.     System.out.println("enter thread3..."); 
  59.     System.out.println("thread2 notify other thread can release wait status.."); 
  60. //由于notify方法并不释放锁,即使thread2调用下面的sleep方法休息了10毫秒,但thread1仍然不会执行,因为thread2没有释放锁,所以Thread1无法得不到锁。 
  61.     T.class.notify(); 
  62. //   T.class.notifyAll(); 
  63.     System.out.println("thread3 is sleeping ten millisecond..."); 
  64.     try { 
  65.         Thread.sleep(10); 
  66.     } catch (InterruptedException e) { 
  67.         e.printStackTrace(); 
  68.     } 
  69.     System.out.println("thread3 is going on..."); 
  70.     System.out.println("thread3 is being over!"); 
  71.  
  72. }

运行结果:

enter thread1...

thread1 is waiting

enter thread2...

thread2 is waiting

enter thread3...

thread2 notify other thread can release wait status..

thread3 is sleeping ten millisecond...

thread3 is going on...

thread3 is being over!

thread1 is going on...

thread1 is being over!

如果调用的是notify,则结果如上,thread1执行完毕,thread2一直被挂着。

10、Java中的volatile 变量是什么?

答:volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。volatile变量可以保证下一个读取操作会在前一个写操作之后发生。

11、 什么是线程安全?Vector是一个线程安全类吗?

答:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

12、什么是ThreadLocal变量?

答:ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。

13、 Java中interrupted 和 isInterruptedd方法的区别?

答:interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变

14、什么是线程池? 为什么要使用它?

答:创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。

15、有三个线程T1,T2,T3,怎么确保它们按顺序执行?

答:在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

16、什么是服务端包含(Server Side Include)?

答:服务端包含(SSI)是一种简单的解释型服务端脚本语言,大多数时候仅用在Web上,用servlet标签嵌入进来。SSI最常用的场景把一个或多个文件包含到Web服务器的一个Web页面中。当浏览器访问Web页面的时候,Web服务器会用对应的servlet产生的文本来替换Web页面中的servlet标签。

17、HTTP响应的结构是怎么样的?

答:HTTP响应由三个部分组成:

状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果Servlet没有返回状态码,默认会返回成功的状态码HttpServletResponse.SC_OK。

HTTP头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。

主体(Body):它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。

18、什么是cookie?session和cookie有什么区别?

答:cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别:

  • 无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。
  • 在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。

19、使用Spring框架的好处是什么?

答:轻量:Spring 是轻量的,基本的版本大约2MB。

  • 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
  • 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
  • 容器:Spring 包含并管理应用中对象的生命周期和配置。
  • MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
  • 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
  • 异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。

20、解释Spring框架中bean的生命周期?

答:-- Spring容器 从XML 文件中读取bean的定义,并实例化bean。

  • -- Spring根据bean的定义填充所有的属性。
  • -- 如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
  • -- 如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。
  • -- 如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
  • -- 如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
  • -- 如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。
  • -- 如果bean实现了 DisposableBean,它将调用destroy()方法。

21Spring框架的事务管理有哪些优点?

答:-- 它为不同的事务API  如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。

  • -- 它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如
  • -- 它支持声明式事务管理。
  • -- 它和Spring各种数据访问抽象层很好得集成。

22、Spring MVC工作机制?

答: 1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。 2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller. 3.DispatcherServlet请请求提交到目标Controller 4.Controller进行业务逻辑处理后,会返回一个ModelAndView 5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象 6.视图对象负责渲染返回给客户端。

23、Hibernate的一级缓存与二级缓存的区别?

答:一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据。二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库。

 

24、Spring框架中的单例Beans是线程安全的么?

答:Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。

 

25、Spring事务是如何传播的?

答:1) PROPAGATION_REQUIRED ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。

2)PROPAGATION_SUPPORTS ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。

3)PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。

4)PROPAGATION_REQUIRES_NEW ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。

这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。

怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW 级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。

5)PROPAGATION_NOT_SUPPORTED ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板抱起来就可以了。

6)PROPAGATION_NEVER ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。

7)PROPAGATION_NESTED ,字面也可知道,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

26、Spring AOP实现原理?

答:java动态代理。接下来描述动态代理(考生自由发挥)。

27、Spring是如何管理事务的?

答:Spring既支持编程式事务管理(也称编码式事务),也支持声明式的事务管理

-- 编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务中,必须在每个业务操作中包含额外的事务管理代码。

-- 声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

@Transactional属性 

属性

类型

描述

value

String

可选的限定描述符,指定使用的事务管理器

propagation

enum: Propagation

可选的事务传播行为设置

isolation

enum: Isolation

可选的事务隔离级别设置

readOnly

boolean

读写或只读事务,默认读写

timeout

int (in seconds granularity)

事务超时时间设置

rollbackFor

Class对象数组,必须继承自Throwable

导致事务回滚的异常类数组

rollbackForClassName

类名数组,必须继承自Throwable

导致事务回滚的异常类名字数组

noRollbackFor

Class对象数组,必须继承自Throwable

不会导致事务回滚的异常类数组

noRollbackForClassName

类名数组,必须继承自Throwable

不会导致事务回滚的异常类名字数组

 

28、Spring MVC的工作原理?

答:

  1. 客户端请求提交到DispatcherServlet
  2. 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
  3. DispatcherServlet将请求提交到Controller
  4. Controller调用业务逻辑处理后,返回ModelAndView
  5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
  6. 视图负责将结果显示到客户端

29、Spring MVC与Struts有什么区别?

答:

1. 机制:spring mvc的入口是servlet,而struts2是filter,这样就导致了二者的机制不同。   

2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。spring3 mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring3 mvc中,一个方法对应一个request上下文。而struts2框架是类级别的拦截,每次来了请求就创建一个Action,然后调用setter getter方法把request中的数据注入;struts2实际上是通 setter getter方法与request打交道的;struts2中,一个Action对象对应一个request上下文。   

3. 参数传递:struts是在接受参数的时候,可以用属性来接受参数,这就说明参数是让多个方法共享的。   

4. 设计思想上:struts更加符合oop(面向对象编程)的编程思想, spring就比较谨慎,在servlet上扩展。   

5. intercepter的实现机制:struts有自己的interceptor机制,spring mvc用的是独立的AOP方式。这样导致struts的配置文件量还是比spring mvc大,虽然struts的配置能继承,所以我觉得论使用上来讲,spring mvc使用更加简洁,开发效率Spring MVC确实比struts2高。spring mvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上spring3 mvc就容易实现restful url。struts2是类级别的拦截,一个类对应一个request上下文;实现restful url要费劲,因为struts2 action的一个方法可以对应一个url;而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。spring3 mvc的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架方法之间不共享变量,而struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码,读程序时带来麻烦。   

6. 另外,spring3 mvc的验证也是一个亮点,支持JSR303,处理ajax的请求更是方便,只需一个注解@ResponseBody ,然后直接返回响应文本即可。

 

30、简单描述Spring的启动过程?

答:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

31、Mybatis中#与$的区别?

答:1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,类似于PreparedStatmte的动态绑定参数。

2. $将传入的数据直接显示生成在sql中,类似于直接拼SQL。

3. #方式能够很大程度防止sql注入。

4.$方式无法防止Sql注入。

5.$方式一般用于传入数据库对象,例如传入表名。

6.一般能用#的就别用$。

32、简单描述Mybatis缓存?

答:mybaits提供一级缓存,和二级缓存。

-- 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。

-- 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的,是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。

33、使用Mybatis的好处?

答:mybatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写,给程序的维护带来了很大便利。mybatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象,大大简化了Java数据库编程的重复工作。因为mybatis需要程序员自己去编写sql语句,程序员可以结合数据库自身的特点灵活控制sql语句,因此能够实现比hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。

34、Mybatis是如何与java bean进行映射的?

答:使用resultMap关键字映射数据库字段和java字段。比如:

<resultMap id="userResultMap" type="User">

  <id property="id" column="user_id" />

  <result property="username" column="user_name"/>

  <result property="password" column="hashed_password"/>

</resultMap>

type关键字为对应的java bean的路径,使用result把具体的字段绑定到java bean中。

35、Mybatis中如何实现一对一、一对多的?

答:“关联”(association)实现一对一的关系,“集合”(collection)实现一对多的关系。

36、MySQL都有哪些存储引擎,有什么区别?

答:主要存储引擎:MyISAM、InnoDB、MEMORY和MERGE,MySQL5.5以后默认使用InnoDB存储引擎。

-- MyISAM不支持事务、也不支持外键,但其访问速度快,对事务完整性没有要求。 

-- InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是比起MyISAM存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。

-- MEMORY存储引擎使用存在内存中的内容来创建表。每个MEMORY表只实际对应一个磁盘文件。MEMORY类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH索引。但是一旦服务关闭,表中的数据就会丢失掉。

-- MERGE存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同。MERGE表本身没有数据,对MERGE类型的表进行查询、更新、删除的操作,就是对内部的MyISAM表进行的。

37、说说常见的数据库优化?

答:1>创建索引,建索引的字段应满足以下条件:

        -- 字段出现在查询条件中,并且查询条件可以使用索引;

       -- 语句执行频率高,一天会有几千次以上;

        -- 通过字段条件可筛选的记录集很小;

2>使用分页

3>只返回必要字段以减少数据在网络上传输开销

4>使用BATCH操作,减少交互的次数

5>加大fetch_size,这样可以减少结果数据传输的交互次数及服务器数据准备时间,提高性能。

 

转载地址:https://supertian.blog.csdn.net/article/details/108468190 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:boost::gil模块实现cmyka转rgba的测试程序
下一篇:boost::gil::channel_view用法的测试程序

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年05月02日 16时46分21秒