字节数组与String类型转换时的默认字符集

引入:在学习javaWeb时,碰到需要将字节数组和String类型做相互转换的,如使用Base64编码时。那么,我们知道String其实提供了API:getBytes() 将字符串转换为字节数组,而通过构造器new String(byte[]) 又可以将字节数组重新转化为字符串,对吧?但我们经常需要跟客户端做交互,此时很容易在这两个转换之间发生乱码问题。所以今天,我们一起来解决这个问题吧!


首先,我们需要明白,要使 字节数组 -> 字符串,或 字符串 -> 字节数组,两个过程来去自如而不乱码,就要求这两个互逆过程所使用的字符集必须统一,才能保证互相转换时不出现乱码。

对于 String.getBytes(String charsetName):可以传入参数charsetName,即字符集的名称,那么就可以根据该字符集对该字符串进行转化成字节数组。
同理,new String(byte[] buffer, String charsetName):也可以传入参数charsetName设置字符集,那么JVM就会根据该字符集将该buffer字节数组转化成一个字符串后返回。


然而,实际上呢我们可以不传入字符集参数,那么在字符串和字节数组进行相互转换时,JVM会采用平台的默认字符集对两者进行转换。而这个默认的字符集到底为何,是我们今天要讨论的重点:

  • 首先,我们先测试第一种情况,运行一个最简单的Java程序,不需要Tomcat服务器,只依赖JVM,代码如下:
public class StringTest {

    @Test
    public void test(){

        //获取当前系统的默认字符集
        System.out.println("当前系统的默认字符集:" + System.getProperty("file.encoding"));

        //测试文本
        String str = "我是测试文本";

        //使用默认字符集,对字符串和字符数组进行相互转化
        byte[] bytes = str.getBytes();

        String result = new String(bytes);

        System.out.println("转化后的结果为:" + result);

    }

}

结果截图如下:
在这里插入图片描述
可以看到,首先,当前系统的默认字符集是UTF-8。我们知道中国的计算机的默认字符集一般都是GBK。那么为什么我这个是UTF-8呢?
其实呢,是因为我在IDEA中设置了当前运行的这个项目的默认编码方式,为UTF-8。所以我运行时获取到的系统默认字符集就是UTF-8。
在这里插入图片描述
现在我改了该项目的默认编码方式为GBK,再次运行查看结果。
在这里插入图片描述
运行结果如下所示:
在这里插入图片描述
很明显,此时的系统的默认字符集已然发生了改变!而且我们还发现,这时候转发出来的字符串,仍然正确!!!

所以,我们先给出第一个结论:系统的默认字符集,即为该项目的默认编码字符集。

接着,当我们试图改变代码,按默认的字符集将字符串转化为字节数组,然后指定“UTF-8”将字节数组转化为字符串,查看运行结果如下:
在这里插入图片描述
结果表明,UTF-8此时不奏效,那么是为什么呢?
我们将UTF-8改成GBK,再次查看结果:
在这里插入图片描述
它又能行了!说明此时,如果我们不指定编码方式,那么字符串和字节数组之间的相互转化,默认采用的字符集是GBK,和我们的系统的默认字符集(也就是该项目的默认字符集) 保持一致。

那么,会不会是巧合呢?
我们将系统的字符集设置为UTF-8,然后实验如下:

  • 项目的字符集设置为UTF-8,用默认字符集将字符串转化为字节数组,仍用GBK字符集将字节数组转化为字符串,实验结果如下:
    在这里插入图片描述
    结果再一次乱码!
  • 再来一次实验,此时指定UTF-8字符集将字节数组转化为字符串,实验结果如下:
    在这里插入图片描述
    运行结果再一次正确了!

经过以上的四个实验,我们其实已经可以得出一个结论:
当我们在字符串和字节数组之间做转化时,若没有显式指定字符集,那么转化时会自动使用系统的默认字符集,而这个系统的默认字符集,其实就是这一整个项目的默认编码字符集,也就是System.getProperty(“file.encoding”) 的属性值。

这个结论呢,其实在JDK源码中也已经说明了:

/**
     * Constructs a new {@code String} by decoding the specified array of bytes
     * using the platform's default charset (翻译:使用平台的默认字符集解码指定的字节数组,构造一个新的{@code String}) .  
     * The length of the new {@code String} is a function of the
     *  charset, and hence may not be equal to the length of the
     *  byte array.
     *
     * <p> The behavior of this constructor when the given bytes are not valid
     * in the default charset is unspecified.  The {@link
     * java.nio.charset.CharsetDecoder} class should be used when more control
     * over the decoding process is required.
     *
     * @param  bytes
     *         The bytes to be decoded into characters
     *
     * @since  JDK1.1
     */
    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }

这就完了?NO NO NO ! 还有更有趣的东西在后面呢!


刚刚我们是做了第一种实验,测试了只依赖JVM进行运行的最基本的Java程序。
那么接下来,我们测试下在Tomcat中运行的Java程序的情况如何:
先上代码:

public class StringServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取当前系统的默认字符集
        System.out.println("当前系统的默认字符集:" + System.getProperty("file.encoding"));

        //测试文本
        String str = "我是测试文本";

        //使用默认字符集,对字符串和字符数组进行相互转化
        byte[] bytes = str.getBytes();

        String result = new String(bytes, "UTF-8");

        System.out.println("转化后的结果为:" + result);
    }
    
}

方法体中的代码都一样,唯一的区别是这是个Servlet程序,需要依赖Tomcat容器运行。

此时呢,整个项目的编码集,是UTF-8。
在这里插入图片描述

OK,那么接下来,我们运行,查看结果:
在这里插入图片描述
项目的字符集明明是UTF-8,可运行结果却告诉我们,系统的默认字符集是GBK!是不是开始疑惑了?别急,我们开头的时候讲过,国内的计算机的默认字符集,基本都是GBK,对吧?
我们可以打开谷歌浏览器的控制台,请求服务器时,查看请求头信息,如下:
在这里插入图片描述
可以看到,划线一行,告诉了服务器,当前的操作系统是“windows”,而我们国内的"windows"的默认字符集,就是GBK!
所以呢,这里我们得出另一个结论:当我们的Java程序是运行在Tomcat容器上时,我们的系统默认字符集就是GBK,无关整个项目的默认编码字符集。

那么,此时的String和byte[]的相互转化,默认使用的是哪个呢?
我们将刚刚的代码,用默认字符集转化字符串成字节数组,然后指定“GBK”字符集,将字节数组再次转化为字符串,查看运行结果如下:
在这里插入图片描述
诶,又能行了!说明此时字符串和字节数组之间的相互转化,默认采用的字符集是GBK,也就是仍然和系统的默认字符集即 System.getProperty(“file.encoding”) 的属性值保持一致。


OK,那么所有的实验都演示完毕,收最后的结论:
无论是运行在Tomcat上的Java程序,还是只运行在JVM上的普通Java程序,他们在进行字符串和字节数组的相互转换过程中,若没有显式指定字符集,那么它们会采用系统的默认字符集。
其中,运行在Tomcat容器的Java程序使用的系统字符集是GBK;
而只运行在JVM上的普通Java程序使用的系统字符集呢,与该Java项目的整体字符集保持一致。




重中之重:但不管该Java程序是运行在Tomcat还是只运行在JVM,可以确定的一点就是,它使用的系统默认字符集,就是System.getProperty(“file.encoding”)的属性值!!!



好了,以上就是我个人对本次内容的理解,如果有什么不恰当的地方,还望各位兄弟在评论区指出哦。
如果这篇文章对你有帮助的话,不妨点个关注吧~
期待下次我们共同讨论,一起进步~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值