Yourbatman的博客

一个可以沉迷于技术的程序猿

【小家java】类中静态代码块、构造代码块、静态变量执行顺序和继承逻辑

1、概述

诚如各位所知,java的三大特性:封装、继承、多态。其中继承,是java中最有学问的一点也是最相对来说最难理解的一些东西,本文针对于此,做一些实例分析,希望能够帮助大家理解java中的继承机制

2、栗子

情况一:当父类和子类有同名同类型的属性时,使用时需要注意

public class Main {
    public static void main(String[] args) {
        Parent chidParent = new Child();
        System.out.println("Parent:" + chidParent.getAge()); //40
        System.out.println("Parent:" + chidParent.age); //18 这个结果你能接受吗?哈哈
        Child child = Child.class.cast(chidParent);
        System.out.println("Child:" + child.getAge()); //40
        System.out.println("Child:" + child.age); //40
    }
}

@Getter
@Setter
class Child extends Parent {
    Integer age = 40; //名称和父类的同名
}

@Getter
@Setter
class Parent {
    Integer age = 18;
}

我们发现,那个18为什么会输出出来呢?父类和子类的变量是同时存在的,即使是同名。子类中看到的是子类的变量,父类中看到的是父类中的变量,它们互相隐藏,而同名的方法则是实实在在的覆盖(重写),属性不存在重写哟。有了这个解释,就好理解了吧
情况二:当父类和子类有同名不同类型的属性时,使用时需要注意

public class Main {
    public static void main(String[] args) {
        // 报错Error:(20, 12) java: com.sayabc.boot2demo1.main.Child中的getAge()
        // 无法覆盖com.sayabc.boot2demo1.main.Parent中的getAge()
        // 返回类型java.lang.String与java.lang.Integer不兼容
        Parent chidParent = new Child();
    }
}

@Getter
class Child extends Parent {
    String age = "40"; //名称和父类的同名

    public void setAge(String age) {
        this.age = age;
    }
}

@Getter
class Parent {
    Integer age = 18;

    public void setAge(Integer age) {
        this.age = age;
    }
}

我们高兴的发现,如果类型不同,编译器还发现不了,但是一运行,就报错啦。这算编译器的bug吗?哈哈
情况三:继承中最基本的类加载顺序,不做过多解释。静态代码块只执行一次,并且优先于mai方法先执行

public class Main {
    public static void main(String[] args) {
        Parent chidParent = new Child();
    }
}

@Getter
class Child extends Parent {

    static {
        System.out.println("Child的静态块");
    }

    {
        System.out.println("Child的构造块");
    }

    Child() {
        System.out.println("Child的构造方法");
    }
}

@Getter
class Parent {
    Integer age = 18;

    static {
        System.out.println("Parent的静态块");
    }

    {
        System.out.println("Parent的构造块");
    }

    Parent() {
        System.out.println("Parent的构造方法");
    }
}

结果如下:

Parent的静态块
Child的静态块
Parent的构造块
Parent的构造方法
Child的构造块
Child的构造方法

备注:此处需要注意,此处子类没有显示调用super(),但父类的构造还是执行了的。但是,但是,但是,如果构造快为有参构造,请记得显示调用super方法,否则父类是不能被初始化的。如果子类的构造器没有显示地调用超类的构造器,则将自动调用超类默认(没有参数) 的构造器。如果超类没有不带参数的构造器,并且在子类的构造器又没有显式地调用超类的其他构造器,则 java 编译器将报告错误

情况四:子类和父类有同名同类型的静态常量的时候

public class Main {
    public static void main(String[] args) {
        Parent parent = new Child();
        System.out.println(parent.name); //fangshixiangParent
        Child child = new Child();
        System.out.println(child.name); //fangshixiangChild
    }
}

@Getter
@Setter
class Child extends Parent {
    static String name = "fangshixiangChild";
}

@Getter
@Setter
class Parent {
    static String name = "fangshixiangParent";
}

有了前面的基础,这个现象就很好解释了。同理:当有同名不同类型的属性时,直接获取属性还是会各自获取到自己的,但get方法就不行,就会报错了。
情况五:静态代码块属于类的,并且优先于main方法执行

public class StaticDemo1 {                             

    public static void main(String[] args) {
        StaticDemo1 t1=new StaticDemo1();    //第2步,初始化构造函数,i=9
        System.out.println(t1.i);            //第3步,按顺序执行,9
        speak();                            //第4步,按顺序执行,调用静态函数

    }    
    static int i=1;                            //静态变量存到静态区域。    
    static void speak()                        //静态函数存到静态区域。调用时执行。
    {
        System.out.println("a");;
    }
    static {                                //第1步,静态代码块随着类的加载,优先执行且只执行一次。i=3,i+3打印结果是4。
        i=i+3;
        System.out.println(i);
    }

    public StaticDemo1(){                    //构造方法,初始化时执行。
        i=i+5;                                //i=9
        System.out.println(i);    
    }
}

但是,但是,但是。如果StaticDemo1没有new或者静态方法没有调用,静态代码块是不会被执行的哦,只有加载了才会执行,并且只执行一次
static块真正的执行时机。如果了解JVM原理,我们知道,一个类的运行分为以下步骤:static代码块真正执行时机
下面我们看看执行static块的几种情况:
1、第一次new A()的过程会打印”“;因为这个过程包括了初始化
2、第一次Class.forName(“A”)的过程会打印”“;因为这个过程相当于Class.forName(“A”,true,this.getClass().getClassLoader());
3、第一次Class.forName(“A”,false,this.getClass().getClassLoader())的过程则不会打印”“。因为false指明了装载类的过程中,不进行初始化。不初始化则不会执行static块。

3、使用场景

各种设计模式,都会以此为依托,才能有更好的设计

4、最后

java的三大特性都非常的重要,如果不理解虚拟机对类的一些处理,有时候会犯迷糊,影响逻辑的设计,所以此文用简单用例希望能帮助大家理解。日后也许会持续更新

—-题后语—-

更多内容持续更新中,欢迎关注我的博客!
有任何问题,可以跟我留言讨论,欢迎指正,不胜感激。
有任何疑问,亦可扫码向我提我哟~

阅读更多

扫码向博主提问

去开通我的Chat快问

f641385712

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • java
  • 微服务
  • SOA
版权声明: https://blog.csdn.net/f641385712/article/details/80350192
个人分类: java基础
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

【小家java】类中静态代码块、构造代码块、静态变量执行顺序和继承逻辑

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭