• Go语言 和 Java语言对比理解系列一:函数参数传递



    Go和Java都是 值传递,传递的是参数的副本。但两者都区分参数是 值类型还是 引用类型

    • 对于Java来说,8种基本数据类型是值类型,数组和对象属于引用类型;
    • 对于Go来说,int,string,float,bool,数组和struct是值类型,指针、slice、map、channel、接口、函数等是引用类型。

    Go语言里的struct在感觉上更像是Java中属于引用类型的对象。但事实并不是。

    • Go语言中用于描述对象的struct属于值类型,而Java中的对象属于引用类型;
    • Go语言中的数组属于值类型,而Java中的数组属于引用类型。

    下面分别用代码来掩饰他们在传参上的区别。

    传对象

    对象赋值

    Go
    // 定义User结构体
    type User struct {
       Name string
       Age int
    }
    
    // 定义一个全局的user
    var guser User = User {
       "yyyy",
       28,
    }
    
    // 定义一个函数,参数为User结构体“对象”,将全局guser指向传递过来的User结构体“对象”
    func modifyUser(user User) {
       fmt.Printf("参数user的地址 = %p\n",&user) 
       fmt.Printf("guser修改前的地址 = %p\n",&guser)
       fmt.Println("guser修改前 = ",guser)
       // 修改指向
       guser = user
       fmt.Printf("guser修改后的地址 = %p\n",&guser)
       fmt.Println("guser修改后 = ",guser)
    }
    
    func main() {
       var u User = User {
          "xxxx",
          29,
       }
       fmt.Printf("将要传递的参数u的地址 = %p\n",&u)
       modifyUser(u)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    执行结果:

    将要传递的参数u的地址 = 0xc0000ac018
    参数user的地址 = 0xc0000ac030
    guser修改前的地址 = 0x51c080
    guser修改前 =  {yyyy 28}
    guser修改后的地址 = 0x51c080
    guser修改后 =  {xxxx 29}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    Java
    public class User {
        private String name;
        private int age;
    
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
        public String print() {
            return "{name = " + name + ",age = " + age + "}";
        }
    }
    
    public class Demo {
        private static User guser = new User("yyyy",28);
        public static void modifyUser(User user) {
            System.out.println("参数user的地址 = " + user);
            System.out.println("guser修改前的地址 = " + guser);
            System.out.println("guser修改前 = " + guser.print());
            guser = user;
            System.out.println("guser修改后的地址 = " + guser);
            System.out.println("guser修改后 = " + guser.print());
        }
        public static void main(String[] args) {
            User u = new User("xxxx", 29);
            System.out.println("将要传递的参数u的地址 = " + u);
            modifyUser(u);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    执行结果:

    将要传递的参数u的地址 = com.example.User@7d2ac533
    参数user的地址 = com.example.User@7d2ac533
    guser修改前的地址 = com.example.User@4ef01483
    guser修改前 = {name = yyyy,age = 28}
    guser修改后的地址 = com.example.User@7d2ac533
    guser修改后 = {name = xxxx,age = 29}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过上述两个程序的执行结果可以分析出如下结论:

    • Go传入函数参数的是原对象的一个全新的copy(有自己的内存地址);Java传入函数参数的是原对象的引用的copy(指向的是同样的内存地址)。
    • Go对象之间赋值是把对象内存的 内容(属性等) copy过去,所以才会看到guser修改前后的地址不变,但是对象的内容变了;Java对象之间的赋值是把对象的 引用 copy过去,因为引用指向的地址变了,所以对象的内容也变了。

    对象字段修改

    Go
    // 定义User结构体
    type User struct {
       Name string
       Age int
    }
    
    // 定义一个函数,参数为User结构体“对象”,修改其Name字段
    func modifyName(user User) {
       fmt.Println("修改前user.Name = ",user.Name)
       // 修改Name字段
       user.Name = "yyyy"
       fmt.Println("修改后user.Name = ",user.Name)
    }
    
    func main() {
       var u User = User {
          "xxxx",
          29,
       }
       modifyName(u)
       fmt.Println("执行修改函数后参数u.Name = ",u.Name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    执行结果:

    修改前user.Name =  xxxx
    修改后user.Name =  yyyy
    执行修改函数后参数u.Name =  xxxx
    
    • 1
    • 2
    • 3
    Java
    public class Demo {
        private static User guser = new User("yyyy",28);
    
        public static void modifyName(User user) {
            System.out.println("修改前user.Name = " + user.getName());
            user.setName("yyyy");
            System.out.println("修改后user.Name = " + user.getName());
        }
    
        public static void main(String[] args) {
            User u = new User("xxxx", 29);
            modifyName(u);
            System.out.println("执行修改函数后参数u.Name = " + u.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    执行结果:

    修改前user.Name = xxxx
    修改后user.Name = yyyy
    执行修改函数后参数u.Name = yyyy
    
    • 1
    • 2
    • 3

    基于上面对对象赋值的理解,理解字段修改就更容易了:

    • Go修改的是一个全新对象(有自己的内存地址)的字段值,不影线原来对象,所以会出现函数里修改成功了,但是原来的参数对象还是没变。
    • Java修改的是同一个对象,所以函数里修改成功了,原来的参数对象也跟着变了。
    Go修改字段如何实现类似Java的效果

    既然Java传递的是引用,那我们也可以让Go传 指针(传递的是对象地址,指向的是同一个对象内存)来实现同样的效果:

    // 定义User结构体
    type User struct {
       Name string
       Age int
    }
    
    // 定义一个函数,参数为User结构体“对象指针”,修改其Name字段
    func modifyNameByPointer(user *User) {
       fmt.Println("修改前user.Name = ",(*user).Name)
       // 修改Name字段
       (*user).Name = "yyyy"
       fmt.Println("修改后user.Name = ",(*user).Name)
    }
    
    func main() {
       var u User = User {
          "xxxx",
          29,
       }
    
       modifyNameByPointer(&u)
       fmt.Println("执行修改函数后参数u.Name = ",u.Name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    传数组

    数组的情况和struct一样,最后的结论也可以参考上面[传对象]的结论,这里就不再赘述。感兴趣的也可以参考上面的程序自己验证一下。

    总结

    • Go里面struct和数组属于值类型;Java里的对象和数组属于引用类型
    • Go对struct和数组的函数传递操作,是复制一块新的内存,与原有的数据相互独立,互不影响。而赋值操作则是拿新属性值覆盖旧属性值。

    相关代码请参考:

  • 相关阅读:
    【Hadoop】HDFS 原理
    非零基础自学Java (老师:韩顺平) 第10章 面向对象编程(高级部分) 10.1 类变量和类方法
    kafka详解(三)
    [附源码]SSM计算机毕业设计医院仪器设备管理系统JAVA
    java压缩base64位图
    在buildroot中自动给kernel打补丁
    PG与MySQL中查询库/表的命令区别
    【业务功能118】微服务-springcloud-springboot-Kubernetes集群-k8s集群-KubeSphere-OpenELB部署及应用
    【面试经典150 | 矩阵】生命游戏
    End of line spacing
  • 原文地址:https://blog.csdn.net/qq_18515155/article/details/126796275