上周六去IBM面试了,面试官问了如下一些问题:
1.String 和 StringBuffer 有什么区别?StringBuffer的构造方法中带String的和带int型的有什么不同?
答:字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
StringBuffer是线程安全的可变字符序列,一个类似于 String的字符串缓冲区。
StringBuffer() 构造一个其中不带字符的字符串缓冲区,初始容量为 16 个字符。 |
StringBuffer(CharSequence seq) public java.lang.StringBuilder(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。 |
StringBuffer(int capacity) 构造一个不带字符,但具有指定初始容量的字符串缓冲区。 |
StringBuffer(String str) 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。 |
StringBuilder
。与该类相比,通常应该优先使用
StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。(引用自JDK1.6说明)
2.ArrayList里的Bean怎么实现排序?(比如这个Bean里有name、age,按age大小排序)
答:利用Collections的Sort(List,Comparator),实现Comparator接口里的 compare()比较方法,即可。
3.怎么理解java的多态?
答:在《核心技术卷1》中说:一个对象变量可以指向多种实际类型这种现象称为“多态”。
在Java中,谈论多态就是在讨论方法调用的绑定,绑定就是将一个方法调用同一个方法主体关联起来。在C语言中,方法(在C中称为函数)的绑定是由编译器来实现的,在英文中称为early binding(前期绑定),因此,大家自然就会想到相对应的late binding(后期绑定),这在Java中通常叫做run-time binding(运行时绑定),我个人觉得这样称呼更贴切,运行时绑定的目的就是在代码运行的时候能够判断对象的类型。
通俗来说就是:如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法。
4.java当中的基本类型和包装类型有什么区别?什么时候用包装类型,什么时候用基本类型?
答:java的类型分两部分,基本类型和引用类型。并且,每个基本类型都对应了一个引用的类型,称为装箱基本类型。
如Integer 对应int,Double对应的double.
两者的主要区别有三:
基本类型只有值,而装箱类型则有与他们的值不同的同一性,也就是两个装箱类型可以具有相同的值,有不同的同一性(不同的引用)
基本类型只有功能完备的值,而每个装箱类型除了它对应基本类型的所有功能值外,还有个非功能的值--null
基本类型通常比装箱类型更节省时间和空间。
反复的拆箱和装箱影响性能。
详解参考 点击打开链接
5.Spring的注入有哪几种方式?比如接口注入?
答: spring的三种注入方式:
接口注入(不推荐)
getter,setter方式注入(比较常用)
构造器注入(死的应用)
------------------------------------------------------------------------------------------------------------
关于getter和setter方式的注入:
autowire="defualt"
autowire=“byName”
autowire="bytype"
例如:有如下两个类需要注入
package org.jia;
public class Order {
private String orderNum;
@SuppressWarnings("unused")
private OrderItem orderitem;
public OrderItem getOrderitem() {
return orderitem;
}
public void setOrderitem(OrderItem orderitem) {
this.orderitem = orderitem;
}
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
}
package org.jia;
public class OrderItem {
private String orderdec;
public String getOrderdec() {
return orderdec;
}
public void setOrderdec(String orderdec) {
this.orderdec = orderdec;
}
}
getter&&setter方式第一种注入:<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="orderItem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" >
<!-----注入变量 名字必须与类中的名字一样------->
<property name="orderNum" value="order000007"></property>
<!--注入对象 名字为orderitem,所属的类的应用id为orderItem-->
<property name="orderitem" ref="orderItem"></property>
--></bean>
</beans>
getter&&setter方式第二种注入: byName<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--此时的id就必须与Order.java中所定义的OrderItem的对象名称一样了,不然就会找不到-->
<bean id="orderitem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" autowire="byName">
<property name="orderNum" value="order000007"></property>
</bean>
</beans>
getter&&setter方式第三种注入:byType<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--按照byType注入则就与id没有关系,可以随便定义id !!!但是不能出现多个此类的id-->
<bean id="orderitdfadafaem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" autowire="byType">
<property name="orderNum" value="order000007"></property>
</bean>
</beans>
--------------------------------------------------------------------------------------------------------
关于构造器的注入:
autowire="constructor"
需要在Order.java中加入一个构造器
public Order(OrderItem item ){ orderitem = item; }
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="orderItem" class="org.jia.OrderItem">
<property name="orderdec" value="item00001"></property>
</bean>
<bean id="order" class="org.jia.Order" autowire="constructor">
<property name="orderNum" value="order000007"></property>
</bean>
</beans>
6.怎么理解Spring的事务管理?
答: 了解事务:
事务是一组原子(Atomic)操作的工作单元,以数据库存取的实例来说,就是一组SQL指令,这一组SQL指令必须全部执行成功,若因为某个原因未全部执行成功(例如其中一行SQL有错误),则先前所有执行过的SQL指令都会被撤消。
Spring是把JDBC事务管理进来了封装,Spring事务管理的抽象关键在于org.springframework.transaction.PlatformTransactionManager接口,里面有 commit 和 rollback
public interface PlatformTransactionManager ...{
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
TransactionDefinition接口的实例定义了
事务的隔离程度(Isolation level)
传播行为(Propagation behavior)
超时(Timeout)
只读(Read-only)等
DataSourceTransactionManager、HibernateTransactionManager、JdoTransaction- Manager、JtaTransactionManager等是实现了该接口
Spring提供编程式的事务管理(Programmatic transaction management)与声明式的事务管理(Declarative transaction management):
1、编程式的事务管理可以清楚地控制事务的边界,也就是让您自行实现事务开始时间、撤消操作的时机、结束时间等,可以实现细粒度的事务控制。
2、然而多数的情况下,事务并不需要细粒度的控制,而是采用声明式的事务管理,好处是Spring事务管理的相关API可以不用介入程序之中,从对象的角度来看,它并不知道自己正被纳入事务管理之中,在不需要事务管理的时候,只要在设置文件上修改一下设置,即可移去事务管理服务。
7.Spring中BeanFactory与ApplicationContext的差别?
答:使用BeanFactory从xml配置文件加载bean:
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class XmlConfigWithBeanFactory {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource(
"build/beans.xml"));
}
}
使用ApplicationConText从xml配置文件加载bean:
public class XmlConfigWithApplication{
public static void main(String[] args){
ApplicationContext application = new ClassPathXmlApplicationContext(beans.xml"));
application.getBean("BeanName");
}
}
ApplicationContext和BeanFacotry相比,提供了更多的扩展功能,但其主要区别在于后者是延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext。8.怎么理解Hibernate 的缓存?
答:Hibernate缓存是一种提高系统性能的比较好的工具,如果使用合理,则能极大地提高系统性能,但如果使用不合理也会使用系统性能下降。Hibernate缓存比较复杂,要想灵活使用hibernate缓存,必须深入研究hiberante缓存原理,最好能分析hibernate的源代码。有很多人使用hibernate的时间比较长也不能正确理解hibernate缓存,下面我就谈谈hibernate缓存的使用,希望能对大家有点帮助。
Session缓存(一级缓存):当调用Session的保存、更新、查询操作时,在Session缓存中不存在相应对象,则把这些对象加入Session缓存。同一个Session操作,第一次通过ID调用load()或get()查询持久对象,先从Session缓存中查询发现该对象不命中,随即发送一条SQL语句生成一个持久对象并把该对象放入Session缓存。第二次再通过相同ID调用load()或get()查询时将直接从Session缓存将该对象返回,避免多余的数据库连接和查询的开销。
Session的load()和get()方法使用区别:
1、当数据库不存在对应ID数据时,调用load()方法将会抛出ObjectNotFoundException异常,get()方法将返回null,我比较喜欢使用get()方法。
2、当对象.hbm.xml配置文件<class>元素的lazy属性设置为true时,调用load()方法时则返回持久对象的代理类实例,此时的代理类实例是由运行时动态生成的类,该代理类实例包括原目标对象的所有属性和方法,该代理类实例的属性除了ID不为null外,所在属性为null值,查看日志并没有Hibernate SQL输出,说明没有执行查询操作,当代理类实例通过getXXX()方法获取属性值时,Hiberante才真正执行数据库查询操作。当对象.hbm.xml配置文件<class>元素的lazy属性设置为false时,调用load()方法则是立即执行数据库并直接返回实体类,并不返回代理类。而调用get()方法时不管lazy为何值,都直接返回实体类。
3、load()和get()都会先从Session缓存中查找,如果没有找到对应的对象,则查询Hibernate二级缓存,再找不到该对象,则发送一条SQL语句查询。关于这点,很多资料说明get ()不会查询二级缓存,比如夏昕编著的《深入浅出Hibernate》245页描述get()方法不查询二级缓存。但我测试发现load()和get()方法都会查询二级缓存,我上网看了很多缓存方面资料也证实了这点,大家可以看看这篇文章:http://blog.csdn.net/woshichenxu/archive/2006/01/22/586361.aspx
Session的evict()方法将持久对象从Session缓存中清除,clear()方法将清空整个缓存。
二级缓存(SesionFactory): 二级缓存由SessionFactory创建的所有Session对象共享使用, 二级缓存可使用第三方的缓存插件,如EHCache、OSChahe、SwarmCache、JBossCache,下面分别介绍二级缓存的类缓存、集合缓存和查询缓存。
1、类缓存:类缓存的key是持久对象的ID,value为持久化对象POJO,无论调用list(),load()还是iterate()查询,只要读出一个持久化对象POJO,都会根据POJO的ID作为key,value为POJO填充到类缓存中。当通过iterate()方法查询时,先会向数据库发送一条select id from POJO的SQL语句,将所有ID查询出来,再根据ID一个个地作为key到类缓存中查询相应POJO,如果类缓存中存在(命中),则从缓存中返回,否则向数据库发一条select * from POJO where id=?语句将查询该对象返回,并填充到类缓存中。当通过list()方法查询时,不会象iterate()先查询ID再查询类缓存,而是直接发送SQL查询数据库将结果返回,但会将查询结果填充到类缓存中,可供itetator()使用。
9.怎么理解Hibernate的懒加载?
答:延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。
xxxx.hbm.xml文件的<class>元素的lazy属性设为true,表示使用延迟检索策略。
10.Statement和PreparedStatement有什么区别?
答:
1: 我们先从这两个单词进行初步的讲解,Prepared(准备好的, 精制的),从这里可以知道PreparedStatement是预先编译的语句,而Statement则不是预先编译的,在DBMS中处理管理中Statement是要进行语法、语义的,而PreparedStatement则不要。
2: PrepareStatement中执行的SQL语句中是可以带参数的,而Statement则不可以。
比如:
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00);
pstmt.setInt(2, 110592);
pstmt. executeUpdate();
3: 当处理批量SQL语句时,这个时候就可以体现PrepareStatement的优势,由于采用Cache机制,则预先编译的语句,就会放在Cache中,下次执行相同SQL语句时,则可以直接从Cache中取出来。
关于PreparedStatement 预编译的语句具体是放在哪儿?为什么能提升性能?
先看看oracle的sql执行细节:
oracle 执行sql时,首先要分析sql生成查询计划,具体有:分析sql的合法性(到数据库schema中查询),决定使用某个的哪段索引(好像是这样说的)... ...,然后在第一次查询时,用物理读,其后相同的查询计划都是逻辑读,是到缓存里面读。
PreparedStatement有利于oracle生成查询计划,或许还有别的帮助,所以提高了性能。
但有一点是肯定的,PreparedStatement一定需要driver具体实现才行,Prepared是缓存在数据库端的。
所以说,PreparedStatement预编译的sql语句是放在数据库端的缓存里面,而放到缓存,有利于sql生成查询计划。
答:略
12.高并发下怎么考虑你的设计
答:略
总结:国际商业机器的面试官很客气,人非常低调,没有架子。提问的以上问题,基本上都是非常基础的,只要有一定工作经验的人,稍微准备一下,都是可以顺利回答的。
不过工作要求是:能适应一定的加班,能适应一定时间(半年或1年)的出差,这个貌似不是每个童鞋都愿意干的,尤其是拖家带口的那种。