Java提供了两类数据类型:一种是基本类型(原始类型),一种是引用类型。

- 数据类型图:

                                         ┏数值型━┳━整数型:byte short int long

              ┏基本数据类型━━┫             ┗━浮点型:float double

              ┃                         ┣字符型:char

数据类型╋                         ┗布尔型:boolean

              ┃                        ┏类(class)

              ┗引用数据类型━━╋接口(interface)

                                        ┗数组(array)

- 基本数据类型

数据类型

大小(bit)

范围

默认值

包装类

byte(字节)

8

-128 - 127

0

Byte

shot(短整型)

16

-32768 - 32767

0

Boolean

int(整型)

32

-2147483648-2147483647

0

Short

long(长整型)

64

-9233372036854477808-9233372036854477807

0

Character

float(浮点型)

32

-3.40292347E+38-3.40292347E+38

0.0f

Integer

double(双精度)

64

-1.79769313486231570E+308-1.79769313486231570E+308

0.0d

Long

char(字符型)

16

‘ \u0000 - u\ffff ’

‘\u0000 ’

Float

boolean(布尔型)

1

true/false

false

Double

注意:

1、在基本数据类型中,除了boolean类型所占长度与平台有关外,其他数据类型长度都是与平台无关的。比如,int永远占4个字节(1 Byte = 8 bit)。

2、void不是基本数据类型

在 Java 中基本类型只有八种,详见 Java Language Specification 的 4.2 节: 
http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2

另外,在 Java Language Specificaion 的第 14.8 节也提及 void 并不是一种类型: 
http://java.sun.com/docs/books/jls/third_edition/html/statements.html#5989

为什么《Thinking in Java》等一些书籍都说,void是基本类型的一种呢?

来看void的包装类java.lang.Void源码:

package java.lang;

/**
 * The Void class is an uninstantiable placeholder class to hold a
 * reference to the Class object representing the Java keyword
 * void.
 *
 * 翻译:Void类是一个不可重复的占位符类,
 *      用于保存对表示Java关键字void的Class对象的引用
**/
public final class Void { //final表明这个类是不允许被其他类继承

    /**
     * The Class object representing the pseudo-type corresponding to
     * the keyword void.
     * 翻译:Class对象表示对应于关键字void的伪类型。
     */
    public static final Class<Void> TYPE = Class.getPrimitiveClass("void");

    /*
     * The Void class cannot be instantiated.
     * 翻译:Void类不能被实例化。
     */
    private Void() {}
}

再看getPrimitiveClass()方法的源码:

/*
     * Return the Virtual Machine's Class object for the named
     * primitive type.
     * 翻译:返回指定基本类型的虚拟机的Class对象。
     */
static native Class getPrimitiveClass(String name);

“Ctrl+Alt+H”查看调用getPrimitiveClass()方法的所有类,确实是“Byte、Boolean、Short、Character、Integer、Long、Float、Double、Void”一共9个包装类,由此猜测把void也归到基本数据类型的说法可能来源于此。

3、基本数据类型的默认值仅在作为类中属性时生效

在方法内部必须先对基本数据类型变量赋值后才能使用,否则编译不通过。Demo如下:

public class DefaultValueTest {
    static int intA;
    static boolean blnA;
    public static void main(String[] args) {
        int intB;
        boolean blnB;
        System.out.println(intA); //输出0
        System.out.println(blnA); //输出false

        //System.out.println(intB); //编译不通过
        //System.out.println(blnB); //编译不通过
    }
}

在程序中如果某些数据可能会返回空值,那么用包装类比较好。比如当结果为空时,转换为int会发生异常,而转换为Integer则不会,因为对象可以为null。此时建议用包装类,虽然会牺牲一些转换效率,但可以避免持久化数据时产生的一些异常。

- 引用数据类型

引用类型(reference type)指向一个对象,不是原始值,指向对象的变量是引用变量。

在java里面除去基本数据类型的其它类型都是引用数据类型,自己定义的class类都是引用类型,可以像基本类型一样使用。

引用类型常见的有:String,StringBuffer,ArrayList,HashSet,HashMap等。 
String也属于引用数据类型:

String a = "china";
String b =a; // 则a、b都指向常量池的"china"。
String a =  new String("china");
String b =a; //则a、b都指向堆中的"china"。
String a =  new String("china");
String b =a;

a = a.substring(0,1); //则b指向栈中的"china",a指向新的对象"c"。

如果要对比两个对象是否相同,则需要使用equals()方法,但有一点需要注意:equals()方法的默认行为是比较引用。如果是你自己写的类,你应该为它重写equals()来比较对象的内容。大多数Java类库都实现了比较对象内容的equals()方法。


==号和equals()方法都是比较是否相等的方法,那它们有什么区别和联系呢? 
首先,==号在比较基本数据类型时比较的是值,而用==号比较两个对象时比较的是两个对象的地址值:

int x = 10;
int y = 10;
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(x == y); // 输出true
System.out.println(str1 == str2); // 输出false

那equals()方法呢?我们可以通过查看源码知道,equals()方法存在于Object类中,因为Object类是所有类的直接或间接父类,也就是说所有的类中的equals()方法都继承自Object类,而通过源码我们发现,Object类中equals()方法底层依赖的是==号,那么,在所有没有重写equals()方法的类中,调用equals()方法其实和使用==号的效果一样,也是比较的地址值,然而,Java提供的所有类中,绝大多数类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值: 

java 中 引用数据类型全爆红 java引用数据类型是什么_java 中 引用数据类型全爆红

 

这里我自己定义了一个Student类,没有重写equals()方法,最后的输出结果是:false

java 中 引用数据类型全爆红 java引用数据类型是什么_java 中 引用数据类型全爆红_02

 

在我重写了equals()方法后,输出结果变成了true。

现在一些基本的已经讲的差不多了,接下来我们回到第一个例子:

String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);

根据上面所讲,第一个是true,第二个是false,确实如此,那继续看下面的例子:

String s1 = "abc";
String s2 = "abc";
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);

这次的结果和上一个的是一样的吗?答案是:true true 
为什么第二个会是true呢? 
这就涉及到了内存中的常量池,常量池属于方法区的一部分,当运行到s1创建对象时,如果常量池中没有,就在常量池中创建一个对象”abc”,第二次创建的时候,就直接使用,所以两次创建的对象其实是同一个对象,它们的地址值相等。

那上一个例子中的

String str1 = new String("abc");

是怎么回事呢? 
这里其实创建了两次对象,依次是在常量池中创建了对象”abc”,一次是在堆内存中创建了对象str1,所以str1和str2的地址值不相等。



- 两种类型对比

最后,基本数据类型和引用数据类型的一些对比如下:

基本数据类型

引用数据类型

在栈中进行分配

在堆中进行分配,堆的读写速度远不及栈

变量名指向具体的数值

变量名指向存数据对象的内存地址,即变量名指向hash值

变量在声明之后java就会立刻分配给他内存空间

它以特殊的方式(类似C指针)指向对象实体(具体的值),这类变量声明时不会分配内存,只是存储了一个内存地址

基本类型之间的赋值是创建新的拷贝

对象之间的赋值只是传递引用

“==”和“!=”是在比较值

“==”和“!=”是在比较两个引用是否相同,需要自己实现equals()方法

基本类型变量创建和销毁很快

类对象需要JVM去销毁