1-引用类型

本文详细介绍了Java中的基本类型与引用类型,包括它们的存储位置(栈vs堆)、对象引用的概念、数组(一维和二维)的内存结构以及参数传递(值传递vs引用传递)。同时讨论了equals和==在不同类型的使用区别。
摘要由CSDN通过智能技术生成

学习王争的Java编程之美,总结一下引用类型篇

一,Java类型:基础类型与引用类型

Java的类型

1)基本类型

        java的基本类型一共有8种,分别为:byte, char, short, int, long, float, double, boolean。

       而这几种又可以按以下分类

       整数类型:byte,short, int, long

       字符型:char

       布尔类型:boolean

       浮点型:float

2)基本类型的JVM图

基本类型一般用在局部变量,类的属性,参数。

以局部变量,如下代码来示例:

public class Demo{
    private int c = 1;
    private int d = 2;
    public static void main(String[] args){
        int a = 1;
        int b = 2;
        a = b;
    } 
}

函数的局部变量是存在于栈上的,所以a和b对应的内存单元都存在栈上,并将其对应的值存为1和2,执行a=b时,则是将b内存单元中的值赋值给a的内存单元,如下:

而像内的属性并不是存在于栈中,而是在堆中(如上代码的c和d),但其内存单元存储的结构是一样的。只是一个在栈中一个在堆中。

3)引用类型:对象的引用

基本类型的存储比较简单,其内存单元中直接存储的就是值。而引用类型相比较复杂。

如下代码

public class Student{
    private int a = 1;
    private int b = 2;
    public Student(int a, int b){
        this.a = a;
        this.b = b;
    }
}
Student stu = new Student(1, 3);

要理解引用,首先要理解什么是“对象本身”,什么是“对象的引用”,如上面的代码stu并不是对象的本身,而是对象的引用,它引向的是对象在堆中的内存地址的首地址,如下图。

再看下面的赋值操作代码

Student stu1 = new Student(1, 3);
Student stu2 = new Student(2, 5);
stu1 = stu2;

在执行stu1 = stu2之前,其内存结构大概如下,stu1和stu2分别指向其对象本身所在的内址首地址,在堆中的内存单元中存储的也是对象本身所以内存的首地址。

而执行stu1 = stu2之的操作后如下,其本质是将stu2的内存首地址的值赋值给stu1,这时stu1和stu2引用相同的变量。

同时说明了一个引用变量,在不同时期可以指向不同的对象,而一个对象可以被多个引用变量所引用。

而对于复杂一些的对象(对象的属性不是基本类型,而是引用类型的),其本质和上面的类似,只不过一个引用存在于栈中,另一个存在于堆中。

public class Group{
    public int gradNo;
    public int classNo;
    public Group(int gradNo, int classNo){
        this.gradNo = gradNo;
        this.classNo = classNo;
    }
}

public class Student{
    public int id;
    public int age;
    public Group group;
    public Student(int id, int age){
        this.id = id;
        this.age = age;
    }
    
}

public class Demo{
    public static void main(String[] arg){
        Student stu = new Student(3, 6);
        stu.group = new Group(1, 4);
    } 
}

上面的代码在没有执行stu.group = new Group(1, 4);之前,在Student的对象属性group里存的为null(对应的二进制就是0),而赋值以后其内存结构如下:

4) 引用类型:数组

数组的引用更为复杂,有基本类型数组和对象数组,而从维度上又有一维数组和二维数组

1,一维数组

基本类型的数组,以int为例

int[] arr = new int[5];
arr[0] = 3;

其在内存的结构如下图,其会在内存中分配一整块连续的内存(堆),而arr是对象的引用,存储于栈中,其值为int[5]在堆中内存的首地址:

再看对象的数组:

Student[] arr = new Student[3];
arr[0] = new Student(1,1);
arr[1] = new Student(2,2);

其内存结构如下图,也会分配一整块的内存在堆中,但其内存储的值为对象所在堆中内存地址的首地址,如果为空存储的是null,而不像基本类型数组直接存储的是值,对象数组存储的是引用

2,二维数组

java的二维数组并不是定义上的传统意义上的二维数组,其一维是固定的大小的,其二维长度是可变的,另外二维数组不是一整块的完整内存。

基本类型的二维数组:

int[][] arr = new int[3][];
arr[0] = new int[2];
arr[2] = new int[4];

其内存结构如下图,发现一维是一整块的内存,但里面存的都是二维的首地址,而每一个二维也是一整块的内存。

再来看看对象的二维数组:

Student[][] arr = new Student[3][];
arr[0]= new Student[2];
arr[0][0]= new Student(2,2);
arr[2]= new Student[3];
arr[2][1]= new Student(4,4);
arr[2][2]=new Student(5,5);

其内存结构如下图,一维存的是二维的地址,而二维里面存的又是对象本身所在的首地址

4)链表的引用说明

链表作为一种基础的数据结构,其中利用了大量的引用。

public class Node{
    public int data;
    public Node next;
    public Node(int data, Node next){
        this.data = data;
        this.next = next;
    }
}

public class Demo{
    public void traverse(Node head){
        Node p = head;
        while(p != null){
            System.out.println(p.data);
            p = p.next
        }
    }
    public void delete(Node head){
        p.next = p.next.next;
    }
}

而其中要注意理解的就是p = head, p = p.next; p.next = p.next.next;

p = head如下图:

p = p.next如下

p.next = p.next.next.

二,参数传递:值传递or引用传递

基本类型参数

以代码来说明

public class Demo{
    public static void main(String[] arg){
        int a = 1;
        fa(a);
        System.out.println(a);
    }

    private static void fa(int va){
        va = 2;
        System.out.println(va);
    }
}

其中fa中的va函数是一个单独的变量,存储于fa的栈帧中,其并不能访问main函数里的数据,而main函数也并不能访问fa函数里的数据。最初va会被赋值为a的值,但只是赋值与a本身并无关联了,所以va的改变不会影响a。

引用类型参数

引用类型参数就比较有意思了,这是很容易让人以为java是引用传递的地方。

public class Demo{
    public static void main(String[] args){
        Student stu = new Student(1,1);
        fa(stu);
        System.out.print(stu.age);
    }

    private static void fa(Student vstu){
        vstu.age = 3;
    }
    private static void fb(Student vstu){
        vstu  = new Student();
    }
}

在fa的函数栈帧中,vstu被赋值为stu中的值,即Student对象的首地址。此时这两个引用变量stu与vstu都指向了同一个对象。因此改变了vstu中age的值,stu的也改变了,因为本质是同一个对象。

而像fb中的更改。

本质是修改了vstu的值,指向一个新的对象,但是因为只是值传递,而不是对stu的引用,没法更改stu里的值,所以stu没有变化。

值传递or引用传递。

先说结论,是值传递。

因为看上面的就发现传递的是值,而不是引用,真正的引用传递是可以通过更改vstu指向新对象,stu也指向新对象的。

三,equals和==

其本类型

其本类型比较简单,直接使用== 判断是否等于。因为基本类型的值直接存储于其对应的内存单元,也就是判断两个的内存单元的值是否相等。

引用类型

引用类型使用equals一般指判断某个具体的值是否相等。

Object默认的如下

但是大多都会重写。如String,而平时使用中可以根据自己的需求重写。

另外也要注意hashCode的重写,因为在一些hash容器中先会利用hash值定位存储位置,然后再是判断是否是相同。

而使用==判断,则一般指是否是同一个对象,本质和基本类型一样,判断两个引用变量存储的值是否一致。

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值