Java面向对象(高级)-- 包装类(wrapper)的使用

2023-12-14 12:51:17

一、概念

(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。

image.png

比如现在有个学生成绩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类型的变量,拿类给它包装了一下,使它具备类的特征。

image.png

把基本数据类型包装了一下,就是包装类。使基本数据类型具备了类的特征。

(3)总结

  1. 为什么要使用包装类?
    为了使得基本数据类型的变量具备引用数据类型变量的相关特征(比如:封装性、继承性、多态性),我们给各个基本数据类型的变量都提供了对应的包装类

  2. (掌握)基本数据类型对应的包装类类型
    数值类型(继承于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 {
    //把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了

}

现在创建一个单元测试:

image.png

前面说注解的时候,已经将“单元测试”设置为模板了,不会的去前面的文章看一看。
Java面向对象(高级)-- 注解(Annotation)-CSDN博客

2.2.1 方式一、使用包装类的构造器

【案例1】

现在定义一个int类型的变量i1,如下:

public class WrapperTest {
    //把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
    /*
    * (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
    * */
    @Test
    public void test1(){
        int i1 = 10;  //创建一个int型的变量
    }
}

此时它没有什么方法可用:

image.png

现在将它包装起来。

这样:

image.png

可以看到,Integer()两个参数都过时了。

这里先用一下。(JDK9被标记为过时,不建议使用了)

image.png

现在建议使用的是valueOf(int),如下:(这个等会再说)

image.png

这是第一种方式,比较传统,就是把刚才的基本数据类型的值传给当前类里面的成员变量。

image.png

现在的ii1已经是一个对象了,所以用它的时候,就有很多方法可以去调用了。

而且还可以以多态的方式出现在方法形参的位置。

如下:

image.png

🌱代码

public class WrapperTest {
    //把基本数据类型包一下,让它具备类的特征,里面很多方法就可以去使用了
    /*
    * (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
    * */
    @Test
    public void test1(){
        int i1 = 10;  //创建一个int型的变量  基本数据类型
        Integer ii1=new Integer(i1);    //包装起来了,具备了类的特征  对象
        System.out.println(ii1);    //结果是数据
    }
}

🍺输出结果

image.png


【案例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);
    }
}

🍺输出结果

image.png

其实Float()里面可以传的参数有好几个,包括float类型、double类型、String类型。

如下:

image.png

比如现在传一个字符串

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);

    }
}

输出结果:

image.png

只要双引号““ 的里面还是一个float或者double类型的即可,若传入的不是浮点型的数据,就会报错,如下:

image.png

所以,双引号里面放的不是数,就会有数据格式化异常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);

    }
}

输出结果:

image.png

若此时将false大写呢?比如:s2="False";

image.png

可以发现它没有报错,说明“False”也可以使用。

那如果是“False123”呢?

image.png

所以布尔类型有点特殊,只要和“true”不一样,那就都是false。

可以看一下它调用的是哪一个函数:

PixPin_2023-12-10_17-11-24.gif

"true".equalsIgnoreCase(s):在忽略大小写的情况下,如果和true一样,那就返回true。如果不一样,那就认为是false。

比如:

image.png


2.2.2 方式二、(建议)调用包装类的valueOf(xxx xx)

Integer为例:

image.png

可以看到,这里建议用valueOf(Xxx)来替换Integer

因为它可能产生明显更好的空间和时间性能。

image.png

【案例1】

image.png

🌱代码

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);
    }
}

🍺输出结果

image.png

(装箱)基本数据类型 —> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的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

image.png

此时i就是一个基本数据类型了,可以进行加一运算了。如下:

//(拆箱)包装类-->基本数据类型:
@Test
public void test2(){
    Integer ii1=new Integer(10);    //包装类对象
    int i=ii1.intValue();
    i=i+1;
    System.out.println(i);

}

输出结果:

image.png


【案例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);
}

输出结果:

image.png


【案例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;     //自动装箱
    }

}

🍺输出结果

image.png


【案例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()字节码文件,比如装箱:

image.png

所以,本质上还是调用包装类的valueOf(xxx xx)进行装箱的,只不过现在封装了一下。

拆箱也一样,如下:

image.png

所以,本质上还是调用包装类的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;
}

🍺输出结果

image.png

🗳?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,可以理解为余额这个对象被初始化了,只不过初始化之后没有往里面放钱,或者花光了。所以这里可以区分是没有初始化还是账户没有钱
}

🍺输出结果

image.png

🗳?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,可以理解为余额这个对象被初始化了,只不过初始化之后没有往里面放钱,或者花光了。所以这里可以区分是没有初始化还是账户没有钱
}

🍺输出结果

img

(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里面的方法(在后者找方法),如下:

image.png

直接将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"
    }
}

🍺输出结果

image.png

控制台输出没有显示双引号,其实是存在的。


【案例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);
    }
}

🍺输出结果

image.png


【案例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);
    }
}

🍺输出结果

image.png


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);
    }

}

🍺输出结果

image.png


【案例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);
    }

}

🍺输出结果

image.png


【案例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异常
    }

}

爆出异常:

image.png

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
    }

}

🍺输出结果

image.png

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()

🚗图
image.png

三、 包装类的其它API

(1) 数据类型的最大最小值

Integer.MAX_VALUEInteger.MIN_VALUE
    
Long.MAX_VALUELong.MIN_VALUE
    
Double.MAX_VALUEDouble.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没有
Character0~127
Booleantrue和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、装箱与拆箱

装箱操作两种方式:
image.png

拆箱操作两种方式:
image.png

3、Vector相关函数

【addElement】

给向量添加元素:v.addElement(Object obj); //obj必须是对象

image.png

E是后边要说的“泛型”,这里先看作Object。

【elementAt】

取出向量中的元素:Object obj=v.elementAt(0);

注意第一个元素的下标是0,返回值是Object类型的。

elementAt()里面是索引,其实Vevtor底层还是数值,只不过将数组封装起来,放入一个类里面体现了。

image.png


🌱代码

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();
    }
}

🍺输出结果

image.png

(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

    }

}

?分析

看一下字节码文件:

image.png

所以“自动装箱”,其实就是调用valueOf()

现在来看一下valueOf()具体的怎么做的。

Ctri+N调出搜索框,然后输入Integer,如下:

image.png

然后Ctrl+F12,输入valueOf,如下:

image.png

得出如下代码:

image.png

可以看一下i的赋值,最大值是127,如下:

image.png

最小值是-128。

所以当i的取值是-128 ~ +127的时候,从现有的数组里面取的。如下:

image.png

所以,若赋值的是128,那么它们的地址值不同,结果就是false,如下:

image.png

那么若是范围在【-128,127】的时候,是从现有数组中取,看一下数组cache

image.png

所以这里传入的是1,取的都是数组中已经new好的Integer里面值是1的位置。

它们用的是同一个对象,所以地址值相同。如下:

image.png

🗳?这样做的好处是什么呢?

一方面直接拿过来用,比较快;另一方面,节省了内存空间,因为平时在用Integer的时候,[-128,+127]用的场景比较多,当需要使用的时候,直接拿现成的对象用即可,用不着每次都new一个,节省了时间。

这种设计模式叫“享元”,享–>共享(把大家都可以使用的公共数据都放到一个结构里面,谁需要用拿来用即可)

这个在笔试题中考的很多

还有这些也使用了享元的设计模式,如下:
image.png

若是将Integer换成Byte,若两个Byte都是1,==之后结果是true;若是128,结果就是false。

Character是字符,范围是[0,127]。

Boolean是布尔类型,只有两个值true和false。

用自动装箱的方式,范围内两个比较结果都是true,出了范围就是false

Float和Double有小数点,没办法指定。

🍺输出结果

image.png

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里面,==可以比较引用数据类型,可以比较地址。

但是前提是,两者类型要相同,或者是子父类的关系,此时IntegerDouble是两个并列的类,Double继承于Number,Integer也继承于Number,所以Integer和Double是Number的两个并列的子类,没有任何关系。

编译的时候都过不了。如下:

image.png


题目三

可以看到编译器没有报错,如下:

image.png

这里明显比较的时候是对n有一个“拆箱”操作,因为如果是装箱的话,double变成Double类型 ,Integer和Double比较,就是第二题,编译都无法通过。

所以拆箱操作之后,Integer变成int,m与n两个基本数据类型类型比较,都是1000,结果就是true

这里为什么是拆箱操作而不是装箱呢?

这就是设计的时候这样规定的,当出现基本数据类型和包装类的时候,优先选择拆箱

因为拆箱的成本低,将对象拆成基本数据类型去比较,速度上更快。要是将基本数据类型装箱成包装类,就得将它变成对象,成本更高。

可以看一下字节码文件

image.png


题目四

image.png

和题目三类似,这里比较的时候也会有“拆箱”操作,Integer类型的x拆成int类型,然后与y比较,结果为true

若是装箱操作,int类型的y装箱为Integer类型,此时1000超出了[-128,127]范围,所以x和y它们都是新创建的对象,结果就是false了。

看一下字节码文件

image.png

🍺输出结果

image.png

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
    }
}

?分析

这里有一个三元运算符,如下:

image.png

很明显,问好前面是true,所以返回的是前者new Integer(1),即:Object o1 = new Integer(1) ;

此时输出结果是1.0,为啥呢?

其实三元运算符有一个兼容问题,返回值类型要相同,还有字符强转功能,返回值类型为两个返回值中类型精度更高的那个类型。

所以,此时的1被转换为了double类型,所以输出的是1.0

若是使用if-else结构,很显然结果就是1。如下:

image.png

🍺输出结果

image.png

本节重点是关注基本数据类型、包装类、String类型三者之间的转换。

文章来源:https://blog.csdn.net/m0_55746113/article/details/134990781
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。