String.getBytes()new String(byte[])问题
如果没有传递字符集服务启动的时候会取系统的默认编码,
可以通过echo $LANG 或者locale查看linux系统编码,如下图所示,表示我的系统是UTF-8编码
Java虚拟机会在启动的时候取到系统默认编码后,并设置进 System.setProperty("file.encoding",$LANG)
最好使用 str.getBytes(charset)、new String(byte[],charset)
样例代码
package file;
public class EncodingExample {
public static void main(String[] args) {
String encoding = System.getProperty("file.encoding");
System.out.println(encoding);
String str = "字符编码";
System.out.println(str + "按照" + encoding +"编码后,十六进制表示为:");
System.out.println(byteArrayToHexString(str.getBytes()));
}
/**
* 字节数组转十六进制
* @param data
* @return
*/
public static String byteArrayToHexString(byte[] data) {
StringBuilder sBuilder = new StringBuilder();
for(int i = 0;i < data.length;i++) {
String str1 = Integer.toHexString(data[i]&0xFF);
sBuilder.append(str1);
}
return sBuilder.toString();
}
}
命令行中执行
/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin/javac ./file/EncodingExample.java
/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin/java file.EncodingExample
执行结果1:
由此可见,java虚拟机保存的file.encoding系统属性值是UTF-8
带-D执行:
如果在Java启动时设置-Dfile.encoding=GBK不会取系统编码,会使用设置的GBK
-D<name>=<value> //set a system property,设置系统属性,对应System.setProperty("file.encoding","UTF-8")
用法 :
java -Dfile.encoding=GBK EncodingExample
/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin/java -Dfile.encoding=GBK file.EncodingExample
执行结果2:可见如果声明了使用file.encoding=GBK,执行过程中就会使用声明的文件编码,由于我的客户端是utf-8编码,所以显示会出现问题。最终的编码结果d7d6b7fbb1e0c2eb 和utf-8的e5ad97e7aca6e7bc96e7a081也不一样!
String编解码源码分析
string.getBytes()编码
public byte[] getBytes() {
return StringCoding.encode(coder(), value);
}
static byte[] encode(byte coder, byte[] val) {
Charset cs = Charset.defaultCharset();
if (cs == UTF_8) {
return encodeUTF8(coder, val, true);
}
if (cs == ISO_8859_1) {
return encode8859_1(coder, val);
}
if (cs == US_ASCII) {
return encodeASCII(coder, val);
}
StringEncoder se = deref(encoder);
if (se == null || !cs.name().equals(se.cs.name())) {
se = new StringEncoder(cs, cs.name());
set(encoder, se);
}
return se.encode(coder, val);
}
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
String csn = GetPropertyAction
.privilegedGetProperty("file.encoding");//会取用系统属性file.encoding
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = sun.nio.cs.UTF_8.INSTANCE;
}
}
return defaultCharset;
}
public String(byte bytes[], int offset, int length) {
checkBoundsOffCount(offset, length, bytes.length);
StringCoding.Result ret = StringCoding.decode(bytes, offset, length);
this.value = ret.value;
this.coder = ret.coder;
}
static Result decode(byte[] ba, int off, int len) {
Charset cs = Charset.defaultCharset();
if (cs == UTF_8) {
return decodeUTF8(ba, off, len, true);
}
if (cs == ISO_8859_1) {
return decodeLatin1(ba, off, len);
}
if (cs == US_ASCII) {
return decodeASCII(ba, off, len);
}
StringDecoder sd = deref(decoder);
if (sd == null || !cs.name().equals(sd.cs.name())) {
sd = new StringDecoder(cs, cs.name());
set(decoder, sd);
}
return sd.decode(ba, off, len);
}
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
String csn = GetPropertyAction
.privilegedGetProperty("file.encoding");//会取用系统属性
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = sun.nio.cs.UTF_8.INSTANCE;
}
}
return defaultCharset;
}
根据源码可见,在String不带字符集编解码的最终都会到defaultCharset方法,会返回此Java虚拟机的默认字符集,默认字符集是在虚拟机启动期间确定的,通常取决于底层操作系统的区域设置local和字符集Lang。
总结
string.getBytes()编码:会取System.getProperty("file.encoding") 的字符集,用该字符集对字符串进行编码;
new String(byte[])解码:也会使用System.getProperty("file.encoding")的字符集
系统属性("file.encoding") 会根据java命令执行的声明-Dfile.encoding=GBK取值,如果没有指定,则使用系统默认编码