Java传值调用和传引用调用方式(参数引用为null问题)

 更新时间:2023年09月29日 09:00:03   作者:Jerry的技术博客  
这篇文章主要介绍了Java传值调用和传引用调用方式(参数引用为null问题),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、问题

近期在项目中遇到一个场景,在多层级调用中需要传递上下文,调用过程中上线文对象可能为空,想通过一个公共方法处理上下文,当上下文为空时生成上下文对象,执行相关操作后将该上下文对象向后传递。

大致逻辑如下:

public class Test {
    public static void handleContext(Context context) {
        if(context == null) {
            context = new Context();
        }
        context.addNum();
    }
    public static void main(String[] args) {
        Context context = null; 
        handleContext(context);
        System.out.println(context.getNum());
    }
    static class Context {
        private int num;
        public int getNum() {
            return num;
        }
        public void addNum() {
            this.num ++;
        }
    }
}

测试执行报空指针错误,context没有按设想在handleContext方法中生成对象。

原因是main方法在栈中创建了Context的引用并将其指向null,handleContext方法参数中的Context引用也被指向null,handleContext方法体在堆中创建Context对象,并将对象地址赋给方法参数中的Context引用,但main方法中的Context引用仍然为null,因此调用context.getNum()时报空指针。

二、java方法传值与传引用

2.1 变量存储

基础类型变量:

Java的8中基础数据类型:byte(8位)、short(16位)、int(32位)、long(64位)、float(32位)、double(64位)、char(16位)、boolean(8位),

基础类型的数据存储在栈中,即是栈中分配内存空间存储所包含的值,其值就代表数据本身,值类型的数据具有较快的存取速度。

引用类型变量:

除了基础类数据外,其余都是引用类型,包括类、数组等。

引用类型数据的具体对象存放在堆中,而栈中存放的是该对象的内存地址。

当引用类型没有赋值时,其引用为null,表示不指向任何对象。

2.2 创建对象与赋值过程

Context context = new Context();

该操作可以分为两个部分:

Context context;
context = new Context();

第一步:创建Context引用,在栈中开辟一块空间用于存储该引用;

第二步:创建Context对象,在堆内存中开辟一块空间存储该对象,并将该对象的存储地址赋值给栈中的引用

2.3 引用赋值过程

将一个引用赋值给另一个引用时,其实是将该引用指向的地址赋值给另个应用,让两个引用指向同一个对象,示意图如下。

Context context1 = new Context();
Context context2 = context1;

在这里插入图片描述

2.4 传值与传引用方法调用

其实无论是传值还是传引用调用,其本质都是将栈中存储的数据复制一份,传递给栈中的另一个变量。

对于基础数据类型,是将数据本身复制一份传递;对于引用类型,是将引用的地址复制一份传递。

因此,上述问题可以描述为如下示意图。当handleContext方法执行返回后,main方法中的context引用仍然为null。

注:为方便理解,将null特殊处理。当引用为null时候表示不指向任何对象

在这里插入图片描述

值得注意的是包装类型(Intger;Long;Short;Double;Float;Char;Boolean;Byte,以及String(char[]的包装类型)),虽然是引用类型数据,但其效果等同于传值调用,示例如下:

public class Test {
    public static void change(String str) {
        str = "xyz";
    }
    public static void main(String[] args) {
        String str = "abc";
        change(str);
        System.out.println(str);
    }
}
---
abc

其原因是String类型是不可变(immutable)的。

String类型及其成员变量均是final的,这意味着String的value字符数组不能指向其它地址,同时value字符数组的值也不可能通过继承String后修改。

在change方法中,参数String引用str初始化时指向对象abc,执行方法后指向了方法区的另一个字符串常量(xyz),而main方法中的String引用str仍然指向方法区的字符串常量(abc)。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
...
    private final char value[];
...
}

2.5 Java常量池

基本类型的包装类型大部分都实现了常量池技术,包括:Byte、Short、Integer、Long、Character、Boolean,但只有在其值 -128<value<127范围内才可使用常量池,数据存放在方法区,数值超出范围或通过new方法生成对象则会在堆上分配存储空间

public class Test {
    public static void main(String[] args) {
        Integer i = 123;
        Integer j = 123;
        System.out.println("i == j : " + (i == j));
        System.out.println("i.equals(j) : " + i.equals(j));
        Integer m = 1234;
        Integer n = 1234;
        System.out.println("m == n : " + (m == n));
        System.out.println("m.equals(n) : " + m.equals(n));
        Integer x = new Integer(123);
        Integer y = new Integer(123);
        System.out.println("x == y : " + (x == y));
        System.out.println("x.equals(y) : " + x.equals(y));
    }
}
---
i == j : true
i.equals(j) : true
m == n : false
m.equals(n) : true
x == y : false
x.equals(y) : true

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java使用Hashtable过滤数组中重复值的方法

    java使用Hashtable过滤数组中重复值的方法

    这篇文章主要介绍了java使用Hashtable过滤数组中重复值的方法,涉及java数组遍历及过滤的相关技巧,需要的朋友可以参考下
    2016-08-08
  • 基于Java SSM实现Excel数据批量导入

    基于Java SSM实现Excel数据批量导入

    这篇文章主要为大家详细介绍了基于Java SSM如何实现excel数据批量导入,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    这篇文章主要介绍了SpringBoot 2.0 整合sharding-jdbc中间件,实现数据分库分表,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-06-06
  • java唯一字符串ID生成方案详解

    java唯一字符串ID生成方案详解

    这篇文章主要给大家介绍了关于java唯一字符串ID生成方案的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • java中transient关键字分析

    java中transient关键字分析

    这篇文章主要介绍了java中transient关键字分析,transient与类对象的序列化息息相关,序列化保存的是 类对象 状态,被transient关键字修饰的成员变量,在类的实例化对象的序列化处理过程中会被忽略,变量不会贯穿对象的序列化和反序列化,需要的朋友可以参考下
    2023-09-09
  • Java LongAdder原理解析与实战应用小结

    Java LongAdder原理解析与实战应用小结

    LongAdder是Java 8中java.util.concurrent.atomic包引入的高性能计数器类,专为高并发场景下的数值累加操作优化设计,本文给大家介绍Java LongAdder原理解析与实战应用小结,感兴趣的朋友一起看看吧
    2025-06-06
  • 实例解析Java设计模式编程中的适配器模式使用

    实例解析Java设计模式编程中的适配器模式使用

    本篇文章主要通过实例对适配器模式进行了详解,需要的朋友可以参考下
    2017-04-04
  • Java微信公众平台开发(2) 微信服务器post消息体的接收

    Java微信公众平台开发(2) 微信服务器post消息体的接收

    这篇文章主要为大家详细介绍了Java微信公众平台开发第二步,微信服务器post消息体的接收,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • 一篇文章带你了解java接口与继承

    一篇文章带你了解java接口与继承

    这篇文章主要介绍了Java接口和继承操作,结合具体实例形式分析了Java接口和继承与使用的相关原理、操作技巧与注意事项,需要的朋友可以参考下
    2021-08-08
  • 解决IDEA2020.1.2IDEA打不开的问题(最新分享)

    解决IDEA2020.1.2IDEA打不开的问题(最新分享)

    由于idea安装多了某个jar,点击出现读条后闪退情况,接下来通过本文给大家分享解决IDEA2020.1.2IDEA打不开的问题,非常不错,具有一定的参考借鉴价值,感兴趣的朋友跟随小编一起看看吧
    2020-07-07

最新评论