373-388进入面向对象高级/类变量/类方法/理解main方法/代码块

一、第十章内容介绍

package com.alice.chapter10;

public class Introduction {
    public static void main(String[] args) {
    }
}

/*
    内容介绍
    第十章,开始进入面向对象的高级部分
    目录:
        类变量和类方法
        理解main方法语法
            main方法前面有一个关键字static,我们会详细讲解static是什么
        代码块
        单例设计模式
        final关键字
        抽象类
        接口
        内部类
    这章节的内容量非常大,难度更高

    类变量和类方法:
        类变量-提出问题
        提出问题的主要目的就是让大家思考解决之道,从而引出要讲解的知识点,
        说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?
        编程解决问题。

    先使用传统的方法来解决:
        使用我们现有的技术来解决这个问题,com.hspedu.static_.ChildGame.java
        1、在main方法中定义一个变量count
        2、当一个小孩加入游戏之后count++,最后count就能够记录有多少小孩在玩游戏。

         // 创建一个小孩类
        class Child {
            private String name;

            public Child(String name) {
                this.name = name;
            }

            public void addGame() {
                System.out.println(this.name + "加入了游戏...");
            }
        }

        main:
        int count = 0;
        Child child1 = new Child("第一个小孩");
        child1.addGame();
        count++;
        Child child2 = new Child("第二个小孩");
        child2.addGame();
        count++;
        Child child3 = new Child("第三个小孩");
        child3.addGame();
        count++;
        System.out.println("目前有"+count+"个小孩加入了游戏!");

        这样写有什么问题?

        1、count是一个独立于对象,很尴尬,因为我们是面向对象编程
        不是相关对象的属性,如果我们想要在另外一个地方使用这个count就难以控制
        2、以后我们访问count很麻烦,因为我们没有使用到oop
        3、因此,我们需要引出类变量/静态变量
 */



二、类变量

package com.alice.chapter10;

/**
 * 使用类变量对前面的问题进行优化
 * 思考:
 *  如果,设计一个int count表示总人数,我们在创建一个小孩的时候,就把count加一,并且
 *  count是所有对象共享就好了。我们使用类变量来解决这个问题。
 *  我们前面的count不是属于所有对象的,引入类变量来成为所有对象的。
 *  我们知道一个对象在堆中会有一个空间,如何让所有对象都共有一个空间呢?
 *  另外类变量还有另外一个名称叫做静态变量,因为使用static来修饰
 *  具体类变量count存放到什么地方,Java的版本不同存放在堆或者是方法区中
 *  其他不用管:
 *      只需要知道两点
 *          1、static类变量是同一个类所有对象共享的。
 *          2、static类变量在类加载的时候就已经生成了。
 *
 *  再来回顾一下:
 *      什么是类变量:
 *          类变量也叫做静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它
 *          的时候,取到的都是相同的值,同样任何一个该类的对象修改它的时候,修改的也是同一个变量。
 *          这个从前面的图中也可以看出来。
 *      如何定义类变量:
 *          定义语法:
 *          访问修饰符 static 数据类型 变量名; 推荐使用这种方式
 *          static 访问修饰符 数据类型 变量名;
 *      关于如何访问类变量这里再编写一个类 VisitStatic.java
 *          类名.类变量名
 *          或者是 对象名.类变量名
 *          tip:静态变量的访问修饰符的访问权限和范围和之前的普通属性是一样的。
 *          推荐使用:类名.类变量名
 *      类变量的使用
 *      类变量的使用的细节讨论
 *      1、我们什么时候需要使用类变量
 *          当我们需要让某个类的所有对象都共享一个变量的时候,就可以考虑使用类变量(静态变量),比如
 *          定义学生类,统计所有学生共交了多少钱。
 *      2、类变量和实例变量(普通属性)的区别
 *          类变量是该类的所有对象共享的,而实例变量是每个对象独享的
 *      3、加上static称类变量或者静态变量,否则称为实例变量或者普通变量,非静态变量
 *      4、类变量可以通过类名.类变量或者对象名.类变量名来访问,但是Java设计者推荐我们使用
 *          类名.类变量名的方式访问。前提是需要满足访问修饰符的访问权限和范围。
 *      5、实例变量不能通过类名.类变量名 方式访问。
 *      6、类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。
 *      7、类变量的生命周期是随类的加载开始,随着类消亡而销毁。
 */
public class ClassVar01 {

    public static void main(String[] args) {
        // 01 介绍类变量
//        Child alice = new Child("alice");
//        alice.playGame();
//        Child bob = new Child("bob");
//        bob.playGame();
//        Child eric = new Child("eric");
//        eric.playGame();
//
//        System.out.println("当前一共有" + Child.count + "个小孩在玩游戏!");
//        // 因为count这个类变量可以被所有对象实例共享,所以随便使用一个对象都能够点出来count
//        // 而类变量也可以被类直接访问
//        // 每个实例对象调用这个类变量的结果都是一样的,因为已经被共享了
//        // 现在大家都可以拿到这个类变量
//        System.out.println(alice.count);
//        System.out.println(bob.count);
//        System.out.println(eric.count);

        // 02 访问类变量
        // 第一种方式是类名.类变量名
        System.out.println(VisitStatic.staticVar);
        // 这里没有创建一个类的对象实例也能够访问,因为类变量依赖的不是对象实例
        // 第二种方式是通过该类创建一个对象实例来访问
        VisitStatic visitStatic = new VisitStatic();
        System.out.println(visitStatic.staticVar);
    }
}

class Child {
    private String childName;
    public static int count;
    // 该变量的最大特点就是会被Child类的所有对象实例共享

    public Child(String childName) {
        this.childName = childName;
        count++;
    }

    public void playGame() {
        System.out.println(this.childName + "加入了游戏");
    }
}

class VisitStatic {
    // 创建一个类变量
    public static String staticVar = "我是静态变量";  // 推荐将访问修饰符写道前面的方式
    // 强调一下类变量的访问权限也是需要遵守的
}



三、类方法

package com.alice.chapter10;

public class ClassMethod02 {
    public static void main(String[] args) {
        // 1使用类方法
//        Student alice = new Student("alice");
//        Student bob = new Student("bob");
//        alice.payTuition(100);
//        bob.payTuition(100);
//
//        Student.printTuition();
    }
}

/*
    类方法基本介绍
    类方法也叫静态方法。
    形式如下:
        访问修饰符 static 数据返回类型 方法名() {} 推荐使用这种写法
        static 访问修饰符 数据返回类型 方法名() {}
    类方法的调用:
        使用方式:同理
            类名.类方法名 或者是
            对象名.类方法名
            前提是需要满足访问修饰符的访问权限和范围。
    这里定义一个静态的变量来累计学生的学费
    创建一个交学费的方法,传进来的形参累计到类变量中
    再写一个打印总学费的
    然后创建两个学生对象交学费
    然后输出当前的总学费看看

    为什么我使用对象调用类变量或者类方法的时候IDEA会标黄色
    1. 为什么会被标记为黄色?
    (1) 违反 Java 编码规范
    类变量(static字段)和类方法(static方法)属于类本身,而不是对象实例。

    规范建议直接通过 类名(如 ClassName.staticMethod())访问,而不是通过对象实例(如 obj.staticMethod())。

    IDEA 的默认检查规则(Java | Code style | Incorrect static member access)会对此提示。

    (2) 潜在问题
    可读性差:其他开发者可能误以为这是一个实例方法/字段。

    设计问题:如果频繁通过实例调用静态成员,可能意味着代码结构需要优化(例如是否应该用实例成员代替静态成员?)。

    类方法的经典使用场景
    当方法中不涉及到任何和对象相关的成员的时候,则可以将方法设计成为静态方法,提高开发效率

    比如:工具类中的方法utils
    Math类,Arrays类,Collections集合类看源码就知道了
    小结:
    在程序实际开发当中,往往会将一些通用的方法设计成静态方法,这样我们就不再需要创建对象就可以使用了,比如打印一组数组
    冒泡排序,完成某个计算任务等等。

    好处是我们不创建实例也可以调用某个方法,把这个方法做成静态方法是非常有好处的。

    类方法在使用的时候需要注意的地方
    1、类方法和普通方法都会随着类的加载而加载,将结构信息存储到方法区当中
    类方法中无this的参数
    普通方法中隐藏含有this的参数
    也就是this和super都不能使用
    2、类方法可以通过类名调用,也可以通过对象名来调用。
    3、普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
    4、类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以
    5、类方法(静态方法)中只能访问静态变量或者是静态方法。
    6、普通成员方法,既可以访问普通方法和变量又可以访问静态方法和变量
 */

class Student {
    private String name;
    private static double tuition;  // 静态变量统计所有学生交的学费

    public Student(String name) {
        this.name = name;
    }
    public void payTuition(double tuitionOfStudent) {
        System.out.println(name + "交了" + tuitionOfStudent + "元");
        Student.tuition += tuitionOfStudent;
    }

    public static void printTuition() {
        System.out.println("收集到的学生总学费是:" + Student.tuition + "元");
    }

}

四、类变量和类方法的练习

package com.alice.chapter10;


public class StaticExercise03 {
//    /**
//     * 第一题:输出什么
//     */
//    static int count = 9;
//    public void count() {
//        System.out.println("count=" + (count++));  // 成员方法中可以访问任何成员
//    }
//
//    public static void main(String[] args) {
//        new StaticExercise03().count(); // 创建一个本主类的实例对象调用成员方法count=9 然后count等于10了
//        new StaticExercise03().count(); // 同理,虽然是另外一个对象,但是和上一个对象共享空间,count=10,然后count等于11了
//        System.out.println(StaticExercise03.count);  // 类变量可以由类来访问,结果是11
//    }

    public static void main(String[] args) {
        // 第二题
//        System.out.println("Number of total is " + Person.getTotalPerson());  // 返回0
//        Person p1 = new Person();  // id和total==1
//        System.out.println("Number of total is " + Person.getTotalPerson()); // 返回1

        // 第三题
        Person.setTotalPerson(3);  // 这里使用类调用一个静态方法 将静态属性变成了3
        new Person();  // 调用构造方法id和total为4
        System.out.println(Person.getTotal());
    }
}

///**
// * 第二题
// */
//class Person {
//    private int id;
//    private static int total = 0;
//    public static int getTotalPerson() {
//        //id++;  // 错误点一,首先这里是一个静态方法,静态方法只能访问静态的属性和方法,所以这里错了。
//        return total;
//    }
//    public Person() {
//        total++;
//        id = total;
//    }
//}

class Person {
    private int id;
    private static int total = 0;  // 这里变成3
    public static void setTotalPerson(int total) {
        // this.total = total;  // 因为这里是一个静态方法,静态方法中是不能出现this和super的
        Person.total = total;
    }
    public Person() {
        total++;
        id = total;
    }

    public static int getTotal() {
        return total;
    }
}



五、深入理解main方法

package com.alice.chapter10;

public class understandMain04 {
    public static void main(String[] args) {

    }
}

/*
    深入理解main方法
    解释main方法的形式:public static void main(String[] args)
    1、Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
    2、Java虚拟机需要在执行main()方法时不必创建对象,所以该方法必须是static
    3、该方法接收String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数,
    4、Java执行的程序参数1 参数2 参数3
    这里手写一段程序
    我这里在vim中手写
    现在讨论的问题是args的形参是什么时候传入的
    来一个for循环,遍历一下这个args数组
    打印第i个参数是。。
    编译,然后我们在终端中可以执行
    public class TestArgs {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("第" + (i+1) + "个元素是->" + args[i]);
        }
    }
}
    特别提醒,在main()方法中,我们可以直接调用main方法所在的类的静态方法和静态属性
    但是,不能直接访问类中的非静态成员,必须创建类的一个实例对象之后,才能够通过这个对象去访问类中的非静态成员

    现在我们可能会有问题,就是之前我们是在终端中传入args的参数的,我们如何在IDEA中传递参数呢?
    我们只需要点击右上角那个锤子的右边的下拉列表
    里面的Edit Configurations...然后在弹出的弹窗的Program arguments中输入要传入的参数即可

 */

六、代码块

package com.alice.chapter10;

public class CodeBlock05 {
    public static void main(String[] args) {
//        new Movie("电影1");
//        new Movie("电影2", "电影2的导演名字");
//        new Movie("电影3", "电影3的导演名字", 100);


//        new AA();  // 因为创建一个实例对象的时候会加载类信息,所以static代码块会被执行。这个式第一种情况
//        new AA();  // 因为创建了一个子类的实例对象,所以会先加载父类的信息到方法区当中,所以父类的静态代码块会被执行
//        // 然后就是加载子类的信息到方法区中,然后才是执行子类AA的静态代码块。这个式第二种情况
//        System.out.println("使用一下Cat类的静态成员打印猫咪们的主人:" + Cat.owner);这个式第三种情况
        // 这里访问了类的静态成员,所以也会加载类的信息,所以static的代码块会被执行。
//        System.out.println("使用一下Cat类的静态成员打印猫咪们的主人:" + Cat.owner);
        // 这里调用了子类的静态成员导致父类的信息被加载到方法区当中,这个式第四种情况
//        new DD();
//        new DD();  // 这里创建了两次DD对象,但是静态代码块只会执行一次,因为只会加载一次类信息。这个是第五种
//        new DD();
//        new DD();  // 这里创建了两次DD对象,但是普通代码块执行了两次。可以理解为普通代码块是对初始化方法的补充。这个是第六种
//        System.out.println(DD.test);  // 只是调用类的静态成员,只会调用类的静态代码块。这个是第七种

//        new A();  // 1
//        new A();  // 2
//        new A();  // 3

        new BBB();
    }
}

/*
    基本介绍
    代码块又称为初始化块,属于类中的成员,即是类中的一部分
    类似于方法,将逻辑语句封装在方法体中,通过{}包围起来

    但是和方法不同的是,代码块没有方法名,没有返回,没有参数,只有方法体,而不用通过对象或者是类
    显示调用,而是加载类时,或者创建对象时隐式调用。

    基本语法:
    修饰符 {

    };
    注意:
    1、修饰符式可选的,要写就只能写static
    2、代码块分成两类,使用static的,这种修饰的称为静态代码块,没有static修饰的叫做普通代码块
    3、逻辑语句可以为任何逻辑语句,比如输入,输出,方法调用,循环,判断等等。
    4、分号可以写上,也可以省略。

    代码块的好处
    1、代码块相当于式另外一种形式的构造器,对于构造器的补充机制,可以做初始化的操作
    2、如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性。
    3、通过编写一个程序快速入门代码块

    创建一个电影类
    电影的属性
    构造器
    一个参数的构造器
    两个参数的构造器
    三个参数的构造器
    电影名称
    电影的导演
    电影的门票价格
    如果三个构造器具有相同的语句,这个时候我们可以将相同的语句放到一个代码块中即可
    不管调用那个构造器,创建对象的时候都会先调用代码块的内容代码块调用的顺序式优先于构造器的

    代码块的注意事项和细节讨论
    1、static 代码块也叫做静态代码块,作用就是对类进行初始化,而它随着类的加载而执行,并且只会执行一次,如果式普通代码块
    每次创建一个对象,就会执行。
    之前我们举例的不是静态代码块,如果加上static就是静态代码块,只会执行一次。刚刚我们的式普通代码块,每次创建一个对象就执行了一次

    2、类什么时候会被加载(这个知识点非常重要)
        1、创建对象实例的时候
        2、创建子类对象实例的时候,父类也会被加载
        3、使用类的静态成员的时候(静态属性,静态方法)
        这里需要举例说明
        创建一个AA类,写一个静态代码块,打印这个式一个静态代码块,AA的
        main中创建对象实例,创建对象实例的时候可以看到这个静态代码块会被执行输出结果
        这个式第一种情况的说明。 1

        第二种情况,现在创建一个BB类,也写一个静态代码块,我们让AA继承BB
        先会加载父类到方法区,然后加载子类,这里还是创建一个子类对象AA
        父类先被加载,子类后被加载,这个式第二种情况。2

        第三种情况式使用类的静态成员的时候。
        创建一个Cat类,写上一个静态成员属性,和静态代码块
        我们使用这个类的静态成员的时候也会执行这个静态代码块
        以上三种情况完成,以上三种情况需要知道。3

        现在创建一个动物类,然后有一个静态代码块,现在让Cat继承动物类
        看看调用这个静态子类属性的时候看看,父类也会被加载。4

        另外看看静态代码块只会执行一次,这里不写继承关系的,写一个DD类
        里面有静态代码块,创建两个DD对象实例,这个静态代码块只会执行一次
        因为这个类只加载一次。
    3、普通代码块,在创建对象实例的时候,会被隐式调用。
    被创建一次,就会调用一次。
    如果只是使用类的静态成时,普通代码块并不会执行。
        这里继续在D类中创建一个普通代码块,这里的普通代码块会被调用两次,因为创建了两次对象
        还有一个细节就是如果只是使用类的静态成员,普通代码块不会被执行。
        在DD中创建一个静态属性,然后main中使用这个静态属性。肯定会输出静态属性的值,静态代码块肯定式会被调用的。
        但是普通代码块不会被调用。普通代码块式在创建对象的时候执行,和类的加载没有关系,静态代码块和类的加载有关系。
    4、创建一个对象的时候,在一个类调用顺序是:(重点,难点)
        调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,
        则按它们定义的顺序调用)
            创建一个A类,写一个静态代码块,再写一个私有的静态属性,再写一个静态方法返回一个给n1赋的值。
            创建一个A对象,因为静态属性的初始化写在静态代码块前面,所以第一个是静态属性的初始化然后静态代码块
            让然我们还可以颠倒过来写进行测试。如果有多个同理。1


        调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,
        则按照定义的顺序调用)
            在A中创建一个普通属性,再来一个方法,返回值赋值给这个变量,
            然后写一个普通代码块。
            也是一样优先级,谁在前面谁执行。
            也可以颠倒来测试
           总的来说是先静态然后普通。2
        最后才是调用构造器
            这里创建一个无参构造器(有没有参数无所谓),这里最终执行。3
    5、构造方法(构造器)的最前面其实隐藏了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕
    因此是优先于构造器和普通代码块执行的
        创建一个AAA类
        然后创建一个BBB类
        AAA中创建一个构造器,BBB中创建一个构造器
        然后在BBB中创建一个普通代码块
        然后BBB继承AAA
        然后创建一个BBB对象
        在BBB的构造方法中其实隐藏了super()
        还有调用本类普通代码块的调用
        即:先会执行父类的构造器,然后是BBB的普通代码块,然后是构造器
        再在AAA中创建一个普通代码块,因为Object中的构造方法没有什么输出,然后又是AAA的普通代码块,然后又是AAA的构造器
        同理。
 */

// 创建一个电影类
class Movie {
    // 1、电影的属性
    private String name;
    private String directorName;
    private double price;

    // * 将这些构造器的共有功能抽取到代码块中
    {
        System.out.println("购买电影票1");
        System.out.println("等待时间入场找到座位2");
        System.out.println("电影开始,开始看电影3");
    }

    // 2、电影的构造器
    public Movie(String name) {
        System.out.println("只有一个参数的构造器被调用一");
        this.name = name;
    }

    public Movie(String name, String directorName) {
        System.out.println("有两个参数的构造器被调用二");
        this.name = name;
        this.directorName = directorName;
    }

    public Movie(String name, String directorName, double price) {
        System.out.println("有三个参数的构造器被调用三");
        this.name = name;
        this.directorName = directorName;
        this.price = price;
    }
}

// 1、创建一个AA类,
class AA extends BB {
    // 写一个静态代码块,即,创建实例对象的时候会加载类
    static {
        System.out.println("这是一个AA类的静态代码块");
    }
}

// 2、创建一个BB类
class BB {
    static {
        System.out.println("这是一个BB类的静态代码块");
    }
}

// 3、创建一个Cat类
class Cat extends Animal {
    // 静态成员属性
    public static String owner = "alice";

    static {
        System.out.println("Cat类被执行了,静态属性->" + Cat.owner);
    }
}

// 4、创建一个动物类
class Animal {
    static {
        System.out.println("这里式Animal的静态代码块");
    }
}

// 5、创建一个DD类
class DD {
    public static String test = "DD";  // DD的静态成员,如果只是直接调用DD的静态成员只会执行static代码块,

    // 因为只会类信息
    static {
        System.out.println("这里式DD类的静态代码块");
    }

    {
        System.out.println("这里是DD类当中的普通代码块");
    }
}

// 创建一个A类型

/**
 * 创建一个A类,写一个静态代码块,再写一个私有的静态属性,再写一个静态方法返回一个给n1赋的值。
 * 创建一个A对象,因为静态属性的初始化写在静态代码块前面,所以第一个是静态属性的初始化然后静态代码块
 * 让然我们还可以颠倒过来写进行测试。如果有多个同理。1
 * <p>
 * 在A中创建一个普通属性,再来一个方法,返回值赋值给这个变量,
 * 然后写一个普通代码块。
 * 也是一样优先级,谁在前面谁执行。
 * 也可以颠倒来测试
 * 总的来说是先静态然后普通。2
 */
class A {
    private int n2 = resN2();

    public A() {
        System.out.println("A类的构造器最后执行。");
    }

    {
        System.out.println("这里是A类型的普通代码块");
    }

    private static int n1 = resN1();

    static {
        System.out.println("这里是A类的静态代码块");
    }

    public static int resN1() {
        System.out.println("对静态属性的赋值已经完成。");
        return 10;
    }

    public int resN2() {
        System.out.println("对普通属性的赋值已经完成");
        return 20;
    }
}

/**
 * 创建一个AAA类
 * 然后创建一个BBB类
 * AAA中创建一个构造器,BBB中创建一个构造器
 * 然后在BBB中创建一个普通代码块
 * 然后BBB继承AAA
 * 然后创建一个BBB对象
 * 在BBB的构造方法中其实隐藏了super()
 * 还有调用本类普通代码块的调用
 * 即:先会执行父类的构造器,然后是BBB的普通代码块,然后是构造器
 * 再在AAA中创建一个普通代码块,因为Object中的构造方法没有什么输出,然后又是AAA的普通代码块,然后又是AAA的构造器
 * 同理。
 */
class AAA {
    {
        System.out.println("我是AAA类中的普通代码块"); // 1
    }
    public AAA() {
        System.out.println("我是AAA的构造方法");  // 2
    }
}

class BBB extends AAA{
    {
        System.out.println("我是BBB中的普通代码块"); // 3
    }
    public BBB() {
        // super()

        System.out.println("我是BBB的构造方法");  // 4
    }
}

package com.alice.chapter10.day2;

/**
 * 6.
     * 我们看一下创建一个子类对象的时候,它们的静态代码块,静态属性初始化,
     * 普通代码块,普通属性初始化,构造方法的调用顺序如下:
     * 1、父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
     * 2、子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
     * 3、父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
     * 4、父类的构造方法
     * 5、子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
     * 6、子类的构造方法//面试题
 * 以上这六步看个大概,直接跑程序就知道了。
 *
 *
 * 7、静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任何成员。
 * 这里和静态方法一个意思,同理。
 */
public class ClassBlock01 {
    public static void main(String[] args) {
        new B02();  // 创建一个子类对象,创建一个对象的第一步是类的加载,因为隐藏了一个super所以先导入的是父类的信息然后子类的信息
        /*
                而加载一个类要先走静态的部分,因为静态相关的是和类加载相关的谁在前面谁执行
                总结:
                先加载类,因为静态的和类的加载有关,然后优先级相同,谁在前面谁执行
                然后是创建实例对象的,创建实例对象和普通的有关,同样优先级相同,谁在前面谁执行
                最后别忘记构造器。
                先父类后子类。
         */
    }
}

class A02 {
    // 类的加载和静态的属性和静态代码块有关,又因为静态的是同一个优先级,谁在前面谁先执行
    private static int n1 = getVal01();  // 先执行这个
    static {  // 然后执行这个
        System.out.println("A02的一个静态代码块...");   //  2222这个是第二个输出的,父类加载完成之后加载子类的
    }
    {  // 在前面先执行
        System.out.println("A02的第一个普通代码块...");  // 5555这个是第五个输出
    }
    public int n3 = getVal02();  // 在后面,后执行。
    public static int getVal01() {
        System.out.println("getVal01");  // 111111这个是第一个输出的
        return 10;
    }
    public int getVal02() {
        System.out.println("getVal02");  // 6666这个是第六个输出,完成这个,回到A02构造器中执行最后一句
        return 10;
    }
    public A02() {
        // 因为A02类的父类是Object这里就没有什么要输出的了
        // 完成类的加载之后回到这里来执行普通的代码块和普通的成员属性
        System.out.println("A02的构造器");  // 77 这个是第七个输出,到此为止子类的super已经完成,回到子类的构造器中执行普通的
    }
}

class B02 extends A02 {
    private static int n3 = getVal03();  // 先执行这个静态
    static {  // 然后执行这个静态
        System.out.println("B02的一个静态代码块。。。");  // 4444这个数第四个输出,到此位置类信息已经加载完成回到A02构造方法中
    }
    public int n5 = getVal04();  // 这个在前面这个普通的先执行
    {  // 在后面后执行,9999第九个输出
        System.out.println("B02的第一个普通代码块。。。");
    }
    public static int getVal03() {  // 3333这个是第三个输出
        System.out.println("getVal03");
        return 10;
    }
    public int getVal04() {  // 8888第八个输出
        System.out.println("getVal04");
        return 10;
    }
    public B02() {
        // 隐藏了一个super(),以及一个普通代码块的执行
        System.out.println("B02的构造器");  // 10 最后是构造方法
    }
}
/*
    输出
    getVal01
A02的一个静态代码块...
getVal03
B02的一个静态代码块。。。
A02的第一个普通代码块...
getVal02
A02的构造器
getVal04
B02的第一个普通代码块。。。
B02的构造器
 */

七、附图

image
image
image

到底count是放到堆中的,还是放到方法区的静态域中,这个老韩分享了一篇博客文章
不管是放到什么地方,反正静态变量有一点是可以肯定地,就是静态变量是会被该类地所有
实例对象共享。不管放到什么地方不影响使用。JDK8以前是放到方法区当中地,JDK8及其以后是放到堆里面地
通过反射机制会加载一个class对象,也就是一个类加载了之后会在堆中生成一个class对象,讲到反射地时候详细
讲解。

https://blog.csdn.net/x_iya/article/details/81260154/

这个博客地结论:
可以看到 static 变量保存在 Class 实例的尾部。
Class 对象确实在堆中。
类型指针保存在 Class 实例 17 * 8 的位置上。
https://www.zhihu.com/question/59174759
这个博客的结论:
先说结论:JDK7以上的版本,静态域存储于定义类的class对象当中,class对象如同堆中其他对象一样,
存在于GC堆当中。
posted @ 2025-05-11 22:50  请叫我虾  阅读(25)  评论(0)    收藏  举报