java语言中通过对象的引用来操纵对象,要注意的一点是对象和对象的引用是存储在不同的地方的,因为对象往往所占的空间要求比较大,所以对象存储在堆中,而为了便于操作,对象的引用则存储在堆栈中。
Object obj = newObject();通过这个动作在Java中可以创建一个对象,其实这包含了4个动作:
new Object,以Object类为模板,在堆中创建一个对象
new Object(),调用Object类的构造方法,对刚生成的对象进行初始化
左边的”Object obj“则是创建了一个Object类的引用变量,即obj变量是可以指向Object对象的对象引用
“=”操作符将对象引用obj指向刚刚创建的那个对象
可以近似的将Java中对象的引用理解为C语言中的指针,即对象引用在堆栈中存储的是它所指向的对象在堆中的地址。
Java中的基本类型,不是通过new来创建变量,尽管基本类型变量存储在堆栈中,但它并不是引用型变量,这个变量直接存储“值”,因此更加高效。此外,由于Java是需要面向对象的,所以基本类型都会有对应的包装类。对这些包装类的值操作实际上都是通过对其对应的基本类型操作而实现的。
包装类的用途主要包含两种:
a、作为和基本数据类型对应的类类型存在,方便涉及到对象的操作。
b、包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法。
c、注意Java的基本类型及其对应的包装器类型所占存储空间的大小,并不像其他大多数语言那样随机器硬件架构的变化而变化,这就使得Java程序更便于移植。
Java中对象的引用是存储在堆栈中的,然而对象是存储在堆中的,由于对象是通过对象的引用进行操作的,所以如果一旦一个对象没有指向它的引用变量,这个对象就成为“垃圾”,需要JVM进行垃圾回收。
Java只有一种参数传递方式:那就是按值传递,即Java中传递任何东西都是传值。如果传入方法的是基本类型(或其对应包类型)的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝,即指向同一块地址空间。
Java中的基本类型及其对应包类型:
这些类型的变量的参数传递都是值传递,此外尽管String类型不是基本类型,但可以将其当做是char[]的包装类,这也就解释了为什么String类型的变量在方法参数传递时是值传递的原因。这也就是为什么当对字符串的操作在通过不同方法来实现的时候,推荐大家使用StringBuffer的真正原因了。
publicclass Main {
publicstaticvoidop1(StringBuffer s){
s.append(" world!");
System.out.println("s:"+s);
}
publicstaticvoidop2(StringBuffer s){
s = newStringBuffer("java");
System.out.println("s:"+s);
}
publicstaticvoidmain(String[] args) {
// TODO Auto-generated method stub
StringBuffer str;
str = newStringBuffer("hello");
System.out.println("str:"+str);
op1(str);
System.out.println("str:"+str);
op2(str);
System.out.println("str:"+str);
}
要理解Java中值传递的运行机制,最好从Java内存空间分配的角度去理解,毕竟对象和对象的引用是存储在不同位置的,所以Java对象和引用的关系可以说是互相关联,却又彼此独立。彼此独立主要表现在:引用是可以改变的,它可以指向别的对象。
运行结果:
分析:
op1操作:
op2操作:str,s是两个独立的引用变量,s是Java在内存中新建立的变量,将s指向“Java”时,原先str仍然指向“hello world”。
参数传递时,如果是对象的引用,则拷贝对象引用,使其指向相同的地址空间。
publicclass Main {
publicstaticvoidop1(String s){
s += " world!";
System.out.println("s:"+s);
}
publicstaticvoidop2(String s){
s = new String("java");
System.out.println("s:"+s);
}
publicstaticvoidmain(String[] args) {
// TODO Auto-generated method stub
String str;
str = new String("hello");
System.out.println("str:"+str);
op1(str);
System.out.println("str:"+str);
op2(str);
System.out.println("str:"+str);
}
23
运行结果:
分析:可以看到外部方法并没有对str产生影响,因为Java语言规定String是不可变的,对String的任何操作都是产生一个新的对象。
在JAVA里,“=”不能被看成是一个赋值语句,它不是在把一个对象赋给另外一个对象,它的执行过程实质上是将右边对象的地址传给了左边的引用,使得左边的引用指向了右边的对象。JAVA表面上看起来没有指针,但它的引用其实质就是一个指针,引用里面存放的并不是对象,而是该对象的地址,使得该引用指向了对象。在JAVA里,“=”语句不应该被翻译成赋值语句,因为它所执行的确实不是一个赋值的过程,而是一个传地址的过程,被译成赋值语句会造成很多误解,译得不准确。
综上,在初始化时,“=”语句左边的是引用,右边new出来的是对象。
在后面的左右都是引用的“=”语句时,左右的引用同时指向了右边引用所指向的对象。
在参数传递时,尤其是传递的是对象的引用时,要注意“形参”和“实参”是独立的,形参在堆栈中是确实存在的,所以如果在调用方法中让形参指向了另外一个对象,实参仍然指向原来的对象。null也是一个“对象”,在Java中让形参值为null,实参的值并不等于null。
Java只有一种参数传递方式:那就是按值传递,即Java中传递任何东西都是传值。如果传入方法的是基本类型的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝,而接收这个引用的变量(形参),虽然是和实参一样指向同一块内存空间的,对指向对象操作后,其他变量访问时会得到同样的结果,但如果在此外部方法中,形参指向了另外一个对象,那么形参和实参就是独立的了,接下来的操作不会对另外一个产生任何的影响!例如,如果要是将想将某个变量置为null的话,千万不要通过类的方法setNull实现,因为那样只是让形参置值为null,不会对实参产生任何的影响,置null直接让其赋值为null即可。