引言

  什么是数据类型?在计算机科学和计算机编程中,数据类型或简单的类型是数据的一个属性,它告诉编译器或解释器程序员打算如何使用数据。

  每一种编程语言都有属于自己的数据类型,可能不尽相同,但万变不离其宗。Java中当然也有一套属于自己的数据类型,分为两大类:基础数据类型和引用数据类型,它们各自又有不同的分类。

  在C语言中,关于函数的参数传递有两个专业术语:按值调用(call by value)和按引用调用(call by reference),它们和数据类型关系密切,但是,Java中只有按值调用。小编楼兰胡杨的这篇文章就和老铁们探索一下Java中的各种数据类型的分类、用途以及之间的关系等,探索java热门基础面试题按值调用(值传递)

数据类型分类

  在Java里面,整体上把数据类型分为两大类——四类八种基础类型(primitive types) 和 3种引用类型(reference types) ,分类图如下图所示:

  接下来,分别介绍基本类型和引用类型。

基本类型

  基本类型是Java语言预定义的类型,用相应的保留关键字来表示,具有明确的取值范围和数学行为,表示了真实的数字、字符和整数。基本类型的数据都是单个值,而不是复杂的对象,所以基本类型并不是面向对象的,这主要是出于效率方面的考虑。

  基本类型包括布尔类型和数值类型,数值类型又分为整型和浮点型。八种基本数据类型分类如下:

1、整型:byte、short、int、long

2、字符型:char(本质上是一种特殊的int)

3、浮点型:float、double

4、布尔型:boolean

  与此同时,Java语言也为基本类型提供了对应的对象版本,即基本类型的包装类(wrapper),具体信息如下表所示:

  如果类的成员变量(字段)是基本类型,那么在类初始化时,每个成员变量将会被赋予一个如上表中默认值列所述的默认值。若为了防止因默认值引起不必要的麻烦,推荐用基本类型的包装类。

引用类型

  引用类型(The value of reference types are references to objects)中的引用,一般是指某个对象的内存地址,其中对象是动态创建的类实例或者数组等。简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小占用不相等的内存空间。

  另外,Java语言本身不支持C++中的结构体(struct) 或联合体(union) 等数据类型,这种复合数据类型一般都是通过类或接口进行构造。Java中,除了基础类型外,其余的类型都是引用类型,诸如Java自带的StringBigDecimalBigInteger以及自定义的业务类等,如下所示:

User user;             //类引用类型
List<Object> list; //接口引用类型
Integer[] array; //数组引用类型

和基础数据类型变量不同的是,引用类型的变量可以被赋值为null,如下所示:

User user = null;             //类引用类型
List<Object> list = null; //接口引用类型
Integer[] array = null; //数组引用类型

  Java是一门面向对象的编程语言,除了基本数据类型以外,它要求每种数据类型都必须是一个类。面向对象的编程思想力图使在计算机语言中对事物的刻画与现实世界中该事物的本来面目尽可能地一致,类(class)和对象(object)就是面向对象方法的核心概念。

  类是对某一类事物的描述,是抽象的、概念上的定义;对象是实际存在的该类事物的个体,因而也称为实例(Instance)。类和对象就如同概念和实物之间的关系一样,类就好比是一个模板,而对象就是该模板下的一个实例。

引用分类

  引用有哪些分类?【划重点】在Java中引用包括Final reference 强引用、Soft reference 软引用、Weak reference 弱引用 和 Phantom reference 虚引用。那么,为什么会提供这四种引用?主要原因如下:

  • 利于 JVM 进行垃圾回收;

  • 利于开发人员灵活的决定某些对象的生命周期。

  如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间(stack,栈空间)来保存。但是它们的值是相同的,都指向同一个对象在堆内存(heap,堆空间)中的位置,比如:

String a="Word";
String b="Word Two";
String b=a;

  如图,b = "Word Two"; b = a; 不是改变了 "Word" 这一对象的值,初始化时,b 的值为绿线所指向的“Word Two”,然后 b=a使 b 的引用变更为红线所指向的”Word“。但要注意,String 对象的值本身是不可更改的

数据存在哪

  Java是面向对象语言,其概念为一切皆为对象,但基本数据类型特立独行。基本数据类型大多是面向机器底层的类型,它是 “值” 而不是一个对象,存放于“栈”中而非“堆”中,但Java一切皆为对象的概念绝非说说而已,它为每一个基本数据类型都提供了相应的包装类(封装器类)。包装类就是一个对象,它存放于“堆”中,下面介绍一下栈内存和堆内存的区别。

  程序是运行在内存中的,也就是我们常说的电脑16g还是8g的内存。而内存空间又划分为栈内存堆内存

  • 栈内存分配速度快,内存空间小。Java的基本类型和引用类型的对象引用存在栈内存中。
  • 堆内存分配速度稍慢,内存空间大。Java的引用类型指向的具体对象存在堆内存中。

  基本类型使用频繁而且占用空间小,放在栈内存中。引用类型的对象引用就是堆内存中对象存放的地址。打个比喻:藏宝图里面记录着宝藏的位置,可以根据藏宝图上面标记的位置找到这些宝藏,这里藏宝图就是栈内存,存放着对象引用,而具体埋放宝藏的地方就是堆内存

基本类型与引用类型的区别

  不论是基本类型还是引用类型,都会先在栈中分配一块内存。对于基本类型来说,这块内存区域中包含的是基本类型的具体数据内容;对于引用类型来说,这块内存区域中包含的是指向真正内容的引用,而真正的内容则被分配在堆上。

  所有的类型在内存中都会分配一定的存储空间,基本类型只有一块存储空间(分配在stack中), 而引用类型有两块存储空间(一块在stack中,一块在heap中);形参在使用的时候也会分配存储空间,方法调用完成之后进行垃圾回收。

值传递

  首先要强调的是java中只存在值传递,只存在值传递!!! 然而我们经常看到对象(数组,类,接口)的传递似乎有点像引用传递,可以改变对象中某个属性的值。但是这样理解是一叶障目,希望老铁们不要被此假象所蒙蔽,实际上传入函数的值是对象引用的拷贝,即传递的是引用的地址值,故依然是按值传递。

  函数的形参类型如果是引用类型,则调用函数时传过来的就是实参的副本,这个副本存放的是实参的引用地址。如果是基本类型,那么传过来的也是实参的副本,但此时副本存放的是实参的值,如果在函数中改变了副本的值不会改变原始的值。

  为什么Java中只有按值调用,没有按引用调用?Java不支持指针,所以Java不支持引用调用。但是C/C++支持指针,故这些语言支持引用调用。

  Java 方法的参数是简单类型的时候,是值传递的。这一点我们可以通过一个简单的例子来说明:

package test;

public class Test {
//交换两个形参的值
public static void swap(int a,int b){
int c=a;
a=b;
b=c;
System.out.println("a: "+a);
System.out.println("b: "+b);
} public static void main(String[] args){
int c=10; // 实参
int d=20; // 实参
swap(c,d);
System.out.println("After swap:");
System.out.println("c: "+d);
System.out.println("d: "+c);
}
}
运行结果:
a: 20
b: 10
After swap:
c: 20
d: 10

  不难看出,虽然在 swap (a,b) 方法中改变了形参的值,但对实参本身并没有影响,即对 main(String[]) 方法里的 a,b 变量没有影响。故参数类型是基本类型的时候,是按值传递的,实际上是将参数的值作了一个拷贝传进函数的,无论在函数里怎么改变其值,其结果都是只改变了拷贝的值,而不影响源值。

引用对象传递之例外null

  众所周知,java中除基本类型外,参数传递的都是引用的地址。但是,有一个例外,就是当为引用类型的实参赋值null时,它依然是值传递。也就是说,传参为null的时候退化为值传递,不管函数体内用这个参数做了什么,跳出函数体后该参数依然是null。

  这里澄清一些误解,null既不是对象也不是一种类型,它仅仅是一个特殊的值;你可以将其赋予任何引用类型变量,即可以将其转化成任何类型。再进一步剖析,其实是基本类型和指向null的非基本类型的引用都存在栈而非堆中,而引用类型传递的是堆内存地址。还有一种常见的、引用失效的场景是引用被改变,示例如下:

User wiener = new User();
wiener.setUserName("Wiener");
wiener.setAge(19);
wiener = new User(); // 覆盖了原来的引用
wiener.setAge(18);
log.info(wiener); // 此时打印出来时,控制台没有姓名只有年龄为18

Reference

深入理解Java引用类型和值调用-变量在内存里存在哪里的更多相关文章

  1. 深入理解Java引用类型

    深入理解Java引用类型 在Java中类型可分为两大类:值类型与引用类型.值类型就是基本数据类型(如int ,double 等),而引用类型,是指除了基本的变量类型之外的所有类型(如通过 class ...

  2. 深入理解Java中配置环境变量

    深入理解Java中配置环境变量 配置的目的: 本来只在安装JDK的bin目下能运行java.exe,javac.exe,jar.exe,javadoc.exe等Java开发工具包命令,我们现在想让在所 ...

  3. 深入理解Java虚拟机:垃圾收集器与内存分配策略

    目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...

  4. 深入理解java:1.3.1 JVM内存区域的划分(运行时数据区)

    学习Java GC机制,可以帮助我们在日常工作中 排查各种内存溢出或泄露问题,解决性能瓶颈,达到更高的并发量,写出更高效的程序. 我们将从4个方面学习Java GC机制, 1,内存是如何分配的: 2, ...

  5. 《深入理解Java虚拟机》学习笔记之内存分配

    JVM在执行Java程序的过程中会把它所管理的内存划分若干个不同的数据区域,如下图: 大致可以分为两类:线程私有区域和线程共享区域. 线程私有区域 程序计数器(Program Counter Regi ...

  6. 深入理解java虚拟机(一)虚拟机内存划分

    Java虚拟机在执行Java程序时,会把它管理的内存划分为若干个不同的数据区.这些区域有不同的特性,起不同的作用.它们有各自的创建时间,销毁时间.有的区域随着进程的启动而创建,随着进程结束而销毁,有的 ...

  7. 《深入理解java虚拟机》读书笔记1--java内存区域

    Java内存管理 本文主要介绍Java虚拟机运行时的内存区域是如何划分的.Java对象的创建过程.Java对象的内存布局.Java对象的访问定位 一:运行时区域划分 主要可以分为以下 几个: 程序计数 ...

  8. 深入理解Java虚拟机二:垃圾收集与内存分配

    垃圾收集:垃圾收集要完成三件事,包括哪些内存需要回收,什么时候回收及如何回收. 1.需要回收的内存判定:没有引用指向原先分配给某个对象的内存时,则该内存是需要回收的垃圾 Java垃圾收集器在对内存进行 ...

  9. 《深入理解Java虚拟机》学习笔记之内存回收

    垃圾收集(Garbage Collection,GC)并不是Java语言的半生产物,事实上GC历史远比Java久远,真正使用内存动态分配和垃圾收集技术的语言是诞生于1960年的Lisp语言.经过半个世 ...

  10. 《深入理解Java虚拟机》——垃圾收集器与内存分配策略

    GC需要完成: 哪些内存需要回收 什么时候回收 如何回收 如何确定对象不再使用 引用计数算法 给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作:当引用失效时,计数器值进行减1操作: ...

随机推荐

  1. Easyexcel(1-注解使用)

    版本依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</a ...

  2. BUGKU_PWN_OVERFLOW2_WP

    WP_OVERFLOW2 拿到程序,首先放到我们的kali里面看看是多少位的程序,然后在看看有没有什么安全属性 64位程序,并且开启了RELRO,NX 也就是说,这道题我们需要使用ROP绕过 使用id ...

  3. vim中文乱码 vim字符集设置

    vim中文乱码 vim字符集设置 vim的设置一般放在/etc/vimrc文件中,不过,建议不要修改它.可以修改~/.vimrc文件(默认不存在,可以自己新建一个),写入所希望的设置. set fil ...

  4. 编写你的第一个 Django 应用程序,第1部分

    让我们通过示例来学习. 在本教程中,我们将引导您完成基本投票应用程序 它将由两部分组成: 一个公共网站,允许人们查看投票并在其中投票. 允许您添加.更改和删除投票的管理网站. 一.开发环境搭建 第一步 ...

  5. Linux脚本-使用jar自动替换配置文件

    背景 最近公司需要在生产服务器上测试字库,需要非常频繁修改配置文件中的字体相关属性,然后实时调试,所以需要频繁的修改配置文件并手动发布出去.之前需要修改配置文件时,我们需要: 把jar包通过FTP传回 ...

  6. 一文速通Python并行计算:04 Python多线程编程-多线程同步(上)—基于条件变量、事件和屏障

    一文速通 Python 并行计算:04 Python 多线程编程-多线程同步(下)-基于条件变量.事件和屏障 摘要: 本文介绍了 Python 多线程同步的三种机制:条件变量(Condition).事 ...

  7. verilog实现32位有符号流水乘法器

    verilog实现32位有符号流水乘法器 1.4bit乘法流程 1.无符号X无符号二进制乘法器 以下为4bit乘法器流程(2X6) 0 0 0 0 0 0 1 0 (2) X 0 0 0 0 0 1 ...

  8. 【C语言】Linux 飞翔的小鸟

    [C语言]Linux 飞翔的小鸟 零.环境部署 安装Ncurses库 sudo apt-get install libncurses5-dev 壹.编写代码 代码如下: bird.c #include ...

  9. 【Java】Java提取${}占位符并组装对应值

    目录 Java提取${}占位符并组装对应值 零.起因 壹.想法 贰.实现 叁.总结 肆.参考文档 Java提取${}占位符并组装对应值 实现了一个${}装配工~ 零.起因 最近写个JavaWeb项目, ...

  10. idhttp的socket error # 10054 错误的处理办法

    在通过http实现restful数据通讯时,死活出现: socket error # 10054 导致这种情况的原因很复杂. 同样的程序,在不同的环境中出现不同结果. 通过观察,发现登录后获取toke ...