Java—对象的引用与传递

初步了解引用传递

引用传递也称为传地址,指的是在方法调用时,传递的参数是按引用进行传递,其实传递的是引用的地址,也就是变量所对应的内存空间的地址。

方法调用时,实际参数的引用——地址被传递给方法中相对应的形式参数,即形式参数和实际参数拥有相同的存储单元。在方法执行过程中,对形式参数的操作实际上就是对实际参数的操作,因此形式参数值的改变将会影响实际参数的值。

如果要想进行引用传递分析,那么首先需要先清楚两块内存。

⑴ 堆内存:堆内存可以理解为一个对象的具体信息,每一个对象保存的只是属性信息,每一块堆内存的开辟都要通过关键字new来完成。

⑵ 栈内存:可以理解为一个整型变量(只能够保存一个数值),其中保存的是一块(只能保存一块)堆内存空间的内存地址数值,但是为了方便理解,现在可以假设其保存的是对象的名字。

先通过对象的实例化操作了解内存分配。

对象的实例化内存分配操作(TestInstant.java)。
在这里插入图片描述
在这里插入图片描述
第01~10行定义了一个Book类。

第15行声明了一个对象,并在第16行对其进行实例化。

第18、19行设置对象中的属性。

下面看一下该程序的内部执行过程。
在这里插入图片描述
如果把范例【TestInstant.java】中的第16行“book = new Book() ;”注释掉,此时会出现如下的错误提示。
在这里插入图片描述
“NullPointerException”表示空指向异常,指的是使用了一个未实例化的对象(未开辟堆内存空间的对象)进行了类中属性或方法的调用的时候就会出现本异常信息。所以对象使用之前一定要开辟堆内存空间。

在程序中一个关键字new产生一个对象,就开辟一个新空间,如果有两个关键字new,就表示要开辟两个新内存空间,此处的两个对象都占有各自的内存空间,彼此的操作应该是独立的。

如下所示。
在这里插入图片描述
用关键字new实例化两个对象bookA、bookB,两个对象各占一个独立的空间,彼此独立。因此只要存在了关键字new,不管何种情况下,都表示要开辟新的内存空间。

引用数据类型的传递

在Java中,类本身就是引用数据类型,而对于引用数据类型实际上就相当于其他语言之中的指针概念。

在Java中对于方法参数的传递,对象是传递引用,基本数据类型是传递值。下面看一个传递基本数据类型的例子。

在函数中传递基本数据类型(TestValue.java)
在这里插入图片描述
在这里插入图片描述
第03~08行定义了一个方法交换两个数的数值。

第13行调用change()方法。

第14~15行输出调用change()方法后a、b的值。

从本范例中可以看到,引用数据类型的传递并没有改变数据本身的值。因为参数中传递的是基本类型a和b的备份,在函数中交换的也是那份备份的值而不是数据本身。

传递引用数据类型(TestValue01.java)
在这里插入图片描述
在这里插入图片描述
第03~07行定义了一个方法给count[0]赋值并输出。

第12行调用change()方法,将数组的引用作为参数传递。

第11、13行分别输出调用change()方法前与调用方法后count[0]的值。

从本范例中可以看到,在方法中传递引用数据类型int数组,实际上传递的是其引用count的备份,它们都指向数组对象,在方法中可以改变数组对象的内容。即:对复制的引用所调用的方法更改的是同一个对象。

对象的传递引用(TestRefDemo.java)
在这里插入图片描述
在这里插入图片描述
第15行将p1的引用赋给p2,相当于p1和p2都同时指向同一块内存。

第18行将p1对象赋值为null,表示此对象不再引用任何内存空间。

程序执行完第18行之后,实际上p1断开了对其之前实例化对象的引用,而p2则继续指向p1原先的引用。

可以理解为,这道程序中p2是通过p1实例化的,或者说p1将自身的引用传递给了p2。

下面给出具体引用的过程。
在这里插入图片描述
通过对上图的分析我们可以得出结论:所谓的引用传递,指的是一块堆内存空间,同时被多个栈内存所指向。引用传递的核心认识:不同的栈内存如果指向了同一块堆内存之中,所做的修改将影响所有的栈内存。

引用传递的使用(TestRefDemo02.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第01~10行定义类Book;

第15、16行分别用关键字new 字义两个对象bookA、bookB;

第17、18行分别对bookA的两个属性进行赋值;

第21、22行分别对bookB的两个属性进行赋值;

第23行把bookA的引用传递给bookB;

第24行重新设置bookB中title的属性值。

执行 bookB = bookA引用传递前,bookA、bookB是两个用new关键字创建的对象,有各自独立的堆内存,属性值各不相同。但执行引用传递后,bookA的引用传递给bookB,bookB不再指向原来的堆内存,而是和bookA指向了同一块堆内存,所以对bookB属性的设置即是对bookA相应的属性值修改,bookA.title( 即bookB.title)= “Android开发”。

可以发现,每一块栈内存只能够保存一块堆内存的地址,但是反过来,一块堆内存可以同时被多个栈内存所指向,在这种情况下,如果要改变某一个栈内存的保存地址内容,则必须先断开已有的堆内存地址连接,才可以指向新的堆内存空间,而如果一块堆内存空间没有任何的栈内存所指向的话,那么这块空间就将成为垃圾,所有的垃圾将等待被JVM中的GC(Garbage Collector)进行不定期的收集,同时进行内存空间的释放。

引用传递与现实生活的联系

一直强调一句话:“一切的程序离不开生活”。尤其是面向对象更是可以方便地进行现实生活的抽象。例如,现在要求通过所学习到的知识,完成以下的一种表示:人有一间房子,房子属于这个人。

对象的引用(TestHouse.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第1~25行定义了一个Person类,在Person类中有一个House属性表示是否有房子。

第26~48行定义了一个House类,并且在House类中有一个表示Person的属性表示一个人只能有一个房子有一个所有人。

引用小分析——集成设计模式

通过分析我们发现不同的事物类之间,都是依靠引用进行连接的,那么思考一个问题:给你一堆电脑零件,是否可以装成电脑?应该可以。

那么下面继续再分析一下电脑的组成,假设给你各个零件:主板、CPU、硬盘、内存、机箱、电源、显示器、键盘、鼠标。那么将电脑看成一个类,这些组件便是这个类中的各个对象。而这些小的对象又可以单独作为一个类拥有自己的对象。
在这里插入图片描述
在这里插入图片描述
下面再看一个实际的问题:要求抽象出当前的教室环境。
在这里插入图片描述
所以这种设计思路在开发模式上讲就称为集成设计模式,即:将多个小的类集合到一个大的类之中,形成一个整体。

对象克隆

对象克隆指的就是将一个对象进行内容的复制,如果要想实现克隆操作,可以使用Object类之中定义的一个方法。

如果要想正常地实现克隆操作,那么对象所在的类必须实现Cloneable接口,但是这个接口里面并没有定义任何的方法,此接口属于标识接口,指的是一种能力的体现。
在这里插入图片描述
实现克隆(TestCloneDemo.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第26~30行重写了克隆的方法。

第41行实例化一个对象,在第42行将bookA克隆到bookB。

对象的克隆其实际上就是新建一个内存空间将原有对象中的信息复制过来,如下图所示。
在这里插入图片描述

反射机制

反射机制可能是现阶段学习过程之中最难以理解的一个部分了,而此部分的内容是支撑日后各个框架开发的核心。

认识反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

Java反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法。

根据对象找到类(TestReflection.java)。
在这里插入图片描述
在这里插入图片描述
第06行新建了一个对象;

第07行通过Java反射机制得到类的包名并输出。

在本程序之中使用的getClass()方法是由Object类所定义的:public final Class<?>getClass(),此方法返回的对象类型为Class,而Class是反射操作的源头。但是如果要想取得Class类的实例化对象在Java之中有三种方式。

方式一:利用Object类的getClass()方法,但是要求必须先产生指定类的对象才可以,几乎不用。
在这里插入图片描述
方式二:利用“类.class”的形式取得Class类的对象,在Hibernate上使用。
在这里插入图片描述
方式三:利用Class类提供的一个方法完成,在系统架构中使用:
在这里插入图片描述
那么取得了Class类的实例化对象到底有哪些用处呢?实际上在Class类之中提供有一个方法。

根据对象找到类(TestReflection01.java)。
在这里插入图片描述
在这里插入图片描述
第01~14行声明了一个Book类并在类中重写了toString方法;

第17、18行通过反射实例化一个对象book,并在第19、20行对book进行赋值。

现在可以发现如果要想实例化对象可以有两种形式,一种是通过关键字new,另外一种是通过反射机制完成。如果要想更好地观察出这两种方式的特点,最好的做法就是通过工厂设计模式完成验证。

传统的工厂设计模式(TestFactory.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第01~04行声明了一个Book接口;

第05~12行定义了一个类MathBook实现了Book接口;

第13~22行定义了一个工厂类;

第27行实例化一个对象book,并在第28行输出。

这个时候,如果要想增加新的子类,则一定要修改工厂类。因为工厂类之中需要使用关键字new实例化,所以在这种情况下,发现关键字new依然会造成耦合,那么如果说现在使用的是反射机制呢?

使用反射机制的工厂模式(TestFactory01.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第01~04行声明了一个Book接口;

第05~12行定义了一个类MathBook实现了Book接口;

第13~20行定义了一个类ComputerBook实现了Book接口;

第21~32行定义了一个工厂类,其中第26行利用反射机制实例化一个对象。

不管有多少个子类,工厂类都可以使用,这就是反射机制所带来的好处,而在日后的开发之中,如果发现有时候需要写出完整的“包.类”,就表示此处使用了反射机制。

反射的其他操作

在之前已经学习了反射机制进行对象实例化的操作过程,但是不要忘记,在之前的类中所提供的都是无参构造方法。如果此时类中没有提供无参构造方法,只提供了有参构造方法,则就必须明确地调用指定的构造方法才可以通过反射实例化对象。

取得指定构造方法如下。
在这里插入图片描述
在Constructor类之中提供有一个实例化对象方法。
在这里插入图片描述
调用构造方法取得实例化对象(TestInstance.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第02~16行声明了一个Book类并提供了一个有参的构造函数;

第21、22行通过反射新建一个对象;

第23行调用构造方法取得实例化对象。

很明显,此时类之中还是提供无参构造方法会更加方便一些,这一点就是在简单Java类之中要求提供无参构造的关键因素。

在之前针对于属性的操作明确给出了要求,利用setter、getter设置和取得,而且对于setter、getter要求其必须按照指定的格式编写。而之所以存在这样的要求,也是因为反射机制的原因。

此时可以使用Class类的如下方法取得方法的对象。
在这里插入图片描述
取得了Method类的对象之后可以利用以下方法进行方法的反射调用。
在这里插入图片描述
setter、getter的使用(TestReflection02.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第18、19行定义了要操作的属性以及属性的值;

第20、21行通过反射新建一个对象分配堆内存;

第24行通过setTitle()方法的调用为对象进行赋值。

发现有了反射之后,只要有Object对象,以及要操作的属性名称就可以直接利用反射完成了,这个就是setter、getter命名标准的定义由来。

框架开发原理 = 反射机制 + XML解析。

1. 对象引用相等

引用相等:即指引用到堆上同一个对象的两个引用是相等的。

如:
在这里插入图片描述
若调用:
在这里插入图片描述
返回结果为true。若调用:
在这里插入图片描述
返回结果为true。

对象bookA、bookB指向同一堆内存,内存地址及内容一致,结果都为true。

两个对象A和对象B,一般情况下用A==B或者A.equals(B)来判断两个对象是否相等,但实际情况并非这样,上面的情况只是判断了两个对象的引用相等。
在这里插入图片描述
因bookA、bookB是两个独立对象,堆内存不同,返回结果都为false。

若要判断对象bookA、bookB内容相同,需要覆写方法equals()。

2. Java中的垃圾回收机制

观察如下代码的执行结果。

java垃圾回收机制(TestRecycle.java)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上图所示,可以发现,每一块栈内存只能够保存一块堆内存的地址,但是反过来,一块堆内存可以同时被多个栈内存所指向,在这种情况下,如果要改变某一个栈内存的保存地址内容,则必须先断开已有的堆内存地址连接,才可以指向新的堆内存空间,而如果一块堆内存空间没有任何的栈内存所指向的话,那么这块空间就将成为垃圾,所有的垃圾将等待被JVM中的GC(Garbage Collector)进行不定期的收集,同时进行内存空间的释放。

虽然Java之中提供了垃圾的自动回收机制,但是考虑到性能的问题,还是建议操作的过程之中尽量少产生垃圾为好。

Java—包及访问权限

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值