Java语言中字符串类型和字节数组类型相互之间的转换经常发生,网上的分析及代码也比较多,本文将分析总结常规的byte[]和String间的转换以及十六进制String和byte[]间相互转换的原理及实现。

1. String转byte[]首先我们来分析一下常规的String转byte[]的方法,代码如下:public static byte[] strToByteArray(String str) {if (str == null) {return null;}byte[] byteArray = str.getBytes();return byteArray;
}很简单,就是调用String类的getBytes()方法。看JDK源码可以发现该方法最终调用了String类如下的方法。/*** JDK source code*/
public byte[] getBytes(Charset charset) {String canonicalCharsetName = charset.name();if (canonicalCharsetName.equals("UTF-8")) {return Charsets.toUtf8Bytes(value, offset, count);} else if (canonicalCharsetName.equals("ISO-8859-1")) {return Charsets.toIsoLatin1Bytes(value, offset, count);} else if (canonicalCharsetName.equals("US-ASCII")) {return Charsets.toAsciiBytes(value, offset, count);} else if (canonicalCharsetName.equals("UTF-16BE")) {return Charsets.toBigEndianUtf16Bytes(value, offset, count);} else {CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count);ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer());byte[] bytes = new byte[buffer.limit()];buffer.get(bytes);return bytes;}
}上述代码其实就是根据给定的编码方式进行编码。如果调用的是不带参数的getBytes()方法,则使用默认的编码方式,如下代码所示:/*** JDK source code*/
private static Charset getDefaultCharset() {String encoding = System.getProperty("file.encoding", "UTF-8");try {return Charset.forName(encoding);} catch (UnsupportedCharsetException e) {return Charset.forName("UTF-8");}
}关于默认的编码方式,Java API是这样说的:The default charset is determined during virtual-machine startup and typically depends upon the locale and charset of the underlying operating system.同样,由上述代码可以看出,默认编码方式是由System类的"file.encoding"属性决定的,经过测试,在简体中文Windows操作系统下,默认编码方式为"GBK",在Android平台上,默认编码方式为"UTF-8"。
2. byte[]转String接下来分析一下常规的byte[]转为String的方法,代码如下:public static String byteArrayToStr(byte[] byteArray) {if (byteArray == null) {return null;}String str = new String(byteArray);return str;
}很简单,就是String的构造方法之一。那我们分析Java中String的源码,可以看出所有以byte[]为参数的构造方法最终都调用了如下代码所示的构造方法。需要注意的是Java中String类的数据是Unicode类型的,因此上述的getBytes()方法是把Unicode类型转化为指定编码方式的byte数组;而这里的Charset为读取该byte数组时所使用的编码方式。/*** JDK source code*/
public String(byte[] data, int offset, int byteCount, Charset charset) {if ((offset | byteCount) < 0 || byteCount > data.length - offset) { throw failedBoundsCheck(data.length, offset, byteCount);}// We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because// 'count' and 'value' are final.String canonicalCharsetName = charset.name();if (canonicalCharsetName.equals("UTF-8")) {byte[] d = data;char[] v = new char[byteCount];int idx = offset;int last = offset + byteCount;int s = 0;outer:while (idx < last) {byte b0 = d[idx++];if ((b0 & 0x80) == 0) {// 0xxxxxxx// Range:  U-00000000 - U-0000007Fint val = b0 & 0xff;v[s++] = (char) val;} else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe)== 0xfc)) {int utfCount = 1;if ((b0 & 0xf0) == 0xe0) utfCount = 2;else if ((b0 & 0xf8) == 0xf0) utfCount = 3;else if ((b0 & 0xfc) == 0xf8) utfCount = 4;else if ((b0 & 0xfe) == 0xfc) utfCount = 5;// 110xxxxx (10xxxxxx)+// Range:  U-00000080 - U-000007FF (count == 1)// Range:  U-00000800 - U-0000FFFF (count == 2)// Range:  U-00010000 - U-001FFFFF (count == 3)// Range:  U-00200000 - U-03FFFFFF (count == 4)// Range:  U-04000000 - U-7FFFFFFF (count == 5)if (idx + utfCount > last) {v[s++] = REPLACEMENT_CHAR;continue;}// Extract usable bits from b0int val = b0 & (0x1f >> (utfCount - 1));for (int i = 0; i < utfCount; ++i) {byte b = d[idx++];if ((b & 0xc0) != 0x80) {v[s++] = REPLACEMENT_CHAR;idx--; // Put the input char backcontinue outer;}// Push new bits in from the right sideval <<= 6;val |= b & 0x3f;}// Note: Java allows overlong char// specifications To disallow, check that val// is greater than or equal to the minimum// value for each count://// count    min value// -----   ----------//   1           0x80//   2          0x800//   3        0x10000//   4       0x200000//   5      0x4000000// Allow surrogate values (0xD800 - 0xDFFF) to// be specified using 3-byte UTF values onlyif ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {v[s++] = REPLACEMENT_CHAR;continue;}// Reject chars greater than the Unicode maximum of U+10FFFF.if (val > 0x10FFFF) {v[s++] = REPLACEMENT_CHAR;continue;}// Encode chars from U+10000 up as surrogate pairsif (val < 0x10000) {v[s++] = (char) val;} else {int x = val & 0xffff;int u = (val >> 16) & 0x1f;int w = (u - 1) & 0xffff;int hi = 0xd800 | (w << 6) | (x >> 10);int lo = 0xdc00 | (x & 0x3ff);v[s++] = (char) hi;v[s++] = (char) lo;}} else {// Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xffv[s++] = REPLACEMENT_CHAR;}}if (s == byteCount) {// We guessed right, so we can use our temporary array as-is.this.offset = 0;this.value = v;this.count = s;} else {// Our temporary array was too big, so reallocate and copy.this.offset = 0;this.value = new char[s];this.count = s;System.arraycopy(v, 0, value, 0, s);}} else if (canonicalCharsetName.equals("ISO-8859-1")) {this.offset = 0;this.value = new char[byteCount];this.count = byteCount;Charsets.isoLatin1BytesToChars(data, offset, byteCount, value);} else if (canonicalCharsetName.equals("US-ASCII")) {this.offset = 0;this.value = new char[byteCount];this.count = byteCount;Charsets.asciiBytesToChars(data, offset, byteCount, value);} else {CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount));this.offset = 0;this.count = cb.length();if (count > 0) {// We could use cb.array() directly, but that would mean we'd have to trust// the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later,// which would break String's immutability guarantee. It would also tend to// mean that we'd be wasting memory because CharsetDecoder doesn't trim the// array. So we copy.this.value = new char[count];System.arraycopy(cb.array(), 0, value, 0, count);} else {this.value = EmptyArray.CHAR;}}
}具体的转换过程较为复杂,其实就是将byte数组的一个或多个元素按指定的Charset类型读取并转换为char类型(char本身就是以Unicode编码方式存储的),因为String类的核心是其内部维护的char数组。因此有兴趣的同学可以研究下各种编码方式的编码规则,然后才能看懂具体的转换过程。
3. byte[]转十六进制String所谓十六进制String,就是字符串里面的字符都是十六进制形式,因为一个byte是八位,可以用两个十六进制位来表示,因此,byte数组中的每个元素可以转换为两个十六进制形式的char,所以最终的HexString的长度是byte数组长度的两倍。闲话少说上代码:public static String byteArrayToHexStr(byte[] byteArray) {if (byteArray == null){return null;}char[] hexArray = "0123456789ABCDEF".toCharArray();char[] hexChars = new char[byteArray.length * 2];for (int j = 0; j < byteArray.length; j++) {int v = byteArray[j] & 0xFF;hexChars[j * 2] = hexArray[v >>> 4];hexChars[j * 2 + 1] = hexArray[v & 0x0F];}return new String(hexChars);
}上述代码中,之所以要将byte数值和0xFF按位与,是因为我们为了方便后面的无符号移位操作(无符号右移运算符>>>只对32位和64位的值有意义),要将byte数据转换为int类型,而如果直接转换就会出现问题。因为java里面二进制是以补码形式存在的,如果直接转换,位扩展会产生问题,如值为-1的byte存储的二进制形式为其补码11111111,而转换为int后为11111111111111111111111111111111,直接使用该值结果就不对了。而0xFF默认是int类型,即0x000000FF,一个byte值跟0xFF相与会先将那个byte值转化成int类型运算,这样,相与的结果中高的24个比特就总会被清0,后面的运算才会正确。
4. 十六进制String转byte[]没什么好说的了,就是byte[]转十六进制String的逆过程,放代码:public static byte[] hexStrToByteArray(String str)
{if (str == null) {return null;}if (str.length() == 0) {return new byte[0];}byte[] byteArray = new byte[str.length() / 2];for (int i = 0; i < byteArray.length; i++){String subStr = str.substring(2 * i, 2 * i + 2);byteArray[i] = ((byte)Integer.parseInt(subStr, 16));}return byteArray;
}文中所有代码可以在个人github主页查看和下载。

另,个人技术博客,同步更新,欢迎关注!转载请注明出处!文中若有什么错误希望大家探讨指正!

Java中String和byte[]间的转换浅析相关推荐

  1. java中string与byte[]的转换

    1.string 转 byte[] byte[] midbytes=isoString.getBytes("UTF8"); //为UTF8编码 byte[] isoret = sr ...

  2. Java中String与Byte之间的各种转换

    这个是很常用的,一直没整理,现在来整理下,供自己备用. 1.一个字符串,用Byte数组形式通过串口发送出去,很简单 String cmd = "xxxx"; byte[] cmdB ...

  3. java中String转byte 编码格式

    在.Net中,字符串转换byte编码格式如下: StringBuilder str = new StringBuilder(); str.Append("妙生活"); var co ...

  4. C#中string与byte[]的转换帮助类

    在写C#程序时,string和byte[]之间的转换比较烦,在移植一些老程序时感觉很不好.我在C#中使用DES和TripleDES时移植一块老代码时也遇到了同样的情况.为了下次不为同样的事情烦恼,就写 ...

  5. java object数组转实体类_详解Java中String JSONObject JSONArray List实体类转换

    JSON使用阿里的fastJson为依赖包 gradle依赖管理如下: compile group: "com.alibaba", name: "fastjson&quo ...

  6. Java中String类的方法及说明

    String : 字符串类型 一.构造函数      String(byte[ ] bytes):通过byte数组构造字符串对象.      String(char[ ] value):通过char数 ...

  7. java集合——集合与数组间的转换+算法

    [0]README 0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java集合--集合与数组间的转换+算法 的相关知识: 0.2) for full sour ...

  8. 【转载】Java中String类的方法及说明

    转载自:http://www.cnblogs.com/YSO1983/archive/2009/12/07/1618564.html String : 字符串类型 一.构造函数      String ...

  9. java string设置编码_详解Java中String类型与默认字符编码

    为什么写这个 至于为什么要写这个,主要是一句mmp一定要讲,绕了一上午,晕死 Java程序中的中文乱码问题一直是一个困扰程序员的难题,自己也不例外,早在做项目时就遇到过很多编码方式的坑,当时想填来着, ...

最新文章

  1. linux-远程管理-xshell
  2. Eclipse java中一个工程引用另一个工程的类
  3. iOS-保存图片到(自定义)相册步骤
  4. SpringCloud工作笔记072---同一浏览器上不同标签页_tab页或者同一浏览器新开一个浏览器窗口也能实现单点登录_单点登录_localStorage_sessionStorage
  5. leetcode python3 简单题1.Two Sum
  6. eclipse -xmx -xms_JVM 调优之 Eclipse 启动调优实战
  7. 手机音频拼接软件_几款好用的修音软件,有需要的小伙伴快来下载吧
  8. 最新php在线扒站程序源码分享
  9. 固定资产管理系统对企业的意义?
  10. 用pc抓取vlan tag数据包
  11. word插入页眉图片
  12. 深度linux笔记本独显,双显卡笔记本安装Deepin教程
  13. 十大热门行业公布 成都仍是跳槽首选
  14. 描述性物理海洋学--第一章学习笔记
  15. 中国越野汽车电子市场趋势报告、技术动态创新及市场预测
  16. 行业标准 | 证券期货业信息系统运维管理规范 JR/T 0099—2012
  17. js特效------实例
  18. 表白墙源码/可用作博客论坛都是不错的
  19. mysql导表1874错误_mysqldump只导出表结构或只导出数据的实现方法 | 吴老二
  20. WinSnap中文版v5.3.6-屏幕截图工具

热门文章

  1. 逻辑卷、物理卷、卷组相关操作
  2. Oracle imp字符集转换,imp/exp 字符集转换
  3. 训练softmax分类器实例_第四章.模型训练
  4. linux登oracle登陆不了,oracle: linux服务器本机不能登陆的解决
  5. python怎么输出小数部分_python 输出小数控制
  6. python对excel操作简书_Python实现EXCEL常用操作——pandas简介
  7. 声明对象_计算机各语言数据类型及对象声明之区别
  8. Flask实战----做了一个简易版CSDN
  9. 腾讯云【人脸识别】服务的一次尝试(JAVA)
  10. LeetCode:62. 不同路径