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
*/
目录
相关文章
|
24天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
36 6
|
12天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
36 17
|
4天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
8天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
41 4
|
9天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
20 2
|
13天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
17天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
17天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
16天前
|
Java
Java 静态变量的初始化顺序
【10月更文挑战第15天】了解 Java 静态变量的初始化顺序对于正确编写和维护代码至关重要。通过深入理解初始化顺序的原理和细节,我们可以更好地避免潜在的问题,并提高代码的质量和可靠性。
|
17天前
|
存储 Java 编译器
java wrapper是什么类
【10月更文挑战第16天】
21 3