您的位置:首页 > 编程语言 > Java开发

Java中的值传递和引用传递

2017-11-29 16:04 267 查看

理解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对象的数据空间,并把该空间的首地址赋给a

String  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);
}
}

//  输出结果
100
99
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类型的值的内容
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: