一方面可以作为面试别人用,另一方面为自己被面试做准备吧,有些面试题还是有项目实用价值的。至于面试的常用算法模块可以看我的数据结构和算法 分类里的文章。
一 . String、StringBuffer与StringBuilder之间区别?
1 . 三者在执行速度方面的比较:StringBuilder > StringBuffer > String
2 . String <(StringBuffer,StringBuilder) 的原因
String:字符串常量
StringBuffer:字符创变量
StringBuilder:字符创变量
从上面的名字可以看到,String是“字符创常量”,也就是不可改变的对象。String类被设计成final了,因此新建的字符串对象初始化之后不可更改,重新赋值后只是开辟新的内存空间并指向它。对于这句话的理解你可能会产生这样一个疑问 ,比如这段代码:
String s = "abcd";
s = s+1;
System.out.print(s);// result : abcd1
我们明明就是改变了String型的变量s的,为什么说是没有改变呢? 其实这是一种欺骗,JVM是这样解析这段代码的:首先创建对象s,赋予一个abcd,然后再创建一个新的对象s用来执行第二行代码,也就是说我们 之前对象s并没有变化,所以我们说String类型是不可改变的对象了,由于这种机制,每当用String操作字符串时,实际上是在不断的创建新的对象, 而原来的对象就会变为垃圾被GC回收掉,可想而知这样执行效率会有多底。
而StringBuffer与StringBuilder就不一样了,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,这样就不会像String一样创建一些而外的对象进行操作了,当然速度就快了。
3 . 一个特殊的例子:
String str = “This is only a” + “ simple” + “ test”;
StringBuffer builder = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成 str 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,实际上:
String str = “This is only a” + “ simple” + “test”;
其实就是:
String str = “This is only a simple test”;
所以不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String str2 = “This is only a”;
String str3 = “ simple”;
String str4 = “ test”;
String str1 = str2 +str3 + str4;
这个时候jvm会规规矩矩的按照原来的方式去做。
4. StringBuilder与 StringBuffer
StringBuilder:线程非安全的
StringBuffer:线程安全的
当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证 StringBuffer是可以正确操作的。当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不 用StringBuffer的,就是速度的原因。
对于三者使用的总结 : 1.如果要操作少量的数据用 = String
2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
二、VECTOR,ARRAYLIST, LINKEDLIST的区别是什么?
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动 等内存操作,所以索引数据快插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要 差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!
三、HashTable,HashMap和TreeMap的区别是什么?
java为数据结构中的映射定义了一个接口java.util.Map,而HashMap Hashtable和TreeMap就是它的实现类。Map是将键映射到值的对象,一个映射不能包含重复的键;每个键最多只能映射一个一个值。
Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力.
Hashtable 与 HashMap类似,但是主要有6点不同。
1 . HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。
2. HashTable不允许null值,key和value都不可以,HashMap允许null值,key和value都可以。HashMap允许key值只能由一个null值,因为hashmap如果key值相同,新的key, value将替代旧的。
3 .HashTable有一个contains(Object value)功能和containsValue(Object value)功能一样。
4 .HashTable使用Enumeration,HashMap使用Iterator。
5. HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
6. 哈希值的使用不同,HashTable直接使用对象的hashCode。
TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
下面是HashTable,HashMap和TreeMap总结的一个经典例子。
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class HashMaps {
/**
* @param args
*/
public static void main(String[] args) {
Map map = new HashMap();
map.put("a", "aaa");
map.put("b", "bbb");
map.put("c", "ccc");
map.put("d", "ddd");
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println("map.get(key) is :" + map.get(key));
}
Hashtable tab = new Hashtable();
tab.put("a", "aaa");
tab.put("b", "bbb");
tab.put("c", "ccc");
tab.put("d", "ddd");
Iterator iterator_1 = tab.keySet().iterator();
while (iterator_1.hasNext()) {
Object key = iterator_1.next();
System.out.println("tab.get(key) is :" + tab.get(key));
}
TreeMap tmp = new TreeMap();
tmp.put("a", "aaa");
tmp.put("b", "bbb");
tmp.put("c", "ccc");
tmp.put("d", "ddd");
Iterator iterator_2 = tmp.keySet().iterator();
while (iterator_2.hasNext()) {
Object key = iterator_2.next();
System.out.println("tmp.get(key) is :" + tmp.get(key));
}
}
}
输出结果如下:
map.get(key) is :ddd
map.get(key) is :bbb
map.get(key) is :ccc
map.get(key) is :aaa
tab.get(key) is :bbb
tab.get(key) is :aaa
tab.get(key) is :ddd
tab.get(key) is :ccc
tmp.get(key) is :aaa
tmp.get(key) is :bbb
tmp.get(key) is :ccc
tmp.get(key) is :ddd
四、ConcurrentMap和HashMap的区别?
ConcurrentMap是线程安全的,Hashmap是线程不安全的。ConcurrentMap不允许键或值为 null。
五、Tomcat,apache,jboss的区?
Apache全球应用最广发的http服务器,免费,出自apache基金组织,一般用于处理静态文件然后把涉及到动态内容链接转发到后面的web server。
Tomcat应用也算非常广泛的web服务器,支持部分j2ee,免费,出自apache基金组织
JBoss开源的应用服务器,比较受人喜爱,免费(文档要收费)
JBoss内置tomcat做web容器同时支持EJB容器,是完整的J2EE应用服务器
tomcat 只能做jsp和servlet的container
六、post和get的区别?
1 . get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。所以get安全性非常低,post安全性较高。
2 . get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。
七、SESSION, COOKIE区别?
1、 HTTP协议本身是“连接-请求-应答-关闭连接”模式的,是一种无状态协议(HTTP只是一个传输协议);
2、 Cookie规范是为了给HTTP增加状态跟踪用的(如果要精确把握,建议仔细阅读一下相关的RFC),但不是唯一的手段;
3、 所谓Session,指的是客户端和服务端之间的一段交互过程的状态信息(数据);这个状态如何界定,生命期有多长,这是应用本身的事情;
4、 由于B/S计算模型中计算是在服务器端完成的,客户端只有简单的显示逻辑,所以,Session数据对客户端应该是透明的不可理解的并且应该受控于服务端;Session数据要么保存到服务端(HttpSession),要么在客户端和服务端之间传递(Cookie或url rewritting或Hidden input);
5、 由于HTTP本身的无状态性,服务端无法知道客户端相继发来的请求是来自一个客户的,所以,当使用服务端HttpSession存储会话数据的时候客户端的每个请求都应该包含一个session的标识(sid, jsessionid 等等)来告诉服务端;
6、 会话数据保存在服务端(如HttpSession)的好处是减少了HTTP请求的长度,提高了网络传输效率;客户端session信息存储则相反;
7、 客户端Session存储只有一个办法:cookie(url rewritting和hidden input因为无法做到持久化,不算,只能作为交换session id的方式,即a method of session tracking),而服务端做法大致也是一个道理:容器有个session管理器(如tomcat的 org.apache.catalina.session包里面的类),提供session的生命周期和持久化管理并提供访问session数据的 api;
8、 使用服务端还是客户端session存储要看应用的实际情况的。一般来说不要求用户注册登录的公共服务系统(如google)采用 cookie做客户端session存储(如google的用户偏好设置),而有用户管理的系统则使用服务端存储。原因很显然:无需用户登录的系统唯一能够标识用户的就是用户的电脑,换一台机器就不知道谁是谁了,服务端session存储根本不管用;而有用户管理的系统则可以通过用户id来管理用户个人数据,从而提供任意复杂的个性化服务;
9、 客户端和服务端的session存储在性能、安全性、跨站能力、编程方便性等方面都有一定的区别,而且优劣并非绝对(譬如TheServerSide号称不使用HttpSession,所以性能好,这很显然:一个具有上亿的访问用户的系统,要在服务端数据库中检索出用户的偏好信息显然是低效的,Session管理器不管用什么数据结构和算法都要耗费大量内存和CPU时间;而用cookie,则根本不用检索和维护session数据,服务器可以做成无状态的,当然高效);
七、SESSION, COOKIE区别?
Servlet运行在Servlet容器中,其生命周期由容器来管理。Servlet的生命周期通过javax.servlet.Servlet接口中的init()、service()和destroy()方法来表示。
Servlet的生命周期包含了下面4个阶段:
1、 加载和实例化:
Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,它必须要知道所需的Servlet类在什么位置,Servlet容器可以从本地文件系统、远程文件系统或者其他的网络服务中通过类加载器加载Servlet类,成功加载后,容器创建Servlet的实例。因为容器是通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),所以我们在编写Servlet类的时候,不应该提供带参数的构造方法。
2、 初始化
在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次。在初始化期间,Servlet实例可以使用容器为它准备的ServletConfig对象从Web应用程序的配置信息(在web.xml中配置)中获取初始化的参数信息。在初始化期间,如果发生错误,Servlet实例可以抛出ServletException异常或者UnavailableException异常来通知容器。ServletException异常用于指明一般的初始化失败,例如没有找到初始化参数;而UnavailableException异常用于通知容器该Servlet实例不可用。例如,数据库服务器没有启动,数据库连接无法建立,Servlet就可以抛出UnavailableException异常向容器指出它暂时或永久不可用。
3、 请求处理
Servlet容器调用Servlet的service()方法对请求进行处理。要注意的是,在service()方法调用之前,init()方法必须成功执行。在service()方法中,Servlet实例通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。在service()方法执行期间,如果发生错误,Servlet实例可以抛出ServletException异常或者UnavailableException异常。如果UnavailableException异常指示了该实例永久不可用,Servlet容器将调用实例的destroy()方法,释放该实例。此后对该实例的任何请求,都将收到容器发送的HTTP 404(请求的资源不可用)响应。如果UnavailableException异常指示了该实例暂时不可用,那么在暂时不可用的时间段内,对该实例的任何请求,都将收到容器发送的HTTP 503(服务器暂时忙,不能处理请求)响应。
4、 服务终止
当容器检测到一个Servlet实例应该从服务中被移除的时候,容器就会调用实例的destroy()方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中。当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。
九、HTTP 报文包含内容?
包括请求头信息和相应头信息,下面是请求google的请求头信息和响应头信息:
GET / HTTP/1.1
Host: www.google.cn
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:2.0) Gecko/20100101 Firefox/4.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: PREF=ID=9a73bab2a85ddccc:U=2eafb1dbdd669aef:NW=1:TM=1300239801:LM=1300270720:S=1tVaRSRDC1EL4n2-; NID=45=Iwu2b9Z0yToGXIAXtHjMjwb5-QecY1hZ926Fi8hep7sTV_niRUxJTeihp30AdmGEiCqwv-2gEfr60QqIqkrzap9f9xQnB8y63Agmx2C_2hmzxjZnYDScrVNXr-xH3ZjP
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Sun, 02 Jan 2011 20:43:59 GMT
Date: Wed, 30 Mar 2011 06:02:15 GMT
Expires: Wed, 30 Mar 2011 06:02:15 GMT
Cache-Control: private, max-age=0
Vary: Accept-Encoding
X-Content-Type-Options: nosniff
Server: sffe
X-XSS-Protection: 0
Content-Encoding: gzip
Transfer-Encoding: chunked
十、Statement与PreparedStatement的区别,什么是SQL注入,如何防止SQL注入?
大致的原因是:
1、 预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个函数)就会得到执行.这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行。
2、 安全性。使用PreparetStatement可防止injection sql。
注入式SQL攻击,举个简单例子说明:
一般来说登框认证界面的语句是这样的。
logName = (String)request.getParameter("txt_LogName");
logPwd = (String)request.getParameter("txt_LogPwd");
sql = "select useName,usePwd from userTable where useName='"+logName+"' and usePwd='"+logPwd+"'";
conn = DBConn.getConn();
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
这样的标准式代码却存在严重的安全性能。如果我们的登录界面的用户框或是密码框加入[' or '1'='1](不包括[],下同)那么上述的sql就变成了。
select useName,usePwd from userTable where useName='user ' and usePwd='test' or '1'='1'
这样的语句在Statement中是肯定成功执行的,后果是不经认证就登录了。
当然hacker从来不会这样就罢休。如果改成执行这样的,在用户框或密码框加入
test';insert into userTable(useName,usePwd) values('hacker','123');
这样的话,就增加了一个永久帐户了。
更破坏性的是
test'; drop talbe userTable;
,后果想想知道了。这也是为什么大力推荐使用PreparedStatement的主要原因。
但,凡事都有度,正因为PreparedStatement是预编译的,所以在第一次执行时要比Statement慢。而且象Oracle本身就是强大的数据库优化策略,也就是说在执行Statement时,也会优化。例insert into table value('1','2'),数据库会优化成insert into table value(?,?)。这时的Statement跟PreparedStatement就是一样的了。当然有个例外,不是所有的数据库会有此优化策略。
总结一下,就是批量写记录时,绝对使用PreparedStatement。如果是多参数动态查询,一般使用Statement。当然,出于安全方面,使用PreparedStatement比较好。
十一、redirect和forward区别?
1. redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。url地址栏会出现新的地址,而且之前的数据不会默认传输。
2. forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。url地址栏,而且之前的数据会默认传输。
十二、关于JAVA内存模型,一个对象(两个属性,四个方法)实例化100次,现在内存中的存储状态, 几个对象,几个属性,几个方法。
100个对象,一般来说,对象和属性还有方法是一个整体,如果属性和方法不是静态的,那么每个对象都有2属性,4方法
。如果属性是静态的,那么属于类的,永远只存在一份,如果方法是静态的,那么属于类的,永远只存在一份。
十三、谈谈Hibernate的理解,一级和二级缓存的作用,在项目中Hibernate都是怎么使用缓存的 ?
1. Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
2. Hibernate一级缓存指的是session的缓存,它是事务级别的,一般不用特别关心。二级缓存指的是SessionFactory的缓存,由于SessionFactory对象的生命周期和应用程序的整个过程对应,所以具有很大的自我操作空间,而且一般借助第三方库如ehcache来实现。
3. 项目中缓存的使用一般都是指的二级缓存。被缓存的数据一般都是读的多,写的少,而且该数据对读实时性要求不高。
十四、是么java反射机制,都在哪需要反射机制,反射的性能,如何优化?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。一般的用途都做框架级别的才会大量的使用,例如spring这个框架就大量使用了放射机制创建对象,反射的性能自然也不高。至于怎么优化除了使用更高版本的jdk或者使用非官方的jdk外还没想到怎么优化。