万物皆对象(二)
还是聊一聊java里面的Object对象,想到哪就引申到哪了,比如jvm内存模型,比如GC,后续如果有必要,我会把引申的东西也发博文,然后回头来编辑这个博文,增加链接;
首先应该理解的是,java里所有的东西都是Object,这句话怎么理解(不包括int,byte等基本类型);
Object o = new ArrayList();
这段代码不难,应该都能看得出来,这是创建了一个ArrayList的对象在堆上,然后在栈上创建一个Object类型的引用,这个引用的值就是堆中ArrayList的地址;
理解了这个,然后明白java里面的传参全是值传递,就不难理解java里面特别绕的各种传递的问题了;
比如说下边这个方法:
public void test(Object obj){
obj = null;
}
test(o);
这个方法传递的是o的值,这个值是O对象的地址,现在test方法把这个值指向了null,但是test里面的o,和main函数的o,不是一个东西(test和main是一个栈中的两个栈帜,栈之间的元素是不共享的,所以test里面所有的东西,跟main不挨着,main只是把o对象引用拷一份传给下层栈帜),相当于main里面的o是一把钥匙,test里面的o是main里钥匙的一个拷贝,然后test里面把钥匙厥断了(指向null)。
这里其实主要理清几个问题:方法之间的传递是值传递,方法之间的局部变量不可见(就是一个栈帜,是完全不知道另一个栈帜上的变量的)。
思考另一个问题:
o明明是final的,为什么在test类里还可以重置为null? 还是因为上边的原因,因为test这个栈帜里面的o,是从main这个栈帜传过来的值。
如果这样就不一样了:
这是因为test栈帜里已经没有局部变量o了,引用o在内存模型中的地址是在方法区(堆里),而方法区的元素是可以共享的,所以main栈帜和test栈帜操作的其实是一个东西。
继续说回刚开始的申明代码:
Object o = new ArrayList();
大部分应该都知道,这里面一个叫形参,一个叫实参;就是说你传递的参数类型是Object,但他实际上是一个ArrayList;这时候如果不进行强制类型转换,那么在调用对象的list方法时,在代码编译期就会报错,因为jvm看到引用对象o是一个Object,而你想调用o.get方法,就会报noMethod的异常;而强制类型转换如下:
可以理解为对象实参没有任何变化(堆里面的数据不变),只是重新在栈帜上申请了一个类型为List的引用变量,这时候就有两个引用指向堆中的一块内存,只是一个引用类型是Object,一个引用类型是List,Object类型的引用是调用不了get方法的,虽然他跟l同时都指向一个地址,但是因为引用的类型不一样,所以在编译期就会报错。而o和l同时调用equals方法或其他方法,其实调用的都是实际在堆里的对象的方法,跟引用类型无关。
顺便理一下Object对象的所有方法:
注册本地方法(C方法),暂时不理会.
getClass方法
说的很明白,返回运行时的对象的类名,这个类就是synchronize关键字锁住的静态方法的地方,所以在static方法上加synchronize参数,和类名.class上加关键字,以及对象.getClass方法上加关键字,效果是一样的;
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
hashCode方法和equals方法,重写一个必须重写另一个,否则使用Hash相关容器会出现错乱情况;
clone方法,浅拷贝;
toString方法, 返回类名和hash值;
最好把这个方法忘了,据说当时这个方法是为了钓鱼用的(勾引c程序员转java)
notify,wait方法; 多线程必了解方法,后续也会开博说他,先了解是多线程用来做线程间通信的吧(就是线程A干完活了,通知其他线程)