JAVA 中基本数据类型及引用数据类型
一、基本数据类型:
byte:8 位 (bit),即 1 个字节,最大存储数据量是 255,存放的数据范围是 - 128~127 之间。默认值 0
short: 16 位,即 2 个字节,取值范围 - 32768~32717,默认值 0
int:32 位,即 4 个字节,取值范围 - 2147483648~2147483647,默认值 0
long: 64 位,即 8 个字节 - 2^63~2^63-1,默认值 0L
float: 浮点型,在内存中占 32 位,即 4 个字节,用于存储带小数点的数字(与 double 的区别在于 float 类型有效小数点只有 6~7 位),默认值 0
double: 双精度浮点型,用于存储带有小数点的数字,在内存中占 64 位,即 8 个字节,默认值 0
char: 字符型,用于存储单个字符,占 16 位,即 2 个字节,取值范围 0~65535,默认值为空
boolean:布尔类型,占 1 个字节,用于判断真或假(仅有两个值,即 true、false),默认值 false
Java 决定了每种简单类型的大小。这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是 Java 程序具有很强移植能力的原因之一。下表列出了 Java 中定义的简单类型、占用二进制位数及对应的封装器类。
BigInteger、BigDecimal 没有相对应的基本类型,主要应用于高精度的运算,BigInteger 支持任意精度的整数,BigDecimal 支持任意精度带小数点的运算。
简单类型 | boolean | char | byte | short | Int | long | float | double | void |
二进制位数 | 1 | 16 | 8 | 16 | 32 | 64 | 32 | 64 | -- |
字节 | 1 | 2 | 1 | 2 | 4 | 8 | 4 | 8 | -- |
默认初始化值 | false | '\u0000' | 0 | 0 | 0 | 0L | 0.0F | 0.0D | |
封装器类 | Boolean | Character | Byte | Short | Integer | Long | Float | Double | Void |
二、Java 数据类型在内存中的存储:
1)基本数据类型的存储原理:所有的简单数据类型不存在 “引用” 的概念,基本数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,而 Java 语言里面八种数据类型是这种存储模型;
2)引用类型的存储原理: 引用类型继承于 Object 类(也是引用类型)都是按照 Java 里面存储对象的内存模型来进行数据存储的,使用 Java 内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用” 是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的;
区别: 基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的(需要 java 中的栈、堆概念),
基本类型和引用类型的内存模型本质上是不一样的。
例 1:我们分析一下”==“和 equals()的区别。
首先,我定以两个 String 对象
Stringa="abc";
Stringb="abc";
然后
if(a==b){
System.out.println("a==b");
}else{
System.out.println("a!=b");}
程序输出 a!=b
原因: a 和 b 的地址是不相同的,a==b 比较的是两个变量的地址
例 2:定义两个基本类型
int a=4;
int b=4;
if(a==b){System.out.println("a==b");}
else
{System.out.println("a!=b");}
输出:a==b
原因:== 比较的是两个变量的内容
猜想:不论是基本数据类型还是引用类型,他们都会先在栈中分配一块内存,对于基本类型来说,这块区域包含的是基本类型的内容;而对于对象类型来说,这块区域包含的是指向真正内容的指针,真正的内容被手动的分配在堆上。
三、Java 基本类型取值范围计算
从计算机组成原理的角度可以解释:
byte 在计算机中是占 8 个字节的,而且 byte 是有符号整形,用二进制表示时候最高位为符号位 0 代表正数 1 代表负数。
最大值:127 即 2 的 7 次方减去 1;最小值:即 2 的 7 次前面加个负符号:-128 。(包含开始,不包含结尾);
正数在计算机中是以原码形式存在的;
负数在计算机中是以其补码形式存在的,就是负数的绝对值的原码转为二进制再按位取反后加 1。
下边这个 10 和 - 10 为例来介绍的 :10 原码:00001010 它在计算机中的存储就是 0000 1010,-10 按照前面说的算除其绝对值为 10,转为二进制 0000 1010 按位取反 1111 0101 再加 1 后:1111 0110,此为 - 10 补码,好的,计算机中的 1111 0110 就是代表 - 10 了。
我们来看 -128 绝对值 128 的二进制表示:1000 0000 按位取反 0111 1111 加 1 后:1000 0000,也就是说 -128 在计算机中的表示就是 1000 0000 了,再来看一下 - 129 在计算机中的表示,绝对值 129 的范围已经超出了了 byte 的位数。所以要注意这类问题;
四、数据类型之间的转换
1). 简单类型数据间的转换, 有两种方式: 自动转换和强制转换, 通常发生在表达式中或方法的参数传递时。
自动转换
具体地讲, 当一个较 "小" 数据与一个较 "大" 的数据一起运算时, 系统将自动将 "小" 数据转换成 "大" 数据, 再进行运算。而在方法调用时, 实际参数较 "小", 而被调用的方法的形式参数数据又较 "大" 时 (若有匹配的, 当然会直接调用匹配的方法), 系统也将自动将 "小" 数据转换成 "大" 数据, 再进行方法的调用, 自然, 对于多个同名的重载方法, 会转换成最 "接近" 的 "大" 数据并进行调用。这些类型由 "小" 到 "大" 分别为 (byte,short,char)--int--long--float—double。这里我们所说的 "大" 与 "小", 并不是指占用字节的多少, 而是指表示值的范围的大小。
①下面的语句可以在 Java 中直接通过:
byte b;int i=b; long l=b; float f=b; double d=b;
②如果低级类型为 char 型,向高级类型(整型)转换时,会转换为对应 ASCII 码值,例如
char c='c'; int i=c;
System.out.println("output:"+i); 输出:output:99;
③对于 byte,short,char 三种类型而言,他们是平级的,因此不能相互自动转换,可以使用下述的强制类型转换。
short i=99 ; char c=(char)i; System.out.println("output:"+c); 输出:output:c;
强制转换
将 "大" 数据转换为 "小" 数据时,你可以使用强制类型转换。即你必须采用下面这种语句格式: int n=(int)3.14159/2; 可以想象,这种转换肯定可能会导致溢出或精度的下降。
2) 表达式的数据类型自动提升, 关于类型的自动提升,注意下面的规则。
①所有的 byte,short,char 型的值将被提升为 int 型;
②如果有一个操作数是 long 型,计算结果是 long 型;
③如果有一个操作数是 float 型,计算结果是 float 型;
④如果有一个操作数是 double 型,计算结果是 double 型;
例, byte b; b=3; b=(byte)(b*3);// 必须声明 byte。
3) 包装类过渡类型转换
一般情况下,我们首先声明一个变量,然后生成一个对应的包装类,就可以利用包装类的各种方法进行类型转换了。例如:
①当希望把 float 型转换为 double 型时:
float f1=100.00f;
Float F1=new Float(f1);
double d1=F1.doubleValue();//F1.doubleValue() 为 Float 类的返回 double 值型的方法
②当希望把 double 型转换为 int 型时:
double d1=100.00;
Double D1=new Double(d1);
int i1=D1.intValue();
简单类型的变量转换为相应的包装类,可以利用包装类的构造函数。即:Boolean(boolean value)、Character(char value)、Integer(int value)、Long(long value)、Float(float value)、Double(double value)
而在各个包装类中,总有形为 ××Value() 的方法,来得到其对应的简单类型数据。利用这种方法,也可以实现不同数值型变量间的转换,例如,对于一个双精度实型类,intValue() 可以得到其对应的整型变量,而 doubleValue() 可以得到其对应的双精度实型变量。
4) 字符串与其它类型间的转换
其它类型向字符串的转换
①调用类的串转换方法: X.toString();
②自动转换: X+"";
③使用 String 的方法: String.volueOf(X);
字符串作为值, 向其它类型的转换
①先转换成相应的封装器实例, 再调用对应的方法转换成其它类型
例如,字符中 "32.1" 转换 double 型的值的格式为: new Float("32.1").doubleValue()。也可以用: Double.valueOf("32.1").doubleValue()
②静态 parseXXX 方法
String s = "1";
byte b = Byte.parseByte(s);
short t = Short.parseShort(s);
int i = Integer.parseInt(s);
long l = Long.parseLong(s);
Float f = Float.parseFloat(s);
Double d = Double.parseDouble(s);
③Character 的 getNumericValue(char ch) 方法
5)Date 类与其它数据类型的相互转换
整型和 Date 类之间并不存在直接的对应关系,只是你可以使用 int 型为分别表示年、月、日、时、分、秒,这样就在两者之间建立了一个对应关系,在作这种转换时,你可以使用 Date 类构造函数的三种形式:
①Date(int year, int month, int date):以 int 型表示年、月、日
②Date(int year, int month, int date, int hrs, int min):以 int 型表示年、月、日、时、分
③Date(int year, int month, int date, int hrs, int min, int sec):以 int 型表示年、月、日、时、分、秒
在长整型和 Date 类之间有一个很有趣的对应关系,就是将一个时间表示为距离格林尼治标准时间 1970 年 1 月 1 日 0 时 0 分 0 秒的毫秒数。对于这种对应关系,Date 类也有其相应的构造函数:Date(long date)。
获取 Date 类中的年、月、日、时、分、秒以及星期你可以使用 Date 类的 getYear()、getMonth()、getDate()、getHours()、getMinutes()、getSeconds()、getDay() 方法,你也可以将其理解为将 Date 类转换成 int。
而 Date 类的 getTime() 方法可以得到我们前面所说的一个时间对应的长整型数,与包装类一样,Date 类也有一个 toString() 方法可以将其转换为 String 类。
有时我们希望得到 Date 的特定格式,例如 20020324,我们可以使用以下方法,首先在文件开始引入,
import java.text.SimpleDateFormat;
import java.util.*;
java.util.Date date = new java.util.Date();
// 如果希望得到 YYYYMMDD 的格式
SimpleDateFormat sy1=new SimpleDateFormat("yyyyMMDD");
String dateFormat=sy1.format(date);
// 如果希望分开得到年,月,日
SimpleDateFormat sy=new SimpleDateFormat("yyyy");
SimpleDateFormat sm=new SimpleDateFormat("MM");
SimpleDateFormat sd=new SimpleDateFormat("dd");
String syear=sy.format(date);
String smon=sm.format(date);
String sday=sd.format(date);
总结:只有 boolean 不参与数据类型的转换
(1). 自动类型的转换:a. 常数在表数范围内是能够自动类型转换的
b. 数据范围小的能够自动数据类型大的转换(注意特例)
int 到 float,long 到 float,long 到 double 是不会自动转换的,不然将会丢失精度
c. 引用类型能够自动转换为父类的
d. 基本类型和它们包装类型是能够互相转换的
(2). 强制类型转换:用圆括号括起来目标类型,置于变量前
五、Java 引用类型
Java 有 5 种引用类型(对象类型):类 接口 数组 枚举 标注
引用类型:底层结构和基本类型差别较大
JVM 的内存空间:(1). Heap 堆空间:分配对象 new Student()
(2). Stack 栈空间:临时变量 Student stu
(3).Code 代码区 :类的定义,静态资源 Student.class
eg:Student stu = new Student(); //new 在内存的堆空间创建对象
stu.study(); // 把对象的地址赋给 stu 引用变量
上例实现步骤:a.JVM 加载 Student.class 到 Code 区
b.new Student() 在堆空间分配空间并创建一个 Student 实例;
c. 将此实例的地址赋值给引用 stu, 栈空间;