Java中对象与引用
Java中对象与引用
初学Java 时,在很长一段时间里,总觉得基本概念很模糊。后来才知道,在许多Java 书中,把对象和对象的引用混为一谈。
如果分不清对象与对象引用,那实在没法很好地理解下面的面向对象技术,把自己的一点认识写下来,或许能让初学Java 的朋友们少走一点弯路。
为便于说明,我们先定义一个简单的类:
class student{
int name;
int age;
int 易做图;
}
有了这个类( 模板) ,就可以用它来创建对象:student stu1 = new student();
通常把这条语句的动作称之为创建一个对象,其实,它包含了四个动作。
1) 右边的"new student" ,是以student 类为模板,在堆空间里创建一个student 类的对象( 也简称为student 对象) 。
2) 末尾的() 意味着,在对象创建后,立即调用student 类的构造函数,对刚生成的对象进行初始化。
构造函数是肯定有的。如果你没写,Java 会给你补上一个默认的构造函数。
3) 左边的"student stu1" 创建了一个student 类引用变量。所谓student 类引用,就是以后可以用来指向某个
student 对象的对象引用,它指向的是某个student 对象的内存地址( 有点C 语言中指针的味道) 。
4)"=" 操作符使对象引用指向刚创建的那个student 对象。
我们可以把这条语句拆成两部分:student stu1; stu1 = new student(); 效果是一样的。
这样写,就比较清楚了,有两个实体:一是对象引用变量(stu1) ,在Sun 公司的实现中, 对象的引用是一个句柄, 其中包含一对指针: 一个指针指向该对象的方法表,一个指向该对象的数据;另一个是对象本身( 就是new 出来的那个对象) 。
在堆空间里创建的实体,与在数据段以及栈空间里创建的实体不同。尽管它们也是确确实实存在的实体, 但是,我们看不见,也摸不着。不仅如此,我们仔细研究一下第二句,想想刚刚创建的student 对象叫什么名字?
有人说,它叫"student" 。不对,"student" 是类( 对象的创建模板) 的名字。一个student 类可以据此创建 出无数个对象,这些对象不可能全叫"student" 。对象连名都没有,没法直接访问它。我们只能通过 对象引用来间接访问对象。
为了形象地说明对象、对象引用及它们之间的关系,可以做一个或许不很妥当的比喻:
对象好比是一只没有线的风筝,引用变量是一根线,可以用来系风筝。如果只执行了第一条语句, 还没执行第二条,此时创建的引用变量stu1 还没指向任何一个对象,它的值是null ,引用变量可以 指向某个对象,或者为null 。这时stu1 是一根线,一根还没有系上任何一个风筝的线。
执行了第二句后,一只新风筝做出来了,并被系在stu1 这根线上。我们抓住这根线,就等于抓住了那只风筝。
再来一句:student stu2; 就又做了一根线,还没系上风筝。如果再加一句:stu2=stu1; 系上风筝了。
这里,发生了复制行为。但是,要说明的是,对象本身并没有被复制,被复制的只是对象引用。
结果是,stu2 也指向了stu1 所指向的对象,也就是两根线系的是同一只风筝。
如果用下句再创建一个对象:stu2=new student(); 则引用变量stu2 改指向第二个对象。
从以上叙述再推演下去,我们可以获得以下结论:
(1) 一个对象引用可以指向0 个或1 个对象( 一根线可以不系风筝,也可以系一个风筝) ,而且可以改指;
(2) 一个对象可以有N 个引用指向它( 可以有N 条线系同一个风筝) 。
如果有下面语句:stu1=stu2;
按上面的推断,stu1 也指向了第二个对象。这个没问题。问题是第一个对象呢?没有一条线系住它,它飞了。
很多书里说,它被Java 的垃圾回收机制回收了,这不确切,准确地说,它已成为Java 垃圾回收机制的处理对象。
至于什么时候真正被回收,那要看垃圾回收机制的心情了。由此看来,new student(); 该语句应该不合法吧,
至少是没用的吧?不对,它是合法的,而且可用的。譬如,如果我们仅仅为了打印而生成一个对象, 就不需要用引用变量来系住它。最常见的就是打印字符System.out.println("I am Java!"); 字符串对象"I am Java!" 在打印后即被丢弃,有人把这种对象称之为临时对象。
java中的构造函数
原则:
1.构造方法不能声明返回类型,不能被static、final、synchronized、abstract、native修饰。
2.当某个类中没有自定义的构造方法时,编译器会自动为你产生一个缺省的构造方法,这是一个不带参数且没有任何代码的构造方法;但是一旦你为你的类定义了一个构造方法,不管是否带参数,那么编译器将不会再为你产生缺省的构造方法。
3.父类的构造方法不能被子类继承。子类不能直接通过方法名调用父类的一个构造方法,而是要通过super关键字调用父类的一个构造方法,super语句必须位于子类构造方法的第一行。
4.当你试图调用某个类的某一构造方法(暂定名为a方法)来产生该类的一个实例时,将首先需要调用其父类的构造方法,此时有两种情况:(1)a方法中显示地用super关键字调用了父类的某一构造方法(是否带参都行)(注意此时super句必需出现在a方法的第一行);(2)a方法中没有显示地用super关键字调用了父类的某一构造方法,此时编译器在编译子类时会a方法中的第一行加上super();语句。
5.当一个类有几个构造方法时如果想在一个构造方法(暂定名为a)中调用另一个构造方法(暂定名为b),需要使用this关键字而不是用方法名直接调用且this句必需放在第一行;在这种情况下,如果通过this关键字调用的构造方法b中显示地调用了父类的某一构造方法时,编译器不会再在a方法中添加super();来调用父类的缺省构造方法。
也谈Java值传递-到底传递了什么?
java里面只有值传递,这是不变的真理,不过由于对象类型的特殊性,有些人还是会疑惑 值传递?还是 引用传递?
可能主要是这句话造成的: 在Java中方法参数的传递,对象是传递引用,基本数据类型是传递值。
如果java里面只有值传递,为何又要说对象是 引用传递?
对于 基本数据类型 是值传递,相信没有人会有疑惑。那对象到底传递了什么?
其实这源于JAVA在解释其变量类型时的一个混淆:JAVA里面只有8种基本数据类型和对象类型
于是便有了 基本数据类型是传递值,对象是传递引用
其实如果把对象类型也解释成一种基本数据类型 ,那就不会出现 对象是传递引用 这句话让人疑惑的话了
下文将 对象类型 看成一种新的基本类型, 姑且称之为 olong (object long),即 所有对象变量 都是 olong,虽然在java书写时可以写成具体的对象类型,如 Long a; 但其传递实质是 olong a;
让我们来重新认识 olong 吧:
赋值:
long a = 1234;
olong oa = new Long(1234); //Long oa = new Long(1234);
传递:
long b = a;
olong ob =oa; //Long ob = oa;
改变:
b = 4321;
ob = new Long(4321); //ob = new Long(4321);
改变了什么?:
System.out.println(a); //输出 1234
System.out.println(b); //输出 4321
System.out.println(oa.longValue()); //输出 1234
System.out.println(ob.longValue()); //输出 4321
到底传递了什么:
long:
long a = 1234; //a的值1234
long b = a;//值传递 b的值1234;a的值1234
b = 4321;//b的值被改变为4321;但a不受影响,依然是1234
olong:
long类型的变量保存和传递的是其指代的基本类型的值,那么olong呢?
可以认为olong类型的变量保存和传递的是一个指向具体对象的内存地址,
olong oa = new Long(1234);//假设new Long生成的对象的地址为0001,则oa的值为0001
olong ob =oa; //值传递,ob的值0001;oa的值0001
ob = new Long(4321);//ob的值被赋予一个新的对象的地址,假设为0002,但oa的值不受影响,依然为0001
因此olong类型的赋值结果与long类型的表现一致,也是值传递
为什么对象是传递引用?
其实这句话也不错,因为olong变量本身就是一个引用,引用了一个对象(的内存地址)
但其实质是 值传递!只不过因为 olong 的值 还有一个 别名 叫做 引用,所以就被叫做了一个让人迷惑的新词:引用传递!
总结:
olong的引入只是为了方便解释Java对象的值传递,说明在参数传递过程中,对象变量的传递和基本变量的传递是没有本质区别的,所以,在Java中,没有 引用传递,只有值传递!
补充:软件开发 , Java ,