文章目录
- 一、概念
- (1)为什么需要包装类
- (2) 有哪些包装类
- (3)总结
- 二、包装类
- (1)自定义包装类
- (2) 包装类与基本数据类型间的转换
- 2.1 为什么需要转换
- 2.2 装箱
- 2.2.1 方式一、使用包装类的构造器
- 2.2.2 方式二、(建议)调用包装类的valueOf(xxx xx)
- 2.3 拆箱
- 2.4 自动装箱与拆箱
- 2.5 注意
- 2.6 代码
- (3) 基本数据类型、包装类与字符串String间的转换
- 3.1 基本数据类型、包装类 -> String类型
- 3.1.1 方法一
- 3.1.2 方法二
- 3.2 String类型->基本数据类型、包装类
- 3.3 代码
- 3.4 总结
- 三、 包装类的其它API
- (1) 数据类型的最大最小值
- (2)字符转大小写
- (3) 整数转进制
- (4) 比较的方法
- 四、 包装类对象的特点
- (1)包装类缓存对象
- (2)类型转换问题
- (3)包装类对象不可变
- 五、 习题
- (1)练习1
- (2)面试题
- 2.1 面试题1
- 2.2 面试题2
- 2.3 面试题3
一、概念
(1)为什么需要包装类
Java提供了两个类型系统,基本数据类型
与引用数据类型
。使用基本数据类型在于效率,然而当要使用只针对对象设计的API或新特性(例如泛型),怎么办呢?例如:
//情况1:方法形参
Object类的equals(Object obj)
//情况2:方法形参
ArrayList类的add(Object obj)
//没有如下的方法:
add(int number)
add(double d)
add(boolean b)
//情况3:泛型
Set<T>
List<T>
Cllection<T>
Map<K,V>
①情况1:方法形参
Object类的equals(Object obj)
equals用于比较引用数据变量是否相等,形参只能传Object类型,可以传各个类的对象(以多态的方式传进来),在Object的API声明当中,数组也可以看作Object实例,所以也可以放数组。
现在的问题是,基本数据类型传不过来。
②情况2:方法形参
ArrayList类的add(Object obj)
ArrayList是集合中的一个类,里面有个add方法,形参是Object类型的。
若是现在想使用这个容器,往里面放学生的成绩,学生成绩是int类型的,这是放不进去的。因为add方法在ArrayList
中只提供了add(Object obj)
这一个方法,没有重载(比如形参是int类型)的方法。
所以,没有如下的方法:
add(int number)
add(double d)
add(boolean b)
③情况3:泛型
后边还会讲一个新特性“泛型”,这个T,我们在使用这个类的时候需要指明它是什么类型,Java规范中说,T只能是引用数据类型。
所以基本数据类型放不进来。
Set<T>
List<T>
Cllection<T>
Map<K,V>
使用基本数据类型在于效率,但是在很多场景下,我们希望它具备类的特征。
换句话说,能不能让基本数据的值作为一个像对象一样的结构放到形参的位置,作为实参来出现。
基本数据类型需要一波类与它们有一一对应的关系,这一波类就是包装类。
(2) 有哪些包装类
Java针对八种基本数据类型定义了相应的引用类型:包装类
(封装类)。
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象。
下面蓝色虚线里面都是数值类型的,这些数值类型它们默认父类是Number,而Number继承于Object。
比如现在有个学生成绩Score,是int类型,为80,然后我们就需要用一个Integer类的对象来表示它的成绩,成绩也是80,实际上是Integer这个对象内部有一个属性,这个属性是int类型的,它是80分。
封装以后的,内存结构对比:
public static void main(String[] args){
int num = 520; //局部变量
Integer obj = new Integer(520);
}
原来的时候,在Main方法中声明了一个局部变量num,值为520,这时候就是纯粹的在栈空间中(main方法的栈帧里面有一个num,值为520)。
若是以一个对象的方式去呈现,那就是new了一个Integer,new的都是对象,放在堆空间里面,变量obj放在栈帧中,指向堆空间创建的对象实体,对象实体里面有个属性是int类型的,对应的值是520。
原来的变量名是num,现在只是将一个也是int类型的变量,拿类给它包装了一下,使它具备类的特征。
把基本数据类型包装了一下,就是包装类。使基本数据类型具备了类的特征。
(3)总结
-
为什么要使用包装类?
为了使得基本数据类型的变量具备引用数据类型变量的相关特征(比如:封装性、继承性、多态性),我们给各个基本数据类型的变量都提供了对应的包装类
。 -
(掌握)基本数据类型对应的包装类类型
数值类型(继承于Number):
byte -> Byte
short -> Short
int ->Integer
long -> Long
float -> Float
double ->Double
不继承于Number:
char -> Character
boolean -> Boolean
左边是基本数据类型,对应也叫“关键字”;右边就只是类,不是关键字。
二、包装类
(1)自定义包装类
如果自己去定义包装类的话,就是这样:
public class MyInteger {
int value;
public MyInteger() {
}
public MyInteger(int value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
这里定义了一个MyInteger,和Integer的定义方式基本一样。
在类里面声明一个int型的变量,这个变量习惯性叫value。
然后对应提供构造器,以及toString方法(把值打印一下,不要默认打印地址)。
当我们通过MyInteger的构造器MyInteger(int value)
造对象的时候,比如MyInteger obj = new MyInteger(520);
传一个520,那么形参value就将实参520传递过来,最终就把MyInteger的属性值value改为了520。
以前咱们都是直接给value赋值为520,现在是赋给MyInteger对象的一个属性value。
原来是直接Int学生成绩,现在是以一个对象的形式来出现的。
继而我们就可以将它放到刚才说的形参的位置了,就可以使用类中丰富的特征了。比如:Object类的equals(Object obj)
(2) 包装类与基本数据类型间的转换
2.1 为什么需要转换
- 对于
基本数据类型
来讲,在有些场景下,需要使用基本数据类型对应的包装类的对象。此时就需要将基本数据类型的变量转换为包装类的对象。比如:ArrayList的add(Object obj);
、Object类的equals(Object obj)
。 - 对于
包装类
来讲,既然我们使用的是对象,那么对象是不能进行+ - * /
等运算的。为了能够进行这些运算,就需要将包装类的对象转换为基本数据类型的变量。
2.2 装箱
装箱:把基本数据类型转为包装类对象
(装箱)基本数据类型 —> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
转为包装类的对象,是为了使用专门为对象设计的API和特性
基本数值---->包装对象
Integer obj1 = new Integer(4);//使用构造函数函数
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException
Integer obj2 = Integer.valueOf(4);//使用包装类中的valueOf方法
【案例】
public class WrapperTest {
//把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
}
现在创建一个单元测试:
前面说注解
的时候,已经将“单元测试”设置为模板了,不会的去前面的文章看一看。
Java面向对象(高级)-- 注解(Annotation)-CSDN博客
2.2.1 方式一、使用包装类的构造器
【案例1】
现在定义一个int类型的变量i1
,如下:
public class WrapperTest {
//把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
/*
* (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
* */
@Test
public void test1(){
int i1 = 10; //创建一个int型的变量
}
}
此时它没有什么方法可用:
现在将它包装起来。
这样:
可以看到,Integer()两个参数都过时了。
这里先用一下。(JDK9被标记为过时,不建议使用了)
现在建议使用的是valueOf(int)
,如下:(这个等会再说)
这是第一种方式,比较传统,就是把刚才的基本数据类型的值传给当前类里面的成员变量。
现在的ii1
已经是一个对象了,所以用它点的时候,就有很多方法可以去调用了。
而且还可以以多态的方式出现在方法形参的位置。
如下:
🌱代码
public class WrapperTest {
//把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
/*
* (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
* */
@Test
public void test1(){
int i1 = 10; //创建一个int型的变量 基本数据类型
Integer ii1=new Integer(i1); //包装起来了,具备了类的特征 对象
System.out.println(ii1); //结果是数据
}
}
🍺输出结果
【案例2】
再举两个例子:
🌱代码
public class WrapperTest {
//把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
/*
* (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
* */
@Test
public void test1(){
int i1 = 10; //创建一个int型的变量 基本数据类型
Integer ii1=new Integer(i1); //包装起来了,具备了类的特征 对象
System.out.println(ii1); //结果是数据,不是地址
float f1=12.3F;
f1=32.2f;
Float ff1=new Float(f1);
System.out.println(ff1.toString());
boolean b1=true;
Boolean bb1=new Boolean(b1);
System.out.println(bb1);
}
}
🍺输出结果
其实Float()
里面可以传的参数有好几个,包括float类型、double类型、String类型。
如下:
比如现在传一个字符串:
package yuyi03;
import org.junit.Test;
public class WrapperTest {
@Test
public void test1(){
String s1="32.4"; //“ ” 的里面还是一个float或者double类型的即可
Float ss1=new Float(s1);
System.out.println(s1);
}
}
输出结果:
只要双引号““
的里面还是一个float或者double类型的即可,若传入的不是浮点型的数据,就会报错,如下:
所以,双引号里面放的不是数,就会有数据格式化异常NumberFormatException
。
要是传递一个基本数据类型是可以的,也可以放字符串,但是字符串内容必须是实打实的相对应的类型的数,不是的就不行。
【案例3】
那布尔类型呢?
比如:
package yuyi03;
import org.junit.Test;
public class WrapperTest {
@Test
public void test1(){
boolean b1=true;
Boolean bb1=new Boolean(b1);
System.out.println(bb1);
String s2="false";
Boolean bb2=new Boolean(s2);
System.out.println(s2);
}
}
输出结果:
若此时将false大写呢?比如:s2="False";
可以发现它没有报错,说明“False”也可以使用。
那如果是“False123”呢?
所以布尔类型有点特殊,只要和“true”不一样,那就都是false。
可以看一下它调用的是哪一个函数:
"true".equalsIgnoreCase(s)
:在忽略大小写的情况下,如果和true一样,那就返回true。如果不一样,那就认为是false。
比如:
2.2.2 方式二、(建议)调用包装类的valueOf(xxx xx)
以Integer
为例:
可以看到,这里建议用valueOf(Xxx)
来替换Integer
。
因为它可能产生明显更好的空间和时间性能。
【案例1】
🌱代码
package yuyi03;
public class WrapperTest {
@Test
public void test1(){
//推荐使用
//Integer类型
int i2=10;
Integer ii2= Integer.valueOf(i2);
System.out.println(ii2);
//Boolean类型
Boolean b2=Boolean.valueOf(true);
System.out.println(b2);
//Float类型
Float f2=Float.valueOf(12.3F);
System.out.println(f2);
}
}
🍺输出结果
(装箱)基本数据类型 —> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
2.3 拆箱
拆箱:把包装类对象拆为基本数据类型
(拆箱)包装类–>基本数据类型:调用包装类的xxxValue()
转为基本数据类型,一般是因为需要运算,Java中的大多数运算符是为基本数据类型设计的。比较、算术等
包装对象---->基本数值
Integer obj = new Integer(4);
int num1 = obj.intValue();
【案例1】
现在有一个包装类对象,如下:
//(拆箱)包装类-->基本数据类型:
@Test
public void test2(){
Integer ii1=new Integer(10); //包装类对象
}
让包装类对象加一,但现在是对象,对象加一没有意义,就得将它转化为基本数据类型才可以加一。
Integer
对应的基本数据类型是Int
,所以是intvalue
。
此时i
就是一个基本数据类型了,可以进行加一运算了。如下:
//(拆箱)包装类-->基本数据类型:
@Test
public void test2(){
Integer ii1=new Integer(10); //包装类对象
int i=ii1.intValue();
i=i+1;
System.out.println(i);
}
输出结果:
【案例2】
当然,Float
类型也一样。
Float
对应基本数据类型是float
,所以是floatValue
。
比如:
//(拆箱)包装类-->基本数据类型:
@Test
public void test2(){
Float ff1=new Float(12.3F); //浮点类型
float f=ff1.floatValue();
f=f+2;
System.out.println(f);
}
输出结果:
【案例3】
Boolean
布尔类型也是一样。
Boolean对应的是基本数据类型的boolean,所以是booleanValue()
。
比如:
//(拆箱)包装类-->基本数据类型:
@Test
public void test2(){
Boolean b2=Boolean.valueOf(true); //布尔类型
boolean b=b2.booleanValue();
}
(拆箱)包装类–>基本数据类型:调用包装类的xxxValue()
2.4 自动装箱与拆箱
自动装箱与拆箱:
由于我们经常要做基本类型与包装类之间的转换,从JDK5.0
开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。
注意:只能与自己对应的类型之间才能实现自动装箱与拆箱。
Integer i = 1;
Double d = 1;//错误的,1是int类型
【案例1-自动装箱】
🌱代码
package yuyi03;
public class WrapperTest {
//jdk5.0新特性:自动装箱、自动拆箱。
@Test
public void test4(){
//自动装箱:基本数据类型 ---> 包装类
//int类型
int i1=10;
Integer ii1=i1; //右边i1是基本数据类型,左边ii1是引用数据类型,右边直接赋给左边(自动装箱)
System.out.println(ii1.toString());
Integer ii2=i1+1; //右边i1+1变成11,然后再自动装箱到左边ii2
System.out.println(ii2);
//其他类型
Boolean bb1=true; //自动装箱
Float f1=12.3F; //自动装箱
}
}
🍺输出结果
【案例2-自动拆箱】
🌱代码
package yuyi03;
public class WrapperTest {
//jdk5.0新特性:自动装箱、自动拆箱。
@Test
public void test4(){
//自动装箱:基本数据类型 ---> 包装类
//int类型
int i1=10;
Integer ii1=i1; //右边i1是基本数据类型,左边ii1是引用数据类型,右边直接赋给左边(自动装箱)
//其他类型
Boolean bb1=true; //自动装箱
Float f1=12.3F; //自动装箱
//自动拆箱:包装类-->基本数据类型
int i2=ii1; //右边ii1是一个对象,左边i2是一个基本数据类型的变量。(自动拆箱)
boolean b1=bb1; //自动拆箱
}
}
🗳️Tips
Integer ii1=i1;
右边i1是基本数据类型,左边ii1是引用数据类型,右边直接赋给左边(自动装箱)。
这里可以理解为“语法糖”,就是一个小的语法特性,似乎不太满足语法正规的要求,但是这样的写法又很轻巧、简洁。
其实底层它调用的还是之前说的方法。
比如现在我们看一下test4()
的字节码文件,比如装箱:
所以,本质上还是调用包装类的valueOf(xxx xx)进行装箱的,只不过现在封装了一下。
拆箱
也一样,如下:
所以,本质上还是调用包装类的xxxValue()进行拆箱的,只不过现在封装了一下。
有了这样一个新特性之后,似乎都感受不到包装类和基本数据类型有什么区别,直接就赋值用了。
这确实有利于开发的效率,不用过多关注它们相互转化的事情了。
2.5 注意
这里再说一个容易出错的地方。
既然变成对象了,初始化值也就不一样了。
【案例1】
🌱代码
package yuyi03;
public class WrapperTest {
//注意
@Test
public void test3(){
Account account=new Account();
System.out.println(account.isFlag1); //基本数据类型,默认值false
System.out.println(account.isFlag2); //默认值null
}
}
class Account{
//成员变量:有默认值
boolean isFlag1;
Boolean isFlag2;
}
🍺输出结果
🗳️Tips
boolean
类型与Booleaan
类型的默认值不一样。
【案例2】
🌱代码
package yuyi03;
public class WrapperTest {
//注意
@Test
public void test3(){
Account account=new Account();
System.out.println(account.balance1); //0.0
System.out.println(account.balance2); //null
}
}
class Account{
//成员变量:有默认值
double balance1; //基本数据类型,若是0.0可以表示账户余额为0,没有钱
Double balance2; //包装类,默认值是null,可以理解为这个账户的余额一开始没有初始化,后期new或者valueOf赋了一个值,若是0.0,可以理解为余额这个对象被初始化了,只不过初始化之后没有往里面放钱,或者花光了。所以这里可以区分是没有初始化还是账户没有钱
}
🍺输出结果
🗳️Tips
①double balance1;
基本数据类型,若是0.0可以表示账户余额为0,没有钱。
②Double balance2;
包装类,默认值是null,可以理解为这个账户的余额一开始没有初始化。
后期new或者valueOf赋了一个值,若是0.0,可以理解为余额这个对象被初始化了,只不过初始化之后没有往里面放钱,或者花光了。
所以这里可以区分是没有初始化还是账户没有钱。
原来使用基本数据类型变量的位置,改成包装类以后,对于成员变量来说,其默认值变化了!
2.6 代码
整体代码留下啦。
🌱代码
package yuyi03;
import org.junit.Test;
/**
* ClassName: WrapperTest
* Package: yuyi03
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/8 0008 10:04
*/
public class WrapperTest {
//把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
/*
* (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
* */
@Test
public void test1(){
int i1 = 10; //创建一个int型的变量 基本数据类型
Integer ii1=new Integer(i1); //包装起来了,具备了类的特征 对象
System.out.println(ii1); //结果是数据,不是地址
float f1=12.3F;
f1=32.2f;
Float ff1=new Float(f1);
System.out.println(ff1.toString());
String s1="32.4"; //“ ” 的里面还是一个float或者double类型的
Float ss1=new Float(s1);
System.out.println(s1);
/*s1="abc";
Float ss2=new Float(s1); //异常:NumberFormatException*/
boolean b1=true;
Boolean bb1=new Boolean(b1);
System.out.println(bb1);
String s2="false";
s2="False123";
s2="True";
Boolean bb2=new Boolean(s2);
System.out.println(bb2);
//推荐使用
//Integer类型
int i2=10;
Integer ii2= Integer.valueOf(i2);
System.out.println(ii2);
//Boolean类型
Boolean b2=Boolean.valueOf(true);
System.out.println(b2);
//Float类型
Float f2=Float.valueOf(12.3F);
System.out.println(f2);
}
//(拆箱)包装类-->基本数据类型:调用包装类的xxxValue()
@Test
public void test2(){
Integer ii1=new Integer(10); //包装类对象
int i=ii1.intValue();
i=i+1;
System.out.println(i);
Float ff1=new Float(12.3F); //浮点类型
float f=ff1.floatValue();
f=f+2;
System.out.println(f);
Boolean b2=Boolean.valueOf(true); //布尔类型
boolean b=b2.booleanValue();
}
//注意
@Test
public void test3(){
Account account=new Account();
System.out.println(account.isFlag1); //基本数据类型,默认值false
System.out.println(account.isFlag2); //默认值null
System.out.println(account.balance1); //0.0
System.out.println(account.balance2); //null
}
//jdk5.0新特性:自动装箱、自动拆箱。
@Test
public void test4(){
//自动装箱:基本数据类型 ---> 包装类
int i1=10;
Integer ii1=i1; //右边i1是基本数据类型,左边ii1是引用数据类型,右边直接赋给左边(自动装箱)
System.out.println(ii1.toString());
Integer ii2=i1+1; //右边i1+1变成11,然后再自动装箱到左边ii2
System.out.println(ii2);
Boolean bb1=true; //自动装箱
Float f1=12.3F; //自动装箱
//自动拆箱:包装类-->基本数据类型
int i2=ii1; //右边ii1是一个对象,左边i2是一个基本数据类型的变量。(自动拆箱)
boolean b1=bb1; //自动拆箱
}
}
class Account{
//成员变量:有默认值
boolean isFlag1;
Boolean isFlag2;
double balance1; //基本数据类型,若是0.0可以表示账户余额为0,没有钱
Double balance2; //包装类,默认值是null,可以理解为这个账户的余额一开始没有初始化,后期new或者valueOf赋了一个值,若是0.0,可以理解为余额这个对象被初始化了,只不过初始化之后没有往里面放钱,或者花光了。所以这里可以区分是没有初始化还是账户没有钱
}
🍺输出结果
(3) 基本数据类型、包装类与字符串String间的转换
3.1 基本数据类型、包装类 -> String类型
基本数据类型、包装类 —> String类型:
① 调用String的重载的静态方法valueOf(xxx xx) ;
② 基本数据类型的变量 + “”
3.1.1 方法一
🍻调用String的重载的静态方法valueOf(xxx xx)
【案例1】
现在有一个基本数据类型i1
,如下:
package yuyi03;
import org.junit.Test;
public class WrapperTest1 {
//基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx)
public void test1(){
int i1=10;
}
}
既然转换成String类型,那就用String
里面的方法(在后者找方法),如下:
直接将i1
往里面写即可。
🌱代码
public class WrapperTest1 {
//基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx)
@Test
public void test1(){
int i1=10; //基本数据类型
String str1 = String.valueOf(i1); //转化为String类型
System.out.println(str1); //"10"
}
}
🍺输出结果
控制台输出没有显示双引号,其实是存在的。
【案例2】
再来看boolean
类型转换为String类型,一样的道理。
🌱代码
package yuyi03;
import org.junit.Test;
public class WrapperTest1 {
//基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx)
@Test
public void test1(){
boolean b1=true;
String str2 = String.valueOf(b1); //转化为String类型
System.out.println(str2);
}
}
🍺输出结果
【案例3】
刚才用的都是基本数据类型,也可以是对应包装类的对象。
🌱代码
public class WrapperTest1 {
//基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx)
@Test
public void test1(){
boolean b1=true;
Boolean b2=b1; //自动装箱
String str2 = String.valueOf(b1); //转化为String类型
String str3 = String.valueOf(b2);
System.out.println(str2);
System.out.println(str3);
}
}
🍺输出结果
3.1.2 方法二
🍻基本数据类型的变量 + ""
基本数据类型和字符串可以做连接运算,连接之后结果还是一个字符串。
🌱代码
package yuyi03;
import org.junit.Test;
public class WrapperTest1 {
//基本数据类型、包装类 ---> String类型:② 基本数据类型的变量 + ""
@Test
public void test1(){
int i1=10; //基本数据类型
boolean b1=true;
//方式2:直接的方式,基本数据类型的变量 + ""
String str4=i1+""; //基本数据类型和字符串可以做连接运算,连接之后结果还是一个字符串
String str5=b1+"";
}
}
3.2 String类型->基本数据类型、包装类
🍻String类型 —> 基本数据类型、包装类: 调用包装类的静态方法parseXxx()
比如在客户端上,客户填了很多信息,发送给后台,后台发送给客户端都可以使用json
格式(本质上是一种特殊格式的字符串)的数据,传送来的时候都可以认为是具体的字符串,发送给后台的时候,需要把数据都获取出来,然后再写入相应的数据库中。
当我们把数据都发送到后台,后台读取到的就是字符串,但是当我们要存入数据库中的时候,所以需要将字符串转化为基本数据类型,方便存储。
【案例1】
前者转化为后者,去后者里面找方法。
基本数据里面没有方法啊,所以只能找包装类了。
🌱代码
package yuyi03;
import org.junit.Test;
public class WrapperTest1 {
//String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()
@Test
public void test2(){
String s1="123";
int i1=Integer.parseInt(s1); //将String类型的s1转化为int类型的i1
System.out.println(i1+10);
}
}
🍺输出结果
【案例2】
再举个例子。
🌱代码
package yuyi03;
import org.junit.Test;
public class WrapperTest1 {
//String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()
@Test
public void test2(){
String s2="true";
boolean b1=Boolean.parseBoolean(s2);
System.out.println(b1);
}
}
🍺输出结果
【案例3】
有个特殊情况。
🌱代码
package yuyi03;
import org.junit.Test;
public class WrapperTest1 {
//String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()
@Test
public void test2(){
//特别的
String s3="123a";
int i2=Integer.parseInt(s3); //NumberFormatException异常
}
}
爆出异常:
3.3 代码
整体代码留下啦。
🌱代码
package yuyi03;
import org.junit.Test;
/**
* ClassName: WrapperTest1
* Package: yuyi03
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/11 0011 10:06
*/
public class WrapperTest1 {
//基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx) ; ② 基本数据类型的变量 + ""
@Test
public void test1(){
//方式1:调用String的重载的静态方法valueOf(xxx xx)
int i1=10; //基本数据类型
String str1 = String.valueOf(i1); //转化为String类型
System.out.println(str1); //"10"
boolean b1=true;
Boolean b2=b1; //自动装箱
String str2 = String.valueOf(b1); //转化为String类型
String str3 = String.valueOf(b2);
System.out.println(str2);
System.out.println(str3);
//方式2:直接的方式,基本数据类型的变量 + ""
String str4=i1+""; //基本数据类型和字符串可以做连接运算,连接之后结果还是一个字符串
String str5=b1+"";
}
//String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()
@Test
public void test2(){
String s1="123";
int i1=Integer.parseInt(s1); //将String类型的s1转化为int类型的i1
System.out.println(i1+10);
String s2="true";
boolean b1=Boolean.parseBoolean(s2);
System.out.println(b1);
//特别的
String s3="123a";
//int i2=Integer.parseInt(s3); //异常:NumberFormatException
}
}
🍺输出结果
3.4 总结
<1> 基本数据类型转为字符串
方式1:调用字符串重载的valueOf()方法
int a = 10;
//String str = a;//错误的
String str = String.valueOf(a);
方式2:更直接的方式
int a = 10;
String str = a + "";
<2> 字符串转为基本数据类型
方式1:除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型,例如:
public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型。public static long parseLong(String s)
:将字符串参数转换为对应的long基本类型。public static double parseDouble(String s)
:将字符串参数转换为对应的double基本类型。
方式2:字符串转为包装类,然后可以自动拆箱为基本数据类型
public static Integer valueOf(String s)
:将字符串参数转换为对应的Integer包装类,然后可以自动拆箱为int基本类型public static Long valueOf(String s)
:将字符串参数转换为对应的Long包装类,然后可以自动拆箱为long基本类型public static Double valueOf(String s)
:将字符串参数转换为对应的Double包装类,然后可以自动拆箱为double基本类型
注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出java.lang.NumberFormatException
异常。
方式3:通过包装类的构造器实现
int a = Integer.parseInt("整数的字符串");
double d = Double.parseDouble("小数的字符串");
boolean b = Boolean.parseBoolean("true或false");
int a = Integer.valueOf("整数的字符串");
double d = Double.valueOf("小数的字符串");
boolean b = Boolean.valueOf("true或false");
int i = new Integer(“12”);
☕其他方式小结:
String 与 基本数据类型、包装类之间的转换:
- 基本数据类型、包装类 —> String类型:① 调用String的重载的静态方法
valueOf(xxx xx)
; ② 基本数据类型的变量 +""
- String类型 —> 基本数据类型、包装类: 调用包装类的静态方法:
parseXxx()
🚗图
三、 包装类的其它API
(1) 数据类型的最大最小值
Integer.MAX_VALUE和Integer.MIN_VALUE
Long.MAX_VALUE和Long.MIN_VALUE
Double.MAX_VALUE和Double.MIN_VALUE
(2)字符转大小写
Character.toUpperCase('x');
Character.toLowerCase('X');
(3) 整数转进制
Integer.toBinaryString(int i)
Integer.toHexString(int i)
Integer.toOctalString(int i)
(4) 比较的方法
Double.compare(double d1, double d2)
Integer.compare(int x, int y)
四、 包装类对象的特点
(1)包装类缓存对象
包装类 | 缓存对象 |
---|---|
Byte | -128~127 |
Short | -128~127 |
Integer | -128~127 |
Long | -128~127 |
Float | 没有 |
Double | 没有 |
Character | 0~127 |
Boolean | true和false |
Integer a = 1;
Integer b = 1;
System.out.println(a == b);//true
Integer i = 128;
Integer j = 128;
System.out.println(i == j);//false
Integer m = new Integer(1);//新new的在堆中
Integer n = 1;//这个用的是缓冲的常量对象,在方法区
System.out.println(m == n);//false
Integer x = new Integer(1);//新new的在堆中
Integer y = new Integer(1);//另一个新new的在堆中
System.out.println(x == y);//false
Double d1 = 1.0;
Double d2 = 1.0;
System.out.println(d1==d2);//false 比较地址,没有缓存对象,每一个都是新new的
(2)类型转换问题
Integer i = 1000;
double j = 1000;
System.out.println(i==j);//true 会先将i自动拆箱为int,然后根据基本数据类型“自动类型转换”规则,转为double比较
Integer i = 1000;
int j = 1000;
System.out.println(i==j);//true 会自动拆箱,按照基本数据类型进行比较
Integer i = 1;
Double d = 1.0
System.out.println(i==d);//编译报错
(3)包装类对象不可变
public class TestExam {
public static void main(String[] args) {
int i = 1;
Integer j = new Integer(2);
Circle c = new Circle();
change(i,j,c);
System.out.println("i = " + i);//1
System.out.println("j = " + j);//2
System.out.println("c.radius = " + c.radius);//10.0
}
/*
* 方法的参数传递机制:
* (1)基本数据类型:形参的修改完全不影响实参
* (2)引用数据类型:通过形参修改对象的属性值,会影响实参的属性值
* 这类Integer等包装类对象是“不可变”对象,即一旦修改,就是新对象,和实参就无关了
*/
public static void change(int a ,Integer b,Circle c ){
a += 10;
// b += 10;//等价于 b = new Integer(b+10);
c.radius += 10;
/*c = new Circle();
c.radius+=10;*/
}
}
class Circle{
double radius;
}
五、 习题
(1)练习1
🌋题目描述
利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。
- 提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。
- 1、创建Vector对象:
Vector v=new Vector();
- 2、给向量添加元素:
v.addElement(Object obj);
//obj必须是对象 - 3、取出向量中的元素:
Object obj=v.elementAt(0);
- 注意第一个元素的下标是0,返回值是Object类型的。
- 4、计算向量的长度:
v.size();
- 5、若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等
Vector作为一个容器,在使用的时候,添加元素addElement和获取元素elementAt,它的obj必须是对象。
所以往容器里面放基本数据类型就不行了,得装箱;
而将数据从容器里面取出来的时候,让这个元素与最高分比较,用差值判断等级,这时候就需要拆箱操作。
🎲分析
1、首先分析一下,大致有以下几个步骤。
public class ScoreTest {
public static void main(String[] args) {
//1.创建Vector对象:Vector v=new Vector();
Vector v=new Vector();
Scanner scanner=new Scanner(System.in);
//2.从键盘获取多个学生成绩,存放到容器v中 (以负数代表输入结束)
while(true){ //或 for(;;)
}
//3.获取学生成绩的最大值(计算最高分)
//4.依次获取v中每个学生成绩,与最高分进行比较,获取学生等级,并输出
scanner.close();
}
}
2、装箱与拆箱
装箱操作两种方式:
拆箱操作两种方式:
3、Vector相关函数
【addElement】
给向量添加元素:v.addElement(Object obj);
//obj必须是对象
E是后边要说的“泛型”,这里先看作Object。
【elementAt】
取出向量中的元素:Object obj=v.elementAt(0);
注意第一个元素的下标是0,返回值是Object类型的。
elementAt()里面是索引,其实Vevtor底层还是数值,只不过将数组封装起来,放入一个类里面体现了。
🌱代码
package yuyi03;
import java.util.Scanner;
import java.util.Vector;
/**
* ClassName: ScoreTest
* Package: yuyi03
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/13 0013 15:51
*/
public class ScoreTest {
public static void main(String[] args) {
//1.创建Vector对象:Vector v=new Vector();
Vector v=new Vector();
Scanner scanner=new Scanner(System.in);
int maxScore=0; //在有效范围内,取一个最小值,来当最高分
//2.从键盘获取多个学生成绩,存放到容器v中 (以负数代表输入结束)
while(true){ //或 for(;;)
System.out.print("请输入学生成绩(以负数代表输入结束):");
int intScore=scanner.nextInt(); //从键盘拿到数据(基本数据类型)
if(intScore<0){
break;
}
//装箱操作方式一
/*//装箱:int-->Integer对象
Integer score=Integer.valueOf(intScore);
//添加成绩到容器v中
v.addElement(score); //参数是Object类型,score以多态的方式传入*/
//装箱操作方式二(jdk5.0之后 自动装箱)
v.addElement(intScore);
//3.获取学生成绩的最大值(计算最高分)
if(maxScore<intScore){
maxScore=intScore;
}
}
System.out.println("最高分为: "+maxScore);
//4.依次获取v中每个学生成绩,与最高分进行比较,获取学生等级,并输出
for (int i = 0; i < v.size(); i++) { //计算向量v的长度:v.size()
Object objScore=v.elementAt(i); //从v中取出的元素是Object类型
/*//拆箱方法一
Integer integerScore= (Integer) objScore; //强转
int score=integerScore.intValue();*/
//拆箱方法二
int score= (Integer) objScore; //先将objScore转化为Integer,然后自动拆箱为int类型的score
char grade=' '; //此时if-else结构中有else结束,就是无论如何最终grade都会有值,所以这里可以赋值为' ',也可以不赋值(只要确保有值就行)
if(maxScore-score <= 10){
grade='A';
} else if (maxScore - score<=20) {
grade='B';
}else if (maxScore - score<=30) {
grade='C';
}else {
grade='D';
}
System.out.println("Student "+ i +" score is " + score + " grade is " + grade);
}
scanner.close();
}
}
🍺输出结果
(2)面试题
2.1 面试题1
输出结果是?
//说明:如果赋值的值在[-128,+127]范围内,则Integer对象使用的是IntegerCache中数组cache中的元素
public class InterviewTest1 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //false 两个对象地址肯定不一样
//底层都会调用Integer的valueOf()
Integer m = 1; //自动的装箱
Integer n = 1;
System.out.println(m == n);//true
Integer x = 128;
Integer y = 128;
System.out.println(x == y);//false
}
}
☕分析
看一下字节码文件:
所以“自动装箱”,其实就是调用valueOf()
。
现在来看一下valueOf()
具体的怎么做的。
Ctri+N
调出搜索框,然后输入Integer
,如下:
然后Ctrl+F12
,输入valueOf
,如下:
得出如下代码:
可以看一下i
的赋值,最大值是127,如下:
最小值是-128。
所以当i
的取值是-128 ~ +127
的时候,从现有的数组里面取的。如下:
所以,若赋值的是128,那么它们的地址值不同,结果就是false,如下:
那么若是范围在【-128,127】的时候,是从现有数组中取,看一下数组cache
:
所以这里传入的是1,取的都是数组中已经new好的Integer里面值是1的位置。
它们用的是同一个对象,所以地址值相同。如下:
🗳️这样做的好处是什么呢?
一方面直接拿过来用,比较快;另一方面,节省了内存空间,因为平时在用Integer的时候,[-128,+127]用的场景比较多,当需要使用的时候,直接拿现成的对象用即可,用不着每次都new一个,节省了时间。
这种设计模式叫“享元”,享–>共享(把大家都可以使用的公共数据都放到一个结构里面,谁需要用拿来用即可)
这个在笔试题中考的很多
还有这些也使用了享元
的设计模式,如下:
若是将Integer换成Byte,若两个Byte都是1,==
之后结果是true;若是128,结果就是false。
Character是字符,范围是[0,127]。
Boolean是布尔类型,只有两个值true和false。
用自动装箱的方式,范围内两个比较结果都是true,出了范围就是false。
Float和Double有小数点,没办法指定。
🍺输出结果
2.2 面试题2
输出结果是?
package yuyi03.interview;
/**
* ClassName: InterviewTest2
* Package: yuyi03.interview
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/14 0014 0:08
*/
public class InterviewTest2 {
public static void main(String[] args) {
//题目1:
int i = 10;
double d = 10.2;
System.out.println(i == d); //地址不相同,结果为false
//题目2:
Integer i1 = 10;
Double d1 = 10.2;
//System.out.println(i1 == d1); //编译报错
//题目3:
Integer m = 1000;
double n = 1000;
System.out.println(m == n);//true
//题目4:
Integer x = 1000;
int y = 1000;
System.out.println(x == y);//true
}
}
☕分析
题目2:
System.out.println(i1 == d1);
编译报错。
在Java里面,==
可以比较引用数据类型,可以比较地址。
但是前提是,两者类型要相同,或者是子父类的关系,此时Integer
和Double
是两个并列的类,Double继承于Number,Integer也继承于Number,所以Integer和Double是Number的两个并列的子类,没有任何关系。
编译的时候都过不了。如下:
题目三:
可以看到编译器没有报错,如下:
这里明显比较的时候是对n有一个“拆箱”操作,因为如果是装箱的话,double变成Double类型 ,Integer和Double比较,就是第二题,编译都无法通过。
所以拆箱操作之后,Integer变成int,m与n两个基本数据类型类型比较,都是1000,结果就是true。
这里为什么是拆箱操作而不是装箱呢?
这就是设计的时候这样规定的,当出现基本数据类型和包装类的时候,优先选择拆箱。
因为拆箱的成本低,将对象拆成基本数据类型去比较,速度上更快。要是将基本数据类型装箱成包装类,就得将它变成对象,成本更高。
可以看一下字节码文件:
题目四:
和题目三类似,这里比较的时候也会有“拆箱”操作,Integer类型的x拆成int类型,然后与y比较,结果为true。
若是装箱操作,int类型的y装箱为Integer类型,此时1000超出了[-128,127]范围,所以x和y它们都是新创建的对象,结果就是false了。
看一下字节码文件:
🍺输出结果
2.3 面试题3
输出结果是?
package yuyi03.interview;
/**
* ClassName: InterviewTest3
* Package: yuyi03.interview
* Description:
*
* @Author 雨翼轻尘
* @Create 2023/12/14 0014 10:41
*/
public class InterviewTest3 {
public static void main(String[] args) {
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//1.0
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);//1
}
}
☕分析
这里有一个三元运算符,如下:
很明显,问好前面是true
,所以返回的是前者new Integer(1)
,即:Object o1 = new Integer(1) ;
此时输出结果是1.0,为啥呢?
其实三元运算符有一个兼容问题,返回值类型要相同,还有字符强转功能,返回值类型为两个返回值中类型精度更高的那个类型。
所以,此时的1被转换为了double类型,所以输出的是1.0。
若是使用if-else
结构,很显然结果就是1。如下:
🍺输出结果
本节重点是关注基本数据类型、包装类、String类型三者之间的转换。