面向对象基础11(初始化块)

语法:

  1. [修饰符]{
  2. 各种语句
  3. }

初始化块是没有名字的

修饰符有且只有static

  • static修饰的叫类初始化块(静态初始化块);
  • 没有static修饰的叫做实例初始化块(非静态初始化块);

类中可有定义变量的语句,但不能有赋值语句;

    1. public class Block{
    2. int age = 2;
    3. String name;
    4. {
    5. name = "ggttnn"
    6. }
    7. }

实例初始化块(没有修饰符static)

实例初始化快是一个“假象”——一个类在编译之后,实例初始化快就会消失——

实例初始化快的所有代码会被还原到每个构造器的开始部分

举例:

  1. public class Init {
  2. {
  3. System.out.println("假的实例初始化块");
  4. //在编译的时候,这句输出语句将自动出现在每个构造器的第一句
  5. }
  6. public Init() {
  7. System.out.println("无参数构造器");
  8. }
  9. public Init(String name) {
  10. System.out.println("有参数构造器" + name);
  11. }
  12. }

在编译时,以上语句的作用等同于

  1. public class Init {
  2. public Init() {
  3. System.out.println("假的实例初始化块");
  4. System.out.println("无参数构造器");
  5. }
  6. public Init(String name) {
  7. System.out.println("假的实例初始化块");
  8. System.out.println("有参数构造器" + name);
  9. }
  10. }

Test类中编译以后:

  1. public class InitTest {
  2. public static void main(String[] args) {
  3. Init a=new Init();
  4. Init b=new Init("233");
  5. }
  6. }
  7. /*
  8. 假的实例初始化块
  9. 无参数构造器
  10. 假的实例初始化块
  11. 有参数构造器233
  12. */

作用——将多个构造器的开始的相同的代码可以提取到实例初始化块进行管理

执行条件——只有程序调用构造器创建对象,程序总会先执行初始化块的语句

定义实例变量时指定的初始值也是“假象”

——指定的初始值在编译之后会变成构造器所有代码之前的一条赋值语句

举例:

  1. public class Init {
  2. {
  3. //在编译的时候,这句赋值语句将自动出现在每个构造器的第一句
  4. age = 20;
  5. }
  6. int age = 2;//此处源代码的排列顺序影响age最终的赋值
  7. public Init() {
  8. System.out.println("无参数构造器" + age);
  9. }
  10. public Init(String name) {
  11. System.out.println("有参数构造器" + age);
  12. }
  13. }

上述代码等同于:

  1. public class Init {
  2. int age;
  3. public Init() {
  4. age = 20;
  5. age = 2;
  6. System.out.println("无参数构造器" + age);
  7. }
  8. public Init(String name) {
  9. age = 20;
  10. age = 2;
  11. System.out.println("有参数构造器" + age);
  12. }
  13. }

固编译执行之后得:

  1. public class InitTest {
  2. public static void main(String[] args) {
  3. Init a = new Init();
  4. Init b = new Init("233");
  5. }
  6. }
  7. /*
  8. 无参数构造器2
  9. 有参数构造器2
  10. */

综上所述,实例初始化块的语句要还原到所有的构造器代码之前,

定义变量的初始值也要还原到构造器的所有代码之前,

二者的先后顺序取决于其在源代码中的先后顺序。

类初始化块(有修饰符static)

负责对类进行初始化——

当程序第一次主动使用该类的时候,系统会自动为该类分配空间,并对其初始化(调用类初始化块)

只要用户使用该类,基本都算主动使用类——出了仅仅使用类声明变量。

对比:

初始化块类型 执行次数 执行先后 执行时间
实例初始化块 1次 第一次主动使用该类
类初始化块 N次 每次调用构造器

同理,定义类变量时所指定的初始值,也是“假象”。

——指定的初始值。编译之后就变成了类初始化中的第一条赋值语句。

但到底是在类初始化代码前还是在其后,取决于他在源代码中的先后顺序。

举例:

  1. public class StaticInit {
  2. static int age = 30;
  3. //类初始化块
  4. static {
  5. age = 300;
  6. System.out.println("类初始化块"+age);
  7. }
  8. }
  1. public class StaticInitTest {
  2. public static void main(String[] args) {
  3. StaticInit a;//单纯的声明变量不会执行类初始化快
  4. StaticInit a=new StaticInit();
  5. System.out.println(StaticInit.age);
  6. }
  7. }
  8. /*
  9. 类初始化块300
  10. 300
  11. */

可见,类初始化只执行一次。

思考:

  1. public class Base {
  2. static {
  3. System.out.println("Base的类初始化块");
  4. }
  5. public Base() {
  6. System.out.println("无参构造器");
  7. }
  8. public Base(String name) {
  9. System.out.println("有参构造器");
  10. }
  11. }
  12. class Mid extends Base {
  13. public Mid(int age) {
  14. this();
  15. System.out.println("Mid的int构造器");
  16. }
  17. public Mid() {
  18. super("just");
  19. System.out.println("Mid的无参构造器");
  20. }
  21. }
  22. class Sub extends Mid {
  23. public Sub() {
  24. System.out.println("Sub的无参数构造器");
  25. }
  26. public Sub(double a) {
  27. System.out.println("Sub的double构造器");
  28. }
  29. }
  1. public class BaseTest {
  2. public static void main(String[] args) {
  3. new Sub(3.1);
  4. new Sub(4.2);
  5. }
  6. }
  7. /*
  8. Base的类初始化块
  9. 有参构造器
  10. Mid的无参构造器
  11. Sub的double构造器
  12. 有参构造器
  13. Mid的无参构造器
  14. Sub的double构造器
  15. */

子类的初始化必然要调用父类的构造器,一直到Object的构造器(祖宗类)。

  • 初始化任何类之前,一定先从Object类开始初始化,然后依次初始化该类的父类以及所有祖先类,最后初始化该类
  • 创建任何对象时,一定是先从Object开始执行,执行完它所有祖先类的构造器,最后执行他自己的构造器