二、基本数值类型、数组和类型转换

基本数值类型、数组和类型转换

本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记

1.1 整数类型

整数类型有byteshortintlong,分别占1、2、4、8个字节(一个字节占8 bit),取值范围如下表所示:

类型名存储空间取值范围
byte1 byte − 2 7 ∼ 2 7 − 1 -2^7\sim2^{7}-1 27271
short2 bytes − 2 15 ∼ 2 15 − 1 -2^{15}\sim2^{15}-1 2152151
int4 bytes − 2 31 ∼ 2 31 − 1 -2^{31}\sim2^{31}-1 2312311
long8 bytes − 2 63 ∼ 2 63 − 1 -2^{63}\sim2^{63}-1 2632631
byte b = 23;
short s = 3333;
int i = 9999;
long l = 32323;

在给long类型赋值时,如果常量超过了int的表示范围,需要在常量后面加大写或小写字母L,即Ll,例如:

long a = 3232343433L;

之所以需要加Ll,是因为数字常量默认为是int类型。

1.2 浮点数类型

小数类型有floatdouble,占用的内存空间分别是 4 4 4 8 8 8字节,有不同的取值范围和精度,double表示的范围更大,精度更高,具体如下表所示:

类型名存储空间取值范围
float4 bytes − 3.40 3 38 ∼ 3.40 3 38 -3.403^{38}\sim3.403^{38} 3.403383.40338
double8 bytes − 1.79 8 308 ∼ 1.79 8 308 -1.798^{308}\sim1.798^{308} 1.7983081.798308

对于double,直接把熟悉的浮点数表示赋值给变量即可,但对于float,需要在数字后面加大写字母F或小写字母f。这是由于小数常量默认是double类型。

double d = 333.33;
float f = 333.33f;

1.3 布尔类型

布尔(boolean)类型很简单,直接使用truefalse赋值,分别表示真和假,例如:

boolean b = true;
b = false;

1.4 字符类型

字符类型char用于表示一个字符,这个字符可以是中文字符,也可以是英文字符,char占用的内存空间是两个字节。字符型常量值的表示形式有如下几种:

  1. 用单引号括起来的单个字符的常量值,如'a''9'等,注意:"""是错误的,里面不能没有内容,必须有单个字符;
  2. 转义字符'\n''\t'等;
  3. 直接使用Unicode值,格式是'\u××××',其中××××表示一个16进制的整数;
  4. 直接使用十进制整型常量值,如10098等。
public class TestCharacter{
    public static void main(String[] args){
        char c1 ='a';
        char c2 ='\"'
        char c3 ='\u0043'; 
        char c4 =100;
        System.out.printIn("c1:" + c1);
        System.out.printIn("c2:" + c2);
        System.out.println("c3:" + c3);
        System.out.printIn("c4:" + c4);
    }
}

大部分的常用字符用一个char就可以表示,但有的特殊字符用一个char表示不了,字符型底层依然是以整型(相当于无符号整型)存储的,关于char更多其他细节,后面会展开。

1.5 数组类型

1.5.1 一维数组

基本类型的数组有3种赋值形式,如下所示:

int[] arr = {1,2,3}; // 第一种
int[] arr = new int[]{1,2,3}; // 第二种
int[] arr = new int[3]; // 第三种
arr[0]=1; arr[1]=2; arr[2]=3;

1种和第2种都是预先知道数组的内容,而第3种是先分配长度,然后再给每个元素赋值。第3种形式中,即使没有给每个元素赋值,每个元素也都有一个默认值,这个默认值跟数组类型有关,数值类型的值为0booleanfalsechar为空字符。

数组长度也可以动态确定,如下所示:

int length = ... ; //根据一些条件动态计算
int arr = new int[length];

数组长度虽然可以动态确定,但定了之后就不可以再改变。数组有一个length属性,是只读属性。还有一个小细节,不能在给定初始值的同时给定长度,即如下格式是不允许的:

int[] arr = new int[3]{1,2,3}

我们可以这么理解,因为初始值已经决定了长度,再给个长度,如果还不一致,计算机将无所适从。数组类型和基本类型是有明显不同的,一个基本类型变量,内存中只会有一块对应的内存空间。但数组有两块:一块用于存储数组内容本身,另一块用于存储内容的位置。用一个例子来说明,有一个int变量a,以及一个int数组变量arr,其代码、变量对应的内存地址和内存内容如下表所示:

代码内存地址内存数据
int a=100;1000100
int[] arr={1,2,3}20003000
30001
30042
30083

基本类型a的内存地址是1000,这个位置存储的就是它的值100。数组类型arr的内存地址是2000,这个位置存储的值是一个位置30003000开始的位置存储的才是实际的数据"1, 2, 3"

为什么数组要用两块空间?不能只用一块空间吗?我们来看下面这段代码:

int[] arrA = {1,2,3};
int[] arrB = {4,5,6,7};
arrA = arrB;

这段代码中,arrA初始的长度是3arrB的长度是4,后来将arrB的值赋给了arrA。如果arrA对应的内存空间是直接存储的数组内容,那么它将没有足够的空间去容纳arrB的所有元素。用两块空间存储就简单得多,arrA存储的值就变成了和arrB的一样,存储的都是数组内容{4,5,6,7}的地址,此后访问arrA就和arrB是一样的了,而arrA {1,2,3}的内存空间由于不再被引用会进行垃圾回收,如下所示:

  arrA          {1,2,3}
        \
          \
  arrB   ->   {4,5,6,7}

由上也可以看出,给数组变量赋值和给数组中元素赋值是两回事,给数组中元素赋值是改变数组内容,而给数组变量赋值则会让变量指向一个不同的位置。

我们说数组的长度是不可以变的,不可变指的是数组的内容空间,一经分配,长度就不能再变了,但可以改变数组变量的值,让它指向一个长度不同的空间,就像上例中arrA后来指向了arrB一样。

1.5.2 多维数组

当要存储一组数据时,可以考虑使用一维数组,那么当有多组数据需要存储和处理时,就需要用到多维数组。

数组的标记是中括号,用一个中括号来表示一维数组,如果要声明二维数组,那么就需要用两个中括号来表示。语法格式如下所示:

  • 格式1:元素数据类型[][] 数组名; // 推荐

  • 格式2:元素数据类型[] 数组名[];

  • 格式3:元素数据类型 数组名[][];

示例代码:

int[][] arr;
int[] arr[];
int arr[][];

和一维数组一样,二维数组的初始化同样分为静态初始化和动态初始化。动态初始化又分为固定列数和不固定列数两种情况。

所谓静态初始化,是指在编译期间,就确定了行数和列数,并且也明确了每个元素的值。语法格式如下所示:

  • 格式1:元素数据类型 [][] 数组名 = new 元素数据举型 [][] {{ 元素1, 元素2, ......}, { 元素1, 元素2, ......},......};
  • 格式2:元素数据类型 [][] 数组名 = {{ 元素1, 元素2, ......}, { 元素1, 元素2, ......},......};

需要指出的是,格式2已将声明和初始化合二为一,格式1可以将声明和初始化分开。

int[][] arr =new int[][]{{10,20,30,40,50},{45,6,8}};
int[][] arr = {{10,20,30,40,50},{45,6,8}};

从如上所示的代码中可以看出,arr中相当于有两个一维数组,分别是{10, 20, 30, 40, 50}{45, 6, 8},如果分别把{10, 20, 30, 40, 50}{45, 6, 8}看成一个整体,那么arr就相当于有两个元素,即arr.length=2

第一个数组元素arr[0]的元素是{10, 20, 30, 40, 50},而很明显arr[0]又是一个一维数组,其长度为5,即arr[0].length=5。第二个元素arr[1]的元素值是{45,6,8},而arr[1]也是一个一维数组,其长度为3,即arr[1].length=3

二维数组再在内存中存储的格式如下:arr是二维数组类型的变量,里面有两个元素,存储的是首地址,而carr[1]均是一个一维数组,因此arr[0]arr[1]中仍然存储一维数组的首地址。

多维数组存储

所谓动态初始化,是指在对数组初始化时,只是确定数组的行数和列数,甚至行数和列数都需要在程序运行期间才能确定。当确定完数组的行数和列数之后,数组的元素是默认值。动态初始化又分为两种,一种是每行的列数可以相同,另一种是每行的列数可以不同。

  • 语法格式1:元素数据类型[][] 数组名 = new 元素数据类型[行数][列数];。语法格式中,行数和列数是固定的,固定列数,即里面所有一维数组的元素个数是一致的。示例代码:int [][] arr=new int[3][2]arr指向一个长度为3的数组对象,其中的每个元素都为存储的一维数组的首地址。每个一维数组中的元素都有默认值,相当于:{{0, 0}, {0, 0}, {0, 0}}
  • 语法格式2:元素数据类型[][] 数组名 = new 元素数据类型[行数][];。语法格式中,行数固定但是列数不固定。不固定列数,即里面所有一维数组的元素个数可以不相同。示例代码:int [][]=new int[3][],使用一维数组时必须先初始化,因为此时一维数组值为null。相当于:{null, null, null}

1.6 数据类型的转换

根据开发需要,不同数据类型的值经常需要进行相互转换,我们需要掌握基本数据类型的转换。Java语言只支持布尔型之外的七大基本数据类型间的转换,它们之间的转换类型分为自动类型转换强制类型转换

1.6.1 自动类型转换

当我们将一个基本数据类型的值直接赋给另一个不同类型的基本数据类型的变量时,如果赋值成功,则说明此时系统进行了一次自动类型转换,Java语言规范将其称为Wideding Conversion。如果赋值失败,则说明系统不能进行自动类型转换。自动类型转换需要满足目标类型的表示范围大于源类型的示数范围的要求,这就相当于将一个体积为2立方厘米的立方体放在体积为5立方厘米的立方体盒子中。

基本数据类型的自动转换关系如下图所示,根据箭头指向,左边类型的值可以自动转换成右边类型,反之,则不可以。

基本数据类型的自动转换关系

自动类型转换的注意事项。

  1. 当多种类型的数据进行混合运算时,系统首先自动将所有数据转换成容量最大的数据类型,再进行计算。例如,float字节数比long字节数小,但是float的容量更大。
  2. byteshortchar之间不会相互转换,它们在计算时首先会转换为int类型。
  3. 布尔类型不能与其他数据类型进行运算。
  4. 当把任何基本数据类型的值和字符串(String)进行"+“运算时,此时的”+"不再是加法的意思,而是表示连接运算。此时,运算的结果是String型。

1.6.2 强制类型转换

在一些开发场景中,我们也需要面对将上图中箭头右边类型转换为左边类型的情况,这时候就要进行强制转换,否则编译会报错,代码如下所示:

int a=200;
byte b = a; //编译报错

可以试想一下,如果将一个体积为5立方厘米的立方体放在体积为2立方厘米的立方体盒子中,正常来讲,是不是放不进去。如果要放进去,则要削去一部分体积,从而造成数据丢失。所以Java语言规范将强制类型转换称为Narrow Conversion。强制类型转换的语法格式如下所示:

(目标类型)数据值

示例代码:

int num = 98
short s = (short)num;
System.out.println(s); //结果:98
double d=9.95;
int i = (int)d;
System.out.printIn(i); //结果:9

具体规则如下所示:

  1. 如果目标类型和源类型都为整型,如将32int类型强转为8byte类型,则需要截断前面的24位,只保留右边8位。例如,int255强制转为byte 时,将会得到-1
  2. 目标类型是整型,源类型是浮点型,将直接截断浮点型的小数部位,这会造成精度损失。例如:int iValue=(int )12.5,将其强转后最终得到的iValue的值是12

  1. [1]马俊昌.Java编程的逻辑[M].北京:机械工业出版社,2018. ↩︎

  2. [2]尚硅谷教育.剑指Java:核心原理与应用实践[M].北京:电子工业出版社,2023. ↩︎

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值