Rookie零基础学java(四)java的基本数据类型及注意事项

时间:2023-01-28 19:00:20

Rookie零基础学java博客目录(不断更新)

1.Java数据类型基本概念:

数据类型在计算机语言里面,是对内存位置的一个抽象表达方式,可以理解为针对内存的一种抽象的表达方式。接触每种语言的时候,都会存在数据类型的认识,有复杂的、简单的,各种数据类型都需要在学习初期去了解,Java是强类型语言,所以Java对于数据类型的规范会相对严格。数据类型是语言的抽象原子概念,可以说是语言中最基本的单元定义,在Java里面,本质上讲将数据类型分为两种:简单类型和复杂类型。

简单类型:简单数据类型是不能简化的、内置的数据类型、由编程语言本身定义,它表示了真实的数字、字符和整数。

复杂类型Java语言本身不支持C++中的结构(struct)或联合(union)数据类型,它的复合数据类型一般都是通过类或接口进行构造,类提供了捆绑数据和方法的方式,同时可以针对程序外部进行信息隐藏。

2.Java中的基本类型:

1)概念:

Java中的简单类型从概念上分为四种:实数、整数、字符、布尔值。但是有一点需要说明的是,Java里面只有八种原始类型,其列表如下:

实数:doublefloat

整数:byteshortintlong

字符:char

布尔值:boolean

复杂类型和基本类型的内存模型本质上是不一样的,简单数据类型的存储原理是这样的:所有的简单数据类型不存在引用的概念,简单数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,而Java语言里面只有这八种数据类型是这种存储模型;而其他的只要是继承于Object类的复杂数据类型都是按照Java里面存储对象的内存模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,引用是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的。

2)详细介绍:

Java的简单数据讲解列表如下:

intint整数类型,在存储的时候,用4个字节存储,范围为-2,147,483,6482,147,483,647,在变量初始化的时候,int类型的默认值为0

shortshort也属于整数类型,在存储的时候,用2个字节存储,范围为-32,76832,767,在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0

longlong也属于整数类型,在存储的时候,用8个字节存储,范围为-9,223,372,036,854,775,8089,223,372,036, 854,775,807,在变量初始化的时候,long类型的默认值为0L0l,也可直接写为0

bytebyte同样属于整数类型,在存储的时候,用1个字节来存储,范围为-128127,在变量初始化的时候,byte类型的默认值也为0。

floatfloat属于实数类型,在存储的时候,用4个字节来存储,范围为32IEEEE 754单精度范围,在变量初始化的时候,float的默认值为0.0f0.0F,在初始化的时候可以写0.0

doubledouble同样属于实数类型,在存储的时候,用8个字节来存储,范围为64IEEE 754双精度范围,在变量初始化的时候,double的默认值为0.0

charchar属于字符类型,在存储的时候用2个字节来存储,因为Java本身的字符集不是用ASCII码来进行存储,是使用的16位Unicode字符集,它的字符范围即是Unicode的字符范围,在变量初始化的时候,char类型的默认值为'u0000'

booleanboolean属于布尔类型,在存储的时候不使用字节,仅仅使用1来存储,范围仅仅为0和1,其字面量为truefalse,而boolean变量在初始化的时候变量的默认值为false

  3)相关介绍:

  【1】当整数类型的数据使用字面量赋值的时候,默认值为int类型,就是直接使用0或者其他数字的时候,值的类型为int类型,所以当使用long a = 0这种赋值方式的时候,JVM内部存在数据转换。

  【2】当实数类型的数据使用字面量赋值的时候,默认值为double类型,就是当字面两出现的时候,JVM会使用double类型的数据类型。

  (*:以上两点在转型中进行详细说明。)

a基本类型在使用字面量赋值的时候,有几个简单的特性如下:

  

  【3】从JDK 5.0开始,Java里面出现了自动拆箱解箱的操作,基于这点需要做一定的说明:

  对应原始的数据类型,每种数据类型都存在一个复杂类型的封装类,分别为Boolean、Short、Float、Double、Byte、Int、Long、Character,这些类型都是内置的封装类,这些封装类(Wrapper)提供了很直观的方法,针对封装类需要说明的是,每种封装类都有一个xxxValue()的方法,通过这种方法可以把它引用的对象里面的值转化成为原始变量的值,不仅仅如此,每个封装类都还存在一个valueOf(String)的方法直接把字符串对象转换为相应的简单类型。

  在JDK 5.0之前,没有存在自动拆解箱的操作,即Auto Box操作,所以在这之前是不能使用以下方式的赋值代码的:

Integer a = 0; //这种赋值方式不能够在JDK 1.4以及以下的JDK编译器中通过

但是JDK 5.0出现了自动拆解箱的操作,所以在JDK 5.0以上的编译器中,以上的代码是可以通过的,关于自动拆箱解箱我会另外用一篇1.4到5.0的升级加以详细说明。

3.Java中简基本数据类型的转型:

  Java中的简单数据类型的转换分为两种:自动转换和强制转换

  1)自动转换:

  当一个较“小”的数据和较“大”的数据一起运算的时候,系统将自动将较“小”的数据转换为较“大”的数据,再进行运算。

  在方法调用过程,如果实际参数较“小”,而函数的形参比较“大”的时候,除非有匹配的方法,否则会直接使用较“大”的形参函数进行调用。

  2)强制转换:

  将“大”数据转换为“小”数据时,可以使用强制类型转换,在强制类型转换的时候必须使用下边这种语句:

int a = (int)3.14;

  只是在上边这种类型转换的时候,有可能会出现精度损失。

  关于类型的自动提升,遵循下边的规则:

  所有的byteshortchar类型的值将提升为int类型;

  如果有一个操作数是long类型,计算结果是long类型;

  如果有一个操作数是float类型,计算结果是float类型;

  如果有一个操作数是double类型,计算结果是double类型;

  自动类型转换图如下:

  byte->short(char)->int->long->float->double

  如果是强制转换的时候,就将上边的图反过来

  3)转换附加:

  当两个类型进行自动转换的时候,需要满足条件:【1】这两种类型是兼容的,【2】目的类型的数值范围应该比源转换值的范围要大。而拓展范围就遵循上边的自动类型转换树,当这两个条件都满足的时候,拓展转换才会发生,而对于几个原始类型转换过程,根据兼容性boolean和char应该是独立的,而其他六种类型是可以兼容的,在强制转换过程,唯独可能特殊的是char和int是可以转换的,不过会使用char的ASCII码值比如:

int a = (int)'a';

  a的值在转换过后输出的话,值为97;

4.Java中的高精度数:

  Java提供了两个专门的类进行高精度运算:BigInteger与BigDecimal,虽然Java原始变量都具有对应的封装类型,但是这两个变量没有对应的原始类型,而是通过方法来提供这两种类型的一些运算,其含义为普通类型能够做的操作,这两个类型对应都有,只是因为精度过大可能效率不够高。至于这两个类的具体操作可以参考JDK的相关API文档。

5.关于数据类型的一些技巧:(以下为参考一篇原文文档)

  若要求精度的结果,尽量避免使用float和double:

  float和double类型本身是为了做科学运算,即执行二进制浮点运算而设计,但是却不能提供完全精确的结果,所以在要求精度的数值中,避免使用float和double,float和double在货币运算中尤其不合适,要让float和double精确表达0.1也是不可能的事。测试一下下边这段代码就明白了:

  System.out.println(2.02-0.42);

  结果是不是出乎意料,这个结果并不是偶然,而是JVM本身设计的目的决定的。而要解决这个问题,最好的办法是使用BigDecimal、int或者long进行相关运算,特别是货币运算,使用BigDecimal代替double是一个很好的办法。

  BigDecimal唯一的缺点在于:BigDecimal没有相对应的原始类型,所以在进行基本数值运算的时候,需要进行方法调用才能操作,这样会使得和我们的编程习惯不相符合,若使用int和long,就需要进行简单的封装运算。

  所以在要求精度答案的计算任务里面,一般慎用float和double,如果在进行商务运算,并且要求四舍五入或者简单的舍入行为,使用BigDecimal可能更加方便。所以尽量避免在精度运算中使用float和double,特别是我们常用的货币运算。

5.程序验证:

  

public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();

// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();

// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();

// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();

// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();

// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();

// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}


 

运行结果:

1、基本类型:byte 二进制位数:8
2、包装类:java.lang.Byte
3、最小值:Byte.MIN_VALUE=-128
4、最大值:Byte.MAX_VALUE=127
5、
6、基本类型:short 二进制位数:16
7、包装类:java.lang.Short
8、最小值:Short.MIN_VALUE=-32768
9、最大值:Short.MAX_VALUE=32767
10、
11、基本类型:int 二进制位数:32
12、包装类:java.lang.Integer
13、最小值:Integer.MIN_VALUE=-2147483648
14、最大值:Integer.MAX_VALUE=2147483647
15、
16、基本类型:long 二进制位数:64
17、包装类:java.lang.Long
18、最小值:Long.MIN_VALUE=-9223372036854775808
19、最大值:Long.MAX_VALUE=9223372036854775807
20、
21、基本类型:float 二进制位数:32
22、包装类:java.lang.Float
23、最小值:Float.MIN_VALUE=1.4E-45
24、最大值:Float.MAX_VALUE=3.4028235E38
25、
26、基本类型:double 二进制位数:64
27、包装类:java.lang.Double
28、最小值:Double.MIN_VALUE=4.9E-324
29、最大值:Double.MAX_VALUE=1.7976931348623157E308
30、
31、基本类型:char 二进制位数:16
32、包装类:java.lang.Character
33、最小值:Character.MIN_VALUE=0
34、最大值:Character.MAX_VALUE=65535

Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的“E+数字”表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。

大家将运行结果与上表信息仔细比较就会发现float、double两种类型的最小值与Float.MIN_VALUE、 Double.MIN_VALUE的值并不相同,这是为什么呢?实际上Float.MIN_VALUE和Double.MIN_VALUE分别指的是 float和double类型所能表示的最小正数。也就是说存在这样一种情况,0到±Float.MIN_VALUE之间的值float类型无法表示,0 到±Double.MIN_VALUE之间的值double类型无法表示。这并没有什么好奇怪的,因为这些范围内的数值超出了它们的精度范围。

基本类型存储在栈中,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象。从Java5.0(1.5)开始,JAVA虚拟机(Java Virtual Machine)可以完成基本类型和它们对应包装类之间的自动转换。因此我们在赋值、参数传递以及数学运算的时候像使用基本类型一样使用它们的包装类,但这并不意味着你可以通过基本类型调用它们的包装类才具有的方法。另外,所有基本类型(包括void)的包装类都使用了final修饰,因此我们无法继承它们扩展新的类,也无法重写它们的任何方法。

各种数值类型之间的赋值与转换遵循什么规律呢?我们来看下面这个例子:
复制内容到剪贴板
代码:
public class PrimitiveTypeTest {
public static void main(String[] args) {
// 给byte类型变量赋值时,数字后无需后缀标识
byte byte_a = 1;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// byte byte_b = 1000;
// 把一个long型值赋值给byte型变量,编译时会报错,即使这个值没有超出byte类型的取值范围
// byte byte_c = 1L;

// 给short类型变量赋值时,数字后无需后缀标识
short short_a = 1;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// short short_b = 70000;
// 把一个long型值赋值给short型变量,编译时会报错,即使这个值没有超出short类型的取值范围
// byte short_c = 1L;

// 给short类型变量赋值时,数字后无需后缀标识
int int_a = 1;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// int int_b = 2200000000;
// 把一个long型值赋值给int型变量,编译时会报错,即使这个值没有超出int类型的取值范围
// int int_c = 1L;

// 可以把一个int型值直接赋值给long型变量,数字后无需后缀标识
long long_a = 1;
// 如果给long型变量赋予的值超出了int型值的范围,数字后必须加L(不区分大小写)标识
long long_b = 2200000000;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// long long_c = 9300000000000000000L;

// 可以把一个int型值直接赋值给float型变量
float float_a = 1;
// 可以把一个long型值直接赋值给float型变量
float float_b = 1;
// 没有F(不区分大小写)后缀标识的浮点数默认为double型的,不能将它直接赋值给float型变量
// float float_c = 1.0;
// float型数值需要有一个F(不区分大小写)后缀标识
float float_d = 1.0F;
// 把一个double型值赋值给float型变量,编译时会报错,即使这个值没有超出float类型的取值范围
// float float_e = 1.0D;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// float float_f = 3.5000000E38F;

// 可以把一个int型值直接赋值给double型变量
double double_a = 1;
// 可以把一个long型值直接赋值给double型变量
double double_b = 1;
// 可以把一个float型值直接赋值给double型变量
double double_c = 1F;
// 不带后缀标识的浮点数默认为double类型的,可以直接赋值
double double_d = 1.0;
// 也可以给数字增加一个D(不区分大小写)后缀标识,明确标出它是double类型的
double double_e = 1.0D;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// double double_f = 1.8000000000000000E308D;

// 把一个double型值赋值给一个byte类型变量,编译时会报错,即使这个值没有超出byte类型的取值范围
// byte byte_d = 1.0D;
// 把一个double型值赋值给一个short类型变量,编译时会报错,即使这个值没有超出short类型的取值范围
// short short_d = 1.0D;
// 把一个double型值赋值给一个int类型变量,编译时会报错,即使这个值没有超出int类型的取值范围
// int int_d = 1.0D;
// 把一个double型值赋值给一个long类型变量,编译时会报错,即使这个值没有超出long类型的取值范围
// long long_d = 1.0D;

// 可以用字符初始化一个char型变量
char char_a = 'a';
// 也可以用一个int型数值初始化char型变量
char char_b = 1;
// 把一个long型值赋值给一个char类型变量,编译时会报错,即使这个值没有超出char类型的取值范围
// char char_c = 1L;
// 把一个float型值赋值给一个char类型变量,编译时会报错,即使这个值没有超出char类型的取值范围
// char char_d = 1.0F;
// 把一个double型值赋值给一个char类型变量,编译时会报错,即使这个值没有超出char类型的取值范围
// char char_e = 1.0D;
// 编译器会做范围检查,如果赋予的值超出了范围就会报错
// char char_f = 70000;
}
}
从上面的例子中我们可以得出如下几条结论:

1、未带有字符后缀标识的整数默认为int类型;未带有字符后缀标识的浮点数默认为double类型。
2、如果一个整数的值超出了int类型能够表示的范围,则必须增加后缀“L”(不区分大小写,建议用大写,因为小写的L与阿拉伯数字1很容易混淆),表示为long型。
3、带有“F”(不区分大小写)后缀的整数和浮点数都是float类型的;带有“D”(不区分大小写)后缀的整数和浮点数都是double类型的。
4、编译器会在编译期对byte、short、int、long、float、double、char型变量的值进行检查,如果超出了它们的取值范围就会报错。
5、int型值可以赋给所有数值类型的变量;long型值可以赋给long、float、double类型的变量;float型值可以赋给float、double类型的变量;double型值只能赋给double类型变量