基本数据类型
- byte:-128 - 127
- boolean:true/false
- char: \u0000 - u\ffff ’
- short:-32768 - 32768
- int:-2147483648-2147483648
- float:-3.40292347E+38-3.40292347E+38
- long:-9233372036854477808-9233372036854477808
- double:-1.79769313486231570E+308-1.79769313486231570E+308
byte x = -1;
System.out.println((int) x);// -1
int y = 255;
System.out.println((byte) y);// -1
在从低到高转换的时候会用符号位填充,而从高到低转换的时候会直接截取。那如果想把byte当做是一个无符号的,那么怎么取到它所表示的值?如下:
byte x = -2;
System.out.println((int) (x & 255));// -1
double x = 1.15;
System.out.printf("%1.1f\n", x);// 四舍五入
System.out.printf("%1.1f\n", x - 0.05);// 向下取整
System.out.printf("%1.1f\n", x + 0.05);// 向上取整
浮点数在存储的时候也是二进制的,在十进制能表示的数在二进制上可能有有一定的偏差。所以需要注意的一点:
不要写while(x != y)这种结束条件,可以写成while(Math.abs(x-y) < e),其中e是一个很小的数。
String
public final class String{
private final char value[];
private final int offset;
private final int count;
private int hash;
}
可以看出来,真正的字符串的内容保存在char value[]中,而在substring的时候是不会新的char数组来重复保存,只需生成一个新的String对象并设置好对应的偏移量即可:
String str1 = "abcd".substring(0, 3);
String str2 = "abc";
String str3 = "abcd";
在运行时可以看到:
str1.value == str3.value
同时我们发现str1 != str2,这是因为在substring的时候生成了新的对象,如果是下面的写法的话,会发现str1 == str2:
String str1 = "abcd".substring(0, 3).intern();
String str2 = "abc";
为了性能和内存资源的考虑,JVM会将String用一个哈希表保存起来,如果在后面用到同样的字符串就直接从这个哈希表中拿出来用,而不需要创建新的String对象,所以就会看到上面的结果。而intern方法就是将String放入String pool中,并返回对应的对象。
也有其他类型的Literal Pool(比如Integer),但是并不是说任意两个相同值的Integer就能用==进行比较了。而在这里有代码运行的结果争议很大:
String s1 = "abc";
String s2 = "abc";
String s3 = s1 + s2;
System.out.println(s3 == s3.intern());
根据前面说的应该返回false,在JDK 6中也的确如此,但是在JDK 7中的返回值却是true:
JDK 7中的String pool并不拷贝调用intern的String实例,只是在池中记录了每组内容相同的String实例首个被intern的那个实例的引用,所以结果为true。而在JDK 6中,在常量池中创建了一个新的String实例,并将该实例的引用返回,所以结果为false。所以intern的行为还是很依赖虚拟机的实现。
这样实现起来常量池中要保存的内容确实少了很多。另外,看到关于创建了多少String实例的帖子:
String s1 = new String("xyz");
String s2 = new String("xyz");
同样和JVM如何实现有关。
编码解码
- class文件采用utf-8编码
- jvm运行时采用utf-16编码
- java中的字符串采用unicode编码
其中unicode使用"\udddd"的形式表示,比如:
String \u4e00 = "\u4e00";
System.out.println(\u4e00);// 一
下面进入正题,要明白为什么会出现乱码问题就有明白两个概念:
- 编码:从string变成byte[]
- 解码:从byte[]变成string
byte[]本身是没有任何含义的,它只是用来存储信息(比如它可以存放数字、图像等),而string是有含义的。string变成byte的过程就好比上课记笔记的时候,将老师的话用英语记录还是用汉语记录,不用的语言就是对信息的不同编码方式。
在使用时自然应该用正确的解码方式对信息进行处理,比如你不懂英语自然就看不懂用英语写的笔记。看下面代码:
String str = "你好";
byte[] b_gbk = str.getBytes("GBK");// [-60, -29, -70, -61]
byte[] b_utf_8 = str.getBytes("utf-8");// [-28, -67, -96, -27, -91, -67]
String str_gbk = new String(b_gbk, "GBK");// 你好
String str_utf_8 = new String(b_utf_8, "utf-8");// 你好
下面来模拟一个出现乱码的过程已经一个伪的解决过程:
// 出现乱码的过程
String str = "你好";
byte[] b_utf_8 = str.getBytes("utf-8");// [-28, -67, -96, -27, -91, -67]
String str_gbk = new String(b_utf_8, "gbk");// 浣犲ソ
byte[] b_gbk = str_gbk.getBytes("gbk");// [-28, -67, -96, -27, -91, -67]
// 从乱码中尝试解析出原来的字符,就是上面的逆过程
b_utf_8 = str_gbk.getBytes("gbk");// [-28, -67, -96, -27, -91, -67]
String str_utf_8 = new String(b_utf_8, "utf-8");// 你好
urlencode
在遇到有编码问题的时候,最简单暴力的方法就是用urlencode对字符串先编码,再传输。等接收方拿到字符串之后,进行解码即可。
----------END----------