• 首页
  • 关于
    • Pg.lost photo

      Pg.lost

    • 更多
    • LinkedIn
    • Github
    • Weibo
  • 文章
    • 列表
    • 标签
  • 项目

Java编程的逻辑-理解数据背后的二进制

17 Aug 2018

Reading time ~1 minute

  • 2.1 整数的二进制表示与位运算
    • 2.1.1 正整数的二进制表示
    • 2.1.2 负整数的二进制表示
    • 2.1.3 十六进制
    • 2.1.4 位运算
  • 2.2 小数的二进制表示
    • 2.2.1 小数的计算为何会出错
    • 2.2.2 二进制的表示
  • 2.3 字符的编码与乱码
    • 2.3.1 常见的非Unicode编码
  • 2.3.2 Unicode编码
  • 2.3.3 编码转换
  • 2.3.4 乱码的原因
  • 2.3.5 从乱码中恢复
  • 2.4 char的真正含义

2.1 整数的二进制表示与位运算

2.1.1 正整数的二进制表示

2.1.2 负整数的二进制表示

  • 负数的二进制表示就是对应的正数的补码。
  • 补码:原码取反加1
  • 原因:计算机只能做加法

    2.1.3 十六进制

  • 0x
  • Java7之前不支持直接写二进制常量,一般补足8位,用16进制表示
  • Java7开始支持二进制常量 0b或0B

2.1.4 位运算

  • 移位运算
    • 左移:« 向左移动,右边低位补0
    • 无符号右移:»> 向右移动,左边高位补0
    • 有符号右移:» 向右移动,左边补0或者补1取决于以前的最高位。
  • 逻辑运算
    • 按位与
    • 按位或
    • 按位取反
    • 按位异或

2.2 小数的二进制表示

2.2.1 小数的计算为何会出错

不是运算本身会出错,而是计算机无法精确的表示很多数。

  • 转化为整数进行运算,运算完成后再转换为小数
  • 或者使用十进制的数据类型(BigDecimal)

2.2.2 二进制的表示

  • float:32位
  • double:64位

2.3 字符的编码与乱码

2.3.1 常见的非Unicode编码

  1. ASCII
    • American Standard Code for Information Interhange 美国信息互换标准代码
    • 单字节,8位,最高位为0,表示127种字符
  2. ISO 8859-1
    • 西欧
    • 单字节,8位
    • 0-127与ASCII码一致
  3. Windows-1252
    • 与ISO 8859-1基本一致
    • 实际在应用中已经取代ISO 8859-1
  4. GB2312
    • 固定双字节,16位
    • 在两个字节中,最高位都是1。如果是0,就认为是ASCII字符。
  5. GBK
    • 向下兼容GB2312
    • 固定双字节,低字节的最高位可能为0。
    • 在解析二进制流的过程中,如果第一个字节的最高位为1,那么就将下一个字节读进来一起解析为一个汉字。而不用考虑它的最高位。解析完成后,跳到第三个字节继续解析。
  6. GB18030
    • 向下兼容GBK
    • 变长编码,双字节或者4字节。
  7. Big5
    • 固定双字节
    • 繁体

2.3.2 Unicode编码

  • 给世界上所有的字符都分配了一个唯一的数字编号
  • 并未规定这个编号对应到二进制表示
  1. UTF-32
    • 字符编号的整数二进制形式
    • 4字节
    • 高位存放在前BE,否则LE
  2. UTF-16
    • 变长,2字节或4字节
  3. UTF-8
    • 使用字节数1-4不等

2.3.3 编码转换

编码转换改变了字符的二进制内容,但并没有改变字符看上去的样子。

2.3.4 乱码的原因

  1. 解析错误
  2. 错误的解析和编码转换

2.3.5 从乱码中恢复

  1. 使用UltraEdit
  2. 使用Java

         public static void recover(String str) 
             throws UnsupportedEncodingException {
             String[] charsets = new String[]{"windows-1252", "GB18030", "Big5", "UTF-8"};
             for(int i = 0; i < charsets.length; i++) {
                 for(int j = 0; j < charsets.length; j++) {
                     if(i!=j) {
                         String s = new String(str.getBytes(charsets[i]), charsets[j]);
                         System.out.println("--- 原来编码(A)假设是:"+charsets[j]+", 被错误解读为了(B):"+charsets[i]);
                         System.out.println(s);
                         System.out.println();
                     }
                 }
             }
         }
    
    • getBytes 可以获取一个字符串的给定编码格式的二进制形式
    • String(byte bytes[], String charsetName)的构造方法可以给定的二进制数组bytes按照编码格式charsetName解读为一个字符串。

2.4 char的真正含义

  • char本质上是一个固定占用两个字节的无符号正整数,这个整数对应于Unicode编号,这个正整数对应于Unicode编号对应的字符
  • 由于固定占用两个字节,char只能表示Unicode编号在65536以内的字符
  • 超过的部分使用两个char。

char的多种赋值方式:

char c = '马';
char c = 39532;
char c = 0x9a6c;
char c = '\u9a6c'


Java编程的逻辑 Share Tweet +1