这篇来说说基本数据类型与其对应的包装类.
基本数据类型有8种(<Java编程思想>中把void也算作基本数据类型,所以有9种的说法.),4中整数类型,2种浮点类型,1中字符类型,1种布尔类型.每种基本数据类型都有其对应的包装类,下面这个表格列出了基本数据类型,占用多少空间,表示范围与它的包装类类型(这里把void也写进去了.):
基本数据类型 | 占用内存大小 | 最小值 | 最大值 | 默认值 | 包装类类型 |
boolean | ? | ? | ? | false | Boolean |
char | 16bits | 0 | 65535 | ''(什么都没有) | Character |
byte | 8bits | -128 | 127 | 0 | Byte |
short | 16bits | -2^15 | 2^15+1 | 0 | Short |
int | 32bits | -2^31 | 2^31-1 | 0 | Integer |
long | 64bits | -2^63 | 2^63-1 | 0 | Long |
float | 32bits | 1.4e-45 | 3.4028235e+38 | 0.0 | Float |
double | 64bits | 4.9e-324 | 1.7976931348623157e+308 | 0.0 | Double |
void | ? | ? | ? | ? | Void |
Java中数值都有正负号.
最大值最小值我们无须刻意记忆,因为其包装类中分别提供了两个常量:
最大值:类型.MAX_VALUE
最小值:类型.MIN_VALUE
为什么把<Java编程思想>会把void也算进去?下面摘抄了一个可能的原因:
众所周知,Java的类型分成两种,一种是基本类型,一种是引用类型。两种的本质区别就是:基本类型是在堆栈处分配空间存“值”。但是引用类型,是在堆里面分配空间存“值”。Void是不能new出来,也就是不能在堆里面分配空间存对应的值。那就是一开始在堆栈处分配好空间了。所以,有些人将Void归成基本类型,也有道理。
在程序设计中经常用到一系列类型,它们需要特殊对待.可以把它们想象成"基本"类型.之所以特殊对待,是因为new将对象存储在"堆"里,故用new创建一个对象--特别是小的、简单的变量,往往不是很有效.因此,对于这些类型,Java采取与C和C++相同的方法.也就是说,不用new来创建变量,而是创建一个并非是引用的"自动变量".这个变量直接存储"值",并置于堆栈中,因此更加高效.
自动装箱与自动拆箱
现在我们对于基本数据类型与包装类的转换可以直接这样:
Integer integer1 = 19970912;
int i = integer1;
但是在Java SE5之前,这是不可能的,需要进行以下步骤:
Integer integer1 = Ingeter.valueOf(19970912);
Integer integer2 = new Integer(19970912);
int i = integer1.intValue(); //float就是调用floatValue(),以此类推.
出于种种原因,在Java SE5的时候,加入了自动拆箱与装箱的功能.其实底层还是调用了构造器或者对应的xxxValue(),我们利用JDK自带的反汇编器来看一下,先来看看原文件的代码:
public class Test{
public static void main(String[] args){
Integer i = 100;
int number = i;
System.out.println(i);
}
}
首先在命令提示符窗口对源文件执行javac操作,得到class文件后输入javap命令(这里需要加.class),这里用-c参数,可以获得较多的信息:
javap -c c:\Test.class
之后回车,可以得到如下输出:
C:\Users\Administrator>javap -c c:\Test.class
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 100
→→→ 2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
→→→ 7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
10: istore_2
11: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
14: aload_1
15: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
18: return
}
别的先不看,只看前面标→→→的两行,其中#后面的数字对应了这条指令对应了代码的第几行.
在"Integer i = 100;"中,可以看到虚拟机自动调用了Integer的静态方法valueOf(),而"int number = i;"中,则调用了intValue().这也就是自动拆箱与装箱的底层实现原理.
包装类中的方法
public static int parseInt(String s, int radix)
throws NumberFormatException
那么为什么要出现包装类呢,在实际的代码编写过程中,有时候我们定义了一个基本数据类型变量,突然想要对其进行操作,然后在IDE里面输入"点",却发现什么方法都没有"点"出来,有的IDE能"点"出来东西,只不过都是表达式.此时就需要包装类"大显身手"了:
"程序是对象的集合,它们通过发送消息来告知彼此所要做的.".
包装类是对象,所以可以给它发送消息,也就是调用方法,这些方法可以对它进行多种操作,下面来举几个常用的方法,这里主要以十分常用的Integer为例.
Ingeter.valueOf()与parseInt()
作用:将传入的参数转换为Integer(valueOf())或int(parseInt())并返回.
String string ="123456";
System.out.println(Integer.valueOf(1)); //1
System.out.println(Integer.valueOf(string)); //123456
valueOf()是用来把传入的参数转换为Integer类型的方法,提供了三个版本,这里只看一个参数的两个版本:
public static Integer valueOf(int i)
public static Integer valueOf(String s) throws NumberFormatException
一个是传入int,返回值是直接创建一个new Integer(i),传入String的内部调用了另一个静态方法parseInt().
那parseInt()又是何方神圣?看看返回值:
public static int parseInt(String s, int radix) throws NumberFormatException
返回了int类型,而valueOf返回的是Integer类型,说明valueOf()中又进行了自动装箱,会抛出NumberFormatException,即数字格式异常,容易理解,传入的字符串可能不是规范的int类型整数的格式,此时就无法转换.
xxxValue()与toString()
作用:转换为xxx类型的值并返回,或者转换成字符串内部逻辑都是进行一次类型转换:
public xxx xxxValue(){
return (xxx)value;
}
支持返回所有的数值类型:
public byte byteValue()
public short shortValue()
public int intValue()
public long longValue()
public float floatValue()
public double doubleValue()
转换成字符串就是toString(),许多类都有这个方法,可以方便的展示这个对象的信息.
equals(),compare()与compareTo()
equals():
Integer中的equals()在类型匹配的情况下是直接比较数值:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
所以只要数值相等就返回true.
int i = 100;
Integer i1 =new Integer(100);
Integer i2 =new Integer(100);
System.out.println(i1.equals(i2)); //true
i2 = i;
System.out.println(i1.equals(i2)); //true
compare():
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
x < y 返回-1,相等返回0,x > y返回1,由于自动拆箱与装箱的关系,直接传入int也是可以的:
int i = 100;
Integer i1 = 100;
Integer i2 = 101;
Integer i3 = 99;
System.out.println(Integer.compare(i1, i2)); //-1
System.out.println(Integer.compare(i1, i)); //0
System.out.println(Integer.compare(i1, i3)); //1
compareTo():
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
调用的是compare(),只需要一个参数,返回结果跟compare()一样,当然只能由Integer对象调用,不能使用int调用,参数是可以传入int类型的:
int i = 100;
Integer i1 = 100;
Integer i2 = 101;
Integer i3 = 99;
System.out.println(i1.compareTo(i2)); //-1
System.out.println(i1.compareTo(i)); //0
System.out.println(i1.compareTo(i3)); //1
toXxxString()
转换为对应进制的String类型:
public static String toBinaryString(int) //二进制
public static String toOctalString(int) //八进制
public static String toHexString(int) //十六进制
底层调用的都是toUnsignedString0():
private static String toUnsignedString0(int val, int shift) {
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
formatUnsignedInt(val, shift, buf, 0, chars);
return new String(buf, true);
}
第一个参数是要转换的值,第二个参数是2的n次幂进制 (比如4就是 2^4=16进制),是一个private方法,我们不能调用它.
System.out.println(Integer.toBinaryString(128)); //10000000
System.out.println(Integer.toOctalString(128)); //200
System.out.println(Integer.toHexString(128)); //80
System.out.println(Integer.toHexString(-128)); //ffffff80
其他方法
sum():对传入的两个数值求和
max():返回传入参数的最大值
min():返回传入参数的最小值
底层调用的都是Math类的同名方法.
System.out.println(Integer.sum(1000, 900)); //1900
System.out.println(Integer.max(1000, 900)); //1000
System.out.println(Integer.min(1000, 900)); //900