引入包装类
首先我们看一个简单的需求:把十进制100转成二级制。
我们可以这么干,采用"除2取余,逆序排列"的方法,类似于小学数学中讲的“短除法”。
PS:画图工具用的不是很溜,大家凑合着看吧~
可以看出十进制整数100的二进制是1100100。
代码实现示例
package String;
public class WrapperClassTest {
public static void main(String[] args) {
int i = 100;
String result0 = toBinary(0);
String result1 = toBinary(1);
String result100 = toBinary(100);
System.out.println("result0 == " + result0);
System.out.println("result1 == " + result1);
System.out.println("result100 == " + result100);
}
// 此方法只能求自然数的二进制
private static String toBinary(int number) {
if (number == 0) {
return "0";
}
int sum;
String result = "";
for (int i = number; i > 0; i = i / 2) {
if (i % 2 == 0) {
sum = 0;
} else {
sum = 1;
}
result = sum + result;
}
return result;
}
}
运行结果:
result0 == 0
result1 == 1
result100 == 1100100
这么看来还不错,但是这个方法只能求某个自然数的二进制,还可能存在未知bug,那么问题来了,如果给的数是负数、小数,我们怎么整?我们都知道java是典型的面向对象编程语言,但其中的8种基本数据类型并不支持面向对象编程,不具备对象的特性——不携带属性、没有方法可调用。如果我们要完成数据的进制转换,或者把一个int数据转成String类型就会很麻烦,为了解决这些问题,java也为每种基本数据类型设计了各自对应的类,称之为包装类(Wrapper Classes)。
基本数据类型 | 对应的包装类 | 基本数据类型 | 对应的包装类 |
byte | Byte | char | Character |
short | Short | float | Float |
int | Integer | double | Double |
long | Long | boolean | Boolean |
Integer构造方法
ps:基本类型包装类使用方法基本相同,这里我们以Integer为例进行科普。
构造方法摘要 | |
---|---|
Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值。 | |
Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。注意:这个字符串必须是由数字字符组成,否则会抛出 NumberFormatException这个异常 |
代码示例:
public static void main(String[] args) {
int i = 100;
String str = "100";
Integer integer = new Integer(i);
Integer integer2 = new Integer(str);
System.out.println("integer == " + integer);
System.out.println("integer2 == " + integer2);
}
解决进制转换问题
- 十进制与其他进制之间的转换
本文刚开始通过求十进制100的二进制引入基本类型包装类,那么jdk提供给我们的基本类型包装类又是怎么解决这个问题的?查看API可知,可以通过toBinaryString(int i)这个方法解决,老规矩,先上代码:
public static void main(String[] args) {
int i = 100;
Integer integer = new Integer(i);
String str = integer.toBinaryString(i);
System.out.println("str == " + str);
}
运行结果:
str == 1100100
其实Integer中还提供了八进制、十六进制、N进制(1<N<37的整数)的转换,look at it!
public static void main(String[] args) {
int i = 100;
Integer integer = new Integer(i);
String str1 = integer.toOctalString(i);// 转成八进制
String str2 = integer.toHexString(i);// 转成十六进制
String str3 = integer.toString(i, 0);// 转成零进制
String str4 = integer.toString(i, 1);// 转成一进制
String str5 = integer.toString(i, 2);// 转成二进制
String str6 = integer.toString(i, 36);// 转成三十六进制
String str7 = integer.toString(i, 37);// 转成三十七进制
String str8 = integer.toString(i, -1);// 转成负一进制
System.out.println("str1 ==" + str1);
System.out.println("str2 ==" + str2);
System.out.println("str3 ==" + str3);
System.out.println("str4 ==" + str4);
System.out.println("str5 ==" + str5);
System.out.println("str6 ==" + str6);
System.out.println("str7 ==" + str7);
System.out.println("str8 ==" + str8);
}
运行结果:
str1 ==144
str2 ==64
str3 ==100
str4 ==100
str5 ==1100100
str6 ==2s
str7 ==100
str8 ==100
我们从运行结果发现,转成零进制、一进制、三十七进制、负一进制,结果都是100,why?
通过查看源码我们看到,进制在小于Character.MIN_RADIX(也就是2)或者大于Character.MAX_RADIX(也就是36)时,就按照十进制处理了,也就是说进制转换的范围是二进制到三十六进制。
二进制这个临界值我们可以理解,那三十六进制这个最大临界值怎么理解?三十七进制怎么就不行了?我们想想,“0~9”、“a~z”加起来是不是刚好是36个,通过查看如下源码,我们也可以找到答案,so,you konow?
- String数据与N进制之间的转换
public static void main(String[] args) {
// 字符串数据转成N进制的整数
System.out.println(Integer.parseInt("100", 10));
System.out.println(Integer.parseInt("100", 2));
System.out.println(Integer.parseInt("100", 8));
System.out.println(Integer.parseInt("100", 16));
System.out.println(Integer.parseInt("100", 36));
// java.lang.NumberFormatException 前面的字串必须是能组成后面进制的数据,二进制逢二进
//一,“123"明显不符合要求
// System.out.println(Integer.parseInt("123", 2));
}
上面的Integer.parseInt("100", 10)表示把字符串类型的“100”转换成十进制整型数据,其他以此类推。
基本数据类型与String之间的转换
- String转int
public static void main(String[] args) {
String str = "100";
int number = 66;
//String转int
//方式一
Integer integer = new Integer(str);
int number1 = integer.intValue();
System.out.println("number1 == " + number1);
//方式二(推荐使用)
int number2 = Integer.parseInt(str);
System.out.println("number2 == " +number2);
}
- int转String
public static void main(String[] args) {
String str = "100";
int number = 66;
// int转String
// 方式一
String str1 = "" + number;
System.out.println("str1 == " + str1);
// 方式二
Integer integer = new Integer(number);
String str2 = integer.toString();
System.out.println("str2 == " + str2);
// 方式三(推荐使用)
String str3 = String.valueOf(number);
System.out.println("str3 == " + str3);
// 方式四
String str4 = Integer.toString(number);
System.out.println("str4 == " + str4);
}
拆装箱
基本类型和对应的包装类可以相互转换。
- 基本类型向对应的包装类转换称为装箱,比如把int转换成Integer类的对象。
- 包装类向对应的基本类型转换称为拆箱,比如把Integer类的对象简化为int。
public static void main(String[] args) {
int i = 100;
Integer integer = new Integer(i);// 通过构造方法手动装箱
int j = integer.intValue();// 通过intValue方法手动拆箱
System.out.println("integer == " + integer);
System.out.println("j == " + j);
}
JDK1.5之后,有了包装类的自动拆装箱:
public class WrapperClass02 {
/*
* JDK5的新特性 自动装箱:把基本类型转换为包装类类型 自动拆箱:把包装类类型转换为基本类型
*/
public static void main(String[] args) {
// 定义了一个int类型的包装类类型变量i
// Integer i = new Integer(100);
Integer ii = 100;
ii += 200;
System.out.println("ii == " + ii);
}
}
可以看到,我直接把int类型数据赋值给Integer对象,按理来说,基本数据类型是不能直接赋值给引用类型的,可是代码并没有报错,why?而且我对int数据和Integer数据做了相加操作,居然也没有报错,这又是为什么?通过反编译class文件,我们发现原来JDK帮我们通过Integer.valueOf()方法将int数据装箱转换成Integer类型之后,才做的赋值;相加的操作是通过intValue()拆箱,将Integer数据转成int数据,做完相加操作之后,再通过Integer.valueOf()方法将int数据装箱成Integer对象,最后完成赋值;字符串的拼接通过实例化StringBuilder对象调用append方法实现。
注意一个小问题:在使用时,Integer x = null;代码就会出现NullPointerException。建议先判断是否为null,然后再使用。
Integer iii = null;
// NullPointerException
if (iii != null) {
iii += 1000;
System.out.println(iii);
}
面试题
根据如下程序,写出程序运行结果:
package String;
/**
* 看程序写结果
*
* @author Ke_Xiaomiao
*
*/
public class WrapperClass02 {
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2);
System.out.println(i1.equals(i2));
System.out.println("------");
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4);
System.out.println(i3.equals(i4));
System.out.println("------");
Integer i5 = 127;
Integer i6 = 127;
System.out.println(i5 == i6);
System.out.println(i5.equals(i6));
System.out.println("------");
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8);
System.out.println(i7.equals(i8));
System.out.println("------");
}
}
首先我们看第一块,i1和i2都是创建的新对象的引用,所以i1==i2结果是false,又因为它俩值相等,所以i1.equals(i2)结果是true;第二块与第一块原理一样;第三块把127直接赋值给i5和i6,其实我们知道这里做了自动装箱操作,即Integer.valueOf(127),虽然都是把127装进“箱子”里了,可是装进的“箱子”不同,所以我们认为i5==i6结果也是false,又因为它俩值相等,即内容一样,所以i5.equels(i6)结果是true;第四块代码原理与第三块是一样的。我们暂时是这么认为的,对不对我们不知道,且看运行结果:
false
true
------
false
true
------
true
true
------
false
true
------
咦,我怎么发现第三块代码的结果跟我们想的不太一样呢,why?
既然第三块代码存在装箱操作,我们不妨看下Integer.valueOf()的源码:
我们发现这里存在一个IntegerCache,暂且叫它Integer数据缓冲池。如果被装箱的数据在low和high之间,它是直接从缓冲池里取的数据,否则创建新的Integer对象。那么low和high分别是多少,继续追踪源码:
我们可以看到low的值是-128,high的值是127,而且类的注释也说明了缓冲池的范围是-128~127之间(包括-128和127)。so,第三块代码因为是直接从缓冲池取的,所以i5==i6结果是true,而第四块代码中,128超出了缓冲池的范围,得重新创建对象,所以i7==i8结果是false。你学会了吗?学会了点赞留言支持一下,没学会的同学欢迎评论区留言提问,我看到会第一时间回复~