java程序值的传递,这段程序的结果是什么,为什么会出现这种结果
首先以两个例子开始:
1)
public class Test2 {
public static void main (String [] args) {
StringBuffer a = new StringBuffer ("A");
StringBuffer b = new StringBuffer ("B");
operate (a,b);
System.out.println(a+","+b);
}
static void operate(StringBuffer x, StringBuffer y){
x.append(y);
y = x;
}
}
输出:AB,B
2)
public class Test2 {
public static void add3 (Integer i){
int val=i.intValue();
val += 3;
i = new Integer (val);
}
public static void main (String args [ ] ) {
Integer i = new Integer (0);
add3 (i);
System.out.println (i.intValue ( ));
}
}
输出:0
首先我们应该明白JAVA中的参数传递全是以值传递的。是基本类型,就拷贝一个基本类型传进方法;是引用,就拷贝一个引用变量传进去方法,理解了这两点就能理解方法操作对象的相关问题了。最好能画出引用指向对象的图出来,就能完全理解了。
第1题,调用operate方法时,传入了两个引用a,b的拷贝x,y,这两个x,y都指向原a,b引用所指向的对象。x.append(y)对它指向的对象(即a指向的对象)进行了操作。而x=y,只是两个拷贝变量在赋值,并没有影响到原b所指向的对象。所以b所指向的对象仍然为B。
第2题,i=new Integer(val)只是一个引用的拷贝指向了另外一个对象,而原来的i仍然是指向对象new Integer(0)的。
把握住了JAVA都是传值并且传的都是拷贝的话,类似的题大家都能迎刃而解了。
Java中的参数传递只有一种方式: by value. 理论说教太麻烦了,直接看些例子吧:
1). 基本类型
public class A{
public static void main(String[] args){
int x = 1;
System.out.println(x); //1
test(x);
System.out.println(x); //还是1==>By value
}
static void test(int a){
a = 2;
}
}
2). 引用类型
public class B{
public static void main(String[] args){
Integer x = new Integer(1);
System.out.println(x);
test(x);
System.out.println(x);
}
static void test(Integer a){
a = new Integer(2);
}
}
理解这里的关键是区分对象和引用。 这里声明的x是一个引用,而不是一个对象(只是Java把它设计为看上去好像是对象一样)。这个引用它指向了一个对象,这个对象就是后面用new关键字生成的对象。因此,可以说x指向了一个Integer对象。
在调用test方法的时候,程序将x作为参数传递给test方法了。这里仍然是值传递,在test调用过程中,会产生一份新的引用(不妨叫做y)。此时,x和y指向了同一个对象。
x和y指向的是同一个对象, 由于Java的设计,我们可以通过操作引用来达到操作对象的目的。因此,如果我们此时使用y来修改对象的属性 (例如,y.someField++); 你可以看到x指向的对象同时也被修改到了。
另一方面,如果我们让y指向另外一个对象, y=new Integer(2); 此时x和y就指向了不同的
对象。y修改了它指向的对象的属性,很显然不会影响到x指向的对象。
有人说了数组。数组也是一个引用类型,它的参数传递方式按照引用类型的参数传递一样可以解释得通:
import java.util.Arrays;
public class A{
public static void main(String[] args){
int[] aa = {3, 2, 1};
System.out.println(Arrays.toString(aa)); //[3, 2, 1]
test(aa);
System.out.println(Arrays.toString(aa)); //[3, 2, 1]
test2(aa);
System.out.println(Arrays.toString(aa)); //[4, 2, 1]
}
static void test(int[] a){
a = new int[]{1, 2, 3}; //指向了新对象
}
static void test2(int[] a){
if(a != null && a.length > 0)
a[0]++; //修改原来的那个对象
}
}
对象是传引用,简单类型是传值,不要被网上的一些概念所迷惑!!!你可以自己做个试验。
至于String等类型传的还是引用。如果你用concat方法,String对象的原值就会被改变。
但你如果按如下方法:
public class Test {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
test(string);
System.out.println(string);
}
}
运行结果:Hello
这里str = "World" 就等同于 String str=new String("World")。所以结果没有改变!!!
下列程序在1处是否会有异常,如果没有,输出是什么?是否会运行到2处,如果会,输出是什么?为什么会有这样的结果?
import java.util.arraylist;
import java.util.list;
public class testclass {
public static void main(string args[]) {
list list = new arraylist();
test2(list);
system.out.println(list.size()); // 1处
test3(list);
system.out.println(list.size()); // 2处
}
public static void test2(list list) {
list = null;
}
public static void test3(list list) {
list.add(“aaaa“);
}
}
plumechen:
不会出错的。结果是0,1。
因为test2(list)传得是list的引用,我理解成指针置的副本,list=null;只是把那个传入的值设置为null,不改变原来list的指针和内容。test3(list)传入的一样,但是执行了list.add()由于传入指针值的副本也指向原来的那个list的地址,所以原来的那个list的内容就改变了,size变成了1了
首先楼主写出这段代码也不会是偶然,是在测试什么,如果大家有兴趣,我们就一块来捋一捋,把这个问题弄得清晰一点,不过在下水平严重有限,有误还望指点。
首先 : 30 行 super();可有可无,大家有兴趣可以把super()注释掉结果是一样的。
因为 :JVM在加载类的时候如果发现他有父类,就会去装载父类。在本例子中会去装载Base类,装载最后会调用Base的构造函数去初始化Base,父类初始化完了之后再去初始化子类,问题就在这里,也就是子类早就装载了,但是没有初始化,这个时候子类的i变量还是0,在父类装载初始化完成之后再去初始化之类,然后再调用子类的构造函数去创建子类的实例子。
上面的东西大家应该都了解,这个问题有意思的地方不仅是类的加载和初始化,还把多态也扯了进来,那就是this的使用。下面我们再来说第二个有意识的地方,然后综和到一块就明白了。
按照前面的分析,不考虑多态我们有这样的结论,JVM装载Derived发现他有父类,然后去装载Base,Base装载完成后 i = 2 然后执行Base的够着函数发现this,不考虑多态那么this就代表当前环境也就是Base的环境,this.display()就是调用当前环境的display()方法,结果会打印2,但2是错的正确的应该是0,下面该多态出场了,我们的捣蛋鬼,多态可以做很多事情,但正如java编程思想里面说的那样,可以用组合就少用多态。
到现在事情就不好办了,我要是把多态说一遍恐怕篇幅有限,我的时间也有限,所以我就把多态造成的影响说一下。
当多态掺和进来后这个Base的this就产生了不确定性,其实它还是确定,分下面几种情况。
1 创建Base类;new Base(),这种条件下this.display(),调用Base的display方法this.i,调用Base的成员i。
2 创建Base类的子类Derived;new Derived(),这种条件下this.display()调用的是Derived的display方法,很有意思吧明明是写在父类的this,this.i 调用的是父类的成员i这一点也很有意思。这就是调皮多态带来的影响。
有了上面的分析问题到这里你们都应该明白了。我们再来串一下,理解的可以跳过哦。
首先 new Derived()会去装载Derived类当Derived装载后发现Derived有父类,这个时候Derived的所有成员都没有初始化(其实只是执行了默认初始化),Derived的i=0;(对了补充一下私有成员不参与继承,也就是Base有一个i,Derived有一个i这两个i是完全独立),然后JVM去装载Base,装载完成后执行Base的无参构造函数,即为执行this.display()去调用Derived的display方法,现在Derived虽然没有初始化,也没有生成任何对象,但是已经装载了所以方法直接可以调用,这也是反射的基本原理,Derived的display会打印Derived的i很明显这个时候i只被默认初始化,i=0;所以就会打印一个零,现在我们就得到正确的结果了。
谈不上请教不请教,大家有兴趣共同探讨探讨,水平有限下面可能会有一些不当的地方,但我还是按照我的理解再把这个问题从头开始再串一遍,看看有没有什么有意思的地方,或者遗漏的地方。其实问题的本质很简单,但本质所演变出的现象可以千奇百怪,上面我们捋清楚了多态和类的加载机制。多态其实还是表象,本质应该是继承的机制,继承决定了那些东西可以从父类那里拿来,那些不能从父类那里拿来。
父类包含,变量和方法,你这个问题主要是在测试变量,我们就来看一下变量继承的情况。其实变量的继承很简单,无非就是两种情况private不能继承 非private可以继承。非private 包括protected 和 public 如果没有写修饰符号默认是protected,在你这个程序里protected和public是等价的,这一点大家都知道,我也不需要说的太多。那么下面就可以把变量继承的情况分一下类。
private 。类会隐藏自己的private变量,让它在本类以外的地方都不可见。上次问题:都是private 所以Base有一个i Derived 有一个 i互相是独立,Derived始终只能访问自己的i变量,其实不可见不等于不能访问,因为变量始终是在内存里的,只要拿到地址就可以访问,这也是为什么用反射只要知道变量名就可以访问不管变量是私有还是非私有的。
非private 。变量参与继承,对于基本类型和非基本类型有细微的差别,通俗理解就是子类把父类的非private都复制了一份保存在知己的环境中。
下面来看一下你的问题,把第一个图片称为图上因为第一张在上面,把第二张图片称为图下。
图上:Base 有 int a ,Derived 就也会有一个 int a ,Derived 还有 private int a ,现在的情况是Derived,有两个a 一个是继承Base得到的,另一个是自己申明的,很明显两个a是完全不同的东西要区别对待,当编译器遇到这种情况就会用this.a,和super.a,来区分这两个a,如果直接写a等价于this.a,所以Derived中的System.out.println(a);等价于System.out.println(this.a);那么问题就很清晰了,Derived一直在打印自己的私有a,跟Base的a没有任何关系,就像上个问题次打印知己的私有i一样,当然会打印出零了。字数超出,再续
广告 您可能关注的内容 |