JAVA面试常见问题总结(一)

一、Struts2


过滤器:
StrutsPrepareAndExcuteFilter:可以在执行action前后添加自定义过滤器

工作原理:
用户请求HttpServletRequest
经过Filter过滤器
到ActionMapper——到ActionProxy——通过ConfigurationManager——查找struts.xml中具体的action类
通过ActionProxy代理创建反向实例
再经过拦截器Interceptor
到具体的Action
返回Result
经过视图(JSP)
再经过拦截器Interceptor
通过HttpServletResponse返回一个用户实例显示

核心文件:
web.xml
struts.xml:
1)全局属性;
2)用户请求和响应Action之间的对应关系;
3)Action可能用到的参数和返回结果;
4)拦截器的配置

八个要点:
1)访问Servlet API
三种方式访问:
1、ActionContext;
2、实现***Aware接口;
3、ServletActionContext;

2)Action搜索顺序
1、判断package存在:直接找action,如果action不存在则去默认namespace的package中去找。都找不到Action则报错。
2、判断package不存在:检查上一级路径,重复上一步骤。

3)动态方法调用
1、指定method,不建议使用:


2、感叹号方式,不建议使用:


3、通配符方式:

/user/{1}.jsp
///user/addUser.jsp
/user/{1}.jsp
访问方式:
http:localhost:8080/YanJiu/user_addUser.action
http:localhost:8080/YanJiu/user_delUser.action

也可以这样写:

4)指定多个配置文件


5)默认Action
"index"名称要对应


/index.jsp


6)Struts2后缀



7)接受参数
1、使用Action的属性接受
2、使用DomainModel的属性接受
3、使用ModelDriven接受
4、request

8)处理结果类型

二、Bean


生命周期:
1)Bean的建立
容器寻找Bean的定义信息并将其实例化。
    2)属性注入
使用依赖注入,Spring按照Bean定义信息配置Bean所有属性
    3)BeanNameAware的setBeanName()
如果Bean类有实现org.springframework.beans.BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。
4)BeanFactoryAware的setBeanFactory()
如果Bean类有实现org.springframework.beans.factory.BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身。
5)BeanPostProcessors的ProcessBeforeInitialization()
如果有org.springframework.beans.factory.config.BeanPostProcessors和Bean关联,那么其postProcessBeforeInitialization()方法将被将被调用。
6)initializingBean的afterPropertiesSet()
如果Bean类已实现org.springframework.beans.factory.InitializingBean接口,则执行他的afterProPertiesSet()方法
7)Bean定义文件中定义init-method
可以在Bean定义文件中使用"init-method"属性设定方法名称例如:
如果有以上设置的话,则执行到这个阶段,就会执行initBean()方法
8)BeanPostProcessors的ProcessaAfterInitialization()
如果有任何的BeanPostProcessors实例与Bean实例关联,则执行BeanPostProcessors实例的ProcessaAfterInitialization()方法

三、常用注解


1)@Controller对应表现层的Bean,也就是Action,例如:
@Controller
@Scope("prototype")
public class UserAction extends BaseAction{
……
}

@Scope("prototype")表示将Action的范围声明为原型,可以利用容器的scope="prototype"来保证每一个请求有一个单独的Action来处理,避免struts中Action的线程安全问题。

2)@Service对应的是业务层Bean,例如:
@Service("userService")
public class UserServiceImpl implements UserService {
………
}
@Service("userService")注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService",这样当Action需要使用UserServiceImpl的的实例时,
就可以由Spring创建好的"userService",然后注入给Action:在Action只需要声明一个名字叫“userService”的变量来接收由Spring注入的"userService"即可,具体代码如下:
// 注入userService
@Resource(name = "userService")
private UserService userService;
private UserService userService;
注意:在Action声明的“userService”变量的类型必须是“UserServiceImpl”或者是其父类“UserService”,否则由于类型不一致而无法注入,由于Action中的声明的“userService”变量
使用了@Resource注解去标注,并且指明了其name = "userService",这就等于告诉Spring,说我Action要实例化一个“userService”,你Spring快点帮我实例化好,然后给我,
当Spring看到userService变量上的@Resource的注解时,根据其指明的name属性可以知道,Action中需要用到一个UserServiceImpl的实例,此时Spring就会把自己创建好的名字叫做
"userService"的UserServiceImpl的实例注入给Action中的“userService”变量,帮助Action完成userService的实例化。

3)@Repository对应数据访问层Bean ,例如:
@Repository(value="userDao")
public class UserDaoImpl extends BaseDaoImpl {
………
}
@Repository(value="userDao")注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。
当Service需要使用Spring创建的名字叫“userDao”的UserDaoImpl实例时,就可以使用@Resource(name = "userDao")注解告诉Spring,Spring把创建好的userDao注入给Service即可。
// 注入userDao,从数据库中根据用户Id取出指定用户时需要用到
@Resource(name = "userDao")
private BaseDao userDao;

4)@value
在db.properties中配置的db.driverclass=com.mysql.jdbc.Driver,则在我们的bean的某个set方法上可以用@Value("${db.driverclass}")来完成注入,

5)@Transactional,spring 事务注解
默认遇到throw new RuntimeException("...");会回滚
需要捕获的throw new Exception("...");不会回滚
// 指定回滚
@Transactional(rollbackFor=Exception.class) 
public void methodName() {
// 不会回滚
throw new Exception("...");

//指定不回滚
@Transactional(noRollbackFor=Exception.class)
public ItimDaoImpl getItemDaoImpl() {
// 会回滚
throw new RuntimeException("注释");

@Transactional(propagation=Propagation.REQUIRED) //如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)//容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) //不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)//必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) //必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) //如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

/*
public void methodName(){
  // 本类的修改方法 1
  update();
  // 调用其他类的修改方法
  otherBean.update();
  // 本类的修改方法 2
  update();
}
other失败了不会影响 本类的修改提交成功
本类update的失败,other也失败
*/
@Transactional(propagation=Propagation.NESTED) 
@Transactional (propagation = Propagation.REQUIRED,readOnly=true) // readOnly=true只读,不能更新,删除 
@Transactional (propagation = Propagation.REQUIRED,timeout=30)// 设置超时时间
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)// 设置数据库隔离级别

四、事务管理

事务的传播行为类型:
1)PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
2)PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
3)PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。
4)PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。
5)PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6)PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
7)PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。

PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED区别:
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务,
它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 
另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 
如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 
而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back. 

事务的隔离级别:
1)ISOLATION_DEFAULT: 这是一个PlatfromTr ansactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
另外四个与JDBC的隔离级别相对应
2)ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
3)ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。SQLserver默认。
4)ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
5)ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。


事物属性:
原子性(atomicity):事务中的所有动作要么都发生,要么都不发生。
一致性(consistency):事务将数据库从一种一致状态转变为下一种一致状态。
隔离性(isolation):一个事务的影响在该事务提交前对其他事务都不可见。
持久性(durability):事务一旦提交,其结果就是永久性的。

五、悲观锁和乐观锁 


1)悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,
这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,
都是在做操作之前先上锁。
2)乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断
一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。

六、接口、抽象类


区别:
1)接口表示这个对象能做什么,抽象类表示这个对象是什么。
2)接口定义方法,抽象类可以实现方法。
3)接口基本数据类型是static和final,抽象类不是。
4)接口可以多继承,抽象类不行。
5)一个类只能继承一个类,但可以实现多个接口实现多继承。
6)实现接口的一定要实现接口里定义的所有方法,实现抽象类可以有选择的重写需要用到的方法。
7)接口是公开的,里面不能有私有的变量和方法,抽象类可以有。

七、ArrayList、LinkedList


区别:
1)ArrayList采用数组形式存放对象是物理顺序;LinkedList存放对象是单独的,存放指向下一个对象的索引。
2)get()和set()方法,ArrayList速度快,查询快。
3)add()和remove()方法,LinkedList速度快,插入删除快。单条插入删除ArrayList速度快。
4)栈先进后出;队列先进先出。


八、Hashtable和HashMap


区别:
1)都实现了Map接口,HashMap是Hashtable的轻量级实现。
2)HashMap允许一条空的key,一条以上空的value,Hastable不允许。
3)HashMap非线程安全,效率高。
4)HashMap方法是synchronize的,多个线程访问时

总结:


1)如果线程要求安全,使用Vector,Hashtable
2)如果不要求线程安全,应使用ArrayList,LinkedList,HashMap
3)如果要求键值对,则使用HashMap、Hashtable
4)如果数据很大,又要线程安全考虑Vector

关系图:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMa

九、mybatis

MyBatis主配置文件
MyBatis配置文件中大标签configuration下子标签包括:
configuration
|--- properties
|--- settings
|--- typeAliases
|--- typeHandlers
|--- objectFactory
|--- plugins
|--- environments
|--- |--- environment
|--- |--- |--- transactionManager
|--- |--- |__ dataSource
|__ mappers

十、SQL


1)查询前十:
1、ORACLE  
SELECT * FROM TABLE1 WHERE ROWNUM<=10
2、SQL SERVER
SELECT TOP 10 * FROM TABLE1 
3、 MYSQL
SELECT * FROM TABLE1 LIMIT 10
2)查询重复数据
1、查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断
select * from people
where peopleId in (select   peopleId from   people group by   peopleId having count(peopleId) > 1)
2、删除表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断,只留有rowid最小的记录
delete from people 
where peopleId in (select   peopleId from people group by   peopleId   having count(peopleId) > 1)
and rowid not in (select min(rowid) from   people group by peopleId having count(peopleId )>1)
3、查找表中多余的重复记录(多个字段) 
select * from vitae a
where (a.peopleId,a.seq) in   (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1)
4、删除表中多余的重复记录(多个字段),只留有rowid最小的记录
delete from vitae a
where (a.peopleId,a.seq) in   (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1)
and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)>1)
5、查找表中多余的重复记录(多个字段),不包含rowid最小的记录
select * from vitae a
where (a.peopleId,a.seq) in   (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1)
and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)>1)


补充:


有两个以上的重复记录,一是完全重复的记录,也即所有字段均重复的记录,二是部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略。
1、对于第一种重复,比较容易解决,使用
select distinct * from tableName
就可以得到无重复记录的结果集。
如果该表需要删除重复的记录(重复记录保留1条),可以按以下方法删除
select distinct * into #Tmp from tableName
drop table tableName
select * into tableName from #Tmp
drop table #Tmp
发生这种重复的原因是表设计不周产生的,增加唯一索引列即可解决。
2、这类重复问题通常要求保留重复记录中的第一条记录,操作方法如下
假设有重复的字段为Name,Address,要求得到这两个字段唯一的结果集
select identity(int,1,1) as autoID, * into #Tmp from tableName
select min(autoID) as autoID into #Tmp2 from #Tmp group by Name,autoID
select * from #Tmp where autoID in(select autoID from #tmp2)
最后一个select即得到了Name,Address不重复的结果集(但多了一个autoID字段,实际写时可以写在select子句中省去此列)

十一、多线程


Java中sleep和wait的区别:
① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
② 锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,
要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间
不到只能调用interrupt()强行打断。
Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
  synchronized(x){ 
 x.notify() 
//或者wait() 
  }

实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。


十二、String,StringBuffer 和 StringBuilder


区别:
1、在执行速度方面的比较:StringBuilder > StringBuffer > String  
2、StringBuffer与StringBuilder,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不像String一样创建一些对象进行操作,所以速度快。
3、StringBuilder:线程非安全的
  StringBuffer:线程安全的
当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下就是我们是在
单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。


总结:
1.如果要操作少量的数据用 = String
    2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
    3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer


十三、ajax


使用Struts2的插件机制:
首先是前台发送Ajax请求:(我这里使用JQuery的post方式)
$("#btnClick").click(function() { 
$.post("hello", {name: "tanzhenyu"}, function(data) { 
alert(data.greeting + ", " + data.name + "!"); 
}, "json"); 
});
Action这样写:
public HelloAction extends ActionSupport { 
private String name;//这里的nam用来接收Ajax的请求数据 
private Map resultMap;//这里的Map用来返回结果JSON数据 
public getName() { 
return name; 

public setName(String name) { 
this.name = name; 

public getResultMap() { 
return resultMap; 

public setResultMap(Map resultMap) { 
this.resultMap = resultMap; 

public String execute() { 
resultMap = new Map<>(); 
resultMap.put("greeting", "Hello"); 
resultMap.put("name", name); 
return Action.SUCCESS; 
}
}
这里注意的是:我们的Map对象不需要手动转成JSON对象,Struts2的JSON插件会帮我们转。

配置文件可以这样写:
 
 
 
resultMap 
 


这里注意的是:extends必须是“json-default”,name为root的param是说明返回时被序列化的对象,值为一个OGNL表达式。


十四、webservice


Axis2

WEB Service 下实现大数据量的传输:
1. 服务器上面取数据,填充数据集,转换为二进制格式.


    public byte[] BinaryUserSelect(ref string err){
        ClearCommand();
        m_commandStringBuilder.Append("SELECT * FROM t_Users ;");
        DataSet dsResult = new DataSet();
        byte[] bArrayResult = null;
        try
        {                              
            dsResult = SqlHelper.ExecuteDataset(m_currentConnectionString, CommandType.Text, m_commandStringBuilder.ToString());
            // 上面都是取数据的,无需关心.二进制压缩数据集是下面一小段
            dsResult.RemotingFormat = SerializationFormat.Binary;
            MemoryStream ms = new MemoryStream();
            IFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, dsResult);
            bArrayResult = ms.ToArray();
            ms.Close();
        }
        catch (Exception ee)
        {
            err = ee.ToString();
        }
        return bArrayResult;        
    }
2. 通过WebService把byte[]格式的数据发送到客户端,这里就是WebService自己的事情了,我们无需关心


3.客户端接收到byte[]格式的数据,对其进行反序列化,得到数据集,进行客户端操作.
public DataSet GetBinaryUserData(){
string err = "";
byte[] bUserData = svc.ByteArrayUserSelect(ref err);
if (err != "")
{
MessageBox.Show(err);
err = "";
return null;
}
// 反序列化的过程
MemoryStream ms = new MemoryStream(bUserData);
IFormatter bf = new BinaryFormatter();
object obj = bf.Deserialize(ms);
DataSet dsResult = (DataSet)obj;
//
ms.Close();
return dsResult;
}

十五、设计模式

1.单例模式


该模式主要目的是使内存中保持1个对象。看下面的例子:


public class Singleton {
//将自身的实例对象设置为一个属性,并加上Static和final修饰符
private static final Singleton instance = new Singleton();
//将构造方法设置成私有形式
private Singleton() {
}
//通过一个静态方法向外界提供这个类的实例
public static Singleton getInstance() {
  return instance;
}


}

2.工厂模式


该模式主要功能是统一提供实例对象的引用。接口。

十六、大数据

查询大数据:

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
7.在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。

更新大数据:

数据库分页:
只查询当前页

十七、防SQL注入


ORDER BY $sortFieldName$ $sortType$,这些参数一定不能是用户输入的。
select * from t_user where name like '%'+#name #+'%

十八、js和jQuery


执行顺序:
head标签中的js,页面中的js(将此段Js放置在body标签之外亦为此顺序),body标签onload事件中的js。
区别:
1、js取的是DOM对象,jQuery取的是封装DOM对象后的jQuery对象。
2、Query对象转成DOM对象:
两种转换方式将一个jQuery对象转换成DOM对象:[index]和.get(index);
(1)jQuery对象是一个数据对象,可以通过[index]的方法,来得到相应的DOM对象。
如:var $v =$("#v") ; //jQuery对象
var v=$v[0]; //DOM对象
alert(v.checked) //检测这个checkbox是否被选中
(2)jQuery本身提供,通过.get(index)方法,得到相应的DOM对象
如:var $v=$("#v"); //jQuery对象
var v=$v.get(0); //DOM对象
alert(v.checked) //检测这个checkbox是否被选中
3、DOM对象转成jQuery对象:
对于已经是一个DOM对象,只需要用$()把DOM对象包装起来,就可以获得一个jQuery对象了。$(DOM对象)
如:var v=document.getElementById("v"); //DOM对象
var $v=$(v); //jQuery对象
转换后,就可以任意使用jQuery的方法了。

十九、数组合并排序


public static void main(String[]args)
{
//创建数组
String[] a = { "0", "3", "2", "1" };
String[] b = { "8", "7", "6", "5", "4" };
String[] c = new String[a.length + b.length];
//复制数据
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
//利用   Arrays排序
Arrays.sort(c);
//遍历展示数据
for(String _var : c)
{
System.out.println(_var);
}
}


你可能感兴趣的:(JAVA面试常见问题总结(一))