小弟最近做项目遇到了这样一个问题:
按照如下的方式初始化一个对象public class Test {
public Test(){
MyObject cs = null;
if(init(cs)){
cs.getA();
}
}
private boolean init(MyObject cs){
cs = new MyObject();
return true;
}
public static void main(String[] args){
Test test = new Test();
}
}
public class MyObject {
int a = 1; MyObject() {
a = 2;
} public int getA() {
return a;
}}编译器会在cs.getA()处提示Null pointer access: The variable cs can only be null at this location。可是按照我的理解,此处Test()将一个MyObject的引用交给init函数,init应该能够替他完成初始化 啊?
请高人指点
按照如下的方式初始化一个对象public class Test {
public Test(){
MyObject cs = null;
if(init(cs)){
cs.getA();
}
}
private boolean init(MyObject cs){
cs = new MyObject();
return true;
}
public static void main(String[] args){
Test test = new Test();
}
}
public class MyObject {
int a = 1; MyObject() {
a = 2;
} public int getA() {
return a;
}}编译器会在cs.getA()处提示Null pointer access: The variable cs can only be null at this location。可是按照我的理解,此处Test()将一个MyObject的引用交给init函数,init应该能够替他完成初始化 啊?
请高人指点
结合这个例子理解一下:
public class Test {
public static void main(String[] args){
StringBuilder builder = new StringBuilder("Hello");
System.out.println(builder);
testPassByValue(builder);
System.out.println(builder);
}
//该方法起不到修改的作用
public static void testPassByValue(StringBuilder builder){
builder = new StringBuilder("World");
}
}
public Test(){
MyObject cs = new MyObject();
change(cs);
System.out.println(cs.getA());
}
private void change(MyObject cs){
cs.setA(100);
}
public static void main(String[] args){
Test test = new Test();
}
}
public class MyObject {
int a = 1; MyObject() {
a = 2;
} public int getA() {
return a;
}
public void setA(int m){
a = m;
}}如上例所示,确实传了引用过去,并通过该引用修改了对象的属性啊
求解释~~~
这个也算是老问题了这里严格来讲不能叫传引用,而是传引用的拷贝(pass by value就体现在了这里)
原引用与引用拷贝都指向同一个对象,自然可以改变对象值了
这里严格来讲不能叫传引用,而是传引用的拷贝(pass by value就体现在了这里)
原引用与引用拷贝都指向同一个对象,自然可以改变对象值了
public Test(){
MyObject cs = null;
if(init(cs)){
cs.getA();
}
}
//关键问题在这里,这个是作用域问题
private boolean init(MyObject cs){
cs = new MyObject();
return true;
}MyObject cs在方法的参数定义了一个MyObject对象的变量cs(局部变量)并且在方法里面实例化了
但是构造方法也有一个MyObject cs,你在传递过去的时候是传递值不是引用...
所以你过去之后返回的CS还是null,而实际上实例化的是方法参数上的cs希望回答对你有帮助
如果代码改成全局变量...public class Test {
private static StringBuilder builder=new StringBuilder("Hello");
public static void main(String[] args){
System.out.println(builder);
testPassByValue(builder);
System.out.println(builder);
}
public static void testPassByValue(StringBuilder bu1){
builder = new StringBuilder("World");
}
}
就会有效果了
public Test(){
MyObject cs = null;
if(init(cs)){
cs.getA();
}
}
private boolean init(MyObject cs){
//改变cs的引用
cs = new MyObject();
return true;
}
public static void main(String[] args){
Test test = new Test();
}
}上面的代码是企图在init里改变构造方法cs的引用是不可行的,也就是说,在init里改变cs的引用是局部的,出了init方法cs的引用也就恢复了,但是你可以在init可能使用cs的引用,增,删,改cs的值,如下代码:
public class Test {
public Test(){
MyObject cs = new MyObject();
change(cs);
System.out.println(cs.getA());
}
private void change(MyObject cs){
//改变cs引用的值,没有改变cs的引用
cs.setA(100);
}
public static void main(String[] args){
Test test = new Test();
}
}
针,也不过是一个数值,不过这个数值代表某个内存的地址编号值。通过这个值,我们的CPU运行时就可
以转到对应的某个内存去读取所需要的数据。说的简单点:
当你调用init(cs)时,传进去的值只是cs值的一份拷贝,当被方法调用完后,这份拷贝就被回
收了,所以你的Test()中的cs仍然为空,就算你在init()中创建了一个对象,但是cs的值是null,而不是这个对象的地址值。说详细点如下:
那么楼主的程序为什么无法改变呢?我的认识如下:
(1)首先理解程序加载Test()方法时发生的事情:
进入到这个函数时,系统为这个函数在栈中分配一块内存,然后,再在这块内存中为局部变量cs
分配一块空间,因为cs只是存一个地址值,那么它被分配4Byte(因为它是一个int类型)的空间;接着后面的
程序也会分配空间。
(2)通过程序我们看到,你给这个变量的赋值为null,那么cs分配到的内存中的数据仍然保持为分配空间
时系统给它赋的初值(其实这就可以解释为什么我们在C中读到数组以外的内存时,读出来的字就是那些看
似乱码一样的字符,其实那是初始化的那些Byte转换出来的),所以这块内存不指向任何地方(这里说的”
指向“其实是逻辑上的”指向“,而不是真正的有个连接,相信楼主能够理解),当你调用
init(cs)时,其实此时发生的是传递了cs的一份拷贝,那么此时就把系统给的这个初始值赋给被调用的这个
函数的这个参数,而这个参数其实就是被调用方法中的一个局部变量。
(3)接着,你在init()方法中创建了一个MyObject的实例,并把这个实例的引用给里面局部变量cs,此时
init()方法中的cs的确保存了一个地址值,这个地址值是MyObject()对象在堆中的起始地址(因为通过new
方法创建对象时,对象是放入堆中的)。但是请楼主思考,在return true;之前,我们的Test()方法中的cs
的值是多少?cs==null吧。而现在init()方法中的cs是多少?cs==某个地址值吧。
(4)当init()方法调用完毕后,该方法所分配到的内存全部回收,也就是你没法再读这段内存中的数据了
,当返回后,将回到Test()方法,可是,此时我们的cs==null,也就是说cs并未指向我们在init()方法中创
建的那个对象(当init()方法返回后,这个在堆里面的对象就已经成了垃圾了),所以,进入你的if后,你
的cs仍为null,所以会报出那样的错误。接着,我要解释,你举的那个例子又为什么可以改变cs对象的内部状态(此时就是改变它的属性值):你调用change(cs),此时这个参数cs的值仍然是 MyObject cs = new MyObject();中的这个cs值的一份拷贝,进入你那个change(cs)方法后,你的cs调用 cs.setA(100);方法改变了cs引用指向的对象的内部状态,当你的方法调用完后,change(cs)方法分配的内存被回收,但是,我们在Test()中创建的那个MyObject对象还在堆中,而且不是垃圾,为什么?因为,当返回Test()方法后,在这个方法里面不是有个cs引用嘛,而且它现在是指向堆中的这个对象的,如果我们再用这个cs来读取它所指向的对象的信息时,我们会发现这个对象的内部状态有所改变。对比这两个例子,第一个例子中Test()的cs的值一直为null,即使init()方法创建了一个对象,它也无法指向这个对象;第二个例子中Test()的cs值一直都是被创建对象的一个引用(或地址值吧),当你在init()方法中改变这个对象时,其实就是改变了Test()方法中cs指向的那个对象。所以你再用cs去看这个对象的状态时,就看到,它指向的对象的状态改变了。
原引用与引用拷贝都指向同一个对象,自然可以改变对象值了