Java中的包装类、Object类和String类(一)

包装类


前言

JDK中有三个特殊的类:包装类、String类以及Object类,这三个类是开发中非常常见的类,所以今天拿出来单独讲讲。每一个类被设计出来都是有其存在的必要性,尤其是Object类,自打JDK1.0以后就默认导入了,可见其重要性。


一、为什么需要包装类

众所周知,Java语言是一个面向对象的语言,但是Java中的基本数据类型却不是面向对象的。基本数据类型不具备对象的特性(没有成员变量和API可以调用)因此,Java为每种数据类型分别设计了对应的类,即包装类。

基本数据类型对应的包装类
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

包装类的特点:
在这里插入图片描述

1)所有包装类都是final类型,不能创建他们的子类。
2)包装类是不可变类,一个包装类的对象创建之后,他所包含的基本数据类型就不能被改变。
3)可以看到所有的包装类都是继承自Object类的,其中除了Boolean和Character,其他是多重继承的。

二、包装类的使用

包装类使用最多,也是被提到最多的,就是装箱(boxing)和拆箱(unboxing),当然还有一些API。

1、拆箱与装箱

既然这些包装类是为了让基本数据类型面向对象而设计出来的,那么必然要实现两者的相互转换,而基本数据类型—>包装类就是装箱,包装类—>基本数据类型就是拆箱,有手动和自动两种方式。

手动使用方法:

包装类装箱拆箱
BytevalueOf()byteValue()
ShortvalueOf()shortValue()
IntegervalueOf()intValue()
LongvalueOf()longValue()
FloatvalueOf()floatValue()
DoublevalueOf()doubleValue()
CharactervalueOf()charValue()
BooleanvalueOf()booleanValue()

自动拆装箱(以Integer为例):

自动装箱与自动拆箱实际上是JDK默认调用了valueOf()和intValue()

 //自动装箱,执行了Integer iii = Integer.valueOf(5)
  Integer i=5;
  //自动拆箱,实际上执行了 int iii2 = iii.intValue()
  int ii=i;
  System.out.println(ii);

2、经典面试题

先看题:

// 1)基本类型和包装类型
int a = 100;
Integer b = 100;
System.out.println(a == b);

// 2)两个包装类型
Integer c = 100;
Integer d = 100;
System.out.println(c == d);

// 3)
c = 200;
d = 200;
System.out.println(c == d);

答案是什么呢?
第一段代码,基本类型和包装类型进行 == 比较,这时候 b 会自动拆箱,直接和 a 比较值,所以结果为 true。

第二段代码,两个包装类型都被赋值为了 100,这时候会进行自动装箱,那 == 的结果会是什么呢?

按照java中对象的比较需要使用eaquls()来看:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符。那结果是 false?但这次的结果却是 true,是不是感觉很意外?

第三段代码,两个包装类型重新被赋值为了 200,这时候仍然会进行自动装箱,但此时的结果又变成了false。为什么?为什么?为什么?

解释:
我们来看看源码:

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

java针对-128—127之间的数据做了一个数据缓冲池,如果数据是该范围内的数,会从 IntegerCache 中取,然后比较,所以第二段代码(100 在这个范围之内)的结果是 true,而第三段代码(200 不在这个范围之内,所以 new 出来了两个 Integer 对象)的结果是 false。

希望大家记住一点:当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象。

三、基本数据类型和包装类的区别

1.包装类可以为null,但基本类型不可以

这一区别使得包装类可以应用于POJO(Plain Ordinary Java Object),翻译一下就是无规则的Java对象,只有属性字段和getter setter方法。

代码示例如下:

class Writer {
	private Integer age;
	private String name;

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

为什么这种无规则的java对象内部属性必须使用包装类型呢?这是因为数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值),就会抛NPE(NullPointerException) 异常。

举个栗子来说就是我们如果自定义了一个Student类,其中有一个属性是成绩score,如果用Integer而不用int定义,一次考试,学生可能没考,值是null,也可能考了,但考了0分,值是0,这两个表达的状态明显不一样。如果我们用包装类型的话,null的话证明没有考,0的话证明考了0分;但是如果我们用基本类型的话,这两种情况都是一个样的,没法区分的。

结论:使用基本类型可能会在一定程度上增大系统的复杂性,让坑变得越来越多。 还有这种使用包装类型定义变量的方式,通过异常来阻断程序的运行,进而可以被立马识别到这种綫上问题。但是我们如果使用基本数据类型的话,系统可能认为无异常,从而继续运行。

2、包装类可用于泛型,而基本数据类型不可以

泛型不能使用基本类型,因为使用基本类型时会编译出错。

List<int> list = new ArrayList<>(); // 提示 Syntax error, insert "Dimensions" to complete ReferenceType
List<Integer> list = new ArrayList<>();

为什么呢?因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个特例。

3、基本数据类型比包装类更高效

基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。在程序运行过程中,包装类需要进行解引用。比如下面这段代码,性能就很差。

long t1 = System.currentTimeMillis();
Long sum = 0L;
for (int i = 0; i < Integer.MAX_VALUE;i++) {
    sum += i;
}
long t2 = System.currentTimeMillis();        
System.out.println(t2-t1);

sum 由于被声明成了包装类型 Long 而不是基本类型 long,所以 sum += i 进行了大量的拆装箱操作(sum 先拆箱和 i 相加,然后再装箱赋值给 sum),导致这段代码运行完花费的时间足足有 2986 毫秒;如果把 sum 换成基本类型 long,时间就仅有 554 毫秒,完全不一个量级。

4、两者的判等规则不同

Integer a = new Integer(10);  //a和b存放在栈区,里面放着堆区的一个地址   10存放在堆区的该地址的内存里
Integer b = new Integer(10);
int c=20;
int d=20;

System.out.println(c == d);  //true  直接比较栈区存储的数值  相等返回true
System.out.println(a == b); // false   比较的是a、b的地址
System.out.println(a.equals(b)); // true  比较的是其指向的地址内部的值10

对于基本数据类型,判断两个变量相等的情况就是使用“= =” 判定符,两者只要值相等,那么其结果就为true。
对于包装类,既然其是类,那么比较两者相等时就需要使用eaquls方法,判断其内部的值是否相等,否则使用“= =”判定符号比较的是其指向的地址是否相等。
将“= =”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符。


总结

以上就是包装类和基本数据类型的所有内容了,String类和Object类内容比较多,隔两天再写~

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭彭彭摆鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值