java对象以及传值

用引用操作对象

每种编程语言都有自己操作内存中的元素的方式。有时候,程序员必须注意要将要处理的数据是什么类型。你是直接操作元素,还是用某种特殊语法的间接表示(例如C和C++里的指针)来操纵对象?

所有的这一切在Java里都得到了简化。一切都被视为对象,因此可采用单一固定的语法。尽管一切都做对象,但操纵的标示符实际上是对象的一个“引用”(reference)。可以将这一情形想象成用遥控器(引用)来操纵电视(对象)。只要握住这个遥控器,就能保持与电视的连接。当有人想改变频道或者减小音量时,实际操控的是遥控器(引用),再由遥控器来调控电视机(对象)。如果想在房间里四处走走,同时仍能调控电视机,那么只需携带遥控器(引用)而非电视机(对象)。

此外,即使没有电视机,遥控器亦可独立存在。也就是说,你拥有一个引用,并不一定需要有一个对象与它关联。因此想操纵一个词或句子,则可以创建一个String引用:

String s;

但这里所创建的只是引用,并不是对象。如果此时向s发送一个消息,就会返回一个运行时的错误。这事因为此时s实际上没有与任何事物相关联(即,没有电视机)。因此,一种安全的做法是,创建一个引用的同时便进行初始化。

String s = “abc”;

但这里用到了Java语言的一个特性:字符串可以用带引号的文本初始化。通常,必须对对象采用一种更通用的初始化方法。


存储到了什么地方

1,寄存器。这是最快的存储区,因为它位于不同于其他存储区的地方------处理器内部。但是寄存器的数量极其有限,所以寄存器根据需求进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象(另一方面,C和C++允许您向编译器建议寄存器的分配方式)。

2,堆栈。位于通用RAM(随即访问存储器)中,但通过堆栈指针可以从处理器那么获得直接支持。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。这事一种开苏邮箱的分配存储方法,仅次于寄存器。创建程序时,Java系统必须知道存储在堆栈内所有项的确切的生命周期,银边上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些Java数据存储于堆栈中-------------特别是对象引用,但是Java对象并不存储于其中。

3,堆。一种通用的内存池(也位于RAM区),用于存放所有的Java对象。堆不同于堆栈的好处是:编译器不需要知道存储的数据再堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当需要一个对象时,只需用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代价:用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多的时间(如果确实可以再Java中像在C++中一样在栈中创建对象)

4,常量存储。常量值通常直接存储放在程序代码内部,这样做是安全的,因为他们永远不需要被改变。有时,在嵌入式系统中,常量本身回合其他部分隔离开,所以在这种情况下,可以选择将其放在ROM(只读存储器中)

5,非RAM存储。如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。其中两个今本的例子是流对象和持久化对象。在流对象中,对象转化成字节流,通常被发送给另一台机器。在“持久化对象中”,对象呗存储于磁盘上,因此,即使程序终止,他们仍可以标尺自己的状态。这种存储方式的技巧在于,把对象转化成可以存放在其他媒介上的事物,在需要时,可以恢复成常规的,基于RAM的对象。


特例,基本类型。一个的对象被new出来讲对象存储在“堆”中,而基本类型,Java采取和C和C++相同的方法,也就是说,不用new来创建变量,而是创建一个并非是引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,因此更加高效。


下面针对Java的传值还是传址说一下,

引用类型传的是地址的副本,值类型传的存储单元内容的副本。简单点来说,基本类型+String传的是“值”的拷贝,其他对象传递的是引用的拷贝。

几个简单的例子:

package com.yecg.java.passvalue;
public class PassReferenceOrValue {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		int a =1;
        passValue(a);
        System.out.println(a);
	}
    
	public  static void passValue(int a){
        a = 5;
    }
 
}

输出为1。那么这里就证明了java实际上是传值的,它在调用函数的时候传入的参数实际上是原数的拷贝,对拷贝进行了修改是不影响原值的


package com.yecg.java.passvalue;
public class PassReferenceOrValue {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
        //paramenter is object 
	Value value = new Value();
        value.setParam("int");
        passValue(value);
        System.out.println(value.getParam());
	}
    
    
	 public static void passValue(Value v){
         v.setParam("changed");
   }
}
class Value{
   String param;
   public String getParam() {
         return param;
   }
   public void setParam(String param) {
         this.param = param;
   }
}

输出为“changed”。这里的确是传的值,只是这个值是value对象地址的拷贝.所以现在我对它做改变原先的对象是会改变的.


public static void passValue(Value v){
         Value value = new Value();
         value.setParam("changed");
        
         v = value;
   }

输出为“int”。所以你现在应该明白传的是对象地址的拷贝这句话的含义了,因为它如果不是拷贝而就是传的地址的话,那么我新new了一个value对象,并对参数进行赋值,(v=value) 那么这个时候两个对象的地址就该一样,我对新的value进行修改,也会对原来的value对象产生影响.但是现在并没有.
所以在java中最好不要对参数进行赋值,无论是对象还是基本类型,因为这时没有意义的,只会把程序搞的更加的不可读


Java 中,改变参数的值有两种情况,第一种,使用赋值号“=”直接进行赋值使其改变;第二种,对于某些对象的引用,通过一定途径对其成员数据进行改变。对于第一种情况,其改变不会影响到方法该方法以外的数据,或者直接说源数据。而第二种方法,则相反,会影响到源数据——因为引用指示的对象没有变,对 其成员数据进行改变则实质上是改变的该对象。



这里涉及到的基本功常识为: 
1. 每个Thread有自己的 运行栈。Stack 
2. 每次函数调用,运行栈顶部上都会为 该函数调用 分配一个Stack Frame.  这也称为Context. 
3. 函数调用结束,栈指针回到上一级函数调用的Context, Stack Frame, 刚才分配的顶部Stack Frame 就失效。上一级函数成为当前Stack Frame, Context。 
4. 每次函数调用, 参数都会 压栈 压入运行栈。注意,这是非常重要的。 
5. 压入运行栈内的 参数,是 Copy 了一份。注意,这也是非常重要的。所以,对参数 赋值,对上一级函数调用来说,是根本没有意义的。 
因为只是改变了顶部Stack Frame里面 参数Copy的内容,根本不对上一级 Stack Frame 造成任何影响。 



参考:http://kakashi.iteye.com/blog/416662

http://dreamhead.blogbus.com/logs/2005/05/1189478.html


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值