Java中的值传递和引用传递

来源:互联网 发布:手机淘宝 平板 编辑:程序博客网 时间:2024/05/22 14:30

理解Java中的值传递和引用传递,形参和实参

1. 两大数据类型以及特殊的String

  • 8种基本数据类型
    int char byte boolean long short float double
  • 对象类型
    1、8种基本数据类型的包装类型
    2、数组等
    3、定义的对象
  • 特殊: String

2. 两大引用类型

  • 基本类型
  • 对象的引用
    这两者是有区别的
基本类型 对象的引用 声明定义的时候就为实际的数据分配了空间 声明时只是分配了一个引用的空间,一般是4个字节,实际对象的空间未分配

特殊的: String 类型只是定义的话,也只是分配了4个字节的引用空间

int a; // 已经为存放a分配了内存a=10;//正确,因为声明a时就分配了空间Date a; //在内存开辟一个引用空间, 即实际才使用了4个字节a = new Date();//开辟存储Date对象的数据空间,并把该空间的首地址赋给aString  a; //在内存开辟一个引用空间

3. 值传递和引用传递

  • 1.形参:
    形式参数,用来接收调用该方法时传递的参数, 相当于一个实际参数的副本,只有在被调用的时候才分配内存空间,一旦调用结束,就释放内存空间。因此仅仅在方法内有效
  • 2.实参:
    传递给被调用方法的值,预先创建并赋予确定值。

值传递

方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是 原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是 对形参这个值的修改,不影响实际参数的值。

引用传递

也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;
在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象

具体例子说明一下:

public class A {    public static void main(String[] args) {         A t = new A();         int a = 99;         t.test1(a);//这里传递的参数a就是按值传递         System.out.println(a);        MyObj obj=new MyObj();         obj.b = 10;        t.test2(obj);//这里传递的参数obj就是引用传递        System.out.println(obj.b);    }     public void test1(int a){         a = a + 1;        System.out.println(a);        }     public void test2(MyObj obj){         obj.b = 100;        System.out.println(obj.b);        }}//  输出结果10099 100 100

特殊的:对于String 类型, 以及8中基本类型的包装类型等immutable不可变的Java类型,其传递方式为值传递

4. 总结

  • 1.java的基本数据类型是值传递,对象引用类型是引用传递,但是特殊的:String, Integer, Double等immutable的不可变类型可以理解为值传递。
  • 2.当传值调用时,改变的是形参的值,并没有改变实参的值,实参的值可以传递给形参,但是,这个传递是单向的,形参不能传递回实参。
  • 3.当引用调用时,传递的是地址,指向的是同一个对象,方法中的操作会改变实参对象的内容。

本文参考了博客: http://www.cnblogs.com/binyue/p/3862276.html
https://www.cnblogs.com/jaylon/p/5721571.html

这里面牵扯到了 不可变类,简单介绍一下:

不可变类
所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类(String+ 8种基本数据类型):Interger、Long 和 String等。

String类型如何设计成不可变类
- 1、类使用final修饰符,设计成不可被继承的
- 2、成员变量,使用final修饰符,设计成不可以被修改的
- 3、成员变量设计成私有的private,而且不设置setter方法
- 4、通过构造器初始化所有成员,进行深拷贝(deep copy) 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝, String(char value[])
- 5、获取,在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝,toCharArray()方法

public final class String    implements java.io.Serializable, Comparable<String>, CharSequence {    /** The value is used for character storage. */    private final char value[]; //存储字符串    /** Cache the hash code for the string */    private int hash; // Default to 0 字符串的hash值    // 构造函数,使用深克隆    public String(char value[]) {      this.value = Arrays.copyOf(value, value.length);    }    // 获取时,不直接返回对象的引用,而是返回复制后的副本,防止外界进行修改    public char[] toCharArray() {       // Cannot use Arrays.copyOf because of class initialization order issues       char result[] = new char[value.length];       System.arraycopy(value, 0, result, 0, value.length);       return result;    }  }

String 设计成不可变类的好处
- 1、设计成不可变的,方便 线程安全,可以被多个线程共享
- 2、字符串常量池 可以将一些字符常量放在常量池中重复使用,避免每次都重新创建相同的对象、节省存储空间。但如果字符串是可变的,此时相同内容的String还指向常量池的同一个内存空间,当某个变量改变了该内存的值时,其他遍历的值也会发生改变。所以不符合常量池设计的初衷
- 3、类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
- 4、支持hash映射和缓存
因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串

String 的intern方法的作用:
如果常量池中存在字符,则返回常量池中字符的引用,
如果常量池中不存在字符,则将字符串添加到常量池中(在新的jdk1.7以上,在常量池中创建的字符串存储的是堆中字符串的引用),返回该对象的引用。
更加详细的请查看这篇博客
http://blog.csdn.net/seu_calvin/article/details/52291082
特殊的是,可以通过反射机制改变这一不可变的特性,通过反射,我们可以改变String类型的值的内容

原创粉丝点击