1. 虚拟机对字符串的处理
虚拟机是将字符串直接量(不用new声明的)对象放到一个对象池中去缓存的,第一次使用的时候会将其放入池中,如果下次有其他变量访问一摸一样的直接量的话直接从对象池中去取该直接量,而不是又再生成一个对象。一般该池中的对象都不会被垃圾回收器回收。
比如:
String str1 = "1";
String str2 = "1";
实际上这段代码仅仅创建了一个字符串直接对象,保存在对象池中,池中对象就是”1”。str1和str2指向了该对象池的对象位置。
2. 编译时能确定的值
在编译时就能确定的值,在编译的时候就能够确定是否创建新的对象池对象。比如:
String str3 = "suhuanzhen123";
String str4 = "suhuanzhen" + 1 + "2" + "3";
System.out.println(str3 == str4);
实际上str3和str4在内存池中都是指向了"suhuanzhen123"这个字符串对象,所以输出时true。因为str4在编译期间就能确定值是suhuanzhen123,所以JVM不会再创建新的对象。直接从对象池中去取。
3. 在编译期间不能确定的值
如果字符串的拼接包含了函数、变量等等编译时不确定因素,那么此时就不能指向对象池中的变量了。比如
String str5 = "suhuanzhen123";
String str6 = "suhuanzhen" + "1".length() + "2" + "3";
System.out.println(str5 == str6);
4. 在编译时就能确定值的字符串变量创建值
比如代码
String str6 = "suhuanzhen" + "1" + "2" + "3";
实际上仅仅创建了一个对象——suhuanzhen123,缓存到对象池。
5. StringBuilder和StringBuffer的区别
一般在单线程的系统环境下优先使用StringBuilder,因为它是非线程安全的。而在多线程的环境下,比如Web系统,优先使用StringBuffer,因为它当中绝大部分方法都使用了synchronized进行修饰,保证了多线程环境下的线程安全问题。
6. 字符串之间的比较
用一个线程池的常量对象和一个变量、函数返回值比较直接使用==其实就可以了比如:
String jqgridparams = request.getParameter("jqgridparams");
if (jqgridparams != null) {
if ("getparams" == jqgridparams) {
//……………………
}
}
更直观一点就是
static String getStr(){
return "suhuanzhen123";
}
public static void main(String[] args) {
String str7 = getStr();
System.out.println("suhuanzhen123" == str7);
}
输出的是true;
如果是2个字符串变量之间比较就不能用==了
String str7 = getStr();
String str8 = new String("suhuanzhen123");
System.out.println(str8 == str7);
输出false。
应该用
System.out.println(str8.equals(str7));
7. 赋值语句的类型提升
上图程序实际上就是类型自动提升发生了错误,num本身是int型,而num = num+33.567;表达式中使用了double类型的数值,那么此表达式会自动向最高级别的基本类型转型,但是呢num自身是int,会提示不能向上转换的编译错误。如果程序这么写
public static void main(String[] args) {
int num = 1;
double numDoub = num+33.567;
}
就不会报错了。
赋值运算中基本数据转型的级别从高到低排序是String->double->float->long->int->chart、short->byte。
如果运算中含有字符串变量,那么二话不说,此表达式所有的运算最后的返回值就是一个字符串了,比如
int num = 1;
double numDoub = num+33.567;
String str = num+numDoub+"";
所以最高级别还是String。
8. 符合运算符中的隐式类型转换
在符合运算符中实际上存在着一个隐藏的类型转换
int sum =1;
sum += 30.44;
System.out.println(sum);
实际上会输出31,如果是之前的例子
int sum =1;
sum = sum+30.44;
编译器是会报错的。那么+=实质上是什么意思呢!就是
sum += 30.44;
等价于
sum = (int)(sum+30.44);
也就是说符合运算符自身还隐式地包含了一个强制转型的动作。这样做有时候会失去精度以及高位段位。
short sum =9999;
sum += 999999999.9999999;
System.out.println(sum);
输出-3826
9. 泛型引起的陷阱
List list = new ArrayList();
list.add("1");
list.add(2);
list.add("3q");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
List<String> listNew = list;
for (String strNew : listNew) {
System.out.println(strNew);
}
第一个循环输出无任何问题,第二个循环报出转型异常。换几话说,集合本身可以存储不同类型元素的对象的,只不过自从JDK1.5之后出现了泛型,才使我们很多人觉得集合变量仅仅能存一种类型的对象元素。泛型是为了在编译时更好的排除类型转换带来的繁琐检查。而在大多数应用中确实集合中就是同类型的元素。而泛型配合上加强的新循环,它会强制进行单一类型的转型操作。所以第二个循环的的结果就是
java.lang.Integer cannot be cast to java.lang.String |
泛型还有可能引起擦除,如下:
class MyT<PK extends Number> {
private PK id;
public PK getId() {
return id;
}
public void setId(PK id) {
this.id = id;
}
}
public class TTrap {
/**
* @param args
*/
public static void main(String[] args) {
MyT<Integer> my = new MyT<Integer>();
my.setId(100);
System.out.println(my.getId() + ":" + my.getId());
MyT mynew = my;
System.out.println(mynew.getId() + ":" + mynew.getId());
}
}
在mynew.getId()这一句中实际上编译器这时候仅仅将它识别为Number了,直接当做Integer使用会报错,除非强制转型方可当做Integer来使用。这里就相当于将泛型信息擦除了,程序只有到了真正运行时环境下才能把其当做Integer来用,编译期间他不认识其实质面目。Java擦除泛型信息是将该对象所有的泛型信息全部擦除掉,不仅仅是那个传入的泛型,如果此类本身也定义了其他泛型相关的方法、变量,统统没有了!
class MyTT<PK extends Number> {
private PK id;
private List<String> list;
public PK getId() {
return id;
}
public void setId(PK id) {
this.id = id;
}
public List<String> findRows() {
list = new ArrayList<String>();
list.add("步惊云");
list.add("聂风");
return list;
}
}
public static void main(String[] args) {
MyTT<BigInteger> myTT = new MyTT<BigInteger>();
for (String str : myTT.findRows()) {
System.out.println(str);
}
MyTT my2 = myTT;
List listNoString = my2.findRows();
}
10. 遇到含有.的字符串分割成字符串数组
public static void main(String[] args) {
String str = "www.google.cn";
String[] array = str.split("\\.");
System.out.println(array.length);
}
直接用String[] array = str.split(".");不起作用,因为”.”代表可以匹配任意字符,必须转义。