java中的编码 关于字符编码的方式很多,比如常用的有unicode系列的utf8,utf16,utf32,还有中国的gb2312,GBK,GBK18030等等,这些网上的资料很多,但都不是本集要讲的内容,本集要讲的内容只有三个
java的文件编码 
java的string的外在编码 
java的string的内在编码,jvm用的编码 
 
本文所有的讨论,直接基于jdk17,不见兼顾jdk8,所以,不会讨论历史的情况 
1. java的文件编码 很多时候,大家都会说,防止文件乱码,然后统一使用utf-8,这样做没问题,但是为什么呢,这样做是对的,但是这样做的原因一定是对的嘛?
java对于文件的编码方式是可以支持别的,不一定是utf-8,这个javac -help可以看到 
用utf-8的确是一个很好的选择,即使你们公司都在国内,用gb2312编码也可以,但是如果,你如果使用了自动的ci,在服务器上编码代码,如果服务器是linux的,那么它的 
 
1 2 3 #  https://docs.oracle.com/en/java/javase/17/docs/specs/man/javac.html  -encoding encoding Specifies character encoding used by source files, such as EUC-JP and UTF-8. If the -encoding option is not specified, then the platform default converter is used. 
java的string的外在编码 java的string的外在编码,指的是String.getBytes()方法使用的编码,这个可以看jdk的源码得到答案,一般oracle jdk会有一些私有代码,代码不是全部公开的,如果有些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  byte [] getBytes() {         return  encode(Charset.defaultCharset(), coder(), value); } 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; } 
java的string的内在编码,jvm用的编码 先下结论,java内在的编码,是utf-16,但是也不是,是UTF-16LE,因为java string的外在编码你只要看到的时候,其实就已经发生转换了,所以,你不能直接窥探到,这里我们
1 2 3 4 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.net.util=ALL-UNNAMED 
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package  pers.apricot.character;import  org.junit.jupiter.api.Test;import  java.lang.reflect.Field;import  java.nio.charset.StandardCharsets;import  java.util.HexFormat;public  class  StringUnicodeTest  {    @Test      void  test1 ()  throws  NoSuchFieldException, IllegalAccessException {         HexFormat  of  =  HexFormat.of();         String 我爱你 = new  String ("我爱你" );         byte [] bytes = 我爱你.getBytes();         System.out.println(bytes.length);         System.out.println(of.formatHex(bytes));         Class<String> stringClass = String.class;         Field  value  =  stringClass.getDeclaredField("value" );         value.setAccessible(true );         byte [] originByteArr = (byte []) value.get(我爱你);         System.out.println(originByteArr.length);         System.out.println(of.formatHex(originByteArr));         byte [] bytes1 = 我爱你.getBytes(StandardCharsets.UTF_16);         System.out.println(bytes1.length);         System.out.println(of.formatHex(bytes1));         byte [] bytes2 = 我爱你.getBytes(StandardCharsets.UTF_16BE);         System.out.println(bytes2.length);         System.out.println(of.formatHex(bytes2));         byte [] bytes3 = 我爱你.getBytes(StandardCharsets.UTF_16LE);         System.out.println(bytes3.length);         System.out.println(of.formatHex(bytes3));     } } 
输出如下
1 2 3 4 5 6 7 8 9 10 9 e68891e788b1e4bda0 6 11623172604f 8 feff621172314f60 6 621172314f60 6 11623172604f 
可以看到直接用反射得到的字节,和UTF_16LE得到的字节是一样的,所以,java的确使用的是UTF_16LE
延伸 java为什么是UTF_16LE 这里应该和cpu的架构有关,比如x86的都是小端,little-endian,因为java是跨平台的吗,所以要入乡随俗,然后比如我的cpu是intel 9400,所以,自然就是小端的,当然这 
为什么utf16多了两个字节 这里可以看到utf16,多了BOM,而且默认是使用大头
结论 
java的文件编码可以支持多种,但是最佳实践是采用utf-8 
java的string,默认编码也是utf-8,可以指定默认的,也可以转成其它的 
jvm的string编码,是utf16le或者utf16be 
 
java编码终极指南2里面会详细介绍sun.jnu.encoding和file.encoding