Go语言 和 Java语言对比理解系列一:函数参数传递
文章目录
- 传对象
- 对象赋值
- Go
- Java
- 对象字段修改
- Go
- Java
- 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)
}
执行结果:
将要传递的参数u的地址 = 0xc0000ac018
参数user的地址 = 0xc0000ac030
guser修改前的地址 = 0x51c080
guser修改前 = {yyyy 28}
guser修改后的地址 = 0x51c080
guser修改后 = {xxxx 29}
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);
}
}
执行结果:
将要传递的参数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}
通过上述两个程序的执行结果可以分析出如下结论:
- Go传入函数参数的是原对象的一个全新的copy(有自己的内存地址);Java传入函数参数的是原对象的引用的copy(指向的是同样的内存地址)。
- Java对象之间赋值是把对象内存的 内容(字段值等) 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)
}
执行结果:
修改前user.Name = xxxx
修改后user.Name = yyyy
执行修改函数后参数u.Name = xxxx
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());
}
}
执行结果:
修改前user.Name = xxxx
修改后user.Name = yyyy
执行修改函数后参数u.Name = yyyy
基于上面对对象赋值的理解,理解字段修改就更容易了:
- 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)
}
传数组
数组的情况和struct一样,最后的结论也可以参考上面**[传对象]**的结论,这里就不再赘述。感兴趣的也可以参考上面的程序自己验证一下。
总结
- Go里面struct和数组属于值类型;Java里的对象和数组属于引用类型。
- Go对struct和数组的函数传递、赋值操作,是复制一块新的内存,与原有的数据相互独立,互不影响。
相关代码请参考:
- https://gitee.com/xujian01/blogcode/tree/master/src/main/java/parampass
- https://gitee.com/xujian01/blogcode_go/tree/master/parampass