最近在回归一些之前学的java知识,慢慢也准备开始沉淀下来了。
但是我们在编程过程中,肯定遇到不少类似的问题,请看一下代码:
public class ReferenceTypeDemo { public static void main(String[] args) { Person a = new Person(18); Person b = new Person(18); modify(a, b); System.out.println(a.getAge()); System.out.println(b.getAge()); } private static void modify(Person a1, Person b1) { a1.setAge(30); b1 = new Person(18); b1.setAge(30); } }
以上输出可想而知
30
18
不是说java一直是值引用吗,为什么我在modify方法内修改原Person的参数,在main中对象却被修改了呢?
不着急,我们来看看下面这图,关于java引用:
由于现在的JIT优化和逃逸分析的开启,java对象可能会被分配到栈或者堆上,不过这不是本文的重点,重点在于java的数据类型。
对象:它就是类的实例,比如所有的人统称为“人类”,这里的“人类”就是一个类(物种的一种类型),而具体到每个人,比如张三这个人,它就是对象,就是“人类”的实例。
对象引用:《Java编程思想》一段话: “每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。”
基本数据类型和引用数据类型:Java的其中基本类型变量有四类8种:byte、 short 、int 、long 、float 、double、 char、 boolean,除了8种基本数据类型变量,其他变量都是引用数据类型,如类、接口、数组等。
基本数据类型,只有一块存储空间, 在栈中,存放的是具体数据值。
引用数据类型,有两块存储空间一个在栈(Stack)中,一个在堆(heap)中。堆中存放对象实体(使用new关键字,即表示在堆中开辟一块新的存储空间),当然现在也可能在栈上分配,栈中存放对象在堆中所在位置的首地址。new 操作符的返回值是一个对象的引用。
一个引用类型变量(栈中的一块内存空间)保存了一个该类型对象在堆中所在位置的首地址,也称作一个引用类型变量指向了一个该类型的对象,通过这个变量就可以操作对象中的数据。
因此我们可以发现,基础对象传递的实打实的副本,而引用对象则是传递的reference的副本
如图所示,红色是我们复制出来的副本,所以我们在修改引用类型对象值的时候,原有的对象会被修改,因为虽然我们是进行的值传递,但是这个是一个reference的复制值,还是指向了原有的对象,所以如果要单独修改当前的则,则需要在创建一个对象,所以平时在修改数组或者对象的时候一定要注意是否的是依旧指向原对象的引用;基础类型的数据由于是在另外一个副本修改,故而无论如何原值都不会发生改变,另外基础类型的包装类型会自动装拆包。
最后附上测试代码
public class Main { public static void main(String[] args) { String aaa = "222"; String aaaa = new String("222"); Integer bbb = new Integer(333); Integer bbbb = 333; int[] ccc = {444,444}; change(aaa); change(aaaa); change(bbb); change(bbbb); change(ccc); System.out.println(aaa+"xxxx"+aaaa); System.out.println(bbb+"xxxx"+bbbb); System.out.println(ccc[0]); } public static void change(String a){ a = new String("111"); } public static void change(Integer b){ b = new Integer(111); } public static void change(int[] c){ c[0] = 111; } } 运行结果: 222xxxx222 333xxxx333 111