BaseDataType 提纲挈领,言简意骇

f62c99d06efe28f0fe473a66116c48ee.jpeg


基本数据类型踩过的坑,一一剖析, 提纲挈领,言简意骇~

相信初入Java,都会有手写基本数据类型的情形,

所以这一次,好好输出一下

其实,基本数据类型

Java层面  byte   short   int   long

                    float   double

                    char

                    boolean

------------- 这八中基本数据类型

JVM层面  其实是九种数据类型

                   What?九种?平时只听说以及用到的是八种。

                   是的,在JVM源码中,在JVM中,

多了一个returnAddress类型

只不过呢,returnAddress类型会被JVM中的无条件转移指令jsr、ret和jsr_w指令所使用。returnAddress类型的值指向一条虚拟机指令的操作码

意思就是:对于 JVM 来说,程序就是存储在方法区的字节码指令,returnAddress 类型的值就是指向特定指令内存地址的指针

returnAddress 数据只存在于字节码层面,与编程语言无关,也就是说,

在 Java 语言中是不会直接与 returnAddress 类型的数据打交道的。


所以,在这里,好好的谈谈Java中 八种基本数据类型

装箱 拆箱 概念以及理论

Java为每种基本数据类型都提供了对应的包装器类型

基本类型    包装类型      字节      位数     默认值(基本类型)  取值范围

byte          Byte                         8           0             -2^7 - 2^7-1

short         Short             2          16          0             -2^15 - 2^15-1

int             Integer          4          32          0             -2^31 - 2^31-1

long          Long              8          64          0             -2^63 - 2^63-1

float          Float              4          32        0.0           -2^31 - 2^31-1

double     Double           8          64        0.0           -2^63 - 2^63-1

char          Character       2          16                         0 - 2^16-1

boolean    Boolean                                 false        true\false

所以:一个字节 就是 八位


装箱 拆箱  顾明思义,意思就类似于 寄快递 与 拆快递的过程

装箱: 基本类型   to      包装类型

           自动将基本数据类型转换为包装器类型

拆箱: 包装类型   to      基本类型

           自动将包装器类型转换为基本数据类型

Integer i = 70; //装箱 (自动将基本数据类型转换为包装器类型)
int n = i;      //拆箱  (自动将包装器类型转换为基本数据类型)

9b669d67a00dfb2ffce8a2ddb4339940.jpeg

装箱 拆箱 字节码剖析

以 int Integer 为例 字节码 剖析 装箱 拆箱
 Integer i = 70;
 int j = i;

ad6738e04b435c8550bf97e3257941b7.jpeg

stack=2, locals=4, args_size=1
         0: bipush        70
         2: invokestatic  #2   
  // 装箱
  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         5: astore_1
         6: aload_1
         7: invokevirtual #3   
  // 拆箱  
  // Method java/lang/Integer.intValue:()I

从字节码内容可以看出,

装箱:自动调用的是Integer的valueOf(int)方法。

拆箱:自动调用的是Integer的intValue方法

装箱  拆箱  总结

总结:

          装箱:调用包装器的valueOf方法实现的

        拆箱:调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)

基本概念的区分:

1、Integer 是 int 的包装类,int 则是 java 的一种基本数据类型
2、Integer 变量必须实例化后才能使用,而int变量不需要
3、Integer 实际是对象的引用,当new一个 Integer时,实际上是生成一个指针指向此对象;而 int 则是直接存储数据值
4、Integer的默认值是null,int的默认值是0

基本数据类型 

  包装类型        使用场景剖析

f4bb49370f8fdb5bfd6a4c5bb6b6a989.jpeg

在代码中 会见到这样的 写法
 int i = 70;
 Integer j = 70;
 Integer k = new Integer(70);
 以上,字节码会是如何,使用场景又是如何?

b800892bb79d675df9ffb3401f994b4b.jpeg

0: bipush         70       // int i = 70
         2: istore_1
         3: bipush         70       // Integer j = 70
         5: invokestatic  #2                  
         // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         8: astore_2
         9: new            #3      
                             // Integer k = new Integer(70)        
         // class java/lang/Integer
        12: dup
        13: bipush         70        
        15: invokespecial  #4                  
        // Method java/lang/Integer."<init>":(I)V
        18: astore_3
        
LocalVariableTable:
     Start  Length  Slot  Name   Signature
      0      20     0  args   [Ljava/lang/String;
      3      17     1     i   I
      9      11     2     j   Ljava/lang/Integer;
      19       1     3    k   Ljava/lang/Integer;

从字节码可以看到:

1、int i = 70         变量 i 是直接存储到 局部变量表中

2、Integer j = 70   自动调用Integer.valueOf() 装箱

                              实际上是 Integer j = Integer.valueOf(70) 

3、Integer k = new Integer(70)

                              通过 new 生成了对象

那么,在实际运用中,到底用哪一种呢?

变量 尽量不要定义为包装类,尽量使用基本类型。

int和Integer的区别

1.存储原理不一样: 

int:属于基本类型,不存在“引用”这个概念;其数据是存储在栈空间中; 

Integer:属于包装类型,引用存储在栈中,对象数据存储在堆中; 

在进行参数传递的时候,int是值传递,其在栈中的数据不可变; 

而Integer类型是引用传递,引用指向的内存地址中的数据是可以变化的,但是栈中的引用是不变的;

2.缺省值不一样 

int的初始化值是0 ,Integer初始化的值是null。 

3.泛型支持不一样 

泛型支持Integer,不支持int 

4.方法不一样 

int i =70; 

Integer j= new Integer(70); 

在方法调用中: 

i是基本类型,并没有什么方法;因为方法是类的特性。 

j是包装类型,有很多方法,因为方法是对象中定义的


4239d3beaaf655858602a2120cba384e.png

int   Integer  剖析

243d2675650e7e064ca8a8ce4529a8db.jpeg

int i = 70;
        Integer j = 70;
        Integer k = new Integer(70);
        //实际上 看看字节码 在以下这三行的时候 发生了什么?
        System.out.println(i==j);
        System.out.println(i==k);
        System.out.println(j==k);

字节码剖析

8f43e2ad3e1ba9ff42afa8e990871ae8.png

1 当进行到这里的时候 
  System.out.println(i==j);
  24: invokevirtual #6                 
  // Method java/lang/Integer.intValue:()I
  看到了  Integer j  自动掉用了 Integer.intValue() 
  即为 拆箱,自动的 转为了  基本数据类型
2 当进行到这里的时候 
  System.out.println(i==k);
  43: invokevirtual #6                 
  // Method java/lang/Integer.intValue:()I
  看到了  Integer k  自动掉用了 Integer.intValue() 
  即为 拆箱,自动的 转为了  基本数据类型

 if_icmpne 指令 : 比较两个栈顶的引用,当结果不相等的时候跳转

由以上 字节码指令 可以 总结:

int  Integer 字节码总结

bcea9a8e0ceba0ae6b16798ac4daa17a.png

1 Integer j = 70

 Integer j = 70 在编译时,会装箱成为 Integer j = Integer.valueOf(70)

Integer.valueOf() -------- 源码分析
 /**
    返回一个表示指定值的{@code Integer}实例
   * {@code int}值。如果没有新的{@code Integer}实例
    需要时,一般应优先使用此方法
   *构造函数{@link #Integer(int)},因为这个方法是可能的
    *以产生更好的空间和时间性能
     缓存频繁请求的值。
 *此方法将始终缓存-128到127的值,
 *包括,并可能缓存此范围之外的其他值。
 */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

2d10afe46efab355433a6ef7727bc92e.jpeg

所以:Integer.valueOf()

java对于-128到127之间的数,会进行缓存。
所以Integer j = 70  时,会将70进行缓存,下次再写Integer j = 70时,就会直接从缓存中取,就不会new了。

意思就是说:

int  取值范围为 : -2^31 - 2^31-1

但是呢?对 -128 到 127 之间的值进行了缓存

如果 传入的值 在-128到127之间,直接返回 缓存中的值,不需要new 对象

否则 要进行new 对象

2  当 int 与  Integer 比较的时候

System.out.println(i==j);
        System.out.println(i==k);
        包装类Integer 和 基本数据类型int 比较时,
        java会自动拆包装为int ,然后进行比较,
        实际上就变为两个int变量的比较

int  Integer 题目练手

ac3614efbd47759a1500c34fb7486d37.jpeg

根据以上的结论(由字节码总结结论)

如果可以一眼说出答案,那么 对于 int Integer 可以ok~

那么 对于 整型数据类型  byte  short  int  long 

                这里以 int 作为例子演示,其他三个 依次类推


浮点类型    double  Double 

8030c5a4661e7f5c8c7967164f3767b4.png

1fb303f633abe3766492d3fa9f60179f.jpeg

根据上文的结论  这里 猜想为   true   true   false ?

答案正确吗?

字节码:

bddeb27b4db51ad1441a4fa0c3fe6ee0.jpeg

0: ldc2_w        #2                  // double 70.0d
         3: dstore_1
         4: ldc2_w        #2           // double 70.0d
         7: invokestatic  #4                  
         // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
        10: astore_3
        11: ldc2_w        #2                  // double 70.0d
        14: invokestatic  #4                  
        // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
        17: astore        4
        19: ldc2_w        #5                  // double 700.0d
        22: invokestatic  #4                 
         // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
        25: astore        5
        27: ldc2_w        #5                  // double 700.0d
        30: invokestatic  #4                  
        // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;

由字节码可知,这里的  double  Double 

                        与上文的 int      Integer  

是如此的相似。都会涉及到 装箱,拆箱

But  这里的 Double.valueOf  源码剖析?

Double.valueOf  源码剖析
 
 /**
  返回一个代表指定值的{@code Double}实例
* {@code double}值。
*如果不需要新的{@code Double}实例,则使用此方法
*通常应该优先使用构造函数
* {@link #Double(Double)},因为这个方法可能会产生
*缓存显著提高了空间和时间性能
频繁请求的值
     */
    public static Double valueOf(double d) {
        return new Double(d);
    }

由源码可以知道了 

Double.valueOf()   并没有范围缓存

                     而是 new 对象

那么 对于 浮点数据类型  float  double

                这里以 double作为例子演示,另外一个 依次类推


char   Character

96116b1277ba933a9a2776300664ace6.jpeg

字节码:

066be3cc9eb6a05218050ca7657aec36.jpeg

4: sipush        20013
         7: invokestatic  #2                 
     // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
        10: astore_2
        11: sipush        20013
        14: invokestatic  #2                 
    // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
        17: astore_3
        18: getstatic     #3                 
         // Field java/lang/System.out:Ljava/io/PrintStream;
        21: iload_1
        22: aload_2
        23: invokevirtual #4                 
         // Method java/lang/Character.charValue:()C


Character.valueOf()源码

/**
*返回一个<tt>字符</tt>实例表示指定的
* < tt >字符< / tt >价值。
*如果不需要新的<tt>字符</tt>实例,则使用此方法
*通常应该优先使用构造函数
* {@link #Character(char)},因为这个方法可能会产生
*缓存显著提高了空间和时间性能
频繁请求的值。
*
*此方法将始终缓存{@code范围内的值
     */
    public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

boolean  Boolean

e50610aafbf24612f1c581169c57dc67.jpeg

字节码:

62ebac4e7069ad19510861ce7f2c9536.jpeg

3: invokestatic  #2                  
   // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
         6: astore_2
         7: iconst_0
         8: invokestatic  #2                 
  // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
        11: astore_3
        12: iconst_1
        13: invokestatic  #2                  
  // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
        16: astore        4
        18: iconst_1
        19: invokestatic  #2                 
 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
        22: astore        5
        24: getstatic     #3                  
 // Field java/lang/System.out:Ljava/io/PrintStream;
        27: iload_1
        28: aload_2
        29: invokevirtual #4                 
 // Method java/lang/Boolean.booleanValue:()Z

同样的 都会涉及到 装箱,拆箱

but  Boolean.valueOf 源码

/**
*返回一个代表指定值的{@code布尔}实例
* {@code boolean}值。如果指定的{@code boolean}值
*是{@code true},这个方法返回{@code Boolean.TRUE};
*如果是{@code false},则此方法返回{@code Boolean.FALSE}。
*如果不需要新的{@code Boolean}实例,则使用此方法
*通常应该优先使用构造函数
* {@link #Boolean(Boolean)},因为这个方法可能会产生
*显著提高空间和时间性能。
     */
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

赶快来分享关注吖

e5c726873cf6551f533c14a6ceb63d9d.jpeg

cd4ebc9c338278c6937a8bdb1f1a876e.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值