引用类的具体实现
Java的数据类型包含两大类,基本类型和引用类型。基本类型就是我们经常提到的8种基本的数据类型(后面会总结),而Java的引用类型只有三种,即类(class),接口(interface)和数组。
《Java编程思想》一书中说到:“Java从一开始就是纯粹的面向对象的编程语言,Java的一切元素都是对象”。那Java到底是如何控制对象的呢?
我们来看一行代码:
String s = new String("kang");
我们通常把这条语句称作创建对象的过程。
其实在Java的运行过程中,内存中会存在“堆”和“栈”的两种不同的存储空间。当在代码块中定义一个变量时,Java就在栈中为这个变量分配内存空间。而堆内存用于存放由new创建的对象或者数组。
我们来看一下上面的那一行代码:
(1)右边的new String(“kang”),意思就是在堆内存里创建了一个String对象,在对象创建完毕后,立即调用String类的构造函数。
(2) 左边的String s意思就是定义了一个String类的引用变量,String会在栈中开辟空间引入引用名“s”。
(3)最后通过“=”,成功的讲两者关联起来了,使引用变量s指向了刚才创建好的String对象,实际上,就是栈中的变量指向堆内存中的变量,充当了指针的作用。
这其实就是引用类型的具体实现过程。
基本类型的引入
基本类型 | 存储空间 | 取值范围 | 对应包装类 |
---|---|---|---|
boolean | 1 bits | true 或者 false | Boolean |
byte | 1字节 | -128 ~ 127 | Byte |
char | 2字节 | 0 ~ 2^16-1 | Character |
short | 2字节 | -2^15 ~ 2^15-1 | Short |
int | 4字节 | -2^31 ~ 2^31-1 | Integer |
long | 8字节 | -2^63 ~ 2^63-1 | Long |
float | 4字节 | —— | Float |
double | 8字节 | —— | Double |
如上就是我们的8中基本类型。前面我们提到在创建String对象的时候,一般不会通过刚才的String s = new String(“kang”)这种方法去创建,而是通过:
String s = "kang";
这其实就是Java本身为String类创建的快捷方式,其实呢,8种基本类型都拥有这种所谓的“快捷方式”。不同于上面的引用类型,基本类型的类型名和类型值都存储在栈中,直接关联变量名与变量值。
引用类与基本类的实际应用
==与equals的正确使用
==:它的作用就是比较两者对象的地址是否相同,其实判断的就是是否为同一个对象。(基本类比较的是值,引用类比较的是内存地址,也就是引用是否相同)。
**equals:**它主要分为两种情况:
(1)类没有覆盖equals方法(例如String类和Integer类),其实等价于 == 的效果。
(2)类覆盖了equals方法,则此时equals比较的 就是对象的内容是否相等。
String a = "kang";
String b = "kang";
String c = new String("kang");
String d = new String("kang");
System.out.println(a == b); // true
System.out.println(c == d); // false
System.out.println(a.equals(b)); // true
System.out.println(c.equals(d)); // true
作为参数的正确使用
我们先写一段Java类的代码:
public class Test {
public static void main(String[] args) {
Person s = new Person("fukang");
int a = 100;
System.out.println(s.getName()); // fukang
System.out.println(a); // 100
change(s,a);
System.out.println(s.getName()); // kangkang
System.out.println(a); // 100
}
public static void change(Person s,int a) {
a = a - 10;
s.setName("kangkang");
}
}
s为引用类型,而a则是基本类型
从运行结果那里,我们可以看出来,基本类型作为方法参数穿进来,在方法里面无法改变它的值,相当于传递了一个参数副本,而对于引用类型,因为它传入的是本身的地址,所以说你在方法里改变了它的属性,它对应的属性值也会发生相应的改变。
包装类的引入
前面那个表已经写出来每个基础类型对应的包装类,我们先来对比一下两者的区别。
与基本类的区别
(1)包装类是具体的对象,拥有方法,对象的调用都是通过引用对象的地址(与引用类型很相似),而基本类型不是。
(2)基本类型的初始值为0或者false,而包装类的初始值都为0.
(3)基本类型可以直接声明,创建对象,而包装类需要new关键字来在堆内存中分配空间。
(4)基本类型都保存在栈中,而包装类型是把对象放在堆中,然后通过对象的引用来调用他们。
其实我认为基本类与包装类的区别与基本类与引用类的区别很类似。
包装类,基本类和字符串之间的转换
基本数据类型转换为包装类:使用new关键字
Integer a = new Integer(100); // int 转换为 Integer
包装类转换为基本数据类型:使用包装类的xxxValue()方法。
int aa = a.intValue(); // Integer 转换为 int
字符串转换为基本数据类型:调用包装类的静态方法parsexxx。
int a = Integer.parseInt("123"); // 字符串 转换为 int
基本类型转换为字符串:
(1)直接数字后面加空字符串。
(2)调用String包装类的静态方法valueOf(包装类都有对应的静态方法valueOf,可以转换为该包装类)。
int a = 123;
System.out.println(a + "");
System.out.println(String.valueOf(a));
包装类转换为字符串:调用包装类的toString()方法。
Integer a = new Integer(100);
System.out.println(a.toString());
拆箱与装箱
装箱就是将基本数据类型转化为包装类型,那么拆箱就是将包装类型转化为基本数据类型。
我们先以基本数据类型int为例,通过上面不同类型数据的转换,可以看出:
Integer的valueOf(int i)方法可以将一个基本数据类型转化为对应的包装类型,即装箱方法。
而Integer的intValue()方法则可以将一个包装类型转化为对应的基本数据类型,即拆箱方法。
我们再来认识一下自动装箱这个概念。又叫隐式装箱,直接给Integer赋值,即Integer d=1,在编译的时候,会直接调用Integer.valueOf()方法完成装箱。
我们来看一下如下的代码:
public class Test {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a == b); // true
System.out.println(c == d); // false
}
}
结果是不是很出人意料啊,我们先来观察一下valueOf的源码。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
我们可以发现,当i的值位于[-128,127]的时候,会直接返回Integer缓存数组中相应对象的引用,如果i大于127或小于-128,会重新创建一个Integer实例,并返回。对于a,b的话,在范围之内,他们都在缓存范围内,指向同一对象,而c,d的话,超出范围,自动创建实例,不是同一对象。
以上就是我对Java数据类型的认识与了解,希望对大家能够有所帮助。