这是《Java与MySQL字符集与编码》 第三篇。本篇主要是从Java编码、内外码、string编码、io编码、jdbc 编码 这几个方面,讲一下Java相关的编码问题。这篇内容稍微有点多,耐心点~
1 系统编码
为什么要提系统编码呢? 因为这涉及到后面Java默认编码的问题。在Java 编译、运行时,如果没有设置编码,会默认读取系统编码。
Mac上查看编码是这么来操作的:
2 内码与外码,以及转换
引用个概念。
内码 & 外码
内码 :程序内部使用的字符编码,特别是某种语言实现其 char 或 String 类型在内存里用的内部编码。Java 的内码就是 UTF-16。
外码 :程序与外部交互时外部使用的字符编码。简单的来说就是除了内码都可以认为是“外码”(包括 class 文件的编码)。
复制代码
如概念所述,Java的内码是UTF-16, 而外码则是需要根据不同的场景进行设置的。
数据到达Java之后,真正需要处理的时候,就需要转换成UTF-16内码进行了。包括下面的讨论也都是在讨论外码。
3 Java在跟编码相关的两个参数
java 中跟编码相关的系统参数有如下两个:
file.encoding
sun.jnu.encoding
复制代码
我们这篇文章只讲一下 file.encoding 。
3.1 file.encoding 是什么?
文件编码。
Java源码中,有四个类调用了file.encoding 这个属性:
(1)java.nio.Charset.defaultCharset()
默认字符集是在 java 虚拟机启动时决定的,依赖于 java 虚拟机所在的操作系统的区域以及字符集。
从代码中可以看出,默认字符集就是从 file.encoding 这个属性中获取的。
此处的默认字符集会影响字符串、文件字符流读写等的默认编码。
复制代码
(2) URLEncoder.encode(String) Web环境中最常遇到的编码使用。
(3)com.sun.org.apache.xml.internal.serializer.Encoding.getMimeEncodings(String) 影响对无编码设置的xml文件的读取 。
(4)javax.print.DocFlavor影响打印的编码。
从如上信息可以得到:
file.encoding 会影响无指定编码的字符串、读写文件、URL编码、打印等内容。
3.2 file.encoding 未指定时,默认编码是什么?
应用里面,在部署的时候如果指定了-Dfile.encoding=UTF-8类似的设定,那么后续的默认编解码就是用这个值,如果没有设定,则使用系统的编解码方案。
来段代码:
import java.nio.charset.Charset;
public class Main{
public static void main(String[] args) {
System.out.println("i am a 中国人");
System.out.println(Charset.defaultCharset().name());
System.out.println(System.getProperty("sun.jnu.encoding"));
System.out.println(System.getProperty("file.encoding"));
}
}
复制代码
编译一下: javac Main.java
然后执行: java Main. 看下结果
stackoverflow上面关于默认编码 有个经典的回答,stackoverflow.com/questions/3… 。
大意是
在Java进程启动之后,默认字符集 就确定了,后续即使使用 System.setProperty("file.encoding", "UTF-8"); 修改,只会修改配置项值 Charset.defaultCharset()的值,但是实际解码的时候,还是使用了系统初始化的编解码方案,不会改变默认字符集编码。
如果需要指定读取文件内容的编码,需要通过字符流的构造器InputStreamReader(InputStream in, Charset cs)设置。
还是上面的代码,那我们在jvm启动时指定一下file.encoding 看下.
发现 Charset.defaultCharset().name() 已经变成了GBK.并且输出也乱码了。这是因为编码的时候使用了GBK,而输出到系统item窗口时我们是按照UTF-8解码的,所以就乱码了。
我们再试下通过system来改一下编码,看看是不是stackoverflow 上所说的那样。
import java.nio.charset.Charset;
public class Main{
public static void main(String[] args) {
System.setProperty("file.encoding", "UTF-8"); // 设置file.encoding
System.out.println("i am a 中国人");
System.out.println(Charset.defaultCharset().name());
System.out.println(System.getProperty("sun.jnu.encoding"));
System.out.println(System.getProperty("file.encoding"));
}
}
复制代码
编译 javac Main.java
运行时指定 java -Dfile.encoding=GBK Main
我们发现,虽然程序的第一行就是设定编码为UTF-8,但是实际的编码还是GBK,这一点跟stackoverflow上一致。 而Charset.defaultCharset 也是gbk , 跟启动时指定的参数一致,这一点跟stackoverflow 上不一致。我用的java version "1.8.0_201" ,也可能跟我的版本有关系,这个暂时就不纠结了。
3.3 指定file.encoding 会产生什么影响?
file.encoding 对string的影响,我们在第4部分讲string 时进行验证。
file.encoding对读写文件内容的影响,我们这篇文章中就先不讲了,可以直接参考 www.jianshu.com/p/7688863e3… 这篇文章。
4 Java 中string 编码问题
灵魂三问 + 长度问题 + file.encoding 对string 的影响。
4.1 string 是什么
string 本身就是一个char数组,那么要表示成字节数组,就得涉及编码问题。
下面我们看几个关于string字符编码的知识点。
4.2 string 从哪儿来, 到哪儿去
我们先看一段代码,里面涉及到几种不同编码
看下输出:
这段代码中主要阐述string是通过字节数组 + 字符编码来生成的。里面讲了三种编码。由于系统默认的编码是UTF-8, 其实上面一段代码底层是这样来操作的【这就是file.encoding 对无编码string的影响】。
通过getBytes 获取了字符串的字节数组,然后按照UTF-8编码重新定义一个新的string用于输出,肯定只有utf-8 的能正常数据。
我们改一下new String 的编码,再来看下:
结果都正常了:
4.3 string 的长度
关于string的长度,我们最常用的一个函数是string.length(), 你真的懂它的真实用途吗?
先来一个demo吧
try {
String s = new String("iam中国人");
System.out.println("========s======= " + s);
System.out.println("length: " + s.length());
System.out.println("codePointCount: " + s.codePointCount(0, s.length()));
String s2 = "\uD834\uDD1E";
System.out.println("========s2======= " +s2);
System.out.println("length: " + s2.length());
System.out.println("codePointCount: " + s2.codePointCount(0, s2.length()));
String s3 = "\uD83D\uDE02";
System.out.println("========s3======= " +s3);
System.out.println("length:" + s3.length());
System.out.println("codePointCount: " + s3.codePointCount(0, s3.length()));
} catch (Exception e) {
System.out.println(e);
}
复制代码
结果是这样的:
从结果我们可以看出,length 跟 codePointCount 返回的结果有时相同,有时不同。
那我先看下二者的区别。
length 是返回代码单元的个数。这就是为什么s1返回了6 ,而s2跟s3 返回了2 而不是1. 因为音乐符号跟表情虽然是一个符号,但是其码元是2.
而codePointCount 是码点的个数,一个字符在字符集中肯定只对应一个码点,所以就是结果中的6、1、1.
5 Java中的IO编码问题
如前文所述,Java中跟字符编码比较相关的,除了string就是IO了,这篇文章我们先不讲,等后续有时间再细讲。如果感兴趣可以直接看 xiaogd.net/category/字符… 相关的文章。
6 spring boot 中jdbc编码设置
上面讲完了Java中对字符集、编码的介绍,那这部分为我们下一节讲MySQL编码做个铺垫。
Java里面跟MySQL交互,主要涉及编码的地方就是jdbc 。spring 中涉及JDBC的编码是
这部分我们就先不写demo了,在下一篇文章中,讲解MySQL编码时,会写详细的demo。
7 总结
本文主要讲解了Java中的编码问题(IO的没讲),file.encoding 的知识,重点讲了string的编码问题。最后提到了jdbc的编码问题,为我们下一节的MySQL编码问题做个铺垫。
8 参考文献
java运行时参数file.encoding和sun.jnu.encoding详解:
www.jianshu.com/p/7688863e3…