Java | Java 基础语法(上)

I、注释、标识符、关键字

1.1 注释

  • 平时我们编写代码,在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了。
  • 注释并不会被执行,是我们写代码的人看的。
  • 注释的内容不参与编译,换句话说,编译后生成的.class文件中不包含注释掉的信息。
  • 注释提高了代码的阅读性,是调试代码的重要方法。
  • 书写注释是一个非常好的习惯。
  • Java 中的注释有三种:
    • 单行注释:// 单行注释只能注释一行
    • 多行注释:可以注释一段文字
      /*
      多行注释
      多行注释
      多行注释
      多行注释
      多行注释
      多行注释
      */
      
    • 文档注释(Java特有):后续 JavaDoc 中详细讲解。
      • 格式:
        /**
        * @author 指定Java程序的作者
        * @version 指定源文件的版本
        */
        
      • 注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。
      • 操作方式:javadoc -d mydoc -author -version HelloWorld.java,其中mydoc是说明文档所在目录。

1.2 标识符

  • Java 语言中的变量常量函数语句块等组成部分都需要名字,我们将这些名字统统称为 Java 标识符。
  • Java 标识符由数字字母(A-Z 或 a-z)下划线_)和美元符号$)或人民币符号)组成。
  • Java 标识符是区分大小写的,并且要求首位不能是数字
  • Java 标识符不能包含空格。
  • Java 关键字和保留字不能用作 Java 标识符。
  • 可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音。
  • 合法的标识符:myName, My_name, Points, $points, _sys_ta, OK, _23b, _3_
    非法的标识符:#name, 25name, class, &time, if
  • 如果标识符不符合上述规定,编译则不通过。
  • 命名约定:在起名字时,为了提高阅读性,要尽量有意义,做到“见名知意”。
    • 包名:多单词组成时所有字母都小写。例:mypackage
    • 类和接口名:每个单词的首字母大写,含有大小写。例:MyClass, HelloWorld等。这种命名方式叫做大驼峰式命名
    • 方法名变量名:第一个单词的首字母小写,其余单词首字母大写,含有大小写,尽量少用下划线,少用美元符号。例:myName, setTime 等。这种命名方式叫做小驼峰式命名
    • 常量名:基本数据类型常量名的全部字母都大写,单词与单词之间使用下划线分隔。对象常量可大小混写。例:SIZE_NAME

1.3 关键字

\quad Java关键字是Java语言种事先定义的,有特别意义的标识符,有时又叫保留字,是具有特殊含义的变量。Java关键字是对 Java 编译器有特殊含义的字符串,是编译器和程序员的一个约定,程序员利用关键字来告诉编译器其声明的变量类型方法特性等信息。Java 中一共有 53 53 53 个关键字,字母均为小写。

  • 访问权限控制( 3 3 3 个):private, protected, public
  • 类,方法和变量修饰符( 13 13 13 个):abstract, class, extends, final, implements, interface, native, new, static, strictfp, synchronized, transient, volatile
  • 程序控制( 12 12 12 个):break, continue, return, do, while, if, else, for, instanceof, switch, case, default
  • 异常处理( 6 6 6 个):assert, try, catch, finally, throw, throws
  • 包相关( 2 2 2 个):import, package
  • 基本类型( 12 12 12 个):boolean, byte, char, double, float, int, long, short, null, true, false, enum
  • 变量引用( 3 3 3 个):super, this, void
  • 保留字( 2 2 2 个):goto, const。现有Java版本尚未使用。

II、数据类型

2.1 强弱类型语言

\quad 强类型语言和弱类型语言其判断的根本为是否隐形进行语言类型转变。

2.1.1 强类型语言

\quad 强类型是针对类型检查的严格程度而言的,它指任何变量在使用的时候必须要指定这个变量的类型,而且在程序的运行过程中这个变量只能存储这个类型的数据。因此,对于强类型语言,一个变量不经过强制转换,它永远是这个数据类型,不允许隐式的类型转换。常用的强类型语言有 Java.netC#ApexPython 等。

2.1.2 弱类型语言

\quad 弱类型语言是一种数据类型可以被忽略的语言,它与强类型定义语言相反,一个变量可以赋不同数据类型的值,当某一个变量被定义数据类型,该变量可以根据环境变化自动进行转换,不需要经过强制转换。常用的弱类型语言有 VB, PHP, JavaScript 等。

2.2 动静态语言

2.2.1 动态性语言

\quad 动态性语言是指在运行期间才去做数据类型检查的语言,也就是说动态类型语言编程时,永远不用给任何变量指定数据类型,该语言会在第一次赋值给变量时,在内部将数据类型记录下来。PythonRuby 就是一种典型的动态类型语言。

2.2.2 静态性语言

\quad 静态性语言是指编译期间做数据类型检查的语言,即写程序时要声明所有变量的数据类型。使用数据之前,必须先声明数据类型(int, float, double 等)。相当于在使用之前,首先要为它们分配好内存空间。C/C++ 是静态类型语言的典型代表。Java也是一种静态性语言。

2.3 Java 数据类型

2.3.1 原码、反码和补码

\quad 在介绍 Java 数据类型之前,让我们先了解一下计算机是如何对数值进行存储的。数值在计算机中的表示形式统称为机器数。计算机中处理数据及运算都是采用二进制,通常规定机器数用八位二进制表示。实用的数据有正数负数,因为计算机只能表示 0、1 两种状态,数据的正号“+”或负号“-”,在计算机里就用一位二进制的 01 来区别,通常放在最高位,称为符号位符号位数值化之后,为能方便的对机器数进行算术运算、提高运算速度,计算机设计了多种符号位与数值一起编码的方法,最常用的机器数表示方法有:原码反码补码,下面就分别介绍一下它们的表示方法。

(1)用法
  • 原码:原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数为 0,负数为 10 有两种表示:+0-0),其余位表示数值的大小。例:-7原码10000111.。
  • 反码正数的反码和原码一样负数的反码就是在原码的基础上符号位保持不变其他位按位取反。对于单个数值(二进制的 01)而言,对其进行取反操作就是将 0 变为 11 变为 0。例:-7反码11111000
    • 负数反码转原码:符号位保持不变,数值位按位取反。
  • 补码正数的补码和原码,反码相同负数的补码则是在原码的基础上符号位保持不变其他位按位取反,然后在末尾加 1在计算机系统中,数值一律用补码来表示和存储。例:-7补码11111001
    • 负数补码转原码:补码的补码即是原码。
(2)为什么要使用原码、反码和补码?

\quad 现在我们知道了计算机可以有三种编码方式表示一个数。对于正数因为三种编码方式的结果都相同:[+1] =[0000 0001] 原 _原 =[0000 0001] 反 _反 =[0000 0001] 补 _补 ,所以不需要过多解释。

\quad 但是对于负数:[-1] =[1000 0001] 原 _原 =[1111 1110] 反 _反 =[1111 1111] 补 _补 ,可见原码、反码和补码是完全不同的。

\quad 对于计算机来说,加减乘除已经是最基础的运算,要设计的尽量简单。计算机辨别“符号位”会让计算机的基础电路设计变得十分复杂,于是人们想出了将符号位也参与运算的方法。同时,根据运算法则减去一个正数等于加上一个负数,即:1-1=1+(-1)=0,所以机器可以只有加法而没有减法,这样计算机运算的设计就更简单了。于是,人们开始探索将符号位参与运算,并且只保留加法的方法.。

  • 首先来看原码计算
    • 计算十进制的表达式:1 - 1 = 0
    • 二进制的表达式:1 - 1 = 1 + (-1) = [0000 0001] 原 _原 + [1000 0001] 原 _原 = [1000 0010] 原 _原
    • [1000 0010] 原 _原 转换成十进制即为 -2,由此可见如果使用原码表示,让符号位也参与计算,显然对于减法来说,结果是不正确的,这也就是为何计算机内部不使用原码表示一个数。为了解决原码做减法的问题,出现了反码
  • 反码操作
    • 计算十进制的表达式:1 - 1 = 0
    • 二进制的表达式:1 - 1 = 1 + (-1) = [0000 0001] 原 _原 + [1000 0001] 原 _原 = [0000 0001] 反 _反 + [1111 1110] 反 _反 = [1111 1111] 反 _反 = [1000 0000] 原 _原
    • [1000 0000] 原 _原 转换成十进制即为 -0,由此发现使用反码计算减法,结果的真值部分是正确的。而唯一的问题其实就出现在“0”这个特殊的数值上。虽然人们理解上 +0 -0 是一样的, 但是 0 带符号是没有任何意义的,而且会有 [0000 0000] 原 _原 [1000 0000] 原 _原 两个编码表示 0于是补码的出现,解决了 0 的符号以及两个编码的问题
  • 补码运算
    • 计算十进制的表达式:1 - 1 = 0
    • 二进制的表达式:1 - 1 = 1 + (-1) = [0000 0001] 原 _原 + [1000 0001] 原 _原 = [0000 0001] 补 _补 + [1111 1111] 补 _补 = [0000 0000] 补 _补 =[0000 0000] 原 _原
    • [0000 0000] 原 _原 转换成十进制即为 0,这样 0[0000 0000] 表示,而以前出现问题的 -0 则不存在了。
    • 可以用 [1000 0000] 表示 -128-1 - 127 = (-1) + (-127) = [1000 0001] 原 _原 + [1111 1111] 原 _原 = [1111 1111] 补 _补 + [1000 0001] 补 _补 = [1000 0000] 补 _补
    • 在使用补码运算的结果中,[1000 0000] 补 _补 就是 -128,但是注意因为实际上是使用以前的 -0 的补码来表示 -128,所以 -128 并没有原码和反码表示。(对 -128 的补码表示 [1000 0000] 补 _补 算出来的原码是 [0000 0000] 原 _原 ,这是不正确的)

\quad 使用补码,不仅仅修复了 0 的符号以及存在两个编码的问题,而且还能够多表示一个最低数。这就是为什么 8 位二进制,使用原码或反码表示的范围为 -127 ~ 127,而使用补码表示的范围为 -128 ~ 127

\quad 也正因为机器使用补码来表示和存储,所以对于编程中常用到的 32int 类型,可以表示范围是: [ − 2 31 , 2 31 − 1 -2^{31}, 2^{31}-1 231,2311],因为第一位表示的是符号位,而使用补码表示时又可以多保存一个最小值。

2.3.2 基本数据类型

(1) 数值类型(有符号数值需要保留一个二进制位存储符号)
1)整数类型(1 个字节 [byte] 为 8 个二进制位 [bit] )

\quad 整数在计算机中一般使用整型处理,其在内存中的存储形式为二进制补码

  • byte:占用 1 个字节,范围 [ − 2 7 , 2 7 − 1 -2^7, 2^7-1 27,271],即 -128 ~ 127
  • short:占用 2 个字节,范围 [ − 2 15 , 2 15 − 1 -2^{15}, 2^{15}-1 215,2151],即 -32,768 ~ 32,767
  • int:占用 4 个字节,范围 [ − 2 31 , 2 31 − 1 -2^{31}, 2^{31}-1 231,2311],即 -2,147,483,648 ~ 2,147,483,647
  • long:占用 8 个字节,范围 [ − 2 63 , 2 63 − 1 -2^{63}, 2^{63}-1 263,2631],即 -9,223,372,036,854,774,808 ~ 9,223,372,036,854,774,807声明时须在数值后加L”,“L” 理论上不区分大小写,但是若写成 “l” 容易与数字 “1” 混淆,不容易分辩。所以最好大写。
2)浮点类型(浮点数是对实数的一种近似表示)
① 浮点数表示法(表示)

\quad 浮点数表示法采用了科学计数法来表示实数,即用一个有效数字、一个基数、一个指数以及一个表示正负的符号来表达实数。例:666.66用十进制科学计数法可以表示为 6.6666 × 1 0 2 6.6666\times10^2 6.6666×1026.6666为有效数字,10为基数,2为指数)。浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数。

\quad 当然,对实数的浮点表示仅作如上的规定是不够的,因为同一实数的浮点表示还不是唯一的。例:上面例子中的666.66可以表示为 0.66666 × 1 0 3 0.66666×10^3 0.66666×103 6.6666 × 1 0 2 6.6666×10^2 6.6666×102 66.666 × 1 0 1 66.666×10^1 66.666×101三种方式。因为这种表示的多样性,因此有必要对其规范化以达到统一表示。规范的浮点数表示方式 ± d . d d ⋯ d × β e ( 0 ≤ d i < β ) \pm d.dd\cdots d\times\beta^e(0\leq d_i<\beta) ±d.ddd×βe(0di<β),其中 d . d d ⋯ d d.dd\cdots d d.ddd 为有效数字, β β β 为基数, e e e 为指数。

\quad 有效数字中数字的个数称为精度,我们可以用 p p p 来表示,即可称为 p p p 位有效数字精度。每个数字 d d d 介于 0 0 0 和基数 β β β 之间,包括 0 0 0。更精确地说, ± d 0 . d 1 d 2 ⋯ d p − 1 × β e \pm d_0.d_1d_2\cdots d_{p-1}\times\beta^e ±d0.d1d2dp1×βe 表示数: ± ( d 0 + d 1 β − 1 + d 2 β − 2 + ⋯ + d p − 1 β − ( p − 1 ) ) β e   ( 0 ≤ d i < β ) \pm(d_0+d_1\beta^{-1}+d_2\beta^{-2}+\cdots+d_{p-1}\beta^{-(p-1)})\beta^e\ (0\leq d_i<\beta) ±(d0+d1β1+d2β2++dp1β(p1))βe (0di<β)

\quad 其中,对十进制的浮点数,即基数 β β β 等于10的浮点数而言,上面的表达式非常容易理解。例:12.34可以根据上面的表达式表示为: 1 × 1 0 1 + 2 × 1 0 0 + 3 × 1 0 − 1 + 4 × 1 0 − 2 1×10^{1}+2×10^{0}+3×10^{-1}+4×10^{-2} 1×101+2×100+3×101+4×102,其规范化的浮点数表达为 1.234 × 1 0 1 1.234×10^1 1.234×101。对于二进制的浮点数来说,唯一不同之处在于:二进制的 β β β 等于2,而每个数字 d d d 只能在01之间取值。例:1001.101可以根据上面的表达式表示为: 1 × 2 3 + 0 × 2 2 + 0 × 2 1 + 1 × 2 0 + 1 × 2 − 1 + 0 × 2 − 2 + 1 × 2 − 3 1×2^{3}+0×2^{2}+0×2^{1}+1×2^{0}+1×2^{-1}+0×2^{-2}+1×2^{-3} 1×23+0×22+0×21+1×20+1×21+0×22+1×23,其规范化的浮点数表达 1.001101 × 2 3 1.001101×2^3 1.001101×23

\quad 对于二进制的浮点数来说,向左移动二进制小数点一位相当于这个数除以2,而向右移动二进制小数点一位相当于这个数乘以2。例:101.11=5.75,而10.111=2.875。除此之外,我们还可以得到这样一个基本规律:一个十进制小数要能用浮点数精确地表示最后一位必须是5(当然这是必要条件,并非充分条件)。

② IEEE 754(存储)

\quad IEEE1985年制订了二进制浮点算术标准IEEE 754IEEE Standard for Binary Floating-Point ArithmeticANSI/IEEE Std 754-1985),又称IEC 60559:1989,同年被美国引用为ANSI标准。该标准提供了如何在计算机内存中以二进制的方式存储十进制浮点数的具体标准,它定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷INF与非数值NaN),以及这些数值的 “浮点数运算符”。目前,几乎所有的计算机都支持IEEE 754标准。

\quad IEEE 754标准规定一个浮点数 (Value) 可以表示为: V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t Value=(-1)^{Sign}×Significand×2^{Exponent} Value=(1)Sign×Significand×2Exponent

  • 符号位Sign)决定浮点数是正数(Sign=0)还是负数(Sign=1),而对于数值0的符号位则作为特殊情况处理;
  • 指数位(Exponent)指定了小数点在数据中的位置,指数位的长度越长则数值的表达范围就越大。指数可以是负数,也可以是正数,所以指数为采用了移码存储的方式,后文详解。现在只需了解计算机中存储的是指数位偏移后的值(指数位的值 + 偏移量)即可,IEEE 754标准规定该偏移量为 2 e − 1 − 1 2^{e-1}-1 2e11e为指数位的bit长度),float127double1023
  • 有效数字位(Significand)是规范化表达的二进制小数,它的取值范围为 1 ≤ 1. M < 2 1\leq1.M<2 11.M<2(规约化)或 0 < 0. M < 1 0<0.M<1 0<0.M<1(非规约化),其中M表示有效数字位中的尾数(Mantissa),也称为小数(fraction),即小数点右侧的数字。

\quad 因此,每一个浮点数在计算机中的存储都可以分成下面三个部分,由于规范化表达的二进制小数的最高位都是1,所以计算机中只存储尾数位:

符号位(Sign)指数位偏移后的值(Exponent bias)尾数位(Mantissa)
③ 规约形式浮点数(normal number)

\quad 如果IEEE 754标准浮点数中指数位偏移后的值在 [ 1 , 2 e − 2 ] [1, 2^e - 2] [1,2e2]e为指数位的bit长度),即指数位不全为0(单精度float:二进制0000 0000,数值0;双精度double:二进制000 0000 0000,数值0),也不全为1(单精度float:二进制1111 1111,数值255;双精度double:二进制111 1111 1111,数值2047),那么这个浮点数就被称为规约形式浮点数。“规约”是指用唯一确定的浮点形式去表示一个值。对于规约浮点数,其有效数(significand=1+Mantissa)大于等于1且小于2(隐式前导位为1)。

  • 单精度浮点数float的指数位-127128和双精度浮点数double的指数位值-10231024被用作特殊值处理。

\quad 因此,规约形式的单精度浮点数float的指数位偏移后的值的范围为[1, 254],这也可以反推出规约形式的单精度浮点数对应的指数部分实际取值是[-126, 127]。同理,规约形式的双精度浮点数double的指数位偏移后的值的范围为[1, 2046],双精度浮点数double对应的指数部分实际取值是[-1022, 1023]

④ 非规约形式浮点数(subnormal number)
  • 由规约形式的浮点数部分的内容,我们可以推算出,规约形式的单精度浮点数float的取值范围为:
    \quad V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t = ± [ 1 , 2 ) × 2 [ − 126 ,   127 ] = ( − 2 × 2 127 ,   − 1 × 2 − 126 ] ∪ [ 1 × 2 − 126 ,   2 × 2 127 ) Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm[1,2)×2^{[-126,\ 127]}=(-2×2^{127},\ -1×2^{-126}]\cup[1×2^{-126},\ 2×2^{127}) Value=(1)Sign×Significand×2Exponent=±[1,2)×2[126, 127]=(2×2127, 1×2126][1×2126, 2×2127)
    转换为十进制后的大致范围是: [ − 3.40 × 1 0 38 , − 1.18 × 1 0 − 38 ] ∪ [ 1.18 × 1 0 − 38 , 3.40 × 1 0 38 ] [-3.40×10^{38},-1.18×10^{−38}]\cup[1.18×10^{−38},3.40×10^{38}] [3.40×1038,1.18×1038][1.18×1038,3.40×1038]
  • 规约形式的双精度浮点数double的取值范围为:
    \quad V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t = ± [ 1 , 2 ) × 2 [ − 1022 , 1023 ] = ( − 2 × 2 1023 ,   − 1 × 2 − 1022 ] ∪ [ 1 × 2 − 1022 ,   2 × 2 1023 ) Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm[1,2)×2^{[-1022, 1023]}=(-2×2^{1023},\ -1×2^{-1022}]\cup[1×2^{-1022},\ 2×2^{1023}) Value=(1)Sign×Significand×2Exponent=±[1,2)×2[1022,1023]=(2×21023, 1×21022][1×21022, 2×21023)
    转换为十进制后的大致范围是: [ − 1.79 × 1 0 308 ,   − 2.23 × 1 0 − 308 ] ∪ [ 2.23 × 1 0 − 308 ,   1.79 × 1 0 308 ] [-1.79×10^{308},\ -2.23×10^{-308}]\cup[2.23×10^{-308},\ 1.79×10^{308}] [1.79×10308, 2.23×10308][2.23×10308, 1.79×10308]

\quad 由此可见,使用规约形式的浮点数我们无法表示非常接近0的极小的数(即 ( − 2 − 126 , 2 − 126 ) (-2^{-126},2^{-126}) (2126,2126) ( − 2 − 1022 , 2 − 1022 ) (-2^{-1022},2^{-1022}) (21022,21022) 之间的数),非规约形式的浮点数的出现就将解决这个问题。如果浮点数中指数位偏移后的值是0(二进制0000 0000),有效数字中的最高有效位为0且尾数部分非0,那么这个浮点数将被称为非规约形式的浮点数。一般是某个数字相当接近零时才会使用非规约型式来表示。

\quad IEEE754标准规定:非规约形式的浮点数的指数偏移值比规约形式的浮点数的指数偏移值小1,即非规约的单精度浮点数float的偏移值为126,非规约的双精度浮点数double的偏移值为1022

\quad 因此,非规约形式的单精度浮点数float的指数的实际值为-126,非规约形式的双精度浮点数double的指数的实际值为-1022

\quad 因此,非规约形式的单精度浮点数float的取值范围为: V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t = ± ( 0 , 1 ) × 2 − 126 = ( − 2 − 126 ,   0 ) ∪ ( 0 ,   2 − 126 ) Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm(0,1)×2^{-126}=(-2^{-126},\ 0)\cup(0,\ 2^{-126}) Value=(1)Sign×Significand×2Exponent=±(0,1)×2126=(2126, 0)(0, 2126);非规约形式的双精度浮点数double的取值范围为: V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t = ± ( 0 , 1 ) × 2 − 1022 = ( − 2 − 1022 ,   0 ) ∪ ( 0 ,   2 − 1022 ) Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm(0,1)×2^{-1022}=(-2^{-1022},\ 0)\cup(0,\ 2^{-1022}) Value=(1)Sign×Significand×2Exponent=±(0,1)×21022=(21022, 0)(0, 21022)

\quad 仔细看下,非规约形式的单精度浮点数的区间范围和规约形式的单精度浮点数的区间范围正好构成了 ( − 2 × 2 127 ,   0 ) ∪ ( 0 ,   2 × 2 127 ) (-2×2^{127},\ 0)\cup(0,\ 2×2^{127}) (2×2127, 0)(0, 2×2127)非规约形式的双精度浮点数的区间范围和规约形式的双精度浮点数的区间范围正好构成了 ( − 2 × 2 1023 ,   0 ) ∪ ( 0 ,   2 × 2 1023 ) (-2×2^{1023},\ 0)\cup(0,\ 2×2^{1023}) (2×21023, 0)(0, 2×21023)0是作为特殊值存在的。

⑤ 特殊值(non-number)
  • 如果浮点数的指数位偏移后的值是0(实际指数值-127-1023),且有效数的尾数位部分是0,则这个数是 ± 0 \pm 0 ±0(和符号位相关)。
  • 如果浮点数的指数位偏移后的值是 2 e − 1 2^e - 1 2e1(实际指数值1281024),且有效数的尾数位部分是0,则这个数是 ± ∞ \pm \infty ±(和符号位相关)。当floatdouble存储的值超过了它的范围,就会是 ± ∞ \pm \infty ±
  • 如果浮点数的指数位偏移后的值是 2 e − 1 2^e - 1 2e1(实际指数值1281024),且有效数的尾数位部分0,则这个数是非数(NaN)。
⑥ float

\quad float占用4个字节,即 32 个二进制位。声明float型时须在数值后加fF”,“f” 不区分大小写。因为float占用的存储空间与int相同,同样是4字节32位,所以同样只能表示4,294,967,296个数值。但float类型可表示的数值范围却要远大于int,这是因为float存储数据的间隔不是等间距的,而是在0的附近间距小,在远离0的位置间距大,当要表示间距中间的一个数字时,只能找它附近离它最近的一个可以表示的数字来代替,这也相应的产生了精度问题。

  • float的存储:float4字节32位划分为符号位指数位尾数位三个部分来存储。
    • 1 b i t 1bit 1bitSign符号位):占据最高位(第31位),用于表示浮点数是正数还是负数,为 0 表示正数,为 1 表示负数。

    • 8 b i t s 8bits 8bitsExponent指数位):占据第30位到第23位这8位,这里采用了移码存储的方式来存储指数,float的指数范围为-127 ~ 128

      • 移码存储:浮点型的指数位都有一个固定的偏移量bias),用于使指数 + 这个偏移量 = 一个非负整数,这样指数位就无需考虑如何表示负数了。IEEE规定了在32位单精度类型中,偏移量是127,在64位双精度类型中,偏移量是1023。后文针对移码存储进行了详解。
    • 23 b i t s 23bits 23bits(尾数位,占据剩余的第 22 位到第 0 位这 23 位,尾数位用的是原码)

      • 尾数位决定了计算精度。因为浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,所以float虽然尾数只有23位,但实际上可用来表示尾数的有24位。精度问题就相当于变成这样的问题:24位的二进制可以精确表示几位十进制?因为 lg ⁡ 2 24 = 7.22 \lg2^{24}=7.22 lg224=7.22。所以float的精度为7,即可以精确表示7位有效数字,第8位为近似位。
        • 10xFFFFFF24位全116777215)的整数均可以表示为不超过24位的二进制整数,因为二进制尾数部分不超过23位,因而可以准确的用浮点数表示。
        • 还有一部分整数,在表示为二进制的科学计数法时,尾数部分不超过23位,也可以准确表示。除此之外,其他整数在转化为float后均会损失精度。
    • 示例:float存储825010000000111010
      在这里插入图片描述

⑦ double

\quad double占用8个字节,即64个二进制位。double的取值范围为 − 2 1024 -2^{1024} 21024 ~ 2 1024 2^{1024} 21024,也即 − 1.79 e + 308 -1.79e+308 1.79e+308 ~ 1.79 e + 308 1.79e+308 1.79e+308

  • double的存储:double8字节64位划分为符号位指数位尾数位三个部分来存储。
    • 1 b i t 1bit 1bit(符号位,占据最高位(第 63 位),用于表示浮点数是正数还是负数,为0表示正数,为1表示负数)
    • 11 b i t s 11bits 11bits(指数位,占据第 62 位到第 52 位这 11 位),double 的指数范围为 -1023 ~ 1024
    • 52 b i t s 52bits 52bits(尾数位,占据剩余的第 51 位到第 0 位这 52 位,尾数位用的是原码)
      • 尾数位决定了计算精度。对于双精度浮点数而言, lg ⁡ 2 53 = 15.95 \lg2^{53}=15.95 lg253=15.95。所以double的精度为16,即可以表示16位有效数字,第17位为近似位。
    • 示例:double 存储 8250(10000000111010)

在这里插入图片描述

⑧ 舍入误差

\quad 由于尾数位长度有限,浮点数能够精确表示的数是有限的,因而也是离散的。在两个可以精确表示的相邻浮点数之间,必定存在无穷多实数是IEEE浮点数所无法精确表示的。如何用浮点数表示这些数,IEEE 754的方法是用距离该实数最近的浮点数来近似表示。

\quad 为了将一个无法精确表示的数字舍入到其最接近的浮点数,IEEE 754提供了四种舍入方式:向偶数舍入、向下舍入、向上舍入以及向0舍入。

  • 向偶数舍入:向偶数舍入是计算机的默认舍入方式,也称为向最接近的值舍入,会将结果舍入为最接近且可以表示的值。 向偶数舍入确保舍入后的最低有效数字是偶数, 对于二进制而言,就代表舍入后最低有效数字必须为0。向偶数舍入只有两条规则:
    • 如果最接近的值唯一,则直接向最接近的值舍入,针对十进制就是四舍六入
    • 如果是处在“中间值”,那么要看保留位是否是偶数,如果是偶数则直接舍去后面的数不进位,如果是奇数则进位后再舍去后面的数。
      • 求中间值的方法:1)保留位和左边的数字保持不变;2)近似位(保留位右一位)改写为 N 2 \displaystyle\frac{N}{2} 2NN为进制数,十进制就是10,二进制就是2);3)粘滞位(近似位右侧所有位)全部写0
    • 示例:
      • 十进制数保留一位小数。

        原始值中间值向偶数舍入备注
        1.361.351.4原始值比中间值,也就是“四舍六入”中的“六入”,所以向十分位进一。
        1.7511.751.8原始值比中间值,也就是“四舍六入”中的“六入”,所以向十分位进一。
        1.741.751.7原始值比中间值,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。
        1.821.851.8原始值比中间值,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。
        1.751.751.8原始值和中间值相等,保留位是奇数,则先向保留位进一,之后舍弃近似位和粘滞位。
        1.851.851.8原始值和中间值相等,保留位是偶数,则直接舍弃近似位和粘滞位。
      • 二进制数保留一位小数。

        原始值中间值向偶数舍入备注
        1.1111.1110.0原始值比中间值,也就是“四舍六入”中的“六入”,所以向小数点右边第1位进一。
        1.01011.011.1原始值比中间值,也就是“四舍六入”中的“六入”,所以向小数点右边第1位进一。
        1.0011.011.0原始值比中间值,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。
        1.101.111.1原始值比中间值,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。
        1.1101.1110.0原始值和中间值相等,保留位是奇数,则先向保留位进一,之后舍弃近似位和粘滞位。
        1.0101.011.0原始值和中间值相等,保留位是偶数,则直接舍弃近似位和粘滞位。
  • 向下舍入:向负无穷 − ∞ -\infty 方向舍入。对负数而言,多余位全为0则直接截尾,不全为0则向最低有效位进1;正数的话不管多余位是多少直接截尾即可。
  • 向上舍入:向正无穷 + ∞ +\infty +方向舍入。对正数而言,多余位全为0则直接截尾,不全为0则向最低有效位进1;负数的话不管多余位是多少直接截尾即可。
  • 0舍入:向数轴零点方向舍入,直接截尾即可。
⑨ 详解精度丢失

\quad 存在一些小数在转换成二进制小数的过程中无法准确的用二进制进行表达,例:11.11的小数部分转成二进制如下所示
\qquad 0.11 × \times ×2=0.22 ⋯ \cdots 0
\qquad 0.22 × \times ×2=0.44 ⋯ \cdots 0
\qquad 0.44 × \times ×2=0.88 ⋯ \cdots 0
\qquad 0.88 × \times ×2=1.76 ⋯ \cdots 1
\qquad 0.76 × \times ×2=1.52 ⋯ \cdots 1
\qquad 0.52 × \times ×2=1.04 ⋯ \cdots 1
\qquad 0.04 × \times ×2=0.08 ⋯ \cdots 0
\qquad 0.08 × \times ×2=0.16 ⋯ \cdots 0
\qquad 0.16 × \times ×2=0.32 ⋯ \cdots 0
\qquad 0.32 × \times ×2=0.64 ⋯ \cdots 0
\qquad 0.64 × \times ×2=1.28 ⋯ \cdots 1
\qquad 0.28 × \times ×2=0.56 ⋯ \cdots 0
\qquad 0.56 × \times ×2=1.12 ⋯ \cdots 1
\qquad ⋯ ⋯ \cdots\cdots
\qquad // 无限循环下去
\quad 所以,11.11用二进制表示为1011.000111000010100011110101110......,用”科学计数法“表示为: 1.011000111000010100011110101110...... × 2 3 1.011000111000010100011110101110......\times2^3 1.011000111000010100011110101110......×23,使用float类型存储的23位尾数为0110 0011 1000 0101 0001 111,后面的数字被截断,由此就导致了精度的丢失。

\quad 很多浮点数在二进制环境下都无法完整的表示,只能截取部分数据来近似的表示,两个数相加的话,就是两个近似的数相加的和,如果相加次数足够多,精确度就会越来越低。

⑩ 详解移位存储

\quad 32 位单精度 float 内存模型为例

\quad 如图所示,通常情况下 8 位存储空间可以存储的数值范围为00000000 ~ 11111111,也就是从 0 开始到 255 结束,一共 256 个数。但是浮点数的幂指数阶码:在机器中表示一个浮点数时需要给出指数,这个指数用整数形式表示,这个整数叫做阶码)是有必要使用负数的。

\quad 既要表示正数,又要表示负数,我们就需要拿出一位来表示正负号。按照传统的方式,使用一个字节的最高位(最左边那位)表示正负,最高位为1时表示负数,为0时表示正数,那么我们就可以得到两个区间:0 0000000 ~ 0 1111111,即+01271 0000000 ~ 1 1111111,即-0-127。这样就会出现20,一个+0,一个-0

\quad 使用移位存储方式会有什么效果呢? 这里我们使用单精度类型(float)举例,float移位存储要在原指数基础上加偏移量127(0111 1111)

  • 使用移位存储方式表示正数:
    • 如果我们要表示0,则有0 + 127 = 127,即00000000 + 01111111 = 01111111
    • 如果我们要表示1,则有1 + 127 = 128,即00000001 + 01111111 = 10000000
    • 如果我们要表示2,则有2 + 127 = 129,即00000010 + 01111111 = 10000001
    • ⋯ ⋯ \cdots\cdots
    • 如果我们要表示128,则有128 + 127 = 255,即10000000 + 01111111 = 11111111128是我们能够使用8位二进制移位存储算法表示的最大正数,再大就会溢出。
  • 同样,我们来看看负数
    • 如果我们要表示-1时,则有 (-1) + 127 = 126,即-00000001 + 01111111 = 01111110
    • 如果我们要表示-2时,则有 (-2) + 127 = 125,即-00000010 + 01111111 = 01111101
    • ⋯ ⋯ \cdots\cdots
    • 如果我们要表示-127时,则有 (-127) + 127 = 0,即-01111111 + 01111111 = 00000000-127是我们能够使用8位二进制移位存储算法表示的最小负数,再小就会溢出。

\quad 由上面的例子,我们可以得出规律:采用移位存储技术,我们可以使用8位二进制来表示从-127 ~ 128,共计:127个负数 + 零(0) + 128个正数 = 256个数。

\quad 为什么要用移码表示阶码,而不用补码表示阶码? 采用这种方式表示的目的是为了简化比较。因为指数必须是有符号数才能表达很大或很小的数值,如果采用补码表示的话,首先,尾数位是有符号的,如果指数也是有符号的,那么就不能简单的对浮点数进行大小比较,因为根据补码比较大小的话,要先将补码转换成原码再进行比较。正因为如此,将指数的值调整到一个正值的范围内以便进行比较。所以移码不存在符号位,我们直接可以由移码的表示形式看出对应数值的大小。

2. 字符(char)类型
  • Java中的char类型占用2个字节(16bits),可以存放单个汉字单个字母单个数字其他 Unicode 字符等(例:A='中'A=‘s’)。
  • 定义char类型变量时通常使用一对单引号' '单引号内部只能写一个字符
  • char类型变量必须进行赋值(空格字符也可以),不可以是空字符''
  • 可以使用ASCII码或Unicode码对char类型进行赋值,例:a=97a='\u0061'
  • 所有字符的本质还是数字,最小值是 \u0000(十进制等效值为 0);最大值是 \uffff(十进制等效值为 65535)。
  • 字符前面加上斜线"\"来表示常见的那些不能显示的ASCII字符,称为转义字符。如:\0(空字符(NULL)),\t,\n等,就称为转义字符,因为后面的字符,都不是它本来的ASCII字符意思了。
3. 布尔(boolean)类型
  • Javaboolean视为完全独立的数据类型,该数据类型具有2个不同的值:truefalse
  • 10int类型,不能隐式转换为boolean,所以不可以直接赋给boolean数据类型。
  • boolean数据类型常用于条件判断循环结构
  • boolean 类型的变量在底层实际会调用 int,那么既然 int 占用 4 个字节,boolean 也自然占用 4 个字节。
  • boolean 类型的数组在底层实际会调用 byte,那么既然 byte 占用 1 个字节,boolean 数组中的 boolean 也就占用 1 个字节。

2.3.2 引用数据类型(后续详解)

  • 类(class
  • 接口(interface
  • 数组([]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软耳朵DONG

觉得文章不错就鼓励一下作者吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值