1. 三大传递概念
JAVA 中三大传递(其实JAVA中只有值传递(传值),每种传递的实质都是“值”的传递,这三种划分是根据程序中“值”的不同表现形式进行划分的):
- 值传递:传递的是基本类型的值。
- 引用传递:传递的是对象的引用地址副本。
- 匿名对象传递:传递的是匿名对象,匿名对象本身返回的就是当前对象的内存地址副本。
每种传递都有两种方式。手工传递和方法间参数传递: - 手工传递必须使用赋值运算符“=”;
- 参数传递是方法间调用,调用者必须将被调用者当中的形参进行实参化。
2. 区分传递、覆盖、可变、重置四个概念
2.1 传递
tips:此处不论是值传递、引用传递还是匿名对象传递
- 父亲的钥匙可以开同一个门(同一块堆内存空间),同时儿子的钥匙也可以开同一个门,这是匿名对象传递(匿名对象传递往往用于向上转型),即向上转型;
- 儿子1的钥匙可以开同一个门,儿子2和儿子3的钥匙也可以开同一个门,这就是引用传递(本身也是值传递),即同一个堆内存空间可以同时被多个引用变量所指向。
- 什么叫传递:将一个变量的内容传递给另外一个变量。传递是两个变量之间的事,数据来源一方是源变量,数据接收一方是目标变量。传递的方式一种是直接赋值,一种是方法调用。不论哪种方式,传递的永远是源变量内容的副本。
如下:
2.2 重置
int a = 100;
// 这种情况就叫做重置
a = 10000;
重置:将一个变量的旧内容修改为新内容。重置是一个变量的事,重置操作意味着一个变量的内容是否可变。只有final修饰的常量其内容是不能被重置的,其他的任何变量的内容都可以被重置。
如下:
总结重置:
- 引用先指向一个实体,然后重新指向了另外一个实体,之前指向的连接将断开,这就叫做重置。
- 当第一次执行了赋值操作,这叫做新增,也叫做初始化;当之后为同名变量重新赋值,这就叫做重置,重置也叫做更新。
2.3 可变
- 可变:指的是一个变量的内容是否可以被改变,同上面的重置。
- 对于可变的扩展:
引用和值:基本类型变量其内容存储的是值,引用类型变量其内容存储的是引用。由此可见,所有的传递传递的都是变量内容的副本,基本类型传递的是值的副本,引用类型传递的是引用的副本。换个思路理解:即只有值传递,没有引用传递,引用不过是特殊的值而已。
注意,在传递中,只要传递的是变量的副本,则这个副本的内容都是可变的。 - 结合传递看可变性:
约定俗称的可变与不可变:传递的永远是内容的副本,则目标副本的改变是不会影响源变量的内容的。
不论是值传递还是引用传递,都是副本,则目标变量的内容改变不会影响源变量的内容。
但是:对于引用变量,存在引用指针和堆内存对象实体之间的引用关系,有了这层面向对象的引用关系后,才有了约定俗称的可变性和不可变性。我们常说的可变与不可变,指的是面向对象中,引用指针指向的堆内存对象实体中的内容是否可变,而不是引用变量本身的引用地址是否可变。
原则上,不论是原始的可变性还是约定俗成的可变性,都指的是某个变量的内容是否可以被重置。任何变量其本身的内容都是可变的,任何变量在传递后其本身的值是不会受到目标变量副本的变化影响的(即传递的是源变量的副本,副本内容的改变不会重置源变量的内容)。不过在面向对象中,因为源变量传递的是引用地址的副本,只要引用地址没变化,则目标变量使用同一个引用副本修改的堆内存对象实体中的对象的改变是会同步到源变量上的,因为源变量和目标变量引用的是同一个堆内存对象实体。
2.4 覆盖
- 覆盖是面向对象语言中,关于父子类继承之间的一种机制,目的是减少代码冗余,提高代码复用率。
- java中父子类之间,子类可以继承父类的成员变量和成员方法,还可以自己扩展自有的成员变量和成员方法,还可以覆盖继承而来的成员变量和成员方法。默认分情况,私有静态构造不继承
3. 引用传递概念
引用传递是整个java的精髓。
引用传递:同一块堆内存空间(某个类的一个对象实体)可以同时被该类的多个引用变量所指向,并且可以操作(调用和修改)其中的属性内容和方法。
java中引用传递图解(原理适用于任何面向对象的语言):
- 步骤一是定义了一个类,保存在JVM的方法区内存中;
- 步骤二是实例化对象,对象实体保存在JVM的堆内存中,并且传递(手工传递=)一个引用变量per1。所以此时per1这个变量保存的是对象的地址,即指向该对象的堆内存地址;
- 步骤三是引用传递,将per1指向的堆内存地址传递(手工传递=)传递给另外一个引用变量per2,此时per2也指向per1这个变量指向的堆内存地址;per3同理,也指向per2这个变量指向的堆内存地址。
总结:引用传递,就是同一块堆内存空间同时被多个引用变量所指向。
注意初始化变量和实例化对象的关系就是引用变量和堆内存空间的关系!
4. 三大传递
- 值传递(方式:手工传递,方法间参数传递)
值传递针对于基本数据类型的变量(成员变量和方法变量)而言的。其中手工传递就是利用“=”直接赋值;参数传递是间接赋值。
深入理解:赋值操作本身就是一种值传递的方式。 - 引用传递(方式:手工传递,方法间参数传递)
引用传递针对于引用型变量(成员变量和方法变量)而言的。其中手工传递就是利用“=”直接进行两个引用变量的赋值操作;参数传递是引用变量的间接赋值。
引用传递的核心:同一块堆内存空间可以同时被多个引用型变量所指向。 - 匿名对象传递(手工传递,方法间参数传递)
匿名对象传递是针对匿名对象而言的。其中手工传递就是利用”=“直接进行对象的实例化操作,或者子类对象向上转型操作;参数传递是对象的间接实例化操作,或者子类对象间接向上转型操作。
向上转型的核心:父类的引用对象既可以指向父类的堆内存空间,也可以指向子类的堆内存空间。(其实匿名对象传递也是引用传递的一种,因为匿名对象本身就是一个引用类型的变量)。即父亲和儿子的钥匙可以开同一个门。
5. 为什么说Java中本质只有值传递?
public class Demo06BuKeBian {
@Test
public void testJiBenLeiXingBuKeBian() {
int a = 100;// 将a传递给另外一个变量
int b = a;// b接收了a,b存放的是a的副本
b = 198;// 重置b,不会改变a
System.out.println("a = " + a);
System.out.println("b = " + b);
// 结论:传递中,基本类型的变量是不可变的。
}
@Test
public void testString() {
String sourceStr = "源字符串";
String targetStr = sourceStr;
targetStr = "目标字符串";
System.out.println("sourceStr =" + sourceStr);
System.out.println("targetStr = " + targetStr);
// 结论:传递中,对于8大包装类和String而言,其内容是不可变的
}
@Test
public void testYinYong() {
Person06 per = new Person06();
per.setName("源Person");
per.setAge(22);
Person06 per2 = per;// 典型的引用传递
per2.setName("目标Person");
per2.setAge(18);
System.out.println("源Person=" + per.getName() + ";" + per.getAge());
System.out.println("目标Person=" + per2.getName() + ";" + per2.getAge());
// 结论:传递中,对于普通引用类型,其内容是可以改变的
}
}
class Person06 {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
文档信息
- 本文作者:Marshall