java中形参不可以是对象吗_(重要)java都是值传递,与对象形参所指向的对象改变,其实参所指向的对象也相应改变并不矛盾(2011年9.30日一天写的两个程序的总结结果)...
1.0建立二叉樹的代碼,在java中必須創(chuàng)建二叉樹的方法必須用返回值,因?yàn)椴淮嬖赾語(yǔ)言中的引用傳遞,在java中只有值傳遞 代碼1為正確,代碼2(沒(méi)有使用返回值)為錯(cuò)誤。那么為什么之前的例子中將對(duì)象作為參數(shù)時(shí),對(duì)對(duì)象內(nèi)容的更改還是正確的,也沒(méi)有使用返回值,二者看似矛盾,其實(shí)并不矛盾,以前都沒(méi)有理解到這個(gè)本質(zhì),本質(zhì)就是參數(shù)中,確實(shí)是有一個(gè)臨時(shí)變量,交換形式對(duì)象參數(shù),實(shí)際的參數(shù)不會(huì)改變,但是改變形式參數(shù)的引用所指向的內(nèi)容,即這個(gè)對(duì)象本身改變,其實(shí)參所引用的對(duì)象也是同一個(gè)對(duì)象,當(dāng)然該對(duì)象也會(huì)發(fā)生變化,因?yàn)閷?shí)參和形參引用的都是同一個(gè)對(duì)象,只不過(guò)是兩份地址的拷貝。
即便是c語(yǔ)言如果不用引用傳遞-&,就是說(shuō)用指針,也需要用返回值的方法建立二叉樹,才能將已建立好的二叉樹頭指針?lè)祷亟o打印函數(shù)的參數(shù)。當(dāng)然java中可以采用對(duì)私有變量等操作,即不不使用參數(shù)傳遞,而僅僅是創(chuàng)建函數(shù)和打印函數(shù)共同操作的是同一個(gè)變量也可以吧,估計(jì)遞歸就不好用了,這個(gè)怎么做沒(méi)去想!~~~
錯(cuò)誤代碼:這個(gè)時(shí)候打印出來(lái)的樹的節(jié)點(diǎn)為空,printInOrder(tree1)輸出節(jié)點(diǎn)為空:
View Code
import java.util.*;
class TNode{
int data;
TNode lchild;
TNode rchild;
TNode(int a)
{
data=a;
lchild=null;
rchild=null;
}
}
public class BTree {
static TNode root=new TNode(1);
static void creatTree(TNode root)
{
Scanner in=new Scanner(System.in);
int a=in.nextInt();
if(a==0){
root=null;
return;
}
else{
root=new TNode(a);
creatTree(root.lchild);
creatTree(root.rchild);
}
}
static void printInOrder(TNode root){
if(root==null)
return;
else{
System.out.print(root.data);
printInOrder(root.lchild);
printInOrder(root.rchild);
}
}
public static void main(String[] args) {
//TODO Auto-generated method stub
creatTree(root);
printInOrder(root);
}
}
正確代碼:
View Code
import java.util.Scanner;
class Tode{
int data;
Tode lchild;
Tode rchild;
Tode(int a)
{
data=a;
lchild=null;
rchild=null;
}
}
public class TREE{
static Tode root=null;
static Tode creatTree(Tode root)
{
Scanner in=new Scanner(System.in);
int a=in.nextInt();
if(a==0){
return null ;
}
else{
root=new Tode(a);
root.lchild=creatTree(root.lchild);
root.rchild=creatTree(root.rchild);
return root;
}
}
static void printInOrder(Tode root){
if(root==null)
return;
else{
System.out.print(root.data);
printInOrder(root.lchild);
printInOrder(root.rchild);
}
}
public static void main(String[] args) {
//TODO Auto-generated method stub
Tode tree1=creatTree(root);
printInOrder(tree1);
}
}
1.1、疑惑解決了,原來(lái)認(rèn)為方法中傳入一個(gè)對(duì)象的話(數(shù)組也行),如果方法內(nèi)部對(duì)這個(gè)對(duì)象的內(nèi)部參數(shù)進(jìn)行了更改,則其實(shí)就是對(duì)外部的這個(gè)靜態(tài)對(duì)象也進(jìn)行了修改,相當(dāng)于c++中的真正的引用傳遞吧,但是傳入的是整型等普通類型,則還是不能夠更改,可以見(jiàn)下面的實(shí)例。如果傳入的對(duì)象是數(shù)組的話,也可以更改,因?yàn)閖ava中數(shù)組也是對(duì)象~,其實(shí)也不是 因?yàn)槭菍?duì)引用所指向的內(nèi)容進(jìn)行了修改,引用可以有多個(gè),但是引用所指向的內(nèi)容只有一份!!!!!!
以下程序的輸出結(jié)果是012
View Code
import java.util.*;
class node{
int data;
node lchild;
node rchild;
public node(int a){
data=a;
lchild=null;
rchild=null;
}
}
public class testclass2 {
public void changeroot(node root){
node newnode1=new node(1);
node newnode2=new node(2);
root.lchild=newnode1;
root.rchild=newnode2;
}
public void print(node root){
System.out.println(root.data);
System.out.print(root.lchild.data);
System.out.print(root.rchild.data);
}
public static void main(String[] args){
node root=new node(0);
testclass2 dd=new testclass2();
dd.changeroot(root);
dd.print(root);
}
}
1.2、下面是另一個(gè)例子,在算法例子中也常用到因?yàn)槭?對(duì)引用所指向的內(nèi)容進(jìn)行了修改,引用可以有多個(gè),但是引用所指向的內(nèi)容只有一份!!!!!!
以下程序輸出結(jié)果是2 4 3 4
View Code
void diguiInorderTraverse(node root){
root.lchild
}
解釋說(shuō)的通了,傳入root,對(duì)root修改,其實(shí)就相當(dāng)于
class A{
int b=2;
}
public class Testclass {
static A a=new A();
static void changeA(A a){
a.b=3;
}
static int b=4;
static void changeB(int b){
b=5;
}
public static void main(String[] args) {
//TODO Auto-generated method stub System.out.println(a.b);
System.out.println(b);
changeA(a);
changeB(b);
System.out.println(a.b);
System.out.println(b);
}
}
1.3、也就是說(shuō)root對(duì)象確實(shí)分配了一個(gè)臨時(shí)對(duì)象,傳入的都是地址的引用,所以對(duì)這個(gè)參數(shù)的改變,對(duì)引用所指向的內(nèi)容進(jìn)行了修改,引用可以有多個(gè),但是引用所指向的內(nèi)容只有一份!!!!!!
我看了一些引用調(diào)用和值調(diào)用的定義,很多人都把是傳遞值,還是傳遞地址?是改變參數(shù)自身內(nèi)容,還是改變參數(shù)所指向的地址中的內(nèi)容作為區(qū)別這兩種調(diào)用的標(biāo)志。這很不恰當(dāng),這些說(shuō)法很容易讓我們聯(lián)想到Java的對(duì)象參數(shù)傳遞是引用調(diào)用,實(shí)際上,Java的對(duì)象傳遞仍然是值調(diào)用。
引用調(diào)用:在參數(shù)傳遞的過(guò)程中,形參并不是實(shí)參的副本,而是實(shí)參本身。這種調(diào)用實(shí)參和形參在內(nèi)存中實(shí)際上都是同樣的一個(gè)區(qū)域,只是這個(gè)區(qū)域的表示(參數(shù)名)不同而已。
值調(diào)用:在參數(shù)傳遞過(guò)程中,創(chuàng)建了一個(gè)實(shí)參的副本——形參。形參和實(shí)參在內(nèi)存中是兩個(gè)完全不同的區(qū)域。因此形參內(nèi)容的改變并不能影響到實(shí)參。
方法調(diào)用(call by)是一個(gè)標(biāo)準(zhǔn)的計(jì)算機(jī)科學(xué)術(shù)語(yǔ)。方法調(diào)用根據(jù)參數(shù)傳遞的情況又分為值調(diào)用(call by reference)和引用調(diào)用(call by value)。江湖上有很多關(guān)于這兩種調(diào)用的定義,最通常的說(shuō)法是傳遞值的是值調(diào)用,傳遞地址的是引用調(diào)用。這其實(shí)很不恰當(dāng),這種這些說(shuō)法很容易讓我們聯(lián)想到Java的對(duì)象參數(shù)傳遞是引用調(diào)用,實(shí)際上,Java的對(duì)象參數(shù)傳遞仍然是值調(diào)用。
以下摘自該段落:
Java代碼
1.publicclassEmployee?{
2.
3.publicString?name=null;
4.
5.publicEmployee(String?n){
6.this.name=n;
7.}
8.//將兩個(gè)Employee對(duì)象交換
9.publicstaticvoidswap(Employee?e1,Employee?e2){
10.Employee?temp=e1;
11.e1=e2;
12.e2=temp;
13.System.out.println(e1.name+"?"+e2.name);//打印結(jié)果:李四張三
14.}
15.//主函數(shù)
16.publicstaticvoidmain(String[]?args)?{
17.Employee?worker=newEmployee("張三");
18.Employee?manager=newEmployee("李四");
19.swap(worker,manager);
20.System.out.println(worker.name+"?"+manager.name);//打印結(jié)果仍然是:張三李四
21.}
22.}
上面的結(jié)果讓人很失望,雖然形參對(duì)象e1,e2的內(nèi)容交換了,但實(shí)參對(duì)象worker,manager并沒(méi)有互換內(nèi)容。這里面最重要的原因就在于形參e1,e2是實(shí)參worker,manager的地址拷貝。
大家都知道,在Java中對(duì)象變量名實(shí)際上代表的是對(duì)象在堆中的地址(專業(yè)術(shù)語(yǔ)叫做對(duì)象引用)。在Java方法調(diào)用的時(shí)候,參數(shù)傳遞的是對(duì)象的引用。重要的是,形參和實(shí)參所占的內(nèi)存地址并不一樣,形參中的內(nèi)容只是實(shí)參中存儲(chǔ)的對(duì)象引用的一份拷貝。
如果大家對(duì)JVM內(nèi)存管理中Java棧的局部變量區(qū)有所了解的話(可以參見(jiàn)《》),就很好理解上面這句話。在JVM運(yùn)行上面的程序時(shí),運(yùn)行main方法和swap方法,會(huì)在Java棧中先后push兩個(gè)叫做棧幀的內(nèi)存空間。main棧幀中有一塊叫局部變量區(qū)的內(nèi)存用來(lái)存儲(chǔ)實(shí)參對(duì)象worker和manager的引用。而swap棧幀中的局部變量區(qū)則存儲(chǔ)了形參對(duì)象e1和e2的引用。雖然e1和e2的引用值分別與worker和manager相同,但是它們占用了不同的內(nèi)存空間。當(dāng)e1和e2的引用發(fā)生交換時(shí),下面的圖很清晰的看出完全不會(huì)影響worker和manager的引用值。
Java對(duì)象參數(shù)傳遞雖然傳遞的是地址(引用),但仍然是值調(diào)用。是時(shí)候需要給引用調(diào)用和值調(diào)用一個(gè)準(zhǔn)確的定義了。
值調(diào)用(call by value):在參數(shù)傳遞過(guò)程中,形參和實(shí)參占用了兩個(gè)完全不同的內(nèi)存空間。形參所存儲(chǔ)的內(nèi)容是實(shí)參存儲(chǔ)內(nèi)容的一份拷貝。實(shí)際上,Java對(duì)象的傳遞就符合這個(gè)定義,只不過(guò)形參和實(shí)參所儲(chǔ)存的內(nèi)容并不是常規(guī)意義上的變量值,而是變量的地址。咳,回過(guò)頭想想:變量的地址不也是一種值嗎!
引用調(diào)用(call by reference):在參數(shù)傳遞的過(guò)程中,形參和實(shí)參完全是同一塊內(nèi)存空間,兩者不分彼此。實(shí)際上,形參名和實(shí)參名只是編程中的不同符號(hào),在程序運(yùn)行過(guò)程中,內(nèi)存中存儲(chǔ)的空間才是最重要的。不同的變量名并不能說(shuō)明占用的內(nèi)存存儲(chǔ)空間不同。
大體上說(shuō),兩種調(diào)用的根本并不在于傳遞的是值還是地址(畢竟地址也是一個(gè)值),而是在于形參和實(shí)參是否占用同一塊內(nèi)存空間。事實(shí)上,C/C++的指針參數(shù)傳遞也是值調(diào)用,不信試試下面的C代碼吧!
C代碼
1.#include
2.voidswap(int*a1,int*b1){
3.int*t=a1;
4.a1=b1;
5.b1=t;
6.}
7.intmain(){
8.intx1=100;
9.intx2=200;
10.int*a=&x1;
11.int*b=&x2;
12.printf("%d?%d\n",*a,*b);
13.swap(a,b);
14.printf("%d?%d\n",*a,*b);
15.return0;
16.}
但C/C++是有引用調(diào)用的,這就是C/C++一種叫做引用的變量聲明方法:int a; int &ra=a;其中ra是a的別名,兩者在內(nèi)存中沒(méi)有區(qū)別,占用了同一個(gè)內(nèi)存空間。而通過(guò)引用(別名)的參數(shù)傳遞就符合引用調(diào)用的特點(diǎn)了。大家可以去試試
void swap(int &a1,int &b1);的運(yùn)行結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的java中形参不可以是对象吗_(重要)java都是值传递,与对象形参所指向的对象改变,其实参所指向的对象也相应改变并不矛盾(2011年9.30日一天写的两个程序的总结结果)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java面向对象编程集合边框_java
- 下一篇: canopy算法 java_mahout