java 是强类型语言,每个变和每个表达式都有一个在编译时就确定的类型,所以所有变量必须显示声明类型,也就是所有的变量必须先声明,后使用。
java的数据类型主要分为两种,一种是基本数据类型,一种是引用类型
基本数据类型
基本数据类型的图式为:
我们定义基本数据类型往往会在两个地方,方法中和类中:在方法中定义的基本数据类型被称为局部变量,在类中定义的基本数据类型被称作成员变量,也就是全局变量。
在内存中,这两种数据存储的位置是不同的。
局部变量是随着线程级的,存储在jvm栈中,随着线程的消亡而被回收,基本数据类型型在栈中存储的方式如下:
对于单个栈针中的字节大小与操作系统相关,32位系统为4个字节,64位系统为8个字节。之所以把double和long划分成两个内存块是因为栈区会存放对象的引用地址和基本数据类型,而地址用一个字存下,其他数据类型1个字也能存下,而double和long则需要两个字。这里需要注意一点,如果一个方法中同时定义int a=1;int b=1;这里a是一个 引用,为a分配一个栈空间,里面数据是1,而b会在栈中搜索1是否存在,如果存在的话就是a的地址。因此在这里b=1和b=a是一样的。都是将a的地址赋予b。
而对于全局变量是存在常量池(方法区)中,因为栈是线程级的,对于全局变量来说,所有的方法都会使用,因此需要将全局变量存在方法区里的常量池中。
基本数据类型的转化如下图
引用类型
由于java是面向对象编程,因此每一个基本类型都有对应的对象
这里右侧的是基本数据类型对应的封装类,在创建以后需要实例化对象才能使用。
这里重点说下Integer,也是有很多有趣的事情发生,运行程序
public static void main(String[] args) {
int a=200;
int b=200;
System.out.println(a == b);
Integer c=200;
int d=200;
System.out.println(c == d);
Integer e=2;
Integer f=2;
System.out.println(e == f);
Integer g=200;
Integer h=200;
System.out.println(g == h);
}
结果为:
true
true
true
false
首先明确(==比较的是地址,equal比较的是内容)
第一种情况,如我们上面所说,基本数据类型,当已经把a赋值为1,则 栈中存在1的引用,当为b赋值的时候,直接把引用地址赋给b这样ab的地址一致。
第二种情况,在执行Integer c=200的时候实际上是java自动装箱的过程,编译器会自动展开成Integer c = Integer.valueOf(200);这里用到的valueOf()方法是装箱,而当比较的时候由于是Integer==int,会将Integer方法进行拆箱使用intValue()方法,这样还是比较int==int,因此是true
第三种情况和第四种情况一起来说,由于有这样的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
因为上文提到,Integer g = 200;在进行装箱操作,实际上进行的是Integer g = Integer.valueOf(200);的操作,而对于-128·127的整数来说,已经预加载进常量池IntegerChache中定义的 static final Integer cache[];静态数组。因此会直接返回该常量的地址。因此在-128~127之间的整数地址相同,而不再这个区间的整数就会新建对象去存值了。
然后再来说下char,char是字符型,用于表示单个字符。字符量必须用''括起来。运行程序
public static void main(String[] args) {
char a = 'a';
a = ++a;
System.out.println(a);
}
输出结果为
b
这里首先定义了一个字符型的引用a,然后在进行++a的操作的时候,会将a强制装换为int进行+1操作,然后赋给char型的a,最后输出为char型的字符b
再来看看浮点型,float和double,二者只是精度不同,这里需要注意一点,就是除法的运算,也就是“/”的操作。
当两个int类型的数字相除的时候,除数为0时是会报错的,而当有一个数字被定义为浮点型,无论是除数还是被除数,因为浮点型的0在存储的时候可能不会是0而是0.00000001等非常接近于0的数,而且当做运算的时候,jvm会向上兼用大数据累型,因此0会被转换成浮点型,因此除以0后不会报错,而是显示无穷大。
int a=100;
Integer i=0;
Float f = 0f;
Double d = 0d;
System.out.println(a/f);
System.out.println(a/d);
System.out.println(a/i);
结果显示为
Infinity
Infinity
Exception in thread "main"
java.lang.ArithmeticException: / by zero
at TestDemo.main(TestDemo.java:9)
最后我们来看下字符串类型String
String本身就是引用类型,是没有对应的基本数据类型的,因此在定义的时候,也是完成了自动装箱。运行程序
public static void main(String[] args) {
String str1_1 = String.valueOf("123");
String str1_2 = "123";
System.out.println(str1_1==str1_2);
String str2_1 = new String("123");
String str2_2 = "123";
System.out.println(str2_1==str2_2);
String str3_1 = new String("123");
String str3_2 = new String("123");
System.out.println(str3_1==str3_2);
String str4_1 = "123";
String str4_2 = "123";
System.out.println(str4_1==str4_2);
}
运行结果为:
true
false
false
true
实际上主要区分String str = "123";与String str = new String("123");的区别,前者创建了一个对象。当再次定义String b = "123";时,jvm会去堆中寻找123的地址并赋给b,而new的对象一定是创建了一个新的对象,因此后者创建了两个对象String和123。
数组也是一种引用类型。
对于数组定义为
public static void main(String[] args) {
int[] arr;
arr = new int[3];
for(int i =0;i<arr.length;i++)
{
arr[i] = i;
System.out.println(arr[i]);
}
}
内存结构的变化为
这是对于基本数据类型数组的内存变化。下面介绍下引用类型数组的内存变化
public static void main(String[] args) {
StudentDto[] arrStu;
arrStu = new StudentDto[3];
for(int i=0;i<arrStu.length;i++)
{
StudentDto studentDto =new StudentDto();
studentDto.setId(i);
studentDto.setName("学生"+i);;
System.out.println(studentDto.getId()+"-------"+studentDto.getName());
}
}