本次谈谈java中的基本数据类型。java中8个基本数据类型说多不多说少也不少,相对简单的记忆方式:整数型(byte,short,int,long)、浮点型(float,double)、逻辑型(boolean)、字符(char)。
java基本类型简介:
类型 | 所占空间(bit) | 所占空间(byte) | 取值范围 | 对应的装箱基本类型 | 基本类型大小判断 |
byte | 8bit | 1byte | -128~127 | Byte | >、<、==等 |
short | 16bit | 2byte | -32768~32767 | Short | >、<、==等 |
int | 32bit | 4byte | -(2^31)~(2^31)-1 | Integer | >、<、==等 |
long | 64bit | 8byte | -(2^63)~(2^63)-1 | Long | >、<、==等 |
float | 32bit | 4byte | (+-)3.402823447E+38F | Float | Float.compare()或Double.compare() |
double | 64bit | 8byte | (+-)1.79769313486231570E+308 | Double | Double.compare() |
boolean | 1bit | 1/8byte | 0、1 | Boolean | 无 |
char | 16bit | 2byte | 0~(2^16)-1 | Char | >、<、==等 |
基本类型&引用类型:
基本类型之所以叫基本类,是因为其数据能够在方法的栈空间中分配;相对应的引用类型是不能够在方法的栈空间中分配,而在堆空间中分配的。栈在性能上高于堆。这里的前提是方法的栈空间,类对象中基本类型域还是分配在堆空间中。
各自对应的装箱基本类型就是属于引用类型,封装相应的装箱基本类型是比较必要。
值传递or引用传递?:
所谓引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
在c/c++是存在引用传递,但是java中没有引用传递(具体看:http://guhanjie.iteye.com/blog/1683637这位博主总结的还是不错的)
1 public class Test { 2 private int id; 3 4 static Test test1; 5 static Test test2 = new Test(); 6 7 static public void main(String[] args) { 8 9 test2.id = 13; 10 System.out.println("doTest:"); 11 System.out.println("test1 is " +test1); 12 doTest(test1); 13 System.out.println("test1 is " +test1); 14 System.out.println("########################"); 15 System.out.println("test2 is " +test2); 16 doTest(test2); 17 System.out.println("test2 is " +test2); 18 } 19 20 static private void doTest(Test test) { 21 System.out.println("start doTest:"); 22 if (test == null) { 23 test = new Test(); 24 } 25 test.id = 31; 26 System.out.println("test is " +test); 27 System.out.println("end doTest."); 28 } 29 30 @Override 31 public String toString() { 32 return ""+this.id; 33 } 34 }
如果是引用传递那么其在函数中对参数的所进行的修改,将影响到实际参数。所以打印的结果应该是:
doTest:
test1 is null
start doTest:
test is 31
end doTest.
test1 is 31
########################
test2 is 13
start doTest:
test is 31
end doTest.
test2 is 31
但是现实容易打脸:
可以看到test1在执行完doTest之后还是null.即没用影响实际参数。但是为什么test2受到影响了呢?
那么真正的引用传递是如何的呢?这里为了方便查看打印出变量的地址。(在线编译器地址:http://codepad.org/)
#include <iostream> using namespace std; struct Test { int id; }test1,test2={13}; void doTest(Test &test) { cout<<"start doTest:"<<endl; cout<<"test address:"<<&test<<endl; test.id = 31; cout<<"test is " <<test.id<<endl; cout<<"end doTest."<<endl; } int main() { cout<<"doTest:"<<endl; cout<<"test1 is " <<test1.id<<endl; cout<<"test1 address:"<<&test1<<endl; doTest(test1); cout<<"test1 is " <<test1.id<<endl; cout<<"########################"<<endl; cout<<"test2 is " <<test2.id<<endl; cout<<"test2 address:"<<&test2<<endl; doTest(test2); cout<<"test2 is " <<test2.id<<endl; return 0; }
地址是一样的。。。。(c忘光了,不敢随便分析一下其内存分配,海涵)
这里给出java模型:
这里的test与test2没直接的关系,唯一相同的就是都引用了堆中地址为AAAA的Test对象
基本类型Or装箱基本类型:
这里探讨基本类型与装箱类型的应用场景:
1、能用基本类型就绝不用装箱类型,我的准则而言。基本类型在性能上远远优于装箱基本类型。
1 import java.util.*; 2 3 public class Test { 4 static public void main(String[] args) { 5 double startTime = System.currentTimeMillis(); 6 Long L = 0l; 7 for (int i = 0; i < Integer.MAX_VALUE; i++) { 8 L += 1; 9 } 10 double endTime = System.currentTimeMillis(); 11 System.out.printf("装箱基本类型耗时:%f%n", (endTime - startTime)); 12 13 startTime = System.currentTimeMillis(); 14 long l = 0l; 15 for (int i = 0; i < Integer.MAX_VALUE; i++) { 16 l += 1; 17 } 18 endTime = System.currentTimeMillis(); 19 System.out.printf("装箱基本类型耗时:%f%n", (endTime - startTime)); 20 } 21 }
代码角度建议能不用装箱基本类型就不用或者减少装箱拆箱次数
2、泛型中只能使用装箱基本类型。原因:存在类型擦除的,将类型都替换成顶级父类(Object)。
装箱基本类型出现的必然性:
java作为一门面向对象的编程语言,各种编程逻辑应该都是基于对象。装箱基本类型的出现,使得各种逻辑更加便捷易懂,基本类型中的各种逻辑划分更加明了清晰。(还有挺多的不总结啦。。。)
譬如:int的边界直接定义在Integer中作为静态常量,long的边界直接定义在Long中作为静态常量;两者各司其职互不影响。