Java软件开发工程师的招聘需求虽然没有在鼎盛时期的每天几万条招聘需求,但是不可否认的是java在软件编程界依然是有发言权的,以及招聘数量的!
下面根据各大公司的招聘需求对大家公布的面试题进行了一个总结,希望对大家能够有多帮助。
1.代理模式与装饰者模式的区别:
答:(1)相同点:
①都要与基础对象实现相同的接口;
②都要在自身对象中引入被代理对象或被装饰对象的引用。
(2)不同点:
①装饰者模式定义:动态的将责任附加到被装饰者对象上,用于扩展对象的功能。比继承的 灵活性大,典型的如IO的设计,就是典型的装饰者模式;
②代理模式的定义:对其他对象进行代理,以控制对被代理对象的访问。Spring的为业务层方法生成的代理类,主要是进行一些事物控制等;
③由定义,我们可以看出:装饰的责任是扩展功能;而代理主要是控制访问。
2,.和自动装配相关的注解有哪些?
答:①@Required:该依赖关系必须装配(手动或自动装备),否则将抛出BeanInitializationException异常。
②@Autowired:自动装配,默认按类型进行自动装配。
③@Qualifier:如果按类型自动装配时,有不止一个匹配的类型,那么可以使用该注解指定名字来消除歧义。
3,.如何使用HibernateDaoSupport整合Spring和Hibernate?
答:①在Spring中配置Hibernate的会话工厂(LocalSessionFactoryBean或AnnotationSessionFactoryBean)。
②让DAO的实现类继承HibernateDaoSupport(继承getHibernateTemplate方法来调用模板方法)。
③让Spring来管理Hibernate的事务(推荐使用声明式事务)。,
4.你是如何理解“横切关注”这个概念的?
答:“横切关注”是会影响到整个应用的关注功能,它跟正常的业务逻辑是正交的,没有必然的联系,但是几乎所有的业务逻辑都会涉及到这些关注功能。通知、事务、日志、安全性等关注就是应用的横切关注功能。
5.如何理解Spring AOP中Advice这个概念的?
答:Advice在国内的很多书面资料中都被翻译为“通知”,但是很明显这个翻译无法表达其本质,有少量的读物上将这个词翻译为“增强”,这个翻译是对Advice较为准确的诠释,我们通过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种增强,这种增强可以是前置增强、后置增强、返回后增强、抛异常时增强和包围型增强。
6.什么是ORM?
答:①对象关系映射(Object-Relation Mapping,简称ORM)是一种为解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术;
②ORM是通过使用描述对象和数据库之间映射的元数据(在Java中可以用XML或者注解),将程序中的对象自动持久化到关系数据库中,或者将关系数据库表中的行转换成Java对象,其本质就是将数据从一种形式转换到另一种形式。
7. Struts2 中的默认包 struts-default 有什么作用?
①. struts-default 包是 struts2 内置的,它定义了 struts2 内部的众多拦截器和 Result 类型,
而 Struts2 很多核心的功能都是通过这些内置的拦截器实现,
如:从请求中把请求参数封装到action、文件上传和数据验证等等都是通过拦截器实现的。
当包继承了struts-default包才能使用struts2为我们提供的这些功能。
②. struts-default 包是在 struts-default.xml 中定义,struts-default.xml 也是 Struts2 默认配置文件。
Struts2 每次都会自动加载 struts-default.xml文件。
③. 通常每个包都应该继承 struts-default 包。
8. 说出 struts2 中至少 5 个的默认拦截器
exception;fileUpload;i18n;modelDriven;params;prepare;token;tokenSession;validation 等
9. 如何在 Struts2 中使用 Ajax 功能 ?
①. JSON plugin
②. DOJO plugin
③. DWR plugin
④. 使用 Stream 结果类型.
10. 在 Hibernate 中 Java 对象的状态有哪些 ?
①. 临时状态(transient):不处于 Session 的缓存中。OID 为 null 或等于 id 的 unsaved-value 属性值
②. 持久化状态(persistent):加入到 Session 的缓存中。
③. 游离状态(detached):已经被持久化,但不再处于 Session 的缓存中。
11. Session的清理和清空有什么区别?
清理缓存调用的是 session.flush() 方法. 而清空调用的是 session.clear() 方法.
Session 清理缓存是指按照缓存中对象的状态的变化来同步更新数据库,但不清空缓存;清空是把 Session 的缓存置空, 但不同步更新数据库;
12. load()和get()的区别
①:如果数据库中,没有 OID 指定的对象。通过 get方法加载,则返回的是一个null;通过load加载,则返回一个代理对象,
如果后面代码如果调用对象的某个属性会抛出异常:org.hibernate.ObjectNotFoundException;
②:load 支持延迟加载,get 不支持延迟加载。
13. 开发中主要使用 Spring 的什么技术 ?
①. IOC 容器管理各层的组件
②. 使用 AOP 配置声明式事务
③. 整合其他框架.
14. 简述 AOP 和 IOC 概念
AOP: Aspect Oriented Program, 面向(方面)切面的编程;
Filter(过滤器)也是一种 AOP.
AOP 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
举例通过事务说明.
IOC: Invert Of Control, 控制反转. 也成为 DI(依赖注入)
其思想是反转资源获取的方向. 传统的资源查找方式要求组件向容器发起请求查找资源.
作为回应, 容器适时的返回资源. 而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件,
组件所要做的仅是选择一种合适的方式来接受资源. 这种行为也被称为查找的被动形式
15. 在 Spring 中如何配置 Bean ?
Bean 的配置方式: 通过全类名(反射)、通过工厂方法(静态工厂方法 & 实例工厂方法)、FactoryBean
16. IOC 容器对 Bean 的生命周期:
①. 通过构造器或工厂方法创建 Bean 实例
②. 为 Bean 的属性设置值和对其他 Bean 的引用
③. 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
④. 调用 Bean 的初始化方法(init-method)
⑤. 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
⑥. Bean 可以使用了
⑦. 当容器关闭时, 调用 Bean 的销毁方法(destroy-method)
17. Spring 如何整合 Struts2 ?
整合 Struts2, 即由 IOC 容器管理 Struts2 的 Action:
> 安装 Spring 插件: 把 struts2-spring-plugin-2.2.1.jar 复制到当前 WEB 应用的 WEB-INF/lib 目录下
> 在 Spring 的配置文件中配置 Struts2 的 Action 实例
> 在 Struts 配置文件中配置 action, 但其 class 属性不再指向该 Action 的实现类, 而是指向 Spring 容器中 Action 实例的 ID
18,Struts工作机制?为什么要使用Struts?
工作机制:
Struts的工作流程:
在web应用启动时就会载入初始化ActionServlet,ActionServlet从struts-config.xml文件里读取配置信息,把它们存放到各种配置对象当ActionServlet接收到一个客户请求时,将运行例如以下流程.
(1)检索和用户请求匹配的ActionMapping实例,假设不存在,就返回请求路径无效信息;
(2)假设ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中;
(3)依据配置信息决定是否须要表单验证.假设须要验证,就调用ActionForm的validate()方法;
(4)假设ActionForm的validate()方法返回null或返回一个不包括ActionMessage的ActuibErrors对象, 就表示表单验证成功;
(5)ActionServlet依据ActionMapping所包括的映射信息决定将请求转发给哪个Action,假设对应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法;
(6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给ActionForward对象指向的JSP组件;
(7)ActionForward对象指向JSP组件生成动态网页,返回给客户;
为什么要用:
JSP、Servlet、JavaBean技术的出现给我们构建强大的企业应用系统提供了可能。
但用这些技术构建的系统很的繁乱,所以在此之上。我们须要一个规则、一个把这些技术组织起来的规则,这就是框架,Struts便应运而生。
基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件,
19,Struts的validate框架是怎样验证的?
在struts配置文件里配置详细的错误提示,再在FormBean中的validate()方法详细调用。
20,说下Struts的设计模式
MVC模式: web应用程序启动时就会载入并初始化ActionServler。用户提交表单时。一个配置好的ActionForm对象被创建。并被填入表单对应的数据,ActionServler依据Struts-config.xml 文件配置好的设置决定是否须要表单验证,假设须要就调用ActionForm的Validate()验证后选择将请求发送到哪个Action,假设 Action不存在。ActionServlet会先创建这个对象。然后调用Action的execute()方法。Execute()从 ActionForm对象中获取数据,完毕业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给 ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。,
21,sleep() 和wait() 的区别
Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。需要注意的是,sleep()并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。
总结: sleep()保持对象锁、wait()释放对象锁。22. 描述一下ArrayList和LinkedList各自实现和区别
答:
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
23. Java中的队列都有哪些,有什么区别。
答:阻塞队列与普通队列。区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列.
24. 反射中,Class.forName和classloader的区别
答:Java中Class.forName和classloader都可以用来对类进行加载。
Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName(name,initialize,loader)带参数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象。
25:Java中Runnable和Callable有什么不同?
Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。我的博客有更详细的说明。
26:Java中CyclicBarrier 和 CountDownLatch有什么不同?
CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。点此查看更多信息和示例代码。
编程题
1.给定一个排好序的链表,删除链表中重复的结点,返回链表头指针。
分析:(1)链表中重复的结点不需要保留一个,要全部删除。
(2)因为相同的结点全部要删除,所以我们设定三个指针,node指向当前节点,prev指向前驱,还有一个指向后继结点。一旦遇到node和后继结点相等,就node++,知道没有重复的再移动prev.
(3)注意:头结点也可能有重复,所以也可能被删除,所以需要定义一个root指向头结点。
public class a {
// 结点定义,包括当前结点的值和next指向
private static class ListNode {
private int val;
private ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public String toString() {
return val + "";
}
}
// 删除节点的函数
public static ListNode delete(ListNode head) {
if (head == null)
return null;
if (head.next == null)
return head;
// 定义一个临时的头结点,因为头结点也可能被删除
ListNode root = new ListNode();
root.next = head;
ListNode prev = root;
ListNode node = head;
while (node != null && node.next != null) {
if (node.val == node.next.val) {
//若有连续相同的结点,则node要一直++
while (node.next != null && node.next.val == node.val)
node = node.next;
prev.next = node.next;
} else {
prev.next = node;
prev = prev.next;
}
node = node.next;
}
return root.next;
}
//打印出来删除后的结果
private static void print(ListNode head) {
while (head != null) {
System.out.print(head + "->");
head = head.next;
}
System.out.println("null");
}
public static void main(String[] args) {
// 按照结点的定义新建一个链表
ListNode n1 = new ListNode(1);
ListNode n2 = new ListNode(1);
ListNode n3 = new ListNode(2);
ListNode n4 = new ListNode(2);
ListNode n5 = new ListNode(2);
ListNode n6 = new ListNode(3);
ListNode n7 = new ListNode(5);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
n6.next = n7;
n7.next = null;
//调用delete函数,传入n1的值,当成头结点
ListNode result = delete(n1);
print(result);
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
2.对于一个有序数组,我们通常采用二分查找的方式来定位某一元素,请编写二分查找的算法,在数组中查找指定元素。
给定一个整数数组A及它的大小n,同时给定要查找的元素val,请返回它在数组中的位置(从0开始),若不存在该元素,返回-1。若该元素出现多次,请返回第一次出现的位置。
分析:重点在返回第一次出现的位置。
public class c {
public static int getPos(int[] A, int n, int val) {
int low = 0, high = n - 1, mid;
if (n == 0 || A == null)
return -1;
while (low <= high) {
mid = (low + high) / 2;
//当第一次找出相等的位置后需要继续向前查找,最后返回第一次出现的位置
if (val == A[mid]) {
for(int j = mid; j >= 0; j--) {
if(A[j] != A[mid]) {
mid = j + 1;
break;
}
else if(A[j] == A[mid]) {
mid = j;
continue;
}
}
return mid;
} else if (val < A[mid])
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
public static void main(String[] args) {
int A[] = { 4, 4, 5, 5, 5, 5 };
int val = 4;
int n = A.length;
int result = getPos(A, n, val);
System.out.println(result);
}
}
Java热点新闻