zoukankan      html  css  js  c++  java
  • Java对象初始化

    自动初始化(默认值)

    一个类的所有基本数据成员都会得到初始化,运行下面的例子可以查看这些默认值:

    class Default{
        boolean t;
        char c;
        byte b;
        short s;
        int i;
        long l;
        float f;
        double d;
        public void show() {
            System.out.println("基本类型   初始化值
    "+
                            "boolean<----->" + t +"
    " +
                            "char<----->" + c +"
    " +
                            "byte<----->" + b + "
    " +
                            "short<----->" + s + "
    " +
                            "int<----->" + i + "
    " +
                            "long<----->" + l + "
    " +
                            "float<----->" + f + "
    " +
                            "double<----->" + d + "
    "
            );
    
        }
    }
    public class InitValue {
        public static void main(String[] args) {
            Default d = new Default();
            d.show();
        }
    }

    【运行结果】:

    基本类型   初始化值
    boolean<----->false
    char<----->
    byte<----->0
    short<----->0
    int<----->0
    long<----->0
    float<----->0.0
    double<----->0.0
    

    其中,char类型的默认值为空(null)。

    对于非基本数据类型而言,对象的句柄也会被初始化:

    class Person {
        private String name;
        // setter
    }
    class Default {
        Person p;
        public void show() {
            System.out.println("Person<----->" + p);
        }
    }
    public class InitValue {
        public static void main(String[] args) {
            Default d = new Default();
            d.show();
        }
    }

    【运行结果】:

    Person<----->null
    

    可见,句柄初始化值为null。这就是说,如果没有为p指定初始化值就调用类似于p.setName的方法,就会出现异常。

    规定初始化

    如果需要自己为变量赋一个初始值,可以在定义变量的同时赋值。

    class Default{
        boolean t = true;
        char c = 'A';
        byte b = 47;
        short s = 0xff;
        int i = 24;
        long l = 999;
        float f = 1.2f;
        double d = 1.732;
        public void show() {
            System.out.println(
                            "boolean<----->" + t +"
    " +
                            "char<----->" + c +"
    " +
                            "byte<----->" + b + "
    " +
                            "short<----->" + s + "
    " +
                            "int<----->" + i + "
    " +
                            "long<----->" + l + "
    " +
                            "float<----->" + f + "
    " +
                            "double<----->" + d + "
    "
            );
    
        }
    }
    public class InitValue {
        public static void main(String[] args) {
            Default d = new Default();
            d.show();
        }
    }

    甚至可以通过一个方法来进行初始化;

    class Person {
        int i = set();
        //...
    }

    这些方法也可以使用自变量:

    class Person {
        int i;
        int j = set(i);
        //...
    }

    构建器初始化

    构建器进行初始化的优点是可以在运行期决定初始化值。例如:

    class Person {
        int age;
        Person() {
            age = 89;
        }
    }

    age首先会初始化为0,然后变成89。对于所有基本类型以及对象的句柄,这种情况都是成立的。

    初始化顺序

    在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,那么变量仍然会在调用任何方法(包括构造函数)之前得到初始化。例如:

    class Pet {
        Pet(int age) {
            System.out.println("Pet(" + age + ")");
        }
    }
    
    class Person {
        Pet t1 = new Pet(1);
    
        Person() {
            System.out.println("---Person()---");
            t3 = new Pet(33);
        }
    
        Pet t2 = new Pet(2);
        void show() {
            System.out.println("show----running");
        }
        Pet t3 = new Pet(3);
    }
    
    
    public class OrderOfInitialization {
        public static void main(String[] args) {
            Person p = new Person();
            p.show();
        }
    }

    【运行结果】:

    Pet(1)
    Pet(2)
    Pet(3)
    ---Person()---
    Pet(33)
    show----running
    

    上例中,虽然t1、t2、t3的定义遍布于类中,但是初始化的先后顺序是由t1、t2、t3的定义顺序决定的(自己动手调换t1、t2、t3看看结果),且初始化优先于构建器执行,当调用Person的构建器时,t3重新初始化。

    静态数据的初始化

    如果数据是静态的(static),同样的过程也会执行。若属于基本类型,而且未对其进行初始化,就会自动获得自己的标准基本类型初始值;若它是指向一个对象的句柄,除非创建一个对象同它连接起来,否则得到一个空值(null)。如果在定义时初始化,采取的方式与非静态值是不同的,这是因为static只有一个存储区域。例如:

    class Bowl {
        Bowl(int marker) {
            System.out.println("Bowl(" + marker + ")");
        }
        void f(int marker) {
            System.out.println("f(" + marker + ")");
        }
    }
    
    class Table {
        static Bowl b1 = new Bowl(1);
        Table() {
            System.out.println("Table()");
            b2.f(1);
        }
        void f2(int marker) {
            System.out.println("f2(" + marker + ")");
        }
        static Bowl b2 = new Bowl(2);
    }
    
    class Cupboard {
        Bowl b3 = new Bowl(3);
        static Bowl b4 = new Bowl(4);
        Cupboard() {
            System.out.println("Cupboard()");
            b4.f(2);
        }
        void f3 (int marker) {
            System.out.println("f3(" + marker + ")");
        }
        static Bowl b5 = new Bowl(5);
    }
    
    public class StaticInitialization {
        public static void main(String[] args) {
            System.out.println("Creating new Cupboard() in main");
            new Cupboard();
            System.out.println("Creating new Cupboard() in main");
            new Cupboard();
            t2.f2(1);
            t3.f3(1);
        }
        static Table t2 = new Table();
        static Cupboard t3 = new Cupboard();
    }

    【运行结果】:

    Bowl(1)
    Bowl(2)
    Table()
    f(1)
    Bowl(4)
    Bowl(5)
    Bowl(3)
    Cupboard()
    f(2)
    Creating new Cupboard() in main
    Bowl(3)
    Cupboard()
    f(2)
    Creating new Cupboard() in main
    Bowl(3)
    Cupboard()
    f(2)
    f2(1)
    f3(1)
    

    静态代码块

    Java允许将其他static初始化工作划分到类内一个特殊的代码块中,这种代码块的形式为static关键字,后面跟着一个方法主体,称为静态代码块。静态代码块只有在第一次生成那个类的对象首次访问属于那个类的static成员时执行。例如:

    class Person {
        Person(int age) {
            System.out.println("Person(" + age + ")");
        }
        void f(int age) {
            System.out.println("f(" + age + ")");
        }
    }
    
    class Persons {
        static Person p1;
        static Person p2;
        static {
            p1 = new Person(1);
            p2 = new Person(2);
        }
        Persons() {
            System.out.println("Persons()");
        }
    }
    
    public class ExplicitStatic {
        public static void main(String[] args) {
            System.out.println("Inside main()");
            Persons.p1.f(18);//1
        }
        static Persons x = new Persons();//2
        static Persons y = new Persons();//2
    }

    在标记为1的行内访问static对象p1的时候,或在行1被注释而行2未被注释是,用于Persons的static初始化模块就会运行。若1和2都被注释掉,则用于Persons的静态代码块不会执行。

    静态属性和静态代码块执行的先后顺序

    class Person {
        Person(int age) {
            System.out.println("Person("+age+")");
        }
    }
    class Persons {
        static Person p = new Person(2); // 1
        static {
            p = new Person(3);
        }
        static Person p = new Person(2); // 2
    
    }
    public class CompStaticInit {
        public static void main(String[] args) {
    
        }
        static Persons x = new Persons();
    }

    根据注释1保留2,注释2保留1的结果分析可知,静态属性和静态代码块的执行顺序取决于编码的顺序。谁在前面就先执行谁。

    非静态属性的初始化

    class Animal {
        Animal(int age) {
            System.out.println("Animal(" + age + ")");
        }
        void f(int age) {
            System.out.println("f(" + age + ")");
        }
    }
    public class NotStaticInit {
        Animal a1;
        Animal a2;
        {
            a1 = new Animal(1);
            a2 = new Animal(2);
            System.out.println("a1 & a2 initialized");
        }
        NotStaticInit() {
            System.out.println("NotStaticInit");
        }
        public static void main(String[] args) {
            System.out.println("Inside main()");
            NotStaticInit x = new NotStaticInit();
        }
    }

    类似于静态代码块,匿名代码块与非静态属性的初始化顺序取决于编码顺序

    继承中的对象初始化过程

    class Insect {
        int i = 1;
        int j;
    
        Insect() {
            prt("i = " + i + ", j = " + j);
            j = 2;
        }
    
        static int x1 = prt("static Insect.x1 initialized");
    
        static int prt(String s) {
            System.out.println(s);
            return 3;
        }
    }
    
    public class Beetle extends Insect {
        int k = prt("Beeklt.k initialized");
    
        Beetle() {
            prt("k = " + k);
            prt("j = " + j);
        }
    
        static int x2 = prt("static Bootle.x2 initialized");
        static int prt(String s) {
            System.out.println(s);
            return 4;
        }
    
        public static void main(String[] args) {
            prt("Beetle constructor");
            Beetle b = new Beetle();
        }
    }

    【运行结果】:

    static Insect.x1 initialized
    static Bootle.x2 initialized
    Beetle constructor
    i = 1, j = 0
    Beeklt.k initialized
    k = 4
    j = 2
    

    对Beetle运行Java时,发生的第一件事情是装载程序到外面找到那个类。在装载过程中,装载程序发现一个基础类,所以随之将其载入。无论是否生成基础类的对象,这一过程都将执行。如果基础类含有另一个基础类,则另一个基础类随即也会载入,以此类推。接下来就在根基础类中执行static初始化,再在下一个衍生类中执行,以此类推。这是因为衍生类的初始化可能要依赖于对基础类成员的初始化。

    当类都装载完毕,就能创建对象。首先,这个对象中的所有基本数据类型都会设置成为他们的默认值,对象句柄设为null。然后执行基础类的构建器。这种情况是自动完成的(衍生类的构造函数中默认调用了super(),也可以通过super指定基类的构建器)。基础类构建器完成后,衍生类实例变量就会按本来的顺序得到初始化,然后执行构建器的剩余的主体部分。

    总结对象创建的过程:

    • 静态只在类加载的时候执行且只执行一次;
    • 非静态只有在实例化的时候执行,每次创建对象都执行;
    • 静态在非静态之前执行,基类静态优先于衍生类静态执行;
    • 静态属性和静态代码块的执行属性取决于它们在类中的位置,谁在前先执行谁;
    • 非静态属性和构造块的执行顺序取决于它们在类中的位置,谁在前执行谁。
  • 相关阅读:
    团队开发项目客户端——游戏子系统的设计(上)
    团队开发项目客户端——注册子系统的设计
    团队项目开发客户端——登录子系统的设计
    协程库
    Linux下用命令查看CPU ID以及厂家等信息
    c++中字符串的截取:
    C++ STL 的各结构实现
    关于mysql查询最近一条记录
    BerkeleyDB原理及其对应API
    高性能服务器设计(Jeff Darcy's notes on high-performance server design
  • 原文地址:https://www.cnblogs.com/xiaoxiaoyihan/p/4934839.html
Copyright © 2011-2022 走看看