java中对象,与类在内存方面的理解,使得对象与类的概念具体点
首先关于类,我们可以把它当成一个瓶子模具,这里的模具只是用它的概念,不需要带入真正的模具(因为瓶子没有模具),当我们在
java中使用new来新建一个对象bottle时,可以想象成用这个模具去造了一个新瓶子,这个新瓶子是纯粹一个瓶子,什么花
纹都没有,我们当成这个就是这个bottle的属性的初始值,然后我们开始对这个新的对象bottle赋值,这个过程就像是在这个新的瓶子上面描上花纹的过程。
那么抽象类像什么呢?抽象类,我们把它想象成半个瓶子模具,残缺的,不完整,我们不能使用它去造一个新的瓶子,也就是new一个新的对象,那么如何才能让他变成一个完整的瓶子模具呢?
我们会把它补全使它成为一个完整的瓶子模具,也就是在这个半个瓶子模具的基础上,补全成为一个新的瓶子模具,就像是java的extends,拥有抽象父类的属性,方法,同时实现抽象父类的抽象方法。
继续,Interface像什么呢?花纹,纯粹的花纹,当把这些花纹刻在模具上时,使用模具造出来的瓶子,也就有了这些花纹,就像是java中的类实现接口。
现在谈谈子类继承父类时,private属性和方法,先只谈谈private属性
/**
* Created by 西皮 on 2017/9/17 14:23.
*/
public class FatherClass {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println(this.toString());
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
System.out.println(this.toString());
}
@Override
public String toString() {
return "FatherClass{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
子类父类多一个fair
/**
* Created by 西皮 on 2017/9/17 14:23.
*/
public class SonClass extends FatherClass{
private String fair;
private String name;
private int age;
public SonClass(){
}
public String getFair() {
return fair;
}
public void setFair(String fair) {
this.fair = fair;
System.out.println(this.toString());
}
@Override
public String toString() {
return "SonClass{" +
"fair='" + fair + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
先看一段测试代码:
@Test
public void testF(){
SonClass son = new SonClass();
FatherClass father = new FatherClass();
father.setName("father");
father.setAge(50);
son.setName("son");
son.setAge(18);
son.setFair("有头发");
System.out.println(son.toString());
System.out.println(father.toString());
}
结果是:
这个可以说明在堆内存中,这个son指向的对象,里面应该是有两个name,和两个age的一份是从父类继承过来的,一份是自己的,而我们的toString方法,打印的是本类的属性值。
那么当我们加一句System.out.println(son.getName()+son.getAge());
,可以很明显得到son18
的输出
这里必须说明的是,父类在子类实例化过程中是并没有被实例化的
public SonClass(){
super();//调用父类的构造器,但只是用来初始化属性
System.out.println(this.hashCode());
System.out.println(super.hashCode());
System.out.println(super.equals(this));
}
public FatherClass(){
System.out.println(this.hashCode());
System.out.println(super.hashCode());
System.out.println(super.equals(this));
}
这里分别在子类和父类中加入构造器,然后SonClass son = new SonClass();
去除多余的输出,可以看到如下输出:
这说明this和super都指向同一对象,这里的hashCode值是jvm根据对象在内存中的地址生成的,是可以一一对应的。也就是,在子类构造器中,调用的super(),只是为了给继承父类而来的属性初始化
这里我们补充一下jvm中的new指令的过程:
jvm虚拟机遇到一条new指令是如何工作的,遇到一条new指令,首先去检查这个指令的参数
是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。
在类加载检查通过后,接下来虚拟机将为新生对象分配内存,在内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),接下来,虚拟机要对对象进行必要的设置。。。。。。
在上面的工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但是这里的属性都是默认属性,
现在我们需要把对象按程序员的意愿初始化,也就是执行new指令之后会接着执行init方法。
以上是《深入理解java虚拟机——jvm高级特性和最佳实践》中对对象的创建的描述。
而在《Thinking in Java》94面中:无法阻止自动初始化的进行,他将在构造器被调用之前发生。
猜测一下,可以能java中构造函数是在new指令执行完毕之后,才进入构造函数,这样构造函数也就是按程序员的意愿初始化了。
总结一下,java中new SonClass
并没有实例化父类,只是调用父类的构造方法初始化了,子类从父类继承来的属性,这个调用是子类的对象调用的父类的构造方法,而子类自己的构造方法完成对自己属性的初始化(这里的初始化是指我们在内存分配完了,虚拟机初始化之后,我们按自己的要求进行的初始化)
而private方法,也是和private属性一样的道理。
也就是子类其实是继承了父类所有的属性和方法,只是private的不能直接访问而已,可以通过public等修饰的方法访问他们。