JAVA工程师面试题

本文详细列举了Java工程师面试中常见的问题,包括Java跨平台原理、字符串类对比、集合类的区别与操作、线程相关知识、并发编程概念、数据库操作、HTTP协议解析以及MyBatis框架的使用和优化等。内容涵盖Java基础知识、多线程、数据库理论以及框架实践,是准备Java面试的重要参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、简单讲一下java的跨平台原理
    Java源程序通过编译器(也就是Java平台)编译成Class文件(字节码文件),然后通过Java虚拟机(JVM)翻译成为对应的OS指令;可以让不同的系统都可以去执行此文件。

2、String和StringBuilder的区别, StringBuffer 和 StringBuilder 的区别
    1)String、StringBuffer、StringBuilder它们三个都是来表示和操作字符串的
    2)String是内容不可变的字符串,Stirng底层使用了一个不可变的字符数组(final char[]);而StringBuilder和StringBuffer是内容可以改变的字符串,StringBuilder和StringBuffer底层使用的是可变数组(没有使用final来修饰)----char value[]
    3)拼接字符串的方式不同:String用cancat或者+号拼接;而StringBuffer与StringBuilder用append来进行追加
    4)StringBuilder是线程不安全的,效率较高,而StringBuffer是线程安全的,效率较低(F安D不)
    5)由以上得知:String:适用于少量的字符串操作的情况;StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况;StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

3、List和Set区别?Set集合去重规则?
    List,Set都是继承自Collection接口
    1)List集合特点:元素放入有序,元素可重复 ,可以存储空值且可以重复空
    2)Set集合特点:元素放入无序,元素不可重复(重复元素会覆盖掉),可以存储空值当时不能重复
    3)如果添加对象,一般都需要实现equals和hashcode方法,Set集合去重规则
        3.1、根据equals和hashCode来判断是否是同一个元素
        3.2、向Set集合中添加一个元素;如果equals返回false->添加成功;如果返回true->再去判断它的hashcode是否相同;如果不相同则添加成功;否则添加失败。

4、ArrayList和LinkedList区别
    ArrayList和LinkedList都实现了List接口
    1)ArrayList特点:是基于索引的数据接口,它的底层是数组;所以查询速度快
    2)LinkedList特点:是以元素列表的形式存储它的数据,底层实现是链表;所以插入、删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
    3)LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

5、HashMap和HashTable区别
    1)它们都属于Map接口的类,实现了将惟一键映射到特定的值上。
    2)对外提供的接口不同: Hashtable比HashMap多提供了elments() 和contains() 两个方法。
    3)对Null key 和Null value的支持不同 :HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值;而Hashtable 类似于 HashMap,但是既不支持Null key也不支持Null value。
    4)线程安全性不同 :HashMap线程不安全;Hashtable线程安全
    5) 初始大小和每次扩充容量大小的不同 :Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
    6)速度不同:因为关系到是否空值和线程安全性的关系;所以Hashtable速度比HashMap速度慢;所以Hashtable很少用。

6、ArrayList和Vector有何异同点
    1)Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
    2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。

7、队列和栈是什么,列出它们的区别
    1)规则不同
        1.1、 队列:先进先出(First In First Out)FIFO
        1.2、栈:先进后出(First In Last Out )FILO
    2)对插入和删除操作的限定不同
        2.1、队列:只能在表的一端进行插入,并在表的另一端进行删除;
        2.2、栈:只能在表的一端插入和删除。
    3)遍历数据速度不同
        3.1、队列:基于地址指针进行遍历,而且可以从头部或者尾部进行遍历,但不能同时遍历,无需开辟空间,因为在遍历的过程中不影响数据结构,所以遍历速度要快。
        3.2、栈:只能从顶部取数据,也就是说最先进入栈底的,需要遍历整个栈才能取出来,而且在遍历数据的同时需要为数据开辟临时空间,保持数据在遍历前的一致性。

8、Iterater和ListIterator之间有什么区别
    (1)我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List。
    (2)Iterator只可以向前遍历,而LIstIterator可以双向遍历。
    (3)ListIterator从Iterator接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

9、哪些集合类是线程安全的?为何Map接口不继承Collection接口?
    1)Vector?、HashTable、ConcurrentHashMap、Statck
    2)尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。

10、遍历一个List、Map有哪些不同的方式
    1)List泛型只有一个;而Map有两个;是以键值对的形式出现的
    遍历一个List:1、使用Iterator或者ListIterator来遍历
             2、因为List底层是数组;所以可以利用普通for循环通过List大小(List.size())来逐个遍历。
             3、使用foreach来遍历
    遍历一个Map:1、同时得到key与value的值使用EntrySet方式
                2、使用KeySet,遍历key,然后通过map.get(key)获得对应的value
                  3、如果只需要value,可以:
            1. 使用EntrySet遍历value,同上面的方法一
            2. 通过keySet遍历key,然后通过map.get(key)得到value,同上面的方法二
            3. 通过Map对象“点”values直接遍历value

11、HashSet内部实现机制?HashMap内部实现机制?
    1)对于HashSet而言,它是基于HashMap实现的,(HashSet底层使用HashMap来保存所有元素,因此HashSet的实现比较简单,相关HashSet的操作),基本上都是直接调用底层HashMap的相关方法来完成。
    2)HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。

12、集合排序方式(不同的集合类型排序方式)
    1)第一种称为自然排序,参与排序的对象需实现comparable接口,重写其compareTo()方法,方法体中实现对象的比较大小规则.
    2)第二种叫定制排序,或自定义排序,需编写匿名内部类,先new一个Comparator接口的比较器对象c,同时实现compare()其方法;?

13、多线程的几种实现方式,什么是线程安全
    1)继承Thread类创建线程
    2)实现Runnable接口创建线程
    3)实现Callable接口通过FutureTask包装器来创建Thread线程
    4)线程安全理解:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

14、线程的生命周期和各种状态变化
    1)多线程理解:当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。
    2)各种状态之间变化:
        1、 新建状态:当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值
        2、就绪状态:当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行
        3、运行状态:如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态
        4、阻塞状态:当处于运行状态的线程失去所占用资源之后,便进入阻塞状态
            4.1、当占用CPU的时间片刻用完(Thread.yield()),线程进入到阻塞状态;需要再一次被选中调度才能再次进入就绪状态。
            4.2、当遇到了synchronized线程进入阻塞状态,需要拿到对象所标记才能再次进入就绪状态。
            4.3、当线程调用了某个对象的wait()方法时;也会使线程进入阻塞状态;需要用notify()或者notifyall()方法来唤醒。
            4.4、一个线程调用了另一个线程的join()方法时,当前线程进入阻塞状态。等新加入的线程运行结束后会结束阻塞状态,进入就绪状态。
            4.5、调用了Thread的sleep(long millis)。线程睡眠时间到了会自动进入就绪状态。
            4.6、当遇到用户输入时候也会进入阻塞状态;要等待用户结束才能再次就绪状态。
            4.7、线程从阻塞状态只能进入就绪状态,而不能直接进入运行状态,即结束阻塞的线程需要重新进入可运行池中,等待系统的调度。
        5、死亡状态:线程的run()方法正常执行完毕或者线程抛出一个未捕获的异常(Exception)、错误(Error),线程就进入死亡状态。一旦进入死亡状态,线程将不再拥有运行的资格,也不能转换为其他状态。

15、sleep 和 wait 的区别
    1)代表意思不一样:sleep是线程休眠,在调用sleep()方法的过程中,线程不会释放对象锁;时间结束就可以使线程进入运行状态;wait是线程等待,线程会放弃对象锁;需要用notify()或者notifyall()方法去唤醒使线程进入。
    2)所属地方不同:sleep是Thread类的静态方法;而wait是Object的方法。

16、用过线程池吗?它的作用?newCache 和 newFixed 有什么区别
    1)作用:限制系统中执行线程的数量;减少了创建和销毁现成的次数;每个工作线程都有可以被重复利用;可执行多个任务。可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多的内存而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
    2)newSingleThreadExecutor:返回一个包含单线程的Executor,将多个任务交给它时候,它是单线程串行执行所有任务。这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
    3)newFixedThreadPool:返回一个包含指定数目线程的线程池,如果任务数量多于线程数目,那么没有没有执行的任务必须等待,直到有任务完成为止。
    4)newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。可能引起内存不足。?底层是基于ThreadPoolExecutor实现,借助reentrantlock保证并发。?coreSize核心线程数,maxsize最大线程数。?

17、谈谈java 同步机制的 wait 和 notify
    都只能再同步代码块中执行;wait释放锁;notify唤醒。

18、synchronized 修饰静态方法和非静态方法有什么区别
    类锁和对象锁?,那么static获取到的锁,是属于类的锁。而非static方法获取到的锁,是属于当前对象的锁。所以,他们之间不会产生互斥。

**19、导致线程死锁的原因?怎么解除线程死锁。
    死锁问题是多线程特有的问题,它可以被认为是线程间切换消耗系统性能的一种极端情况。在死锁时,线程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。死锁问题是在多线程开发中应该坚决避免和杜绝的问题。?
    一般来说,要出现死锁问题需要满足以下条件:?
        1)互斥条件:一个资源每次只能被一个线程使用。?
        2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。?
        3)不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。?
        4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。?
    只要破坏死锁 4 个必要条件之一中的任何一个,死锁问题就能被解决。

19、字节流和字符流的区别
    1)字节流操作的基本单元为字节,采用ASCII编码;字符流操作的基本单元为Unicode码元。
    2)字节流默认不使用缓冲区;字符流使用缓冲区。
    3)字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元

20、Http状态码有哪些
    200 – 请求成功;301 – 资源(网页等)被永久转移到其它URL;404 – 请求的资源(网页等)不存在;500 – 内部服务器错误
    1**信息:服务器收到请求,需要请求者继续执行操作(100Continue继续。客户端应继续其请求)
    2**成功:操作被成功接收并处理(200 – 请求成功)
    3**重定向:需要进一步的操作以完成请求(301 – 资源(网页等)被永久转移到其它URL)
    4**客户端错误:请求包含语法错误或无法完成请求(404 – 请求的资源(网页等)不存在)
    5**服务器错误:服务器在处理请求的过程中发生了错误(500 – 内部服务器错误)

21、Http协议中TCP三次握手和四次挥手的流程
    三次握手:首先Client端发送连接请求报文,Server段接受连接后回复ACK=1的报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发出ACK=(服务的seq+1)的报文,并分配资源,这样TCP连接就建立了。
        1)连接请求(SYN=1, seq=x):客户端向服务器发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及保存在包头的序列号(Sequence Number)字段里初始序号X。
        2)授予连接(给出响应;SYN=1, ACK=1, seq=y, ACKnum=x+1):服务器给客户端给出响应;服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。
        3)确认连接(ACK=1,ACKnum=y+1):客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
发送完毕后,客户端进入?ESTABLISHED?状态,当服务器端接收到这个包时,也进入?ESTABLISHED?状态,TCP 握手结束。
        
    四次挥手:
        1)发送关闭请求(FIN=1,seq=x):客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。发送完毕后,客户端进入 FIN_WAIT_1 状态。
        2)给出响应(ACK=1,ACKnum=x+1):服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
        3)服务器准备关闭(FIN=1,seq=y):服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
        4)客户端确认关闭(ACK=1,ACKnum=y+1):客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。?
    【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
        答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
    【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
        答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

21、Http协议中Get和Post请求方式的区别
    1)请求报文格式不同:即Get使用Url或cookie传参数,而post使用body传参数
    2)对数据的长度限制不同:get的url会有长度限制(最大2048个字符),而Post数据则可以不受url的限制,可以很大
    3)对数据类型限制不同:GET只允许ASCII码字符;POST没有限制;也可以是二进制数据
    4)安全性不同:post比get安全,因为传递参数在url中不可见;
    5)细分用途不同:get请求主要用于获取、查询资源信息;post请求,更新数据,一般要到form(表单),比较麻烦。
    6)响应速度不同:GET速度快;POST速度慢

22、简单介绍一下AJAX?作用是什么?使用场景?
    1)什么是AjAx:异步的javascript和xml。
    2)作用是什么:通过AjAx与服务器进行数据交换,AjAx可以使用网页实现布局更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
    3)怎么来实现Ajax:XmlHttpRequest对象,使用这个对象可以异步向服务器发送请求,获取响应更新,完成局部更新。?Open send responseText/responseXML?局部响应。
    4)使用场景:登录失败不跳转页面。注册实时提示用户名是否存在。省市区的联动。管理图片服务器,进行延时加载

23、Ajax怎么访问SpringMVC返回JSON格式数据?
    前端传JSON字符串;后端用@ResponseBody接收。比如说当你实现一部登陆验证的时候;如果返回一个JSON数据;那么在success后面定义一个function(data)去接收它。

24、你对servlet的理解?或者servlet是什么?
    1)servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它那五个方法,其中最主要的是两个生命周期方法 init()和destroy(),还有一个处理请求的service();HttpServlet 重写doGet和doPost方法或者可以重写service方法完成对get和post请求的响应。
    2)Servlet必须存放在一个web容器中——这个容器就是一个WEB服务器,tomcat就可以。
    3)把Servlet放到tomcat中以后,tomcat就是是与客户端直接打交道的家伙,他监听了端口;HttpServletRequest请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法去处理;至于具体是get请求还是post请求它里面自己去判断;但是最终还是调用doGet()方法;方法返回一个HttpServletResponse对象,tomcat再把这个对象返回给客户端。

25、servlet的工作原理与生命周期?
    1)工作原理:
        ①Servlet接收和响应客户请求的过程,首先客户发送一个请求,Servlet是调用service()方法对请求进行响应的,service()方法中对请求的方式进行了匹配,选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。
        ②Servlet没有主方法,不能够独立的运行,它的运行需要容器的支持,Tomcat是最常用的JSP/Servlet容器。Servlet运行在Servlet容器中,并由容器管理从创建到销毁的整个过程。这时就有了Servlet的生命周期。
    2)生命周期:
        ①Servlet生命周期分为四个部分:1.加载和实例化 ->2.初始化 - > 3.请求处理 -> 4.销毁
        ②Servlet中三个方法涉及到Servlet的生命周期,分别是init(),service(),destroy()方法。
        ③具体实现:
            1.加载和实例化:当检测到需要Servlet的第一个请求时,读取xml文件找到要加载的servlet类,创建Servlet实例。?加载一般是在运行tomcat容器时来完成,将servlet类加载到tomcat中,或者是客户端发来请求时也可以
            2.初始化:也就是init()方法--->初始化信息一般是读取配置信息、读取初始化参数等,对于每一个servlet实例,inin()方法值被调用一次。就是在用户第一次请求到来的时候执行,如果不配置load-on-startup;那么该servlet会在请求第一次到来初始化。如果配置了初始化的相关操作【load-on-startup】会在服务器启动时候执行;如果设置了多个那么servlet加    载的优先级设置的值越小优先级越高。
            3.请求处理:也就是service()方法--->调用Servlet的service()方法对请求进行处理,在调用该方法之前,init()方法必须先成功执行。每次请求到来都会执行,并且在init()方法之后执行。可以不用重写这个方法,因为父类已经很好的实现了service()方法;会自动根据提交方式的不同调用以之对应的doGet()或者duPost()方法。
            4.销毁:也就是destroy()方法--->作用回收关闭相关资源;在更新代码时候执行。整个Servlet的生命周期结束。一般tomcat关闭,servlet就会被销毁。

26、forward()与redirect()的区别?
    1)从地址栏显示来说 :forward(请求转发)地址栏不变直接跳转要访问的页面;redirect(重定向)地址栏发生改变,变成要访问的那个URL
    2)从安全性来说:forward比redirect安全
    3)从数据共享来说 :forward转发页面和转发到的页面可以共享request里面的数据.redirect不能共享数据.        
    4)从运用地方来说 :forward一般用于用户登陆的时候,根据角色转发到相应的模块;redirect一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
    5)从效率来说 :forward效率比redirect高
    6)从本质上来说:redirect两次请求两次响应;forward一次请求一次响应

27、jsp有哪些内置对象?作用分别是什么?
    1)request对象:request对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。
    2)response对象:response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。
    3)session对象(一次会话):session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。
    4)application对象(属于系统级别):application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。
    5)out 对象:out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。
    6)pageContext 对象:pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。
    7)config 对象:config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。
    8)page 对象:page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。
    9)exception 对象:exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。

28、session工作原理
    同一个session会话的ID相同。用户第一次请求的时候服务器会响应返回一个sessionID;Servlet如果没有从请求中发现sessionID那么会创建一个新的sessionID;sessionID响应的时候交给浏览器,浏览器会将sessionID保存在本地浏览器Cookie中。服务器中有一个Map<String,Map>来保存sessionID和session绑定的数据。且以后每一次发送请求时都会附带这个ID。(Cookie主要就是用来保存数据的)

29、session和cookie的区别?
    1)Cookie和Session有很多相似的地方,都是用来临时存储来访者信息,有很多情况下,使用两者都可以实现某些特定功能,而两者的根本区别是Cookie对象将信息存放在客户端,Session对象存放在服务器端;从生存期上讲,Cookie可以长期保存,而Session的生存期仅仅到会话结束。
    2)Cookie保存在客户端,用户可以看到Cookie文件,并能对Cookie文件进行类似修改、删除的操作,Cookie数据的安全性很难得到保障;而Session数据保存在服务器端,有较好的安全性,若和数据库配合使用,可以使Session数据长期保持,并得到很好的安全性。

30、什么是Spring框架?Spring框架有哪些主要模块?依赖注入的三种方式?
    1)Spring框架:Spring框架是一个为Java应用程序的开发提供了综合、广泛的基础性支持的Java平台。Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。Spring框架本身亦是按照设计模式精心打造,这使得我们可以在开发环境中安心的集成Spring框架,不必担心Spring是如何在后台进行工作的。
    2)Spring主要模块:Spring框架至今已集成了20多个模块。有数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。
    3)SpringIOC依赖注入三种方式:构造器注入、setter方式注入、接口注入

31、Spring中的两大核心机制?
    SpringIOC:
    1)控制反转。由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权进行了转移。控制反转是将对象的生命周期控制权限交由Spring容器管理. 程序不再编写对象创建语句,只使用对象. 这样程序员的精力可以集中在业务开发中. 并且通过Spring容器实现了各层代码之间的解耦, 让代码没有依赖关系. 且实现了代码的可插拔性。所以这叫控制反转。降低了业务对象之间替换的复杂性,使之能够灵活的管理对象。(例如:病人、医生、药三者的关系)
    2)DI 依赖注入 实现:DI依赖注入:说的是创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。
    SpringAOP:面向切面编程;(在传统的面向对象(Object-Oriented Progr amming,OOP)编程中,对垂直切面关注度很高,横切面关注却很少,也很难关注。也就是说,我们利用OOP思想可以很好的处理业务流程,却不能把系统中的某些特定的重复性行为封装在某个模块中。比如在很多的业务中都需要记录操作日志,结果我们不得不在业务流程种嵌入大量的日志记录代码。无论是对业务 代码还是对日志记录代码来说,今后的维护都是非常复杂的。由于系统种嵌入了这种大量的与业务无关的其它重复性代码,系统的复杂性、代码的重复性增加了,从而使bug的发生率也大大的增加。) 那么什么可以解决这个问题呢?这时候,我们需要AOP,关注系统的“截面”,在适当的时候“拦截”程序的执行流程,把程序的预处理和后处理交给某个拦截器 来完成。比如在操作数据库时要记录日志,如果使用AOP的编程思想,那么我们在处理业务流程时不必再考虑日志记录,而是把它交给一个特定的日志记录模块去 完成。这样,业务流程就完全的从其它无关的代码中解放出来,各模块之间的分工更加明确,程序维护也变得容易多了。
    SpringAOP概念:
        1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
        2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
        3)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
        4)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
        5)通知(advice):AOP在特定的切入点上执行的增强处理目前AOP定义了五种通知:
        前置通知(Before advice):在目标方法被调用之前调用通知功能。
        后置通知(After advice):在被通知的方法调用之前和调用之后执行自定义的行为。
        环绕通知(Around Advice):在目标方法成功执行之后调用通知。
        异常通知(After throwing advice):在目标方法抛出异常后调用通知。
        返回后通知(After returning advice):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。

32、Spring中怎么实现AOP?
    1)实现AOP的技术:1.静态代理(编译时增强)AspectJ
                优点: 不改变目标对象代码,实现扩展功能
                 缺点: 代理对象和目标对象,需要实现同一个接口
                       2.动态代理(运行时增强)JDK动态代理与cglib代理
    2)Spring实现AOP:JDK动态代理和CGLIB代理
            JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理;其核心的两个类是InvocationHandler和Proxy。
            动态代理 - 在内存中创建代理对象,让代理对象实现和目标对象相同的接口
             jdk代理 - 使用到jdk内部Proxy类
              接口代理 - 目标对象必须实现接口
              优点: 1.不改变目标对象代码,实现扩展功能
                    2.代理对象和目标对象,不需要实现同一个接口
              缺点: 目标对象,需要实现接口
                 CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。只需要一个接口;不需要目标对象与实现类,通过CGlib库来创建目标对象的代理对象。创建代理对象,其实是在运行创建出来目标对象的子类的对象实例。

33、Spring Bean的作用域之间有什么区别?
    Spring容器中的bean可以分为5个范围
    1)singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
    2)prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
    3)request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
    4)Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
    5)global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

34、Spring 框架中都用到了哪些设计模式?
    1)代理模式—在AOP和remoting中被用的比较多。
    2)单例模式—在spring配置文件中定义的bean默认为单例模式。
    3)模板方法—用来解决代码重复的问题。比如.?RestTemplate,?JmsTemplate,?JpaTemplate。
    4)前端控制器—Spring提供了DispatcherServlet来对请求进行分发。
    5)视图帮助(View Helper?)—Spring提供了一系列的JSP标签,高效宏来辅助将分散的代码整合在视图里
    6)依赖注入—贯穿于BeanFactory?/?ApplicationContext接口的核心理念。
    7)工厂模式—BeanFactory用来创建对象的实例。

35、SpringMVC请求流程
    (1)客户端发送请求到DispathcherServlet;最终调用doDispatch()方法
    (2)前端控制器调用处理器映射器查找Handler;调用getHandler()方法;也就是通过HandlerMapping来获取到HandlerExecutionChain;其中包含执行控制器Controller
    (3)通过HandlerExecutionChain调用处理器适配器【调用getHandlerAdatpter()方法】来获取HandlerAdatpter
    (4)再通过HandlerAdatpter来调用invoke方法,来调用控制器方法,会得到一个ModelAndView对象
    (5)解析视图,通过视图解析器解析视图,调用ViewResolver方法生成View对象,View对象中包含要渲染视图的地址
    (6)渲染视图: 把模型中数据添加到请求作用域,跳转到视图页面【用View调用render()方法】;最后响应给客户端

36、SpringMVC怎么样设定重定向和转发?
    (1)返回的结果使用 redirect  "redirect:/products "【表示重定向;地址栏会跟着变化】
    (2)返回的结果使用 forward  "forward: /products"【表示请求转发;地址栏不会跟着变化】

37、SpringMVC拦截器的作用和使用场景?
    1)拦截器作用:就是拦截WEB请求,类似于Filter。Filter主要针对请求过滤,拦截器主要针对控制器进行拦截。主要是拦截控制器的方法执行(preHandle -> postHandle -> afterCompletion)
    2)定义拦截器
        1. 实现HandlerIntercepter接口
            boolean preHandle()
                在控制器方法执行前,如果返回false,就不会执行控制方法
            void postHandle()
                在控制器方法执行后执行
            void afterCompletion()
                在视图解析并且渲染后执行
        2. 继承HandlerIntercepterAdapter类
    3) 配置拦截器
        1. 拦截所有的请求
            <mvc:intercepters>
                <bean class="拦截器类名"/>
                <ref bean="拦截器名称"/>
            </mvc:intercepters>
        
        2. 自定义拦截的路径
            <mvc:intercepters>
                <mvc:intercepter>
                    <mvc:include path="拦截路径"/>
                    <mvc:exclude path="排除拦截路径"/>
                    <bean class="拦截器类名"/>
                </mvc:intercepter>
            </mvc:intercepters>
    4)拦截器应用
        1.拦截用户是否登录
        2.拦截上传文件类型是否正确

38、拦截器(SpringMVC)和过滤器区别?
    1)过滤器:依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据.
    2)拦截器:依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用.因此可以使用spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
    3)执行顺序:过滤器的运行是依赖于servlet容器的,跟springmvc等框架并没有关系。并且多个过滤器的执行顺序跟web.xml文件中定义的先后关系有关。拦截器的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关。

39、事务的四个基本特征?
    数据库事务transanction正确执行的四个基本要素。ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)。
    1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一
样。
    2)一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
    3)隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行?相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。
这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请?求,使得在同一时间仅有一个请求用于同一数据。
    4)持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

40、Spring的事务的传播特性?
    1)Propagation.REQUIED:如果不存在事务,则开启新的事务,否则使用原来的事务
    2)Propagation.REQUIES_NEW:无论如何都开启新的事务
    3)Propagation.SUPPORTS:如果存在事务,则使用原来的事务,否则使用本地事务(抛出异常无影响)
    4)Propagation.NOT_SUPPORTS:无论如何都使用本地事务(抛出异常无影响)
    5)Propagation.MANDATORY:自身不开启事务,必须在事务环境使用否则报错
    6)Propagation.NEVER:自身不会开启事务,在事务范围使用抛出异常
    7)Propagation.NESTED:和Propagation.REQUIED相似

41、Spring事务的隔离级别?Mysql默认隔离级别是什么?
    1)读未提交 Read Uncommited:意思就是即使一个更新语句没有提交,但是别的事务可以读到这个改变.这是很不安全的。允许任务读取数据库中未提交的数据更改,也称为脏读。
    2)读已提交 Read Commited:可防止脏读,意思就是语句提交以后即执行了COMMIT以后
别的事务就能读到这个改变. 只能读取到已经提交的数据。但是不可重复读
    3)可重复读 Repeated Read:这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
    4)序列化 Serializable:意思是说这个事务执行的时候不允许别的事务并发执行. 完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
    实际开发,隔离级别设置为Read Commited
    Mysql默认隔离级别:可重复读 Repeated Read
    orecle默认隔离级别:读已提交 Read Commited
    
42、什么是幻读,哪种隔离级别可以防止幻读?

    例如:目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。此时,事务B插入一条工资也为5000的记录。这时,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。
    可重复读 Repeated Read:这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
    防止幻读:把隔离级别升级为序列化 Serializable:意思是说这个事务执行的时候不允许别的事务并发执行. 完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

43、简单谈一下MyBatis?
    ORM框架,简化JDBC处理,可以实现关系表和对象之间映射。
    优点:
    1. 易于上手和掌握。
    2. sql写在xml里,便于统一管理和优化。
    3. 解除sql与程序代码的耦合。
    4. 提供映射标签,支持对象与数据库的orm字段关系映射
    5. 提供对象关系映射标签,支持对象关系组建维护
    6. 提供xml标签,支持编写动态sql。
    缺点:
    1. sql工作量很大,尤其是字段多、关联表多时,更是如此。
    2. sql依赖于数据库,导致数据库移植性差。
    3. 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
    4. 字段映射标签和对象关系映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。(比如配置了一对多Collection标签,如果sql里没有join子表或查询子表的话,查询后返回的对象是不具备对象关系的,即Collection的对象为null)
    5. DAO层过于简单,对象组装的工作量较大。
    6.  不支持级联更新、级联删除。
    7. 编写动态sql时,不方便调试,尤其逻辑复杂时。
    8 提供的写动态sql的xml标签功能简单(连struts都比不上),编写动态sql仍然受限,且可读性低。
    9. 若不查询主键字段,容易造成查询出的对象有“覆盖”现象。
    10. 参数的数据类型支持不完善。(如参数为Date类型时,容易报没有get、set方法,需在参数上加@param)
    11. 多参数时,使用不方便,功能不够强大。(目前支持的方法有map、对象、注解@param以及默认采用012索引位的方式)
    12. 缓存使用不当,容易产生脏数据。

44、#{}和${}的区别是什么?
    #{}是预编译处理,${}是字符串替换。Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;Mybatis在处理${}时,就是把${}替换成变量的值。使用#{}可以有效的防止SQL注入,提高系统安全性。

45、MyBatis中如何获取自动生成的(主)键值?
    再Mapper配置文件中:插入语句节点中;把是否生成主键(usegeneratedkeys)设置为true,然后再设置一个去接收主键值的名字(keyproperty=”id”);和对象的成员变量一样吧!
        <insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
                 insert into names (name) values (#{name})
         </insert>
    
46、mapper中如何传递多个参数?
    1)DAO层的函数
        Public UserselectUser(String name,String area);
         对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
    <select id="selectUser"resultMap="BaseResultMap">  
            select *  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}  
    </select>
    2)使用 @param 注解:
        import org.apache.ibatis.annotations.param;
        public interface usermapper {
         user selectuser(@param(“username”) string username,
                    @param(“hashedpassword”) string hashedpassword);
        }
         然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
        <select id=”selectuser” resulttype=”user”>
                     select id, username, hashedpassword
                     from some_table
                     where username = #{username}
                     and hashedpassword = #{hashedpassword}
        </select>
    3)直接传递一个map<String,Object>集合

47、Mybatis一对一、一对多的关联映射和查询 ?
    1)一对一使用association节点进行关系映射
        在resultMap中配置association进行一对一的关系映射
        <resultMap type="Orders" id="OrderWithUserResultMap">
            <!-- 先Order映射配置 -->
            <id column="id" property="id"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createTime"/>
            <result column="note" property="note"/>
            <!-- 再通过association来配置关联的user属性的映射内容 -->
            <!-- 配置用户对象的映射信息 -->
            <!-- <association property="user" javaType="User">
                <result column="username" property="username"/>
                <result column="birthday" property="birthday"/>
                <result column="sex" property="sex"/>
                <result column="address" property="address"/>
            </association> -->
            <!-- 配置用户对象的映射信息 -->
            <!-- 分离ResultMap -->
            <association property="user" javaType="User" resultMap="UserMap"/>
        </resultMap>
    2)一对多使用Conllection节点进行关系映射
        在resultMap中配置conllection进行一对多的关系映射
        <resultMap type="Orders" id="OrderWithUserResultMap">
            <!-- 先Order映射配置 -->
            <id column="id" property="id"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createTime"/>
            <result column="note" property="note"/>
            <!-- 再通过association来配置关联的user属性的映射内容 -->
            <!-- 配置用户对象的映射信息 -->
            <collection property="集合属性名" ofType="别名或者类全路径">
                <id property="id" column="ooid"/>
                <result property="itemsId" column="items_id"/>
                <result property="itemsNum" column="items_num"/>
            </collection>
        </resultMap>

48、MyBatis的一级缓存和二级缓存?
    一级缓存默认是开启的;二级缓存需要用户自己开启。有二级缓存先利用二级缓存
    1)Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。
    2)Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
    3)Mybatis的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个namespace中查询sql可以从缓存中获取数据。二级缓存是可以跨SqlSession的。

49、请介绍关系数据库三范式?
    1)第一范式(1NF):字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式。数据库表中的字段都是单一属性的,不可再分。
    2)第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一的区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。要求实体的属性完全依赖于主关键字。简而言之,第二范式就是非主属性部分依赖于主关键字。
    3)满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
    所以第三范式具有如下特征:
        1,每一列只有一个值
        2,每一行都能区分。
        3,每一个表都不包含其他表已经包含的非主关键字信息。

50、请描述数据库优化的思路?
    1.SQL语句优化
        1)应尽量避免在?where?子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
        2)应尽量避免在?where?子句中对字段进行?null?值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select?id?from?t?where?num?is?null;可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select?id?from?t?where?num=0
        3)很多时候用?exists?代替?in?是一个好的选择
        4)用Where子句替换HAVING?子句
因为HAVING?只会在检索出所有记录之后才对结果集进行过滤
    2.索引优化:看上文索引
    3.数据库结构优化
        1)范式优化: ? ?比如消除冗余(节省空间。。)
        2)反范式优化:比如适当加冗余等(减少join)
        3)拆分表:分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O,一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。
        4)拆分其实又分垂直拆分和水平拆分:
    案例:简单购物系统暂设涉及如下表:
    1.产品表(数据量10w,稳定)
    2.订单表(数据量200w,且有增长趋势)
    3.用户表 (数据量100w,且有增长趋势)
以mysql为例讲述下水平拆分和垂直拆分,mysql能容忍的数量级在百万静态数据可以到千万
    垂直拆分:
        解决问题:表与表之间的io竞争
        不解决问题:单表中数据量增长出现的压力
    方案:
        把产品表和用户表放到一个server上
        订单表单独放到一个server上
    水平拆分:
        解决问题:单表中数据量增长出现的压力
        不解决问题:表与表之间的io争夺
?
    方案:
        用户表通过性别拆分为男用户表和女用户表
        订单表通过已完成和完成中拆分为已完成订单和未完成订单
        产品表 未完成订单放一个server上
        已完成订单表盒男用户表放一个server上
        女用户表放一个server上
    4.服务器硬件优化
        
    
    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值