JavaSE基础:包装类

栏目: Java · 发布时间: 6年前

内容简介:JavaSE基础:包装类

Java在设计之初有一个基本原则:一切皆对象,一切的操作都要求用对象的形式进行描述。但是这里面就会出现一个矛盾, 基本数据类型不是对象 。那么我们会如何修复这个BUG呢?最简单的做法是将基本数据类型作为一个类的属性保存起来,这样就相当于把基本数据类型包装了一下.

实现基本数据类型的包装类

package com.shxt.demo01;

public class Int {//类
    private int number;//基本数据类型
    
    public Int(int number){//提供一个参数的构造函数,传入基本数据类型
        this.number = number;
    }
    
    public int intValue(){//取得包装类中的数据
        return this.number;
    }
    
    //还可以提供其他的方法
}
package com.shxt.demo01;

public class MyTest {
    public static void main(String[] args) {
        Int temp = new Int(100);    //将基本数据类型包装后变为类
        int result = temp.intValue();       //从类中取得基本数据类型
        System.out.println(result+result);
    }
}

代码分析:

我们实现了基本数据类型转成 Java 对象的方式,Java中给我们提供了类似的实现类

包装类表格

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

我们观察上述的表格发现除了int->Integer,char->Character,其他的都是基本数据类型的首字母大写,很好记忆的.

但是以上给出的包装类又分为两种子类型:

  • 对象型包装类(Object直接子类):Character、Boolean。
  • 数值型包装类(Number直接子类):Byte、Short、Integer、Float、Double、Long。

Number是一个抽象类,里面一共定义了六个操作方法:intValue()、shortValue()、byteValue()、floatValue()、longValue()、doubleValue()。

2.装箱和拆箱

现在已经存在有基本数据类型与包装类,那么这两种变量间的转换就通过以下方式定义。

  • 装箱操作:将基本数据类型变为包装类的形式。
    • 每个包装类的构造方法都可以接收各自数据类型的变量。
  • 拆箱操作:从包装类之中取出被包装的数据。
    • 利用Number类中提供的一系列的:xxxValue()方法完成。

示例1-以int和Integer为例演示装箱和拆箱操作的过程

public class Demo01 {
    public static void main(String args[]) {
        Integer obj = new Integer(10);  // 将基本数据类型装箱
        int temp = obj.intValue();  	// 将基本数据类型拆箱
        System.out.println(temp * 2);  // 结果为20
    }
}

之前使用所编写的Int类,现在换成了Integer这个系统类。

示例2-以double和Double为例演示装箱和拆箱操作的过程

public class Demo02 {
    public static void main(String args[]) {
        Double obj = new Double(10.2);  	// 将基本数据类型装箱
        double temp = obj.double Value();  	// 将基本数据类型拆箱
        System.out.println(temp * 2);  		// 结果为20.4
    }
}

示例3-以boolean和Boolean为例演示装箱和拆箱操作的过程(不是Number子类)

public class Demo03 {
    public static void main(String args[]) {
        Boolean obj = new Boolean(false);  	// 将基本数据类型装箱
        boolean temp = obj.booleanValue();  // 将基本数据类型拆箱
        System.out.println(temp);  			// 结果为false
    }
}

现在可以发现,所有的包装类都使用了同样形式的方法进行操作。 在JDK1.5之前能够使用的操作都是以上形式的代码,但是JDK1.5之后,Java为了方便开发提供了 自动装箱自动拆箱 的机制,并且可以直接利用包装类的对象进行数学计算。

示例4-以int和Integer为例观察自动装箱和自动拆箱操作的过程

public class Demo04 {
    public static void main(String args[]) {
        Integer obj = 10;  	// 自动装箱 int->Integer
        int temp = obj;  	// 自动拆箱 Integer->int 实际上调用了obj.intValue()方法
        obj ++;
        System.out.println(temp * obj);  // 结果为110
    }
}

示例5-以boolean和Boolean为例观察自动装箱和自动拆箱操作的过程(不是Number子类)

public class Demo05 {
    public static void main(String args[]) {
        Boolean obj = false;  	// 自动装箱 boolean->Boolean
        boolean flag = obj;  	// 自动拆箱 Boolean->boolean
      	if(!flag){
        	System.out.println("Boolean不是Number的子类");
      	}
    }
}

重点:正式因为Java提供了自动装箱和自动拆箱的机制,那么Object可以接收一切的数据类型(Object可以统一天下了)

转换流程:基本数据类型 → 自动装箱(成为对象) → 向上转型为Object。

示例6-以Object接收int数据类型演示转换过程

public class Demo06 {
    public static void main(String args[]) {
        Object obj = 10;  	// int->Integer(自动装箱为对象)->Object
        int temp = (Integer)obj;  	// Object->Integer(强制类型转换为包装类)->自动拆箱
        obj ++;
        System.out.println(temp * obj);  // 结果为110
    }
}

“莫名其妙”的NullPointException

在我们开发过程中,碰到过不少因为请求参数或者接口定义字段设置为int(或者其他基本类型)而导致NullPointException(空指针异常)。代码大致地运行步骤如下所示,当然不会跟这个完全一样。

public class Demo06 {
    public static void main(String args[]) {
       Integer a = null;

		int b = a; // 抛出NullPointException a.intValue();
    }
}

代码分析:

上面的代码可以编译通过,但是会抛出空指针异常(NullPointException)。

前面已经说过了, int b = a 实际上是 int b = a.intValue()

由于a的引用值为null,在空对象上调用方法就会抛出NullPointException。

3.==和equlas()

大家都应该清楚明了的了解两者的区别,

一句话说就是 == 比较的是内存中地址, equlas() 对比的为数值,因为基本类型相同的数值指向的同一块内存,所以可以用==来比较,而引用类型则不可以。

public class Demo07 {
    public static void main(String args[]) {
        Integer obja = 10;  // 直接赋值
        Integer objb = 10;	// 直接赋值
        Integer objc = new Integer(10);  	// 构造方法
        System.out.println(obja == objb); 	// true 不是比较内存地址吗?为什么?
        System.out.println(obja == objc);	// false
        System.out.println(objb == objc);	// false
        System.out.println(obja.equals(objc));	// true
    }
}

代码分析:

obja == objb 不是应该比较内存地址吗?为什么能相等呢?我们需要解决这个问题,源码分析

在使用包装类的时候很少会利用构造方法完成,几乎都是直接赋值(这一点与String相同),但是在内容是否相等的时候,请一定要记住使用equals()方法。

两个包装类引用相等性

在Java中,“==”符号判断的内存地址所对应的值得相等性,具体来说,基本类型判断值是否相等,引用类型判断其指向的地址是否相等。看看下面的代码,两种类似的代码逻辑,但是得到截然不用的结果。

public class Demo08 {
    public static void main(String args[]) {
      Integer a1 = 127;
      Integer a2 = 127;
      System.out.println(a1 == a2); // true

      Integer b1 = 128;
      Integer b2 = 128;
      System.out.println(b1 == b2); // false
    }
}

这个必须从源代码中才能找到答案。 Integer 类中的 valueOf() 方法的源代码如下:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high) // 判断实参是否在可缓存范围内,默认为[-128, 127]
        return IntegerCache.cache[i + (-IntegerCache.low)]; // 如果在,则取出初始化的Integer对象
   	return new Integer(i); // 如果不在,则创建一个新的Integer对象
}

代码分析:

由于127属于[-128, 127]集合范围内,所以valueOf()每次都会取出同一个Integer对象,故第一个“==”判断结果为true;而128不属于[-128, 127]集合范围内,所以valueOf()每次都会创建一个新的Integer对象,由于两个新创建的对象的地址不一样,故第一个“==”判断结果为false。

再次分析比较过程

package com.shxt.demo01;

public class MyTest {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;

        System.out.println(c==d); // 缓存机制,返回值true
        System.out.println(e==f); // 超出了缓存机制, 返回值false
      	// (a+b) 计算了自动拆箱变成了int类型, c比较需要自动拆箱变成int , 返回值true
        System.out.println(c==(a+b));
        //(a+b) 计算了自动拆箱变成了int类型,因为使用equals比较内容(a+b)的结果有自动装箱,返回值true
      	System.out.println(c.equals(a+b));
      	// (a+b) 计算了自动拆箱变成了int类型, g自动拆箱变成long类型, (a+b)的结果自动类型转换为long , 返回值true
        System.out.println(g==(a+b));
      	//(a+b) 计算了自动拆箱变成了int类型,因为使用equals比较内容(a+b)的结果有自动装箱Integer
      	//Long和Integer比较 , 返回值false
        System.out.println(g.equals(a+b));
      	//涉及到自动类型转换, 返回值true
        System.out.println(g.equals(a+h));
    }
}

4.数据类型转换(核心)

使用包装类最多的情况实际上是它的数据类型转换功能上,在包装类里面,最大的优点就是提供将String型数据变为基本数据类型的方法,使用几个代表的类做说明:

  • Integer类:public static int parseInt(String s)。
  • Double 类:public static double parseDouble(String s)。
  • Boolean类:public static boolean parseBoolean(String s)。

**特别注意:**Character类里面并不存在字符串变为字符的方法,因为String类有一个charAt()的方法可以根据索引取出字符内容,并且一个字符的长度才有一位。

范例1:将字符串变为int型数据

public class Demo01 {
    public static void main(String args[]) {
        String str = "123";  // 字符串
        int temp = Integer.parseInt(str);//String->int
        System.out.println(temp * 2);
    }
}

此时实现了字符串变为基本数据类型的操作。但是在这样的转换过程之中请一定要注意:被转换为数字的字符串一定要由数字所组成。如果不是数字组成,转换过程中会报异常:NumberFormatException

范例2:错误代码

public class Demo02 {
    public static void main(String args[]) {
        String str = "1sss3";  // 字符串
        int temp = Integer.parseInt(str);
        System.out.println(temp * 2);
    }
}
Exception in thread "main" java.lang.NumberFormatException:For input string:"1sss3"

范例3:将字符串变为double型数据

public class Demo03 {
    public static void main(String args[]) {
        String str = "13";  // 字符串
        double temp = Double.parseDouble(str);
        System.out.println(temp * 2); //输出结果 26.0
    }
}

范例4:将字符串变为boolean型数据

public class Demo04 {
    public static void main(String args[]) {
        String str = "true";  // 字符串
        boolean flag = Boolean.parseBoolean(str);
        if(flag) {
            System.out.println("满足条件!"); 
        } else {
            System.out.println("不满足条件!");
        }
    }
}
//控制台输出: 条件满足

范例5:将字符串变为boolean型数据

public class Demo05 {
    public static void main(String args[]) {
        String str = "hanpang";  // 错误字符串
        boolean flag = Boolean.parseBoolean(str);
        if(flag) {
            System.out.println("满足条件!");
        } else {
            System.out.println("不满足条件!");
        }
    }
}
//控制台输出: 不满足条件!

代码分析:

在Boolean进行转换的过程里面,如果要转换的字符串不是true或者是false,那么将统一按照false进行处理。

现在既然实现了字符串变为基本数据类型操作,那么也一定可以实现基本数据类型变为字符串的操作,对于此类操作有两种做法:

  • 操作一:任何基本数据类型与字符串使用了“+”操作之后都表示变为字符串。

    public class Demo06 {
        public static void main(String args[]) {
            int num = 100;
            String str = num + "";  // 变为String
            System.out.println(str.replaceAll("0", "9"));
        }
    }
    
    //控制台输出: 199

    这样的操作虽然可以简单的完成,但是会存在有垃圾的问题。

  • 操作二:public static String valueOf(数据类型 变量) 开发推荐

    public class Demo07 {
        public static void main(String args[]) {
            int num = 100;
            String str = String.valueOf(num);  // 变为String
            System.out.println(str.replaceAll("0", "9"));
        }
    }

    这样的转换不会产生垃圾,所以在开发时往往会使用以上做法。

5.小结

  • 一定要清楚JDK1.5之后才提供有自动装箱与拆箱操作。
  • 字符串与基本数据类型的互相转换:
    • 字符串变为基本数据类型,依靠包装类的parseXxx()方法。
    • 基本数据类型变为字符串,依靠String.valueOf(数据类型 变量)方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Netty实战

Netty实战

诺曼·毛瑞尔(Norman Maurer)、马文·艾伦·沃尔夫泰尔(Marvin Allen Wolfthal) / 何品 / 人民邮电出版社 / 2017-5-1 / 69.00

编辑推荐 - Netty之父”Trustin Lee作序推荐 - 阿里巴巴中间件高级技术专家为本书中文版作序推荐 - 系统而详细地介绍了Netty的各个方面并附带了即用型的优质示例 - 附带行业一线公司的案例研究 - 极实用的Netty技术书 无论是构建高性能的Web、游戏服务器、推送系统、RPC框架、消息中间件还是分布式大数据处理引擎,都离不开Nett......一起来看看 《Netty实战》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器