Java类初始化和实例化

简介:

Java有以下几种方式创建类对象:

  • 利用new关键字
  • 利用反射Class.newInstance
  • 利用Constructor.newIntance(相比Class.newInstance多了有参和私有构造函数)
  • 利用Cloneable/Object.clone()
  • 利用反序列化

Constructor.newInstance不支持带原型入参的构造函数。

调用Class.getConstructor()方法获取无参默认构造Constructor时,如果用户自定义了有参构造函数,因为此时java并不会生成默认构造函数,所以Class.getConstructor()方法因找不到无参默认构造函数而抛异常。此时需要显示定义默认构造函数:

// Initialization.java
public class Initialization {
    private int age = 2000;
    private int salary = age + 1000;
    private String name = "Tom";

    public Initialization() {
        print();
    }

    public Initialization(Integer salary, String name) {
        print();

        this.salary = salary;
        this.name = name;

        print();
    }

    /**
    * Static code
    */
    {
        salary += 500;
    }

    private void print() {
        System.out.println("age=" + this.age);
        System.out.println("salary=" + this.salary);
        System.out.println("name=" + this.name);
    }

    public static Initialization construct(int salary, String name) throws Exception {
        Constructor<Initialization> constructorWithNoParams = Initialization.class.getConstructor();
        Constructor<Initialization> constructorWithParams = Initialization.class.getConstructor(Integer.class, String.class);
        return salary <= 0 || name == null ? constructorWithNoParams.newInstance() : constructorWithParams.newInstance(salary, name);
    }

    public Initialization deSerialize() throws Exception {
        // 写对象
        ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("student.txt"));
        output.writeObject(this);
        output.close();

        // 读取对象
        ObjectInputStream input = new ObjectInputStream(new FileInputStream("student.txt"));
        return (Initialization) input.readObject();
    }
}

再来看下Initialization类被编译为.class文件后的信息:

public class Initialization {
    private int age = 2000;
    private int salary;
    private String name;

    public Initialization() {
        this.salary = this.age + 1000;
        this.name = "Tom";
        this.salary += 500;
        this.print();
    }

    public Initialization(Integer salary, String name) {
        this.salary = this.age + 1000;
        this.name = "Tom";
        this.salary += 500;
        this.print();
        this.salary = salary.intValue();
        this.name = name;
        this.print();
    }

    private void print() {
        System.out.println("age=" + this.age);
        System.out.println("salary=" + this.salary);
        System.out.println("name=" + this.name);
    }

    public static Initialization construct(int salary, String name) throws Exception {
        Constructor constructorWithNoParams = Initialization.class.getConstructor(new Class[0]);
        Constructor constructorWithParams = Initialization.class.getConstructor(new Class[]{Integer.class, String.class});
        return salary > 0 && name != null?(Initialization)constructorWithParams.newInstance(new Object[]{Integer.valueOf(salary), name}):(Initialization)constructorWithNoParams.newInstance(new Object[0]);
    }

    public Initialization deSerialize() throws Exception {
        ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("student.txt"));
        output.writeObject(this);
        output.close();
        ObjectInputStream input = new ObjectInputStream(new FileInputStream("student.txt"));
        return (Initialization)input.readObject();
    }

    public static void main(String[] args) throws Exception {
        Initialization result = construct(0, "Paul");
    }
}

1、无论实例变量还是实例代码块,均遵从先父类后子类的初始化顺序。
2、对实例变量直接赋值或者利用实例代码块赋值,编译器会其代码填充到类的构造函数中。不允许书写顺序靠前的实例代码初始化在其后定义的实例变量。

Java强制要求Object对象(Object是Java的顶层对象,没有超类)之外的所有对象构造函数的第一条语句必须是超类构造函数的调用语句或者是类中定义的其他的构造函数,如果我们既没有调用其他的构造函数,也没有显式调用超类的构造函数,那么编译器会为我们自动生成一个对超类构造函数的调用。这样确保当前对象完成初始化前其父类已完成初始化,从而构建完整的对象。

如果默认构造函数内部调用了有参构造函数,仅允许在有参构造函数里调用父类构造函数。

静态代码块

  • 多个static按编码顺序依次处理
  • static变量的申明和初始化是两个不同的操作
  • static变量在编译期已确认值

以下代码是等价的:

// code list1
public class StaticInitialization {
    static {
        data = 1;
    }

    public static void main(String[] args) {
        System.out.println(data);
    }

    private static int data = 2;
}
// code list 2
public class StaticInitialization {
    private static int data;
    static {
        data = 1;
        data = 2;
    }

    public static void main(String[] args) {
        System.out.println(data);
    }
}

code list 2的字节码为:

public class StaticInitialization {
  public StaticInitialization();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: getstatic     #3                  // Field data:I
       6: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       9: return

  static {};
    Code:
       0: iconst_1
       1: putstatic     #3                  // Field data:I
       4: iconst_2
       5: putstatic     #3                  // Field data:I
       8: return
}

可以看到static变量在编译期就已放到常量const指向的内存地址里。

初始化顺序

如果没有继承关系,初始化顺序为:静态代码、静态代码块 > 成员变量、实例代码块 > 构造函数。否则,初始化顺序为先父后子。

所以初始化顺序:父类static静态变量 > 父类static代码块 > 子类static静态变量 > 子类static代码块 > 父类变量 > 父类实例代码块 > 父类构造函数 > 子类变量 > 子类实例代码块 > 子类构造函数

举个例子:

public class StaticTest {
    public static void main(String[] args) {
        staticFunction();
    }

    private static int data;

    static StaticTest st;

    static {   //静态代码块
        System.out.println("1");
    }

    {       // 实例代码块
        System.out.println("2");
    }

    static {
        data = 1;
    }

    static {
        st = new StaticTest();
    }

    StaticTest() {    // 实例构造器
        System.out.println("3");
        System.out.println("a=" + a + ",b=" + b);
    }

    public static void staticFunction() {   // 静态方法
        System.out.println("4");
    }

    int a = 110;    // 实例变量
    static int b = 112;     // 静态变量
}
/**
* 输出结果
* 1
* 2
* 3
* a=110,b=0
* 4
*/
目录
相关文章
|
5月前
|
Java 编译器 API
Java 密封类:精细化控制继承关系
Java 密封类:精细化控制继承关系
379 83
|
3月前
|
安全 Java 数据建模
Java记录类:简化数据载体的新选择
Java记录类:简化数据载体的新选择
264 101
|
3月前
|
安全 Java 开发者
Java记录类:简化数据载体的新方式
Java记录类:简化数据载体的新方式
300 100
|
6月前
|
IDE Java 数据挖掘
Java 基础类从入门到精通实操指南
这份指南专注于**Java 17+**的新特性和基础类库的现代化用法,涵盖开发环境配置、数据类型增强(如文本块)、字符串与集合处理进阶、异常改进(如密封类)、IO操作及实战案例。通过具体代码示例,如CSV数据分析工具,帮助开发者掌握高效编程技巧。同时提供性能优化建议和常用第三方库推荐,适合从入门到精通的Java学习者。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
251 36
|
4月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
444 143
|
2月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
114 4
|
2月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
206 5
|
2月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
196 1
|
2月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
246 1
|
2月前
|
存储 Java 程序员
【Java】(6)全方面带你了解Java里的日期与时间内容,介绍 Calendar、GregorianCalendar、Date类
java.util 包提供了 Date 类来封装当前的日期和时间。Date 类提供两个构造函数来实例化 Date 对象。第一个构造函数使用当前日期和时间来初始化对象。Date( )第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。
199 1