java 值类型和引用类型

java值类型:

四类八种:

四类:整型,浮点型,字符型,逻辑型

八种:整型  byte  short  int  long

           浮点型  float  double

          字符型:char

          逻辑型:boolean

java引用类型:除值类型之外的类型


引用c#的一段程序说明值传递和引用传递

本文主要是讨论栈和堆的含义,也就是讨论C#的两种类据类型值类型和引用类型;


  以下是c#值类型与引用类型的表:

 

 

   我们来看下面一段代码:

    首先在类中声明一个class类,和一个struct结构,如图:

  并使用在程序入口调用它们,如图

 

 

   

    现在我们来看一看,它们在内存当中是如何存储的?

 

    从这张图可以看出,class(类)实例化出来的对象,指向了内存堆中分配的空间

                         struct(结构) 实例化出来的对象,是在内存栈中分配

 

   接下来,我们再来在上面的程序做如下修改:

  

    红框,代码定义一个class实例化对象s2,然后把对象s1赋值给s2

    蓝框,代码定义一个结构实例化对象r2,然后把对象r1赋值给r2

 

    那它们输出的结果是多少呢?请选择(   )

   

A、  s1的值为:12     s2的值为222
       r1的值为:16     r2的值为666

 

B、  s1的值为:12     s2的值为222
       r1的值为:666    r2的值为666

 

C、  s1的值为:222    s2的值为222
       r1的值为:16     r2的值为666

 

D、  s1的值为:222     s2的值为222
       r1的值为:666     r2的值为666

 

点击查看答案
复制代码

正确答案是:C

为什么会这样呢?所以我们来看一看,多个值类型和引用类型在内存里面是如何存储的,如图:

  从图中,可以看出,两个引用类型 s1,s2都指向了同一个拖管堆上的空间,

           当某一个发生改变的时候,其于的会发生变化

 

       而结构是值类型,虽然使用r2=r1,把r1对象赋值给r2,

     但是它会在线程栈中分配一个独立的空间,

     当修改某一个对象的值的时候,不会影响到另一个对象 


回到java:

 [内存分配]

一个具有值类型(value type)的数据存放在内的一个变量中。即是在栈中分配内存空间,直接存储所包含的值,其值就代表数据本身。值类型的数据具有较快的存取速度。

一个具有引用类型(reference type)的数据并不驻留在栈中,而是存储于中。即是在堆中分配内存空间,不直接存储所包含的值,而是指向所要存储的值,其值代表的是所指向的地址。当访问一个具有引用类型的数据时,需要到栈中检查变量的内容,该变量引用堆中的一个实际数据。引用类型的数据比值类型的数据具有更大的存储规模和较低的访问速度。

 

[java 中的垃圾回收机制]

       当一个堆内存中的对象没有被栈内存中表示地址的值“引用”时,这个对象就被称为垃圾对象,它无法被使用但却占据着内存中的区域,好比这样:

String s = new String(“person”); s = new String(“man”); s本来是指向堆内存中值为person的对象的,但是s突然讨厌person了,它指向了堆内存中的man对象了,person就像一个孤儿一样被s遗弃了,但是person比孤儿还要惨,因为没有什么能找的到它,除了位高权重的‘垃圾回收器’,不过被当官的找到往往没什么好事,尤其是这个‘垃圾回收器’,它会豪不留情把‘垃圾’们清理走,并且无情的销毁,以便释放内存。

[装箱与拆箱]

其实装箱就是值类型到引用类型的转化过程。将一个值类型变量装箱成一个引用类型变量,首先会在托管堆上为新的引用类型变量分配内存空间,然后将值类型变量拷贝到托管堆上新分配的对象内存中,最后返回新分配的对象内存地址。装箱操作是可逆的,所以还有拆箱操作。拆箱操作获取只想对象中包含值类型部分的指针,然后由程序员手动将其对应的值拷贝给值类型变量


来几个例子:

 在下面的例子中。将变量 a 以值传递方式传给方法 Test(),在Test执行a++操作时,实际是对a的副本进行操作,Main方法中打印a的值,结果仍为 a=1 。

复制代码
 1     class Program
 2     {
 3         public static void Main(string[] args)
 4         {
 5             int a = 1;
 6             Test(a);
 7             Console.WriteLine(a);
 8             
 9             Console.Write("Press any key to continue . . . ");
10             Console.ReadKey(true);
11         }
12         
13         //值传递
14         static void Test(int a)
15         {
16             a++;
17         }
18     }


    结果:1

 例子中,将Person对象p和变量 a (通过ref关键字修饰)以引用传递方式传给方法 Test()。在Test对变量进行操作时,是通过传递过来的地址010x,在堆中找到p,并对其进行操作。所以Main函数中再打印结果,已经发生变化。(ref,out 这种关键字在java里没有,看看引用传递就好了。忽略那个a)

复制代码
 1     class Program
 2     {
 3         public static void Main(string[] args)
 4         {
 5             int a = 1;
 6             Person p=new Person{Age=20};
 7             //通过ref关键字,对值类型变量a进行引用传递
 8             Test(ref a,p);
 9             Console.WriteLine(a);
10             Console.WriteLine(p.Age);
11             
12             Console.Write("Press any key to continue . . . ");
13             Console.ReadKey(true);
14         }
15         
16         //引用传递
17         static void Test(ref int a,Person p)
18         {
19             a++;
20             p.Age++;
21         }
22         
23     }
24     
25     class Person
26     {
27         public int Age{get;set;}
28     }

    结果:2
       21
 
   
public class TestMain {

    /**
     * @author lcq
     * @date 2013-4-8
     * @param args
     */
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        operator(a, b);
        StringBuilder x = new StringBuilder("x"); 
        StringBuilder y = new StringBuilder("y");
        operator(x, y);
        System.out.println(a + "," + b);
        System.out.println(x + "," + y);
    }

    public static void operator(String a, String b) {
        a += b;
        b = a;
    }

    public static void operator(StringBuilder a, StringBuilder b) {
        a.append(b);
        b = a;
    }
}
复制代码

 正确的答案是

 

 

a,b
xy,y


Java String对象以“引用”方式被传递


这是Java的一个经典问题。很多类似的问题已经在StackOverflow被问,这里面有很多的不正确/不完整的答案。这个问题很简单,如果你不去想太多。但如果你更深入地思考,它可能是非常令人困惑的。


1、一个段有趣和令人困惑的代码

  1. public static void main(String[] args) {  
  2.     String x = new String("ab");  
  3.     change(x);  
  4.     System.out.println(x);  
  5. }  
  6.    
  7. public static void change(String x) {  
  8.     x = "cd";  
  9. }  
public static void main(String[] args) {
	String x = new String("ab");
	change(x);
	System.out.println(x);
}
 
public static void change(String x) {
	x = "cd";
}

打印结果:ab

在C++中,代码如下

  1. void change(string &x) {  
  2.     x = "cd";  
  3. }  
  4.    
  5. int main(){  
  6.     string x = "ab";  
  7.     change(x);  
  8.     cout << x << endl;  
  9. }  
void change(string &x) {
    x = "cd";
}
 
int main(){
    string x = "ab";
    change(x);
    cout << x << endl;
}

打印结果:cd

 

2、常见的困惑问题


x存储指向堆中的“ab”字符串的引用。所以,当x被作为参数传递到change()方法时,它仍然指向堆中“ab”,像下面这样:


 
因为java是按值传递的(pass-by-value), x的值是“ab”的引用。当方法的change()被调用,它会创建一个新的“cd”的对象,而x现在是指向“cd”像下面这样:


 
这似乎是一个相当合理的解释。他们明确表示,Java是总是传递按值。但是,错在哪里呢?


3、代码真正做了些什么呢?


上面的解释有几个错误。为了容易理解这一点,这是一个好主意,简单地描述整个过程。
当字符串“ab”被创建,java分配存储字符串对象所需的内存空间。然后,将对象分配给变量x,该变量是被实际分配的引用对象。此引用是该对象被存储在内存的地址。
变量x包含一个字符串对象的引用。 x不是引用本身!它是用于存储一个引用(内存地址)的变量。
Java是仅仅是按值传递。当x传递给change()方法,x的(引用)值的副本被传递。方法change()创建另一个对象“cd”,它有一个不同的引用。它是被改变的x副本指向“cd”的变量,而不是x引用本身。
下面的图表展现了真实的过程:


 
4、错误的解释


从第一个代码片段中所提出的问题是没有联系到String对象的不变性。即使字符串被替换StringBuilder的,结果还是一样。关键的一点是,变量存储的是引用,而不是引用本身!

5、这个问题的解决方法


如果我们真的需要改变对象的值。首先,对象应该是可变的,例如,StringBuilder对象。其次,我们需要确保没有创建新的对象并将此对象分配给参数变量,因为Java是只是按值传递(passing-by-value)。

  1. public static void main(String[] args) {  
  2.     StringBuilder x = new StringBuilder("ab");  
  3.     change(x);  
  4.     System.out.println(x);  
  5. }  
  6.    
  7. public static void change(StringBuilder x) {  
  8.     x.delete(02).append("cd");  
  9. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值