经典面试题(Java)
1、Spring的优点:
- 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
- 依赖注入:DI——Dependency Injection,反转控制(IOC)最经典的实现。
- 面向切面编程:Aspect Oriented Programming——AOP
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
- 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以 使用XML和Java注解组合这些对象。
一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。
2、 什么是ioc 什么是依赖注入
-
Ioc:inversion of control 控制反转 我们不再手动创建对象 而是将对象的创建权交给IOC容器,需要获取对象时直接从IOC容器中获取
-
DI:dependency injection 依赖注入 IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。
-
IOC开发中怎么实现?两种方式:xml 注解
通过配置Spring.xml文件 或者使用注解 -
ioc XML文件实现方式,注解实现方式
- 在spring.xml文件里 写一个组件并为其赋值 然后在需要的地方 通过实例化IOC容器 根据组件对应的类型或id值 从容器中提取 ClassPathXmlApplicationContext
- 使用注解,需要先在web.xml文件中配置监听器ContextLoaderListener 再在dao层使用注解@Reponsitory service层加注解@Service WEB层加注解@Controller 需要使用对象的时候 使用@Autowired注解 这样IOC容器就会自动创建好这个对象
3、请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?
- 用户发送请求至前端控制器DispatcherServlet;
- DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
- DispatcherServlet 调用 HandlerAdapter处理器适配器;
- HandlerAdapter经过适配调用 具体处理器(Handler,也叫后端控制器); (6)Handler执行完成返回ModelAndView;
- HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
- ViewResolver解析后返回具体View;
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户。
4、Spring MVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?
答:是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。
5、常用注解
注解原理是什么
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
Spring MVC常用的注解有哪些?
- @RequestMapping:用于处理请求 url,映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
- @RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
- @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
6、Spring MVC怎么和AJAX相互调用的?
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :
(1)加入Jackson.jar
(2)在配置文件中配置json的映射
(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。
7、springmvc如何处理异常
三种方法
- 在本类中:在方法上使用@ExceptionHandler 声明属性value={具体的异常类.class}如果不知道是什么类型的可以写成Exception e 因为Exception是所有异常类的父类,但是如果有精确处理,会优先走精确处理
- 外部处理类来处理异常 关键注解:@ControllerAdvice 在方法上使用@ExceptionHandler 声明属性value={具体的异常类.class}
- 在springmvc中配置异常处理解析器
- 若想将错误信息显示在页面上 可以使用ModelAndView模型对象 将错误信息存入其中
异常处理的优先级别:本类>外部处理类>配置文件
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">error2</prop>
<prop key="java.lang.NullPointerException">error2</prop>
</props>
</property>
</bean>
8、SpringMVC怎么样设定重定向和转发的?
- 转发:在返回值前面加"forward:",比如"forward:user.go?name=1"
- 重定向:在返回值前面加"redirect:",比如"redirect:user.go"
9、SpingMVC中函数的返回值是什么?
- 返回值可以有很多类型,有String,ModelAndView。
ModelAndView类把视图和数据都合并的一起的,但一般用String比较好
10、SpringMvc中有个类把视图和数据都合并的一起的,叫什么?
- ModelAndView
11、什么是SpringMVC restful风格,如何在spring mvc实现RESTful 服务
RESTful是一种架构的规范与约束、原则,符合这种规范的架构就是RESTful架构 在RESTful接口中,所有的方法都是返回JSON,没有返回页面的(ModelAndView),因此,所有的方法上都需要添加
- @ResponseBody注解。
- 一个替代的简化方案,是使用 @RestController 代替@Controller。@RestController实际上是一个组合注解,是@Controller和@ResponseBody的组合:
- 导入jackson2包
- 开启注解驱动mvc:annotation-driven/
12、springMVC和struts2的区别有哪些?
- springmvc的入口是一个servlet即前端控制器(DispatchServlet)struts2入口是一个filter过滤器(StrutsPrepareAndExecuteFilter)。
- springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例)
struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。C.springmvc通过参数解析器将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面,Jsp视图解析器默认使用jstl
Struts采用值栈存储请求和响应的数据,通过OGNL存取数据。
13、spring的aop:
AOP(这里的AOP指的是面向切面编程思想,而不是Spring AOP)主要的的实现技术主要有Spring AOP和AspectJ。
-
AspectJ的底层技术。
AspectJ的底层技术是静态代理,即用一种AspectJ支持的特定语言编写切面,通过一个命令来编译,生成一个新的代理类,该代理类增强了业务类,这是在编译时增强,相对于下面说的运行时增强,编译时增强的性能更好。
-
Spring AOP
Spring AOP采用的是动态代理,在运行期间对业务方法进行增强,所以不会生成新类,对于动态代理技术,Spring AOP提供了对JDK动态代理的支持以及CGLib的支持。
JDK动态代理只能为接口创建动态代理实例,而不能对类创建动态代理。需要获得被目标类的接口信息(应用Java的反射技术),生成一个实现了代理接口的动态代理类(字节码),再通过反射机制获得动态代理类的构造函数,利用构造函数生成动态代理类的实例对象,在调用具体方法前调用invokeHandler方法来处理。
CGLib动态代理需要依赖asm包,把被代理对象类的class文件加载进来,修改其字节码生成子类。但是Spring AOP基于注解配置的情况下,需要依赖于AspectJ包的标准注解,但是不需要额外的编译以及AspectJ的织入器,而基于XML配置不需要。
14、@Resource和@AutoWire有什么区别?
- @AutoWire默认按类型装配,默认情况下要求依赖对象必须存在,如果允许为空就设置required属性为false。
- @resource默认按名称去装配,当找不到与名称匹配的Bean时才按照类型去装配。
- @resource注解来源为JDK,其属性为name,type,而@autoWire来源于Spring,其属性为required
- @Resource装配顺序
(1). 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
(2). 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
(3). 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
(4). 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
另外,由于@Autowired是按照类型进行装配的 所以可以用集合去接收,
比如
@Autowired
Collection xxs;//BaseXxx可为接口或父类,装配的为全部的实现类或子类
15、java中常用注解都有哪些,都代表什么意思?
spring中常用注解:
- @Serice 业务层,给类起一个别名,方便注入到其它类当中
- @Controlle 控制层,,于标记在一个类上,使用它标记的类就是一个SpringMVC Controller对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是真正处理请求的处理器。
- @RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
- @RestController 和Controller注解是一样的,唯一不同就是返回的数据格式类型以Json返回,它是@aaa@qq.com两个注解的结合。
- @ResponseBody注解的作用是以json数据格式进行返回。
- @PathVariable注解是用来获取请求uri中,变量的值。
- @RequesParam注解可以对传入参数指定参数名。
- @Resource注解和@AutoWire注解 都是注入,把声明的类注入到当前类中 不需要再new,即可使用声明类中的方法。
springboot中常用注解:
@SpringbootApplication中包含三个注解
- @SpringBootConfiguration注解: 这个注解的作用就是声明当前类是一个配置类,然后Spring会自动扫描到添加了@Configuration的类,读取其中的配置信息,而@SpringBootConfiguration是来声明当前类是SpringBoot应用的配置类,项目中只能有一个。所以一般我们无需自己添加。
- @EnableAutoConfiguration注解: 开启自动配置,告诉SpringBoot基于所添加的依赖,去“猜测”你想要如何配置Spring。比如我们引入了spring-boot-starter-web,而这个启动器中帮我们添加了tomcat、SpringMVC的依赖,此时自动配置就知道你是要开发一个web应用,所以就帮你完成了web及SpringMVC的默认配置了!我们使用SpringBoot构建一个项目,只需要引入所需框架的依赖,配置就可以交给SpringBoot处理了。
- @ComponentScan注解: 配置组件扫描的指令,提供了类似与context:component-scan标签的作用,通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包,而我们的@SpringBootApplication注解声明的类就是main函数所在的启动类,因此扫描的包是该类所在包及其子包。因此,一般启动类会放在一个比较前的包目录中。
16、多线程有几种实现方法?有什么区别?
实现方法:
- 1.继承Thread类
- 实现Runnable接口(Callable接口) 一个类如果实现了Runnable接口或者继承了Thread类,那么它就是一个多线程类,如果是要实现多线程,还需要重写run()方法,所以run() 方法是多线程的入口。
多线程的两种实现方式的区别:
- Thread是Runnable接口的子类,实现Runnable接口的方式解决了Java单继承的局限
Runnable接口实现多线程比继承Thread类更加能描述数据共享的概念
同步的实现方面有两种,分别是synchronized,wait与notify
- wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉 InterruptedException异常。
- notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
- Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
如何避免死锁?
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件,就可以避免死锁发生,一般有以下几种方法:
- 按同一顺序访问对象。
- 避免事务中的用户交互。
- 保持事务简短并处于一个批处理中。
- 使用较低的隔离级别。
- 使用基于行版本控制的隔离级别。
- 使用绑定连接。
17、#{} 和 ${}的区别?
#{}表示一个占位符号
- 通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc
类型转换,#{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType
传输单个简单类型值,#{}括号中可以是 value 或其它名称。
${}表示拼接 sql 串
- 通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换,
可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}可以接收简单类型值或 pojo 属性值,如果parameterType传输单个简单类型值,可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是 value。
18、重定向和转发的异同?
- 重定向是两次请求,转发是一次请求,因此转发的速度要快于重定向
- 重定向之后地址栏上的地址会发生变化,变化成第二次请求的地址,转发之后地址栏上的地址不会变化,还是第一次请求的地址
- 转发是服务器行为,重定向是客户端行为。
- 重定向时的网址可以是任何网址,转发的网址必须是本站点的网址。
19、cookie和session的区别?
- 数据存放位置不同:
cookie数据存放在客户的浏览器上,session数据放在服务器上。 - 安全程度不同:
cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。 - 性能使用程度不同:
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。数据存储 - 大小不同:
单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie,而session则存储与服务端,浏览器对其没有限制。
20、get和post请求的区别?
- Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
- Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
- Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
- Get执行效率却比Post方法好。Get是form提交的默认方法。 get是从服务器上获取数据,post是向服务器传送数据。
21、arrylist和linklist的区别?
- 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,而LinkedList要移动指针一步一步的移动到节点处。(参考数组与链表来思考)
- 对于新增和删除操作add和remove,LinedList比较占优势,只需要对指针进行修改即可,而ArrayList要移动数据来填补被删除的对象的空间。
22、string和stringBuffer,stringBuilder的区别?
- String类的内容一旦声明后是不可改变的,改变的只是其内存的指向,而StringBuffer类的对象内容是可以改变的。
- 对于StringBuffer,不能像String那样直接通过赋值的方式完成对象实例化,必须通过构造方法的方式完成。
- StringBuffer的在进行字符串处理时,不生成新的对象,在内存使用上要优于串类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入,删除等操作,使用StringBuffer要更加适合一些。
- StringBuilder,StringBuffer 之间的最大不同在于 StringBuilder的方法不是线程安全的(不能同步访问)。
- StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类,然而在应用程序要求线程安全的情况下,则必须使用StringBuffer 类。
23、谈一下HashMap的特性?
1.HashMap的特性有哪些?
1.HashMap存储键值对实现快速存取,允许为null。key值不可重复,若key值重复则覆盖。
2.非同步,线程不安全。
3.底层是hash表,不保证有序(比如插入的顺序)
2.谈一下HashMap的底层原理是什么?
基于hashing的原理,jdk8后采用数组+链表+红黑树的数据结构。我们通过put和get存储和获取对象。当我们给put()方法传递键和值时,先对键做一个hashCode()的计算来得到它在bucket数组中的位置来存储Entry对象。当获取对象时,通过get获取到bucket的位置,再通过键对象的equals()方法找到正确的键值对,然后在返回值对象。
添加的时候进行hashCode计算出backet 然后进行entry对象的存储,获取的时候获取到bucket的位置,通过equals方法找到相对应的值然后返回对象。
3.谈一下hashMap中put是如何实现的?
hashMap中是如何添加的
- 首先,如果散列表为空时 ,直接调用resize()方法初始化列表
- 然后就是看发生碰撞没有 没有直接放入散列表中
-
最后发生了碰撞 则进行三个判断
(1):key相同 或者内容相同 ,替换旧值
(2):如果是红黑树结构,就调用树的插入方法
(3):链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之后判断链表个数是否到达变成红黑树的阙值8;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。
(4):如果桶满了大于阀值,则resize进行扩容
4.谈一下hashMap中什么时候需要进行扩容,扩容resize()又是如何实现的?
调用场景:
- 初始化数组table
- 当数组table的size达到阙值时即++size > load factor * capacity 时,也是在putVal函数中
实现过程:(细讲)
- 通过判断旧数组的容量是否大于0来判断数组是否初始化过
否:进行初始化 - 判断是否调用无参构造器,
是:使用默认的大小和阙值
否:使用构造函数中初始化的容量,当然这个容量是经过tableSizefor计算后的2的次幂数
是,进行扩容,扩容成两倍(小于最大值的情况下),之后在进行将元素重新进行与运算复制到新的散列表中 - 概括的讲:扩容需要重新分配一个新数组,新数组是老数组的2倍长,然后遍历整个老结构,把所有的元素挨个重新hash分配到新结构中去。
5.谈一下hashMap中get是如何实现的?
- 对key的hashCode进行hashing,与运算计算下标获取bucket位置,如果在桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果有hash冲突,则利用equals方法去遍历链表查找节点。
6.谈一下HashMap中hash函数是怎么实现的?还有哪些hash函数的实现方式?
- 对key的hashCode做hash操作,与高16位做异或运算。
- 还有平方取中法,除留余数法,伪随机数法。
24、HashMap和HashTable的区别?
相同点:都是存储key-value键值对的。
不同点:
- hashMap允许为空null,hashTable不允许
- map线程不安全,
- .HashMap允许Key-value为null,hashTable不允许;
- .hashMap没有考虑同步,是线程不安全的。hashTable是线程安全的,给api套上了一层
- synchronized修饰;
- HashMap继承于AbstractMap类,hashTable继承与Dictionary类。
- 容量的初始值和增加方式都不一样:HashMap默认的容量大小是16;增加容量时,每次将容量变为"原始容量x2"。Hashtable默认的容量大小是11;增加容量时,每次将容量变为"原始容量x2 + 1";
- 添加key-value时的hash值算法不同:HashMap添加元素时,是使用自定义的哈希算法。Hashtable没有自定义哈希算法,而直接采用的key的hashCode()。
25、传统hashMap的缺点(为什么引入红黑树?)
- JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。
- 当 HashMap中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。
- 针对这种情况,JDK 1.8 中引入了 红黑树(查找时间复杂度为
O(logn))来优化这个问题。
上一篇: 关于WebClient的10篇文章推荐
下一篇: 关于php继承的问题
推荐阅读
-
C#经典排序算法的图文代码详解(下)
-
剑指offer面试题53:在排序数组中查找数字(Java 实现)
-
《剑指offer》面试题53:在排序数组中查找数字
-
剑指Offer系列(java版,详细解析)53.在排序数组中查找数字
-
经典PHP加密解密函数Authcode()修复版代码
-
java软件工程师学php - 4. gettype()/settype()
-
JS经典正则表达式笔试题汇总
-
java 返回图片到页面_html/css_WEB-ITnose
-
java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
-
深入理解Java和MySQL乱码问题_MySQL