Java数据类型之细讲char类型与编码关系

一、开头

Java是一种强类型的语言,这意味着必须为每一个变量声明一种类型

Java中一共有8种基本数据类型(整形有4种,浮点型有2种,字符型1种,还有一种布尔类型)

由于Java程序必须保证在所有机器上都能得到相同的运行结果,所以各种数据类型的取值范围必须是固定的

二、整形

整形共有4种

  • byte:一个字节
  • short:2个字节
  • int:4个字节(刚好超过二十亿)
  • long int:8个字节

这里要注意的一些地方是

  • 长整形数值有一个后缀L或者l
  • 十六进制数值有一个前缀0x或者0X
  • 八进制有一个前缀0(容易混淆,不推荐使用)
  • 从Java7(JDK1.7)开始可以使用0b或者0B写二进制
  • 从JAVA7开始,还可以为数字字面量加下划线,如使用1_000_000表示100W(Java编译器会去除这些下划线)

开头已经说了,各种数据类型的取值范围必须是固定的,所以4种整形的范围都为有符号位的范围,Java也因此没有Unsigned符号。

所以针对Unsigned的整形,基本数据类型的包装类有对应的API的

这里首先要认识的一点是,只要不溢出,加法、减法和乘法都能正常计算,但除法是会出问题的

三、Byte.toUnsignedInt

这个API的功能是针对Unsigned的Byte的转化成Unsigned

从源码上看,其实就是将其强制转换成int,相当于扩大了位数,然后通过与上0xff,0xff是十六进制,转化成二进制就是11111111,这个与运算的作用其实为了限制位数,因为byte是1个字节,顶多只有8位,超过8位的那些都不要,对于Unsigned来说,应该都为0.

四、Integer.divideUnsigned

这个API的功能是针对Unsigned的int类型除法的


可以看到,他的处理与Byte一样,都是转化成更高位的类型,这里转化成long,然后通过与运算舍弃后面多出来的位数(其实是改为0)

五、Integer.remainderUnsigned

这个是用来求余数的

可以看到同样也是转化成更高位去处理

六、Long.divideUnsigned

现在Long没有更高位了怎么办呢?

下面是源码

 public static long divideUnsigned(long dividend, long divisor) {
        //divisor是除数
        //而divident是被除数

        //首先判断除数是否为Unsigned(<0就代表为unsigned,只不过将符号位看成1,变为负数)
        if (divisor < 0L) { // signed comparison
            // Answer must be 0 or 1 depending on relative magnitude
            // of dividend and divisor.

            //可以看到这里的返回值只有0和1
            //这是因为除数为unsigned,根据整形的向下取整规则
            //得到的结果只能为1和0(dividend大于divisor就为1,小于就为0)
            //dividend不可能为divisor的两倍(因为位数不过)
            return (compareUnsigned(dividend, divisor)) < 0 ? 0L :1L;
        }

        //如果除数不是Unsigned,那么就判断被除数

        if (dividend > 0) //  Both inputs non-negative
            //如果被除数不是Unsigned,就直接除就好
            return dividend/divisor;
        else {
            /*
             * For simple code, leveraging BigInteger.  Longer and faster
             * code written directly in terms of operations on longs is
             * possible; see "Hacker's Delight" for divide and remainder
             * algorithms.
             */
             //如果是,那么就将除数和被除数换成更高位的BigInt型,去进行

            return toUnsignedBigInteger(dividend).
                divide(toUnsignedBigInteger(divisor)).longValue();
        }
    }

下面我们就来看看compareUnsigned方法



MIN_VALUE是代表长整形可以取的最小值,也就是 − 2 63 -2^{63} −263

可以看到,具体的过程就是让两个数减去最小值,然后进行比较

原理是,即使是Unsigned,只要减去了最小值,就不会超过有符号位的范围,然后通过比较减去后的大小,就可以判断除数和被除数谁大谁小,然后就返回0和1。

七、浮点型

浮点型有两种类型,一种为float,一种为double。

这里,我们认识一下精度损失

在两种浮点型,小数都是使用二进制表示的,比如 2 − 1 或 者 2 − 2 2^{-1}或者2^{-2} 2−1或者2−2,也就是0.5,0.125这些,也就是说,有一些小数是无法使用二进制表示的,只能通过后面的位数进行无限逼近,所以就会产生精度损失。

那什么是双精度和单精度呢?

这是根据double和float的位数来区分的,double为8字节,而float为4字节,所以double可以使用更多位数进行逼近,所以double会更加精确。

这里要注意的是,所有的浮点数计算都遵循IEEE754规范

对于表示溢出和出错情况,使用了三个特殊的浮点数值去表示

  • 正无穷大
  • 负无穷大
  • NaN(不是一个数字)

八、字符型

char类型本来用来表示单个字符,但如今有些Unicode字符可以用一个char表示,也就是两个字节,但有时一些Unicode字符需要用多个字节表示,也就是使用多个char表示

char类型的值可以表示为十六进制值,从\u0000~\uffff。这里是\u充当了一个转义序列的功能,同时\u转义序列是可以出现在字符常量或字符串,所以使用注释和参数的时候,要注意一下

在Java中,char类型描述了UTF-16编码中的一个代码单元

九、Unicode

在认识UTF-16前,我们需要认识Unicode

Unicode其实相当于一本很厚的字典,里面储存了世界上所有语言的字符,使用Unicode码点唯一地对应一个字符。

Unicode是没有规定字符对应的二进制码占用的空间是多少,那么问题来了,以“汉”字为例,它的Unicode码点为0x6c49,对应的二进制为110110001001001,也就是15位二进制,也就说明了,这个字需要用2个字节去存储这个字,那么,对于其他字体,很有可能出现3个字节,或者更多的字节去存储,对于计算机来说,计算机怎么知道这两个字节表示的是一个字符,而不是与后面的字节形成一个字符?

所以,为了解决Unicode的这个问题,新的编码方式UTF-8、UTF-16和UTF-32就出现了

十、UTF-8

UTF其实是Unicode Transformation Format的缩写,即统一Unicode编码转换格式

UTF-8的特点就是可变长,即对于不同长度字节的字符有很好的兼容性

编码规则如下

对于单个字节的字符(也就是基本字符),也就是8位,会将第一位设为0,后面的七位会对应这个字符的Unicode码点,因此对于0~ 2 7 2^7 27号字符是完全可以的,甚至与ASCII(另一种编码方式,只不过不支持中文只有英文和符号)完全相同(这时候可能会有人说那么对于 2 8 至 2 7 2^8至2^7 28至27里面的字符呢?其实这一段被分在了使用2个字节表示)

对于需要使用N个字节来表示的字符(N>1),第一个字节的前N位都设为1,第N+1位设为0(用来记录这个字符是用多少个字节来存储的,让计算机可以识别出),剩余后面的N-1个字节的前两位都要设置为10,剩下的二进制位则使用这个字符的Unicode码点来进行补充

Unicode十六进制码点范围 UTF-8二进制
0000 0000 ~ 0000 007F(注意这里只有7位) 0xxxxxxx(对应表示码点的七位)
0000 0080 ~ 0000 07FF(注意这里为11位) 110xxxxx 10xxxxxx (对应码点11位)
0000 0800 ~ 0000 FFFF(注意这里位16位) 1110xxxx 10xxxxxx 10xxxxxx(对应码点16位)
0001 0000 ~ 0010 FFFF(这里为18位) 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(虽然超过码点位数,但不影响表示)

0000 0800 ~ 0000 FFFF(注意这里位16位)1110xxxx 10xxxxxx 10xxxxxx(对应码点16位)0001 0000 ~ 0010 FFFF(这里为18位)11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(虽然超过码点位数,但不影响表示)

通过表格,可以看到UTF-8d的编码其实很简单,下面还是以"汉"为例,具体说一下如何进行UTF-8的编码和解码的

汉的编码为0x00006c49,对应在Unicode十六进制码点范围的第三行,所以对应的UTF-8二进制为 1110 x x x x 10 x x x x x x 10 x x x x x x 1110xxxx 10xxxxxx 10xxxxxx 1110xxxx10xxxxxx10xxxxxx,然后将0x0006c49变为二进制为0x0110110001001001,然后填入到x里面即可(从第最后一位开始),结果为111001101011000110001001,然后再转换成十六进制为:0xE6 0xB7 0x89

解码的过程也十分简单:专为二进制之后,先判断多少个字节,如果第一个字节的第一位是0,则代表是只有1和字节,如果不是,就判断前面总共有多少个1就碰到0,多少个1就是多少个字节,通过知道多少个字节,就可以知道后面要读多少个字节来对应这个字符,只要去掉开头的10就行

十一、UTF-16

在认识UTF-16之前,先认识平面这个东西

前面提到过Unicode编码是一本很厚的字典,将全世界的字符都定义在这个集合里面,但这本字典不是一次性完成的,而是经过持续地收集才完成的,所以也就产生了分区,进行分区定义。每个区可以存放65536,也就是2^16字符,一个区就称为一个平面。目前Unicode一共有17(2 ^ 4+1)个平面(剩余16个为辅助平面),所以整个Unicodez字符集大小为 2 21 2^{21} 221

第一个平面,也就是第一个区,被称为基本平面,前 2 16 2^{16} 216个字符也就被成为基本字符,码点范围也就是从0~ 2 16 − 1 2^{16}-1 216−1,写成十六进制就是从U+0000到U+FFFF,最常见的字符大多都在这个区了。那么剩余的17个区,对应的码点就是从U+10000到U+10FFFF(刚好16倍),那么要如何解决确定字符与字节对应的问题呢?

基本平面有一个很巧妙的地方,在基本平面内,从U+D800到U+DFFF是一个空段,也就是再这个区间内的码点是没有对应任何字符的,因此UTF-16就利用了这个空段来做了一个映射辅助平面的码点(利用基本平面来储存辅助平面)

在辅助平面码点对应的字符总共有 2 20 2^{20} 220个,所以至少需要20个二进制位才可以完全对应辅助平面码点的字符。

UTF-16将这20个二进制位分成一半,前十位映射在U+D800到U+DBFF之间(称为高位),后10位映射在U+DC00到U+DFFF之间(成为低位),所以当遇到多个字节时,如果发现有码点位于这两个段区间,这就意味着这是辅助平面码点的映射,辅助平面字符被拆分成多个基本平面的码点表示

据个栗子

汉字"?“的 Unicode 码点为 0x20BB7,该码点显然超出了基本平面的范围(0x0000 - 0xFFFF),因此需要使用四个字节表示。首先用 0x20BB7 - 0x10000 计算出超出的部分,然后将其用 20 个二进制位表示(不足前面补 0 ),结果为0001000010 1110110111。接着,将前 10 位映射到 U+D800 到 U+DBFF 之间,后 10 位映射到 U+DC00 到 U+DFFF 即可。U+D800 对应的二进制数为 1101100000000000,直接填充后面的 10 个二进制位即可,得到 1101100001000010,转成 16 进制数则为 0xD842。同理可得,低位为 0xDFB7。因此得出汉字”?"的 UTF-16 编码为 0xD842 0xDFB7

现在我们回到字符型里面的重点在Java中,char类型描述了UTF-16编码中的一个代码单元,可以知道char类型采用的是utf-16编码方式,代码单元其实指的就是U+D800~U+DBFF和U+DC00~U+DFFF这两个映射区(这里是两个个代码单元,一个char只能使用一个代码单元,不过通常一个代码单元能表示绝大多数的字符了,但也是因为这个原因,有些字符char不可以完整表示),通过这个代码单元,就可以进行解码获取Unicode编码了

十二、布尔类型

boolean类型有两个值,false和true,用来判断逻辑条件,整形值和布尔值之间是不能互换的

到此这篇关于Java数据类型之细讲char类型与编码关系的文章就介绍到这了,更多相关Java char类型与编码关系内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入了解Java中String、Char和Int之间的相互转换

    在做在线编程题目的时候,需要了解一下数据的输入格式.这样可以对数据处理有比较好的把握,不需要把太多的时间放在这个上面,注重主要的算法逻辑即可.这里总结一下,为之后笔试做个准备. 1.从终端输入的方式 Scanner类的使用方法: Scanner scanner = new Scanner(System.in); 从终端获取输入流,输入流传入Scanner初始化对象时,作为参数传递进去. Scanner类的重要几个方法: next方法(读取一个字符) 1.一定要读取到有效字符后才可以结束输入(要是

  • Java 中的CharArrayReader 介绍_动力节点Java学院整理

    CharArrayReader 介绍 CharArrayReader 是字符数组输入流.它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流.CharArrayReader 是用于读取字符数组,它继承于Reader.操作的数据是以字符为单位! CharArrayReader 函数列表  CharArrayReader(char[] buf) CharArrayReader(char[] buf, i

  • Java Character类对单个字符操作原理解析

    Character 类用于对单个字符进行操作,该对象中包装一个基本类型 char 的值. Character 类提供了一系列方法来操纵字符.你可以使用Character的构造方法创建一个Character类对象,当你将一个char类型的参数传递给需要一个 Character 类型参数的方法时,这时编译器会自动将char类型参数转化为 Character 类型参数,这样我们可以称其为装箱,反之为拆箱. 在 Character 类中,我们还需要了解转义序列(前面有反斜杠()的字符代表转义字符,它对编

  • Java后台批量生产echarts图表并保存图片

    一个围绕统计分析功能的系统,在最后制作统计分析时需要一个批量点击的功能,用以批量制作echarts图形后生成图片并保存图形和图片.方便后续导出. public class EchartsUtils { private static final String JSpath = "C:\\echarts-convert\\echarts-convert1.js"; public static void main(String[] args) { String imgName = "

  • Echarts基本用法_动力节点Java学院整理

    echarts太完美了: 1,开源软件,无私的为我们提供漂亮的图形界面: 2,使用简单,默默的为我们封装了重要的js,只要会引用就会使用echarts: 3,种类多,echarts为我们提供了各种图标,其中最具象征的就是地图了: 4,兼容性好,基于html5动画渲染超棒. echarts官网 提供了源码和说明文档,使用echarts需要先到官网下载需要的js源文件. 官网上的demo中夹杂着很多我们用不到的东西,想使用饼状图就得从demo中把不用的去掉,劈植斩叶留下最原始的功能实现.这样毕竟比较

  • Java基本类型包装类概述与Integer类、Character类用法分析

    本文实例讲述了Java基本类型包装类概述与Integer类.Character类用法.分享给大家供大家参考,具体如下: 基本类型包装类概述 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据. 常用的操作之一:用于基本数据类型与字符串之间的转换. 基本类型和包装类的对应 Byte,Short,Integer,Long,Float,Double,Character,Boolean Integer类 为了让基本类型的数据进行更多的操作,Java就为每种基本类型提供了对应的包装

  • Java中s.charAt(index)用于提取字符串s中的特定字符操作

    charAt(int index)方法是一个能够用来检索特定索引下的字符的String实例的方法. charAt()方法返回指定索引位置的char值.索引范围为0~length()-1. 如: str.charAt(0)检索str中的第一个字符,str.charAt(str.length()-1)检索最后一个字符. 警告:在字符串s中越界访问字符是一种常见的程序设计错误.为避免此类错误要确保使用的下标不会超过s.length()-1. public class hash { public sta

  • java中char对应的ASCII码的转化操作

    java中,char类型变量可以强制转化为int类型变量,int类型变量也可以强制转化成char类型的变量: char c='a'; int i=98; System.out.println((int)c); System.out.println((char)i); 对于数组类型,其下标为int类型,所以可以直接使用char类型变量,默认强制转换: int[] array=new int[100]; for(int i=0;i<array.length;i++){ array[i]=i; } c

  • Java.toCharArray()和charAt()的效率对比分析

    LeetCode中的一道算法题,使用toCharArray()时间超时,换成charAt()之后通过,所以测试一下两者的运行效率: public static void test() { String s = "a"; for(int i = 0; i < 100000; i++) { s += "a"; } long start1 = System.currentTimeMillis(); char[] cs = s.toCharArray(); for(c

  • java char数据类型原理解析

    这篇文章主要介绍了java char数据类型原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 java中的 char 数据类型使用 Unicode 编码,占用两个字节内存. 因为Unicode 采用无符号编码,一共可以存储 0x0000 ~ 0xffff 共65536 个字符, 而 int 是有符号4个字节,刚好一半是2个字节,所以在 java 将 char 看作整数(0-65535),于是我做了一个测试: //unicode 无符号编码

  • javaSystem.out.println()输出byte[]、char[]异常的问题详析

    前言 今天 突然有人问我他写的byte[]和char[],在用System.out.println()输出的时候所得的值有问题? 细思恐惧啊 我之前怎么没发现................ 废话不多说 代码如下 /** * @Author: * @Date: 2019/1/9/00099:23 * @Version 1.0 */ public class arrayTest { public static void main(String[] args) { //创建一个byte数组 byte

  • 如何通过java实现highcharts导出图片至excel

    1. 目的 通过java后台实现将前端页面的highcharts图表导出至生成的excel文件中.使用于报表页面导出类功能. 2. 说明 前端页面将图表的svg信息字符串作为参数传递 后台使用batik工具包生成图片 使用poi工具包操作excel 3. 使用jar包 1.batik-all-1.7.jar 2.poi-3.12.jar 3.commons-codec-1.12.jar 不导入不会报错,但使用时异常,提示信息:Exception in thread "main" jav

  • java读取文件:char的ASCII码值=65279,显示是一个空字符的解决

    java读取文件遇到个很神奇的问题,String str1 = "1"; String str2 = "1"; str1 .equals(str1 ) 返回值是false.且他们的长度是不一样的. 这是个用眼睛看不见的问题,因为有个空字符.这里要去掉他 补充知识:ArrayList.addAll(),clone()都是浅克隆,改变一个集合,另一个会跟着改变 ArrayList.addAll(),clone()都是浅克隆. (浅克隆:当改变克隆对象的引用类型的值(注意

随机推荐