这仅仅是我个人的理解,因为是初学者,可能存在理解错误或理解不到位的情况,如有发现,望指出,谢谢!
我们都知道在创建子类对象时,调用子类构造方法前,必须调用父类的构造方法。
那么调用构造方法就代表着创建对象了吗
假设1:
如果创建子类对象没有创建父类对象,那么我们子类继承过来的属性和通过super调用的方法来自哪里?
// 父类A
public class A {
int i;
public A() {
// TODO Auto-generated constructor stub
System.out.println("I'm a ");
}
public void hello() {
System.out.println("Hello, i'm a");
}
}
// 子类B
public class B extends A{
public B() {
// TODO Auto-generated constructor stub
System.out.println("I'm b");
}
// 重写父类方法
public void hello() {
// 调用父类的方法
super.hello();
System.out.println("Hello, i'm b");
}
}
// 通过子类对象去调用父类属性和方法
public class Demo1 {
public static void main(String[] args) {
B b = new B();
b.i = 10;
System.out.println(b.i);
b.hello();
}
}
结果:
假设2:
1.如果创建子类对象时创建父类对象, 多重继承的子类会浪费很多空间;我们知道所有类都继承与Object类,那样内存中充满了许多相同的Object对象,浪费空间。
// 多重继承 C继承B,B继承A
// 父类A
public class A {
public A() {
// TODO Auto-generated constructor stub
System.out.println("I'm a");
}
}
// 父类B
public class B extends A {
public B() {
// TODO Auto-generated constructor stub
System.out.println("I'm b");
}
}
// 子类C
public class C extends B {
public C() {
// TODO Auto-generated constructor stub
System.out.println("I'm c");
}
}
// 测试
public class Demo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
C c = new C();
}
}
多重继承结果:
2.如果一个子类是抽象类的子对象,那么创建该子类时,能创建一个抽象类的对象吗?(抽象类不能创建对象).如果能创建,岂不是违反了java的语法规则。
// 实现类会调用抽象类的构造函数,但不意味着创建了抽象类对象
// 抽象父类A
public abstract class A {
public A() {
// TODO Auto-generated constructor stub
System.out.println("I'm a ");
}
public abstract void show();
}
// 实现类B
public class B extends A {
public B() {
// TODO Auto-generated constructor stub
System.out.println("I'm b");
}
@Override
public void show() {
// TODO Auto-generated method stub
}
}
// 虽然调用了抽象类的构造方法,但没有创建抽象类对象,因为抽象类不能创建对象
public class Demo3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
B b = new B();
}
}
抽象类结果:
创建子类对象时,不会创建父类对象。
明确:
1.创建对象指的是在堆区开辟空间(new)
2.编译器在运行子类构造器之前,必须先执行父类构造器;且调用父类构造器的语句必须在子类构造器的第一行。
3.构造方法的作用是为堆区中的对象的属性初始化,不是创建对象。
类加载时机:
生成该类对象的时候,会初始化该类及该类的所有父类;
访问该类的静态成员的时候(子类中访问父类中定义的静态变量时,只会初始化父类,不会造成子类的初始化);
class.forName(“类名”);
成员方法什么时候加载呢?
成员方法在方法区,方法区的特点是唯一存在,那么成员方法只存在一份。当类的字节码文件被加载到内存时,类的实例方法不会被分配入口地址,当该类创建对象后,类中的实例方法才分配入口地址,从而实例方法可以被类创建的任何对象调用执行。需要注意的是,当我们创建第一个对象时,类中的实例方法就分配了入口地址,当再创建对象时,不再分配入口地址,也就是说,方法的入口地址被所有的对象共享,当所有的对象都不存在时,方法的入口地址才被取消。
实例化对象的过程:
1.为对象分配内存,执行默认初始化;
2.执行显式初始化,即类成员变量声明时的赋值语句与实例初始化块。 两者顺序执行,并且初始化块中可以对在其之后
声明的属性进行操作(但不能引用)。即:
Class Cat
{
{
brand = "Tom";
}
String brand;
}
3.执行构造方法;
4.返回对象在内存中的首地址等重要信息。
子类为什么能调用父类的方法?
子类会有一个父类的亚对象,拥有父类的完全属性和方法,但没有自己独立的空间,依赖于子类对象的空间,但不完全属于子类,可以通过super调用,注意:这里的super不是引用,因为亚对象不是真正意义的对象(没有自己的空间),而是一种标识符。
// 亚对象没有自己的空间,需要依赖于子类对象的空间
// 父类
public class A {
public A() {
// TODO Auto-generated constructor stub
System.out.println(this);
}
}
// 子类
public class B extends A {
public B() {
// TODO Auto-generated constructor stub
System.out.println(this);
}
}
// 比较内存地址是否相同
public class Demo4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
B b = new B();
}
}
结果:
// 父类的亚对象,有父类的所有属性和方法
// 子类有亚对象,也就有了父类的所有属性和方法,只是private修饰的变量和方法,子类无法使用
// 父类
public class A {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
// 子类
public class B extends A {
}
// 看子类能不能通过父类提供public方法访问父类的private属性
public class Demo5 {
public static void main(String[] args) {
// TODO Auto-generated method stub
B b = new B();
b.setI(10);
System.out.println("i=" +b.getI());
}
}
结果:
总结
创建子类对象时,并没有创建父类对象,只是子类对象中有一个父类的亚对象。