Javaの開梱および梱包操作で詳細なテキスト

まず、自動ボクシングとアンボクシングは何ですか。

私たちは、あなたがIntegerオブジェクト10に値を生成する場合は、そうでなければならない、そのJavaの8つの基本タイプは、Java SE5前に、それぞれ、対応するパッケージの種類を提供して知っています:

Integer i=new Integer(10);

値はIntegerオブジェクト10のために生成される場合は、Java SE5ではそれだけで必要そうという、自動包装特性を提供することで始まります。

Integer i=10;

このプロセスは、自動的に値の種類に応じて、Integerオブジェクトを作成し、それが自動的に箱詰めされ、共感

Integer i=10;int j=i;

上記のコードは、自動的にINT Integerオブジェクトに開梱自動的にアンパックされ

単純にパッキングするタイプのパッケージに自動基本データ型で、パッケージタイプを開梱すると、自動的に基本型に変換され、

第二に、ボクシングとアンボクシングを実現する方法です。

アンパック処理が行われている間、JavaプロセスのvalueOf包装ラッパークラスのメソッド呼び出しは、(基本的なタイプに対応するXXXを表す)、xxxValueメソッド呼び出しのラッパークラスの実装実装
:ボクシングとアンボクシングの種類のニーズに
ここに画像を挿入説明
ここに画像を挿入説明
このプロセスは自動的です実行は、我々はその実装方法を見てする必要があります。

public class Main {
    public static void main(String[] args) {
    //自动装箱
    Integer total = 99;

    //自定拆箱
    int totalprim = total;
    }
}

デコンパイル、クラスファイルの後、次の取得する:
ここに画像を挿入説明
整数の合計= 99;
上記のコードの実行文、我々はシステムを実装している:
整数の合計= Integer.valueOf(99)。

INT totalprim =総;
上記文章コードを実行し、我々は、システムを実行する:
INT = totalprim total.intValue()。

:私たちは、例えば、そのソースを分析するために、整数で今ある
最初のInteger.valueOf機能を見てみましょう、1を

 public static Integer valueOf(int i) {
 return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
 }

これは、最初のiの大きさを決定する:iが128 -128に等しい又はより大きい場合、Integerオブジェクトが作成され、そうでなければSMALL_VALUES [I + 128]。

首先我们来看看Integer的构造函数:

private final int value;

public Integer(int value) {
    this.value = value;
}

public Integer(String string) throws NumberFormatException {
    this(parseInt(string));
}

它里面定义了一个value变量,创建一个Integer对象,就会给这个变量初始化。第二个传入的是一个String变量,它会先把它转换成一个int值,然后进行初始化。
下面看看SMALL_VALUES[i + 128]是什么东西:

private static final Integer[] SMALL_VALUES = new Integer[256];
它是一个静态的Integer数组对象,也就是说最终valueOf返回的都是一个Integer对象。

所以我们这里可以总结一点:装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能。
所以在Integer的构造函数中,它分两种情况:

1、i >= 128 || i < -128 =====> new Integer(i)
2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]
2、接着看看intValue函数

 @Override
 public int intValue() {
    return value;
 }

这个很简单,直接返回value值即可。

例子:

public class Main {
    public static void main(String[] args) {

        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println(i1==i2);  //true
        System.out.println(i3==i4);  //false
    }
}

代码的后面,我们可以看到它们的执行结果是不一样的,为什么,在看看我们上面的说明。
1、i1和i2会进行自动装箱,执行了valueOf函数,它们的值在(-128,128]这个范围内,它们会拿到SMALL_VALUES数组里面的同一个对象SMALL_VALUES[228],它们引用到了同一个Integer对象,所以它们肯定是相等的。

2、i3和i4也会进行自动装箱,执行了valueOf函数,它们的值大于128,所以会执行new Integer(200),也就是说它们会分别创建两个不同的对象,所以它们肯定不等。
下面我们来看看另外一个例子:

public class Main {
    public static void main(String[] args) {

        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;

        System.out.println(i1==i2); //false
        System.out.println(i3==i4); //false
    }
}

看看上面的执行结果,跟Integer不一样,这样也不必奇怪,因为它们的valueOf实现不一样,结果肯定不一样,那为什么它们不统一一下呢?
这个很好理解,因为对于Integer,在(-128,128]之间只有固定的256个值,所以为了避免多次创建对象,我们事先就创建好一个大小为256的Integer数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。

但是对于Double类型来说,我们就不能这样做,因为它在这个范围内个数是无限的。
总结一句就是:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

所以在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。

public static Double valueOf(double d) {
    return new Double(d);
 }

下面我们进行一个归类:
Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。

下面对Integer派别进行一个总结,如下图:
ここに画像を挿入説明
下面我们来看看另外一种情况:

public class Main {
    public static void main(String[] args) {

        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;

        System.out.println(i1==i2);//true
        System.out.println(i3==i4);//true
    }
}

可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象。

public static Boolean valueOf(boolean b) {
     return b ? Boolean.TRUE : Boolean.FALSE;
}

可以看到它并没有创建对象,因为在内部已经提前创建好两个对象,因为它只有两种情况,这样也是为了避免重复创建太多的对象。

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

三、进一步讨论其他情况

上面把几种情况都介绍到了,下面来进一步讨论其他情况。

Integer num1 = 400;  
int num2 = 400;  
System.out.println(num1 == num2); //true

说明num1 == num2进行了拆箱操作,会将num1执行intValue操作再进行比较

Integer num1 = 100;  
int num2 = 100;  
System.out.println(num1.equals(num2));  //true

我们先来看看equals源码:

 public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

我们指定equal比较的是内容本身,并且我们也可以看到equal的参数是一个Object对象,我们传入的是一个int类型,所以首先会将num2进行装箱成Integer,然后比较,比较的时候再将其拆箱intValue比较,之所以返回true,是由于它比较的是对象里面的value值。

 Integer num1 = 100;  
 int num2 = 100;  
 Long num3 = 200L;  
 System.out.println(num1 + num2);  //200
 System.out.println(num3 == (num1 + num2));  //true
 System.out.println(num3.equals(num1 + num2));  //false

1、当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
2、对于num3.equals(num1 + num2)为false的原因很简单,我们还是根据代码实现来说明:

@Override
 public boolean equals(Object o) {
  return (o instanceof Long) && (((Long) o).value == value);
 }

equals它必须满足两个条件才为true, 而==不用,会拆箱对基础类型进行运算
1、类型相同
2、内容相同
上面返回false的原因就是类型不同。

 Integer num1 = 100;
 Ingeger num2 = 200;
 Long num3 = 300L;
System.out.println(num3 == (num1 + num2)); //true

我们来反编译一些这个class文件:javap -c StringTest
ここに画像を挿入説明
可以看到运算的时候首先对num3进行拆箱(执行num3的longValue得到基础类型为long的值300),然后对num1和mum2进行拆箱(分别执行了num1和num2的intValue得到基础类型为int的值100和200),然后进行相关的基础运算。

我们来对基础类型进行一个测试:

 int num1 = 100;
 int num2 = 200;
 long mum3 = 300;
 System.out.println(num3 == (num1 + num2)); //true

就说明了为什么最上面会返回true.

所以,当 “==”运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。

陷阱1:

1  Integer integer100=null;  
2  int int100=integer100;

这两行代码是完全合法的,完全能够通过编译的,但是在运行时,就会抛出空指针异常。其中,integer100为Integer类型的对象,它当然可以指向null。但在第二行时,就会对integer100进行拆箱,也就是对一个null对象执行intValue()方法,当然会抛出空指针异常。所以,有拆箱操作时一定要特别注意封装类对象是否为null。

总结

1、需要知道什么时候会引发装箱和拆箱
2、装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。
3、equals(Object o) 因为原equals方法中的参数类型是封装类型,所传入的参数类型(a)是原始数据类型,所以会自动对其装箱,反之,会对其进行拆箱
4、当两种不同类型用= = 比较时,包装器类的需要拆箱, 当同种类型用==比较时,会自动拆箱或者装箱
5.装箱就是执行valueof方法创建integer对象,在-128-127会直接返回cache的常量,其他范围会创建新的对象;自动将基本数据类型转换为包装器类型;
6.= =(或者其他算术类型的运算时)在两个Integer进行比较的时候,首先判断是不是在缓存池中,是则相等
在一个Integer 一个int时,Integer会进行拆箱,所以Integer a=400 = = int a=400
equals比较时,要同时比较内容value和类型!

其他

二つのアプローチの間の差を、1、iは新しい整数(XXX)及び整数I = XXXには整数を回します。
1)第一の方法は、自動充填処理をトリガしないと、第2の方法は、トリガされます。自動梱包(のvalueOf)キャッシュから値を取得
2)及び資源利用の効率の違い。効率及びリソースは一般的なケースに占める第二の方法は、最初のケース(これは絶対的ではないことに注意してください)よりも良好です。
コンテンツが同じである2は、同じタイプに等しく、キャッシュは-128-127梱包に使用される
ここに画像を挿入説明
128 127は、自動的に同じであり、新たな整数の実施形態を使用する超えます
ここに画像を挿入説明

3、Integeri = 1; I + = 1; 何のことを行いますか?
最初の整数iが1 =(INTパッキング整数型のInteger.valueOf()メソッドを使用して)オートボクシングか、私+ = 1; iが最初に自動的にアンパックINT整数型(Integer.intValueを使用して()整数INTをアンパックする方法)、iが充填型整数に添加が完了した後、再度実行します。
4、
ここに画像を挿入説明

参考記事:
https://www.cnblogs.com/wang-yaz/p/8516151.html

公開された107元の記事 ウォン称賛14 ビュー40000 +

おすすめ

転載: blog.csdn.net/belongtocode/article/details/102967678