Java中文乱码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sarafina527/article/details/89349011

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取值,如果没有指定,则使用系统默认编码
   

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/89349011