最近在准备面试题,准备找个好工作,以我准备好就发的形式,一般一次三道,答案内容是我自行查阅相关资料获得.
1、StringBuffer、StringBuilder
StringBuffer 代码片段:
private transient char[] toStringCache;
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder 代码片段:
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
--StringBuffer、StringBuilder的区别
区别1:线程安全
StringBuffer:线程安全,StringBuilder:线程不安全.因为StringBuffer的所有公开方法都是synchronized修饰的,而StringBuilder并没有synchronizedr修饰
区别2:缓冲区
可以看出,StringBuffer每次获取toString都会直接使用缓冲区的toStringCache值来构造一个字符串
而StringBuilder则每次都需要复制一次字符串,再构造一个字符串
所以,缓冲区这也是对StringBuffer的一个优化吧,不过StringBuffer的这个toString 方法仍然是同步的
区别3:性能
既然StringBuffer是线程安全的,它的所有公开方法都是同步的,StringBuilder是没有对方法加锁同步的,所以StringBuilder的性能要远大于StringBuffer.
总结:所以StringBuffer适用于用在多线程操作同一个StringBuffer的场景,如果是单线程场合StringBuilder更适合
2、#{}和${}的区别
1.编译过程
1.#{}是占位符:动态解析->预编译->执行
2.${}是拼接符:动态解析->编译->执行
预编译可以类比java类的编译,java类被编译成class文件,载入虚拟机,sql预编译后会在参数位置用占位符表示.
预编译:数据库驱动在发送sql和参数到DBMS之前,先对sql语句进行编译处理,之后DBMS则可以直接对sql进行处理,不需要再次编译,提供了性能.这一点mybatis默认情况下,将对所有的sql进行预编译处理
预编译可以将多个操作步骤合并成一个步骤,一般而言,越复杂的sql,编译程度也会复杂,难度大,耗时,费性能,而预编译可以合并这些操作,预编译之后DBMS可以省去编译直接运行sql.
预编译语句可以重复利用.
把一个sql预编译后产生的PreparedStatement对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的PreparedState对象
2.是否自动加单引号
1.#{}对应的变量会自动加上单引号
2.${}对应的变量不会加上单引号
3.安全性
1.#{}能防止sql注入
2.${}不能防止sql注入
4.Mybatis默认值不同
1.#{}默认值arg0、arg1、arg2 或0、1
2.${}默认值param1、param2、param3
三.如何选择#{}和${}
1.能用#{}的地方就用#{},尽量少用${}
2.表示作参数,或者order by 排序时用${}
3.传参时参数使用@Param("")注解,@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值(相当于又加了一层密),正确的将参数传入sql语句中(一般用过#{}会有sql注入的问题)如下:
Role selectById(@Param("id") String id);
List<Role>selectByNameAndOrgId(@Param("name")String name,@Param("orgId")String orgId);
3、hashtable与currenthashmap
hashtable与currenthashmap的区别
HashTable
1.底层数组+链表实现,key还是value都不能为null
2.线程安全,在修改数据时锁住整个HashTable,效率低,使用synchronize修饰
3.初始size为11,扩容:newsize = oldsize * 2 +1
4.多线程访问时,只要有一个线程访问或操作该对象,其他线程只能阻塞,并发形成非常差.
CurrentHashMap
1.jdk1,7采用数组+segment+分段锁来实现
分段锁技术:将数据分成一段一段的存储,然后把每一段数据配一把锁,当一个线程占用锁访问其中一段数据时,其他段数据也能被其他线程访问,能实现真正并发访问.
缺点:hash的过程比普通hashmap要长
2.jdk1.8采用数据+链表+红黑树,内部大量使用CAS操作,CAS是一种乐观锁
悲观锁:将资源锁住,等一个之前获得锁的线程释放锁之后,下个线程才可以访问.
乐观锁:通过方式不加锁处理资源,性能优于悲观锁.
3.jdk1.8彻底放弃segment而采用node,不再使用分段锁
4.线程安全
jdk1.7采用segment分段锁机制,segment继承自ReetrantLock
jdk1.8采用CAS + synchronized,是对hashtable进行优化,将锁细粒化到每个table的每个元素,来提升并发性能.
5.链表节点数量大于8时,链表转换为红黑树
6.时间复杂度:从遍历链表O(n),变成遍历红黑树O(logN)
以上观点,纯属个人理解,若有不正之处,烦请指出