在编程中,传值和值址的不同,那么我们相应的代码就有可能会发生变化。对于VB、C#、C++在传值和值址的时候,都会在方法的形参上有所区别(具体什么样子自己搜下吧)。但我学Java也快一年了吧,却还没有见过这个东西。于是查了一下,原来在Java中,值类型的是按址传递的,而对于像自定义类、String这样的类型,是按址传递的。
下面就来看一下吧,首先先看一下按值传递,只列一个int,其它的也类似是这样的:
public static void main(String[] args){
int a=1,b=2;
System.out.println("====before swap====");
System.out.println("a:"+a);
System.out.println("b:"+b);
swap(a, b);
System.out.println("====after swap====");
System.out.println("a:"+a);
System.out.println("b:"+b);
}
public static void swap(int a,int b){
a=a+b;
b=a-b;
a=a-b;
}
====before swap====
a:1
b:2
====after swap====
a:1
b:2
下面来看一个按址传递的例子:
public class TestSwap {
public static void main(String[] args){
Container con=new Container();
System.out.println("====before swap====");
System.out.println("con.a:"+con.a);
System.out.println("con.b:"+con.b);
swap(con);
System.out.println("====after swap====");
System.out.println("con.a:"+con.a);
System.out.println("con.b:"+con.b);
}
public static void swap(Container container){
container.a=container.a+container.b;
container.b=container.a-container.b;
container.a=container.a-container.b;
}
}
class Container{
int a=1,b=2;
}
输出结果如下,可以看出,形参的改变导致了实参的改变,所以为按址传递
====before swap====
con.a:1
con.b:2
====after swap====
con.a:2
con.b:1
==================================华丽的分割线====================================
下面来说远程调用时的参数的传递方式:
上面的测试都是用的main函数测试的,也就是说这些是在一个虚拟机里的,如果不在一个虚拟机里,例如EJB远程调用,那么如果我传一个对象会怎样进行传递呢?
在EJB里,形参的类型是要求实现Serializable接口,这样这个参数就可以序列化了(具体序列化成什么在这里不进行说明),那么在进行本地调用时(Local),测试结果会与上面的相同。
如果是远程调用(Remote),不论是不是调用的本地计算机,那么它都会将这些个参数序列化,传到对方的虚拟机里进行运行,这个对象在对面不论进行什么样的操作都不会影响到实参。
那么在如下的系统结构中就会出现问题(EJB的实体部分与Hibernate如出一辙,所以我就写了个Hibernate):
会出现什么问题呢?我在业务逻辑层有一个类Student,我通过远程调用将其保存到了数据库,那么按照ORM中对象状态,此时Student应该是有Id的才对。可是执行完后却意外的发现Student的Id依然为空,再看数据库,已经存进去了。
Why?!
这就是前面说到的,逻辑层通过远程调用将Student传了过去,在封装操作的EJB中,对Student进行了操作,并且对Id进行赋值,但此时它获取到的Student只是逻辑层的Student序列化反序列化的结果。
难道在EJB中我们就不能传引用了么?
个人感觉是的,不过我们可以自己做到和传址一样的结果:我们用传址是为了干嘛,不就是想获取一个那边修改后的对象嘛,我们再把它传回来不就可以了。事实上,这也是在远程调用方面的一个解决方式,下面我简单说明一下,
业务逻辑层{
public void addStudent(Student student){
student=封装Hibernate操作类.add(student);
//对student的id进行应用
}
}
封装Hibernate操作类{
public Student add(Student student){
EntityManager.persist(student);
}
}
注:这样写,只有在远程调用时才有必要,如果非远程调用(在同一个虚拟机内),就别这样写了,多余。
这样,远程调用时的值址问题也就解决了。