这篇文章是我关于Java数据类型的笔记分享,如有错误欢迎指正。
就像数学中会把数字划分为正数负数、小数整数、实数复数,代码世界中的数据也有他们各自的类型,每一种类型具有他自身的一定属性、定义、规范和功能等,各种变量、常量、方法等的定义都离不开数据类型。下面是我记录的Java数据类型的分类和使用。
基本数据类型是Java提供的内置类型。一般在定义变量、常量或者是作为方法返回值的时候使用。而在定义变量、常量的时候,虚拟机读到声明语句,会为定义的变量在内存中开辟一定大小的空间,存储不同的类型数据,这个空间大小就是数据类型决定的,而基础数据类型在类加载的时候就会被赋一个初始值,详细内容我记录在简析虚拟机一文。
一个byte类型占用8位空间,即1字节。是一种有符号整形,以二进制补码表示。
取值范围为[-128,128),默认值为0。
字节类型在一些数据量确定在[-128,128)范围内的整形数据处理中,替代最常用的int类型。因为一byte类型数据正好占用1字节空间,所以经常使用在字节流文件传输中。
public byte myByte = 1;
一个short类型数据占用16位空间,即2字节。是一种有符号整数,以二进制补码表示。
取值范围为[-32768,32768),默认值为0。
短整型介于byte和int之间,在运算数据确定不会超过其取值范围的时候,可以用short类型代替int类型来节省空间。
public short myShort = 1000;
一个int类型数据占用32位空间,即4字节。是一种有符号整数,以二进制补码表示。
取值范围为[-2,147,483,648,2,147,483,648),默认值为0。
int类型是使用范围最广的数据类型,因为它能处理大部分整数类型的运算,是Java中整形的默认数据类型。比如一个123作为字面值,java会认为它是一个int类型而不是byte。
public int myInt = 1000000;
一个long类型数据占用64位空间,即8字节。是一种有符号整数,以二进制补码表示。
取值范围为[-263,263),默认值为0L。
long类型一般用于需要较大整数的情况下,如果不是特别需要尽量不使用long类型,因为它会开辟较大空间,容易造成资源浪费。需要注意的是,为了区分long类型和int类型,long类型整数在定义的时候,其字面值需要在结尾加上L或者l,这里不区分大小写,不过小写的l和数字1比较像,推荐使用L。需要指出一点,如果等号后面的值在[-2,147,483,648,2,147,483,648)范围内,即使末尾不加L也不会出现编译错误,但如果在这个范围外不加就会,这其实是因为不加L的整数字面值,编译器会把它理解为int类型,而在赋值的时候进行了隐式类型转换,详情下文会讲解。
public long myLong = 1000000000L;
public long myLong2 = 2147483647;
//public long myLong3 = 2147483648; 这样就会出现编译错误
一个float类型数据占用32位空间,即4字节。单精度、符合IEEE 754标准的浮点数。
IEEE 754标准将一串二进制内存划分为三个功能区, 其中s为符号位,f为尾数位,e为指数位,按s e f的顺序存储在内存中。
float各位位数分别是:s占1位,e占8位,f占23位。
默认值是0.0f。
和long类型的表示方法类似,在定义单精度float的时候需要在字面值末尾添加f或者F。浮点数因为存储和计算方法的问题,浮点数不能表示需要精确表示的值,比如货币。
public float myFloat = 1.1f;
一个float类型数据占用64位空间,即8字节。双精度、符合IEEE 754标准的浮点数。
double的各位位数:s占1位,e占11位,f占52位
默认值是0.0。
double 是浮点数的默认类型,就是浮点数字面值结尾如果不加f,Java默认这个字面是是double类型的,不过也可以在浮点数字面值结尾加上d来表示这个数是双精度类型的。
public double myDouble = 1.1;
public double myDouble2 = 1.1d;
字符类型是一个单一的16位Unicode字符,和C++不同,C++的char是占8位空间的。
默认值为空字符’’,引号里面没有东西,是一个字符而不是Java提供的null,对它进行字符相关操作不会抛出空指针异常。
Java中对char类型的赋值方式有几种
char myChar = 'A';
//以下赋值结果都为A
char myChar = 0b1000001;
char myChar1 = 65;
char myChar2 = 0101;
char myChar3 = 0x41;
布尔数据类型表示1位信息(并不代表它占用1位字节),代表0或者1,值作为标志记录true/false情况。
默认值是false。
布尔本身是Java提供的状态标识信息,但是使用打印方法将布尔类型的数据输出到控制台的时候,打印的内容会是true或者false两个单词。而且通过布尔类型,可以直接对流程控制语句进行操作而不用添加判断运算符。
boolean flag = true;
if(flag){//不用再使用flag==true来进行判断
//...
}
这里再注解一个知识点,上文提到过布尔类型的占用空间大小其实是不固定的,Java提供的八中基本数据类型中只有boolean没有确定这个属性。经过查阅得知,这是因为Java虚拟机(JVM)根本不认识boolean这个类型,布尔类型数据在编译之后会转换成其他数据类型存储。
Although the Java Virtual Machine defines a boolean type, it only
provides very limited support for it. There are no Java Virtual
Machine instructions solely dedicated to operations on boolean values.
Instead, expressions in the Java programming language that operate on
boolean values are compiled to use values of the Java Virtual Machine
int data type.
上文是jvm规范对boolean的介绍,翻译后解释就是,虚拟机定义了布尔类型,但是没有单独的虚拟机指令专门用于布尔值的操作,就是说虚拟机对布尔值提供的支持很少,更多情况下是将它转换为其他类型进行运算,所以布尔类型数据占用多少空间,主要看虚拟机实现方(上面只是规范,和具体实现还是不一样的)。
比如hotspot虚拟机,Java代码编译后,单个boolean类型数据会被编译成int类型,一个boolean数组会被编译为byte数组。而对于其他版本的虚拟机来说,实现方式也许又不一样。
字面值可以理解为,是程序员在赋值时手动输入的值,可以与变量的实际值对应理解。最直观的例子就是上面对char变量的赋值:
//以下赋值结果都为A
char myChar = 0b1000001;
char myChar1 = 65;
char myChar2 = 0101;
char myChar3 = 0x41;
像0b1000001、65等都是整形字面值,就是程序员输入的值,但是char变量实际存储的值是’A’。
字面值种类很多,大体上可以分为:整数型、浮点型、字符及字符串型和特殊字面值。
int x = 123_123_123;
x = 123___123;//也可以使用连续的下划线
double d = 1.2_3;
d = 1._23;//这就是不合法的状况
long l = 1234_L;//这也是不合法的状态
System.out.println(Integer.class);
//输出结果为 class java.lang.Integer
IEEE浮点表示法标准形式: V = (-1)s×M×2E
java中两种浮点类型的内存占用格式上文已经介绍过了,发现公式中有参数M、E,而内存表示的尾数位是用f指代,指数位用e指代,这并不是表示失误,而是因为f和M之间、e和E之间还有一个关系计算。下面进行举例解释。
首先先弄清楚二进制内存中的数据转换为公式的值之间的转换关系:
例:
float f = 1.1f;
//将浮点类型转化为进制整数表示
int bit = Float.floatToIntBits(f);
//将bit转换为二进制整数并打印
System.out.println(Integer.toBinaryString(bit));
//结果: 11 1111 1000 1100 1100 1100 1100 1101
上面例子中,打印的结果总共30位,理论上float一共占用32位,这是因为1.1f在内存中占不到32位,只有31位,指数位的第一位为0省略,符号位为0也被省略,补上之后整理:
0 01111111 00011001100110011001101
这是规格化的值,用公式计算
尾数位表示的是二进制小数,即第一位代表1/2,第二位1/4,第三位1/8依次类推。
M=1+f= 1+1/24+1/25+1/28+1/29…+1/223=1.10000002384185791015625=1.1
E=e-Bias=127-127=0;
最后结果值=(-1)0x1.1x20=1.1。不过通过上面的计算可以发现,浮点类型终究需要一定的舍入规则才能的到想要的值,所以浮点类型是不精确的。
java变量是参与程序运算的基本单位之一,就跟数学里的变量一样,如一个数学方程y=x+1,这里的x和y就是变量,1可以看做常量,当然程序中也可以直接用整形变量来表示1,上面这个程序就可以写成
double x,y;
x = 2;
y = x + 1;
//输出y得到y的值
java变量完整的定义需要三个元素:变量数据类型、变量名、变量值。
private static int myStaticInt = 10;
public int myInt = 10;
//这个时候再声明一个 public static int myInt = 1;会出现编译错误。
public int a = 1;
public void function(){
int a = 10;
System.out.println(a);//打印的结果为10而不是1
}
public void function(){
int a = 1;
for(int i=0;i<10;i++){
a = a + i;
int temp = a;
}
}
Java程序的运行是基于Java虚拟机(JVM)的,而程序在虚拟机中运行时,肯定要占用虚拟机的内存来存储类、变量等运算所需的基本信息,下面是我理解的java基本数据类型变量的存储。
了解基本数据类型的存储之前,有必要先简单了解一下,java的类在从高级语言被编译解释成机器语言的过程,以及这些数据在java虚拟机(JVM)中是如何存储的。
虚拟机中各个内存空间具体是什么功能,存储哪些数据在另一篇文章中记录,这篇文章简单介绍一下虚拟机栈和堆。
java程序在执行时,编译器会将.java文件编译成一种.class文件,然后java虚拟机对class文件进行解释执行,这也是java具有良好平台性的原因,有class文件和jvm虚拟机存在,就可以实现“一次编译,到处运行”。其他面向对象像C++,它会直接调用操作系统提供的方法控制资源,如果将代码移植到另一个操作系统上,就容易出现问题。
java在编译时,创建一个.class文件,这个文件中包含了一些编译期就确定下来的数据。然后虚拟机读取.class文件,将其中存储的数据装载到内存中。虚拟机加载类的步骤大致上为:加载-连接-初始化。
在执行完上述步骤后,jvm会创建一个类的Class对象实例,这个对象存储了类的相关信息。
一般情况下,所有基本数据类型都是存储在栈中的,但是也有特例,不同作用域的基本类型变量的存储方式也会有所区别。基本数据类型的名和值一般是一起存储的,引用类型名和实际值可能就存储在两个不同的内存中
布尔类型不参与数据类型转换
顾名思义,显式类型转化就是手动在数据的字面值前加上(想要转化为的数据类型),例如:
int myInt = 1;
short i2s = (shrot)myInt;//这里如果不加short进行显式转换会出现编译错误
显式类型转化一般用于编译器不能自动进行类型转换,或者用于标识清楚数据类型的时候使用。
java隐式类型转化基于一定规则:
byte myByte =1;
short myShort = myByte;
int myInt = myShort;
char myChar = 'c';
myInt = myChar;
//因为int类型可以隐式转换成long类型,所以long类型赋值时数字末尾不加L也不会编译异常
long myLong = myInt;
//float类型占用32位内存,long类型有64位内存,但是可以进行转换
float myFloat = myLong;
double myDouble = myFloat;
上面就是几种数据类型的隐式转换,两两之间转化不需要添加(数据类型),编译器会自动分析等号左边的数据类型,然后把等号右边的类型强制转换成左边的。
ps:上面有几点细节
这篇文章是我学习java基本数据类型的笔记。其中有基本数据类型的简介(附带有java字面值的含义和IEEE标准计算浮点数的方法)、java变量、java基本类型变量的存储以及基本类型之间的转换,如果有错误欢迎指出。