1、String,StringBuffer和StringBuilder
String:字符串常量,不可变;
StringBuffer:字符串变量,可改变的对象,线程安全,多线程处理大量数据选择;
StringBuilder:字符串变量,可改变的对象,速度快,单线程情况下处理大量数据选择;
补充:字符串String类型全方位考察
考察一:
String a = "a1";
String b = "a" + 1;
System.out.println(a == b);
输出结果:true
总结:当两个字符串字面值连接时(相加),得到的新字符串依然是字符串字面值,保存在常量池中。
考察二:
String a = "ab";
String bb = "b";
String b = "a" + bb;
System.out.println(a == b);
输出结果:false
总结:当字符串字面值与String类型变量连接时,得到的新字符串不再保存在常量池中,而是在堆中新建一个String对象来存放。
考察三:
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println(a == b);
输出结果:true
总结:道理同考察一,这里的变量bb是String类型常量。
考察四:
String a = "ab";
final String bb = getBB();
String b = "a" + bb;
System.out.println(a == b);
private static String getBB() {
return "b";
}
输出结果:false
总结:这里的bb变量虽然定义成了final,但是并不会保存到常量池,因为getBB()方法相当于new String("b")。
考察五:
private static String a = "ab";
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);
System.out.println(s.intern() == a);
}
输出结果:false true
总结:主要考察了String的intern()方法,当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
考察六:
private static String a = new String("ab");
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);
System.out.println(s.intern() == a);
System.out.println(s.intern() == a.intern());
}
输出结果:false false true
2、Java内存模型简介
Java内存模型用来定义程序中各个变量的访问规则(在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节)。包括两部分内容:主内存和工作内存。
所有的变量都存储在主内存中,每条线程还有自己的工作内存。线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。如果要把一个变量从主内存复制到工作内存,就需要按顺序执行read和load操作,如果把变量从工作内存同步回主内存中,就要按顺序执行store和write操作。
内存间交互操作:
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
3、HashMap和Hashtable的区别
它们都是通过key-value的方法来存储数据的,不同之处主要有以下三点:
1、HashMap是java本身Map(java.util.Map)接口的一个实现,而Hashtable是基于旧的Dictionary类的;
2、HashMap是非线程安全的,而Hashtable线程安全;
3、HashMap可以允许空值作为key或value,但是只允许一个空的key,value可以多个为空;Hashtable不允许空值作为键或值;
另外:HashMap去掉了Hashtable的contains方法,使用containsKey和containsValue方法;性能方面的话,由于它们采用的hash/rehash算法基本上一样,所以性能上不会有太大差异。
HashMap原理总结:
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用LinkedList来解决碰撞问题,当发生碰撞了,对象将会储存在LinkedList的下一个节点中。 HashMap在每个LinkedList节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的LinkedList中。键对象的equals()方法用来找到键值对。
默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。