首页 > 编程语言 >Java 值传递与引用传递

Java 值传递与引用传递

时间:2024-09-16 21:51:58浏览次数:3  
标签:Java String 对象 传递 Person 引用 public name

在这里插入图片描述
以下是包含引用的完整博客文章,以markdown格式输出,附带“Java 只有值传递”的相关参考来源。


Java 是一种广泛使用的面向对象编程语言,但对于值传递(pass by value)和引用传递(pass by reference)的理解,很多开发者往往会混淆。在这篇文章中,我将详细解释 Java 的传递机制,并引入对象克隆、深浅拷贝和不可变类的概念。

值传递还是引用传递?

首先,我们必须明确一点:Java 只有值传递。这是什么意思呢?每次我们在方法中传递参数时,实际上传递的是值的副本。无论是基本类型还是对象引用,传递的都是副本。

Java 的参数传递机制是值传递,无论是基本类型还是对象类型。引用类型传递时,传递的是引用的副本,也就是地址的副本。

基本类型的值传递

对于基本类型(如 intfloat 等),传递的是变量的值副本。我们来看一个简单的例子:

public class ValueDemo {
    public static void main(String[] args) {
        int a = 10;
        changeValue(a);
        System.out.println(a);  // 输出仍然是 10
    }

    public static void changeValue(int x) {
        x = 20;
    }
}

在上面的代码中,a 的值并没有发生改变。这是因为 Java 将 a 的值复制给了参数 x,所以 x 的修改不会影响原来的 a

对象类型的值传递

对于对象类型(如 StringArrayList 等),传递的依然是引用的副本,而不是引用本身。换句话说,我们传递的是对象的地址副本。这意味着我们可以通过引用修改对象的内部状态,但不能更改引用本身。

看下面的例子:

public class ReferenceDemo {
    public static void main(String[] args) {
        Person person = new Person("Alice");
        changeName(person);
        System.out.println(person.getName());  // 输出是 "Bob"
    }

    public static void changeName(Person p) {
        p.setName("Bob");  // 修改对象的内部状态
    }
}

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在这个例子中,Person 对象的 name 被成功修改为 "Bob",因为我们通过引用修改了对象的内部状态。但要注意,这并不意味着 Java 支持引用传递,只是传递了引用的副本。

深拷贝与浅拷贝

当我们需要复制对象时,可能会遇到浅拷贝(shallow copy)和深拷贝(deep copy)的概念。理解这两个概念对于处理复杂对象非常重要。

浅拷贝

浅拷贝只复制对象的引用,而不复制实际的对象内容。换句话说,浅拷贝后,新旧对象共享相同的内部对象。常见的浅拷贝方式是通过实现 Cloneable 接口的 clone() 方法。

class Person implements Cloneable {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }

    public String getName() {
        return name;
    }
}

public class ShallowCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("Alice");
        Person person2 = (Person) person1.clone();

        System.out.println(person1 == person2);  // 输出 false
        System.out.println(person1.getName() == person2.getName());  // 输出 true
    }
}

上例中,person1person2 是两个不同的对象,但它们内部的 name 字符串引用了相同的对象。这就是浅拷贝。

深拷贝

深拷贝则不同,它不仅复制对象本身,还会复制所有引用的对象。实现深拷贝通常需要手动编写复制逻辑,或使用序列化机制。

class Person implements Cloneable {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.name = new String(this.name);  // 深拷贝,创建新字符串对象
        return cloned;
    }

    public String getName() {
        return name;
    }
}

public class DeepCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("Alice");
        Person person2 = (Person) person1.clone();

        System.out.println(person1 == person2);  // 输出 false
        System.out.println(person1.getName() == person2.getName());  // 输出 false
    }
}

在深拷贝中,新对象 person2name 是一个全新的对象,和 person1name 没有任何关联。

不可变类

在讨论值传递和引用传递时,不可变类(immutable class)是另一个重要的概念。不可变类一旦创建,内部状态就不能改变。不可变类的经典例子是 String 类。由于不可变性,我们无需担心对象被其他方法意外修改

要创建一个不可变类,我们可以遵循以下规则:

  1. 将类声明为 final,防止子类修改。
  2. 所有字段都声明为 final,防止字段被修改。
  3. 提供深拷贝的 getter 方法,避免返回可变对象的引用。
final class ImmutablePerson {
    private final String name;

    public ImmutablePerson(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class ImmutableDemo {
    public static void main(String[] args) {
        ImmutablePerson person = new ImmutablePerson("Alice");
        // 无法修改 person 的状态,因为它是不可变的
    }
}

不可变类的设计能确保对象的状态安全,尤其是在多线程环境下,它们天生具有线程安全性

总结

  • Java 只有值传递,无论是基本类型还是对象类型,传递的都是副本 。
  • 对于对象类型,传递的是引用的副本,这使得可以通过引用修改对象的内部状态。
  • 浅拷贝只复制对象的引用,而深拷贝会复制对象本身及其引用的对象。
  • 不可变类是一种特殊的类,它的状态一旦初始化就不能再改变,提供了更高的安全性。

通过理解这些概念,我们可以更好地控制 Java 中的对象传递和状态管理,避免出现意外的修改和不必要的对象共享。


参考文献

  1. 《Java 编程思想》(Thinking in Java:

    “Java passes everything by value. When you pass a primitive, you get a copy of the primitive. When you pass a handle, you get a copy of the handle.”

  2. Java 官方文档(Java Language Specification, JLS):

    “When an object is passed to a method, the reference to the object is passed by value.”

  3. 《Effective Java》(Effective Java:

    “Java is strictly pass-by-value. When you pass a reference to an object, you’re passing the value of the reference, which is the address of the object.”

标签:Java,String,对象,传递,Person,引用,public,name
From: https://blog.csdn.net/problc/article/details/142307686

相关文章

  • JAVA-IO 指定目录中查找文件,文件合并分割
    指定目录中查找文件publicstaticList<String>findFile(Filetarget,StringfileName){ArrayList<String>path=newArrayList<>();if(target==null){returnpath;}if(target.isDirectory()){File[]files=target.li......
  • JAVA-IO获取resource WEB-INF 中文件 JAR包中
    getResource+getPath()classPaththis.getClass().getClassLoader().getResource(StringUtils.EMPTY).getPath()Stringpath=this.getClass().getClassLoader().getResource(fileName).getPath();StringfilePath=URLDecoder.decode(path,StandardCharsets.UTF_8);......
  • [Java面向对象]封装继承多态
    封装、继承和多态是面向对象编程的三大特征。封装封装概念封装就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法)才能对数据进行操作。封装的好处隐藏实现的细节一个操作具体的实现过程往往很复杂,通过封装用户......
  • VUE3组合API中跨层数据传递 provide和inject
    1.provide顶层组件通过该函数提供数据2.inject底层组件通过该函数获得数据、        示例:                目的:数据从底层传到顶层底层:创建一个底层dowen.vue文件<scriptsetup>import{inject}from'vue';constvueData=inject('data-ke......
  • 软件设计原则(Java实现/给出正例反例)
    文章目录前言1.开闭原则(Open/ClosedPrinciple)违反开闭原则的示例遵循开闭原则的示例2.里氏代换原则(LiskovSubstitutionPrinciple)违反里氏代换原则的示例遵循里氏代换原则的示例3.依赖倒转原则(DependencyInversionPrinciple)违反依赖倒转原则的示例遵循依赖倒转......
  • 铁路订票平台小程序的设计与实现(Java+Mysql+万字文档+ppt+系统源码+数据库 +调试)
    目  录目  录第1章 绪论1.1 课题背景1.2课题意义1.3研究内容第2章 开发环境与技术2.1MYSQL数据库2.2Java语言2.3微信小程序技术2.4 SpringBoot框架2.5 B/S架构2.6 Tomcat介绍2.7HTML简介2.8MyEclipse开发工具第3章 系统分析......
  • Java - 1
    特点面向对象OOP健壮性:强类型机制、异常处理、垃圾的自动收集跨平台性:.class文件可以在多个系统下运行(java虚拟机-JVM)解释性语言:编译好的代码需要解释器来执行JVM-JavavirtualmachineJDK包含JVM​ -运行->JVMforLinux​ ......
  • 基于微信小程序的使命召唤游戏助手的设计与实现(Java+Mysql+万字文档+ppt+系统源码+数
    目 录1绪  论1.1开发背景1.2国内外研究现状和发展趋势综述1.3开发设计的意义及研究方向2系统开发技术2.1JAVA编程语言2.2springboot框架2.3IDEA介绍2.4B/S架构2.5MySQL数据库介绍2.6微服务架构2.7微服务架构的优势3系统分析3.1整体分析......
  • 【Java】深入理解Java中的多线程同步机制
    一、多线程的数据不一致    当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。    这个时候,一个在单线程模型下不存在的问题就会发生:如果多个线程同时读写共享......
  • Java 双括号初始化(匿名内部类初始化)
    原文:Java:双括号初始化/匿名内部类初始化法ArrayList可以这样初始化://新建一个列表并赋初值A、B、CArrayList<String>list=newArrayList<String>(){{add("A");add("B");add("C");}};还有其他集合比如HashMap的初始化:Mapmap=newHashMap()......