《On Java 8》- 对象中数据(变量、方法、代码块)初始化的顺序

本文详细介绍了Java中对象的初始化过程,包括静态与非静态变量、方法及代码块的初始化顺序,探讨了构造器初始化的特点,并展示了如何使用静态与非静态初始化块。


根据《On Java 8》: 第六章初始化和清理-构造器初始化 总结

一、对象中数据(变量、方法、代码块)初始化的顺序(总结)

顺序:静态变量(静态代码块)> 非静态变量(非静态代码块)> 静态方法 > 非静态方法

无论创建多少个对象,静态数据都只占用一份存储区域,只初始化一次。

1、类中的变量:

  • 类中的变量在类中任何方法(包括构造器)被调用之前得到初始化。【即,变量初始化在所有方法之前】
  • 变量定义的顺序决定了它们的初始化顺序
  • 静态变量的初始化优先于非静态变量(不考虑定义顺序)

2、类中的方法:

  • 静态方法的初始化优先于非静态方法
  • 构造器实际上是静态方法

3、对象:

  • 静态对象的初始化优先于非静态对象(前提是:之前静态对象没有初始化,因为静态数据只会初始化一次。)

对于静态数据:

  • 无论创建多少个对象,静态数据都只占用一份存储区域。
  • 当加载完一个类对应的字节码(.class)文件后,有关静态初始化的所有动作都会执行。因此,静态初始化只会在首次加载 Class 对象时初始化一次。

静态代码块与非静态代码块:

  • 静态代码块与其他静态初始化动作一样,仅执行一次
  • 非静态代码块(不加static),在每一次创建对象时都会执行,创建一次执行一次。(可以使用它保证某些操作一定会发生)
  • 实例初始化子句是在构造器之前执行的。(静态代码块也是,并且静态代码块相当于静态变量,他们之间的初始化顺序如同变量之间的初始化顺序)
  • 就可以认为,在初始化顺序方面:可以把静态代码块当成静态变量,把非静态代码块当成普通变量来看待。

二、原文:构造器初始化

​ 可以用构造器进行初始化,这种方式给了你更大的灵活性,因为你可以在运行时调用方法进行初始化。但是,这无法阻止自动初始化的进行,他会在构造器被调用之前发生。因此,如果使用如下代码:

// housekeeping/Counter.java
public class Counter {
  int i;
  Counter() {
    i = 7; 
  }
  //...
}

i 首先会被初始化为 0,然后变为 7。对于所有的基本类型和引用,包括在定义时已明确指定初值的变量,这种情况都是成立的。因此,编译器不会强制你一定要在构造器的某个地方或在使用它们之前初始化元素——初始化早已得到了保证。

初始化的顺序

​ (变量的初始化顺序:)在类中变量定义的顺序决定了它们初始化的顺序。即使变量定义散布在方法定义之间,它们仍会在任何方法(包括构造器)被调用之前得到初始化。例如:

// housekeeping/OrderOfInitialization.java
// Demonstrates initialization order
// When the constructor is called to create a
// Window object, you'll see a message:
class Window {
  Window(int marker) {
    System.out.println("Window(" + marker + ")");
  } 
}
class House {
  Window w1 = new Window(1); // Before constructor
  House() {
    // Show that we're in the constructor:
    System.out.println("House()");
    w3 = new Window(33); // Reinitialize w3
  }
  Window w2 = new Window(2); // After constructor
  void f() {
    System.out.println("f()");
  }
  Window w3 = new Window(3); // At end
}
public class OrderOfInitialization {
  public static void main(String[] args) {
    House h = new House();
    h.f(); // Shows that construction is done
  } 
}
//输出:
Window(1)
Window(2)
Window(3)
House()
Window(33)
f()

​ 在 House 类中,故意把几个 Window 对象的定义散布在各处,以证明它们全都会在调用构造器或其他方法之前得到初始化。此外,w3 在构造器中被再次赋值。

​ 由输出可见,引用 w3 被初始化了两次:一次在调用构造器前,一次在构造器调用期间(第一次引用的对象将被丢弃,并作为垃圾回收)。这乍一看可能觉得效率不高,但保证了正确的初始化。试想,如果定义了一个重载构造器,在其中没有初始化 w3,同时在定义 w3 时没有赋予初值,那会产生怎样的后果呢?(只是一个引用)

静态数据的初始化

无论创建多少个对象,静态数据都只占用一份存储区域static 关键字不能应用于局部变量,所以只能作用于属性(字段、域)。如果一个字段是静态的基本类型,你没有初始化它,那么它就会获得基本类型的标准初值。如果它是对象引用,那么它的默认初值就是 null

​ 如果在定义时进行初始化,那么静态变量看起来就跟非静态变量一样。

​ 下面例子显示了静态存储区是何时初始化的:

// housekeeping/StaticInitialization.java
// Specifying initial values in a class definition
class Bowl {
    Bowl(int marker) {
        System.out.println("Bowl(" + marker + ")");
    }
    void f1(int marker) {
        System.out.println("f1(" + marker + ")");
    } }
class Table {
    static Bowl bowl1 = new Bowl(1);
    Table() {
        System.out.println("Table()");
        bowl2.f1(1);
    }
    void f2(int marker) {
        System.out.println("f2(" + marker + ")");
    }
    static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);
    Cupboard() {
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }
    void f3(int marker) {
        System.out.println("f3(" + marker + ")");
    }
    static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {
    public static void main(String[] args) {
        System.out.println("main creating new Cupboard()");
        new Cupboard();
        System.out.println("main creating new Cupboard()");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }
    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}
//输出:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
main creating new Cupboard()
Bowl(3)
Cupboard()
f1(2)
main creating new Cupboard()
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)

Bowl 类展示类的创建,而 TableCupboard 在它们的类定义中包含 Bowl 类型的静态数据成员。注意,在静态数据成员定义之前,Cupboard 类中先定义了一个Bowl 类型的非静态成员 b3(结果还是先显示的Bowl 类型的静态数据成员:bowl4、bowl5)

由输出可见,静态初始化只有在必要时刻才会进行。如果不创建 Table 对象,也不引用 Table.bowl1Table.bowl2,那么静态的 Bowl 类对象 bowl1bowl2 永远不会被创建。只有在第一个 Table 对象被创建(或被访问)时,它们才会被初始化。此后,静态对象不会再次被初始化

初始化的顺序先是静态对象(如果它们之前没有被初始化的话),然后是非静态对象,从输出中可以看出。要执行 main() 方法,必须加载 StaticInitialization 类,它的静态属性 tablecupboard 随后被初始化,这会导致它们对应的类也被加载,而由于它们都包含静态的 Bowl 对象,所以 Bowl 类也会被加载。因此,在这个特殊的程序中,所有的类都会在 main() 方法之前被加载。实际情况通常并非如此,因为在典型的程序中,不会像本例中所示的那样,将所有事物通过 static 联系起来。

概括一下创建对象的过程,假设有个名为 Dog 的类:

  1. 即使没有显式地使用 static 关键字,构造器实际上也是静态方法。所以,当首次创建 Dog 类型的对象或是首次访问 Dog 类的静态方法或属性时,Java 解释器必须在类路径中查找,以定位 Dog.class。

  2. 当加载完 Dog.class 后(后面会学到,这将创建一个 Class 对象),有关静态初始化的所有动作都会执行。因此,静态初始化只会在首次加载 Class 对象时初始化一次。

  3. 当用 new Dog() 创建对象时,首先会在堆上为 Dog 对象分配足够的存储空间。

  4. 分配的存储空间首先会被清零,即会将 Dog 对象中的所有基本类型数据设置为默认值(数字会被置为 0,布尔型和字符型也相同),引用被置为 null。

  5. 执行所有出现在字段定义处的初始化动作。

  6. 执行构造器。你将会在 “复用” 这一章看到,这可能会牵涉到很多动作,尤其当涉及继承的时候。

显式的静态初始化(静态代码块)

​ 你可以将一组静态初始化动作放在类里面一个特殊的 “静态子句”(有时叫做静态块)中。像下面这样:

// housekeeping/Spoon.java
public class Spoon {
    static int i;
    static {
        i = 47; 
    } 
}

​ 这看起来像个方法,但实际上它只是一段跟在 static 关键字后面的代码块。与其他静态初始化动作一样,这段代码仅执行一次:当首次创建这个类的对象或首次访问这个类的静态成员(甚至不需要创建该类的对象)时。例如:

// housekeeping/ExplicitStatic.java
// Explicit static initialization with "static" clause
class Cup {
    Cup(int marker) {
        System.out.println("Cup(" + marker + ")");
    }
    void f(int marker) {
        System.out.println("f(" + marker + ")");
    } }
class Cups {
    static Cup cup1;
    static Cup cup2;
    static {
        cup1 = new Cup(1);
        cup2 = new Cup(2);
    }
    Cups() {
        System.out.println("Cups()");
    } }
public class ExplicitStatic {
    public static void main(String[] args) {
        System.out.println("Inside main()");
        Cups.cup1.f(99); // [1]
    }
// static Cups cups1 = new Cups(); // [2]
// static Cups cups2 = new Cups(); // [2]
}

//输出:
Inside main
Cup(1)
Cup(2)
f(99)

​ 无论是通过标为 [1] 的行访问静态的 cup1 对象,还是把标为 [1] 的行去掉,让它去运行标为 [2] 的那行代码(去掉 [2] 的注释),Cups 的静态初始化动作都会执行。如果同时注释 [1] 和 [2] 处,那么 Cups 的静态初始化就不会进行。此外,把标为 [2] 处的注释都去掉还是只去掉一个,静态初始化只会执行一次

非静态实例初始化(非静态代码块)

​ Java 提供了被称为实例初始化的类似语法,用来初始化每个对象的非静态变量,例如:

// housekeeping/Mugs.java
// Instance initialization
class Mug {
    Mug(int marker) {
        System.out.println("Mug(" + marker + ")");
    } 
}
public class Mugs {
    Mug mug1;
    Mug mug2;
    { // [1]
        mug1 = new Mug(1);
        mug2 = new Mug(2);
        System.out.println("mug1 & mug2 initialized");
    }
    Mugs() {
        System.out.println("Mugs()");
    }
    Mugs(int i) {
        System.out.println("Mugs(int)");
    }
    public static void main(String[] args) {
        System.out.println("Inside main()");
        new Mugs();
        System.out.println("new Mugs() completed");
        new Mugs(1);
        System.out.println("new Mugs(1) completed");
    } 
}
//输出:
Inside main
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs()
new Mugs() completed
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs(int)
new Mugs(1) completed

​ 看起来它很像静态代码块,只不过少了 static 关键字。这种语法对于支持 “匿名内部类”(参见 “内部类” 一章)的初始化是必须的,但是你也可以使用它保证某些操作一定会发生,而不管哪个构造器被调用。从输出看出,实例初始化子句是在两个构造器之前执行的

FY25专业力考试·Java考试大纲 一、基本要求 1. 掌握编程思维,能够运用逻辑思维和抽象思维,构建数据结构和算法实现业务需求。 2. 掌握Java基础语法:基本数据类型、变量、常量、运算符、泛型、控制语句(条件、循环)、方法(定义、调用、重载)。 3. 掌握Java面向对象编程:包、作用域与访问控制、类、对象、实例化、继承、封装、多态、抽象类和接口、静态变量和静态方法、内部类和匿名类。 4. 掌握Java异常处理:异常类和异常处理机制、try-catch-finally语法、throw与throws、自定义异常。 5. 掌握Java数组与集合类型的使用方法:数组的定义和初始化、多维数组、List-Set-Map接口与实现类(ArrayList、HashMap、HashSet、Vector)。 6. 掌握注解:注解定义和使用、元注解。 7. 掌握Java数据库编程和I/O编程:JDBC基础(Connection、Statement、ResultSet)、流的概念、文件读写、字节流和字符流、缓存流。 8. 掌握Java多线程编程:线程的生命周期、创建和启动线程、线程同步、线程池。 9. 掌握Java反射机制:Class 类型、动态创建对象、反射访问属性和方法。 10. 掌握Lambda 表达式:函数式接口、Lambda 表达式和方法引用、Stream API。 11. 掌握Java Web 开发基础:理解Servlet/JSP工作原理、MVC模式、MVVM模式、Spring框架基础知识。 12. 掌握自动化单元测试工具与方法:熟悉Junit、DBUnit。 二、考试内容 1. 编程思维和算法构建 - 能够综合运用Java语言各种特性,编写程序实现业务要求 - 能够通过阅读现有代码,分析理解调用关系和处理过程,进而形成有效的修改方案 - 掌握对代码进行重构和优化的常见方法:抽出方法、复杂判断优化、定义共通处理、抽象基类 - 掌握SOLID原则 - 掌握常见设计模式:工厂模式、单例模式、装饰器模式、模板方法模式、适配器模式 2. Java基础语法 - 掌握java数据类型的分类。 - 掌握基本数据类型的种类 - 掌握Java基本类型变量的定义和使用。 - 掌握java整数类型分类、长度、字节大小和表示范围 - 掌握java浮点数类型遵从标准的浮点规则 - 掌握java基本数据类型转换 - 掌握布尔类型的转换 - 掌握java变量的声明。 - 掌握java变量的作用域 - 掌握java中定义常量的关键字 - 掌握java中基本的运算符使用 - 掌握java中运算符的优先级 - 掌握跳转语句 - 掌握方法定义内容 - 掌握方法调用方式 - 掌握调用方法时传参类型 - 掌握构造方法的特点 - 掌握重载的定义和使用场景 - 掌握重载和重写的区别 3. Java面向对象编程 - 掌握什么是Java包,如何声明和使用包,以及它们在代码组织中的作用 - 掌握访问修饰符(public、private、protected和默认)的作用和区别,以及它们在不同作用域中的使用 - 掌握什么是类和对象,如何定义类和创建对象 - 熟悉实例化对象的过程,如何使用构造方法初始化对象 - 掌握继承,如何使用Extends着急字来实现继承以及如何处理Java中的单继承 - 掌握封装,如何使用访问修饰符实现封装,以及它在java中的重要性和优势 - 掌握多态性的概念,包括编译时多态和运行时多态,以及如何实现方法重写和方法重载 - 掌握抽像类,如何声明和使用抽像类,抽像类和普通类之间有哪些区别 - 掌握接口,理解接口的概念和作用,以及如何声明、实现和使用接口 - 掌握静态变量和静态方法,如何声明和使用它们,与实例变量、实例方法有何区别 - 理解静态声明和静态导入的概念,以及它们的用法 - 掌握内部类概念,包括静态内部类、非静态内部类、局部内部类和匿名内部类,以及如何创建和使用它们 - 掌握如何实现单继承和多重继承 - 掌握方法的final修饰符和作用与使用场景 - 掌握Super关键字调用父类的构造方法方法的用法和注意事项 - 理解动态绑定(动态多态性)的概念,包括什么时候发生动态绑定和它的优势 - 掌握如何使用Final关键字来定义常量、阻止方法被重写和类被继承 - 掌握接口与抽像类之间的差异,以及在什么情况下使用它们更适合 - 掌握构造方法的作用和特点,以及默认构造方法的使用 - 掌握对象之间的关系,包括关联、聚合和组合 - 掌握重截构造方法,如何在一个类中实现多个构造方法 - 掌握类加载器和类加载机制在Java中的作用 - 掌握泛型的概念和作用,掌握泛型类、泛型接口和泛型方法 - 掌握使用枚举类型来定义常量和限制变量的取值范围 - 掌握序列化和反序列化的概念,以及如何在Java中实现对象的序列化 - 掌握反射,如何在java中使用反射机制获取类的信息和调用类的方法 - 掌握Record的语法特性和使用方法 4. Java异常处理 - 掌握日常工作或学习过程中常见的异常,理解异常原因 - 掌握运行时异常RuntimeException和非运行时异常的概念和区别 - 理解Checked Exceptions 和 Unchecked Exceptions的概念和区别 - 掌握异常栈信息的分析,判断根异常(… Caused by …) - 掌握try ... catch ... finally ... return各语句块,理解其执行顺序 - 掌握多个catch语句的匹配顺序(继承关系顺序- 掌握finally语句作用和用途 - 掌握try-with-resource语法,以及各语句块执行顺序 - 理解Throwable、Error、Exception的概念和区别(异常继承关系) - 理解OutOfMemoryError,能够说明该异常发生的可能原因和相应的处理措施 - 理解StackOverflowError,能够说明该异常发生的可能原因和相应的处理措施 - 理解NoSuchMethodError、NoSuchMethodException区别,能够说明该异常发生的可能原因和相应的处理措施 - 理解NoSuchFieldError、NoSuchFieldException区别,能够说明该异常发生的可能原因和相应的处理措施 - 理解NoClassDefFoundError,能够说明该异常发生的可能原因和相应的处理措施 - 理解ClassNotFoundException,能够分析异常发生的可能原因和相应的处理措施 - 掌握ArrayIndexOutOfBoundsException异常原因和解决方法 - 掌握ConcurrentModificationException异常原因和解决方法 - 掌握NullPointerException异常发生的原因、如何排查、避免措施 - 掌握NumberFormatException异常原因和解决方法 - 掌握FileNotFoundException异常原因和解决方法 - 能够对SocketException、ConnectException网络编程中常见异常进行分析 - 能够对ConnectTimeoutException、ReadTimeoutException网络编程中常见异常进行分析 - 掌握断言关键字assert用法、优点、注意事项 - 掌握自定义异常,编写静态工具类,实现对象非空校验(Object、数组、集合) 5. Java数组与集合类型 - 掌握数组的特点 - 掌握数组的遍历和排序算法 - 掌握数组的动态扩容、插入、删除 - 结合代码片段,理解数组的引用 - 掌握数组和集合的区别 - 掌握Map、Collection、List、Set、Queue、Stack体系结构 - 掌握Set和List的区别 - 掌握ArrayList、HashMap、HashSet、Vector概述和区别 - 掌握ArrayList的遍历和删除 - 掌握ArrayList和ArrayIndexOutOfBoundsException - 掌握ArrayList的排序 - 掌握ArrayList和数组之间转换 - 结合stream流,掌握对ArrayList的数据做排序、聚合、分组、去重、取值 - 理解Properties的用途 - 掌握HashMap和HashTable的区别 - 掌握HashMap的常用方法和讲解 - 理解HashMap底层实现原理 - 掌握HashMap的遍历方法 - 理解HashMap如何处理hash冲突 - 理解HashMap容量扩展原理 - 掌握ConcurrentHashMap原理、常见用途、注意事项 - 掌握Collections、Arrays工具类常用方法 6. Java注解 - 理解注解(Annotation)概念和作用 - 掌握元注解(@Target、@Retention、@Repeatable、@Inherited、@Report、@Documented) - 掌握Java内置常见注解的参数、用途、应用场景,包括:@Override、@Deprecated、@SuppressWarnings等。 - 掌握自定义注解,包括注解声明和元素定义。 - 掌握注解在AOP中的应用,比如配置切面日志。 7. Java数据库编程和I/O编程 - 理解JDBC概述和作用 - 掌握Connection接口及其方法 - 掌握Statement接口及其方法 - 掌握PreparedStatement接口及其方法 - 掌握如何执行SQL查询和更新操作 - 掌握ResultSet接口及其方法 - 掌握批处理和事务管理 - 掌握异常处理和资源关闭 - 理解BLOB和CLOB数据类型 - 理解连接池作用 - 理解流的概念和作用 - 理解字节流和字符流的区别 - 理解输入流和输出流的概念 - 掌握文件读写的基本操作 - 掌握字节流的常用类和方法 - 掌握字符流的常用类和方法 - 理解缓存流的作用和原理 - 掌握缓存流的常用类和方法 - 理解字符编码和字符集的概念 - 掌握字符串和字节数组的转换 - 掌握文件处理异常的处理方式 - 掌握流处理异常的处理方式 8. Java多线程编程 - 创建线程:掌握如何创建线程,掌握创建线程的各种方式。 - 启动线程:掌握如何启动线程,理解使用start()和run()方法有何区别。 - 线程同步:掌握什么是线程同步,理解为什么要进行线程同步。 - synchronized关键字:掌握如何使用synchronized关键字实现线程同步,掌握其使用方式。 -对象:掌握什么是锁对象,掌握如何使用锁对象进行线程同步。 - volatile关键字:理解volatile关键字的作用,理解什么时候使用volatile关键字。 - 线程安全:理解什么是线程安全,掌握如何保证线程安全。 - 线程池概述:理解什么是线程池,掌握为什么要使用线程池。 - 线程池的优势:能够列举使用线程池的优势和好处。 - Executor框架:理解什么是Executor框架,掌握它的主要组件。 - 线程池的创建:掌握如何创建线程池,掌握常见的线程池实现类。 - Callable和Future:理解什么是Callable和Future,掌握如何使用它们获取线程执行结果。 - 线程池的关闭:掌握如何正确关闭线程池,以及有哪些关闭方式。 - 并发集合:理解Java中常用的并发集合类及其使用场景。 - 同步器:理解CountDownLatch和CyclicBarrier。 9. Java反射机制 - 反射机制概述:理解什么是Java反射机制,以及为什么要使用反射。 - Class类:理解描述Class类的作用和常用方法- 获取Class对象:理解获取Class对象的三种方式。 - 动态创建对象:掌握如何使用反射动态创建对象- 反射访问属性:掌握如何使用反射访问对象的属性。 - 反射访问方法:掌握如何使用反射调用对象方法- 获取构造方法:掌握如何使用反射获取对象的构造方法- 获取字段信息:掌握如何使用反射获取对象的字段信息。 - 修改字段值:掌握如何使用反射修改对象的字段值。 - 反射应用:理解反射在实际开发中的典型应用场景。 10. Lambda 表达式 - 理解Lambda 表达式的语法和结构 - 掌握Lambda 表达式的使用场景和好处 - 熟练编写简单的Lambda 表达式 - 理解函数式接口的定义和特点 - 掌握使用@FunctionalInterface注解声明函数式接口 - 掌握常见的函数式接口,如Consumer、Predicate、Function- 熟悉函数式接口在Lambda 表达式中的应用 - 理解Lambda 表达式和方法引用的关系 - 能够比较Lambda 表达式和匿名内部类的异同 - 理解方法引用的概念和用法 - 掌握四种方法引用的类型:静态方法引用、实例方法引用、类方法引用、构造方法引用 - 掌握使用Lambda 表达式和方法引用简化代码 - 了解Stream API的作用和优势 - 理解如何创建Stream 对象 - 掌握Stream API中常用的中间操作和终端操作 - 熟悉Stream API在集合数据处理中的应用场景 11. Java Web 开发基础 - 理解Servlet的生命周期和工作原理 - 理解Servlet的请求和响应处理机制 - 理解JSP和Servlet的关系 - 了解MVC模式的基本思想 - 理解MVC模式的三个核心组件以及他们的关系:模型、视图和控制器 - 掌握如何将业务逻辑、数据和界面分离,实现代码的可维护性和可扩展性 - 理解MVVM模式在前端开发中的作用和优势 - 掌握ViewModel的概念和作用,VewMode如何实现业务逻辑与视图逻辑分离 - 熟悉数据绑定、事件处理等MVVM模式的关键特性 - 了解Spring框架的起源和发展 - 理解Spring的核心概念和作用:IOC、AOP - 理解SpringBoot和Spring的关系 12. 自动化单元测试 - 掌握JUnit注解:@Test、@Before, @After等 - 理解DBUnit工作原理 - 掌握JUnit中如何使用断言 - 掌握Mocking,Stubbing 三、题型设置 题型 比例(分数) 数量 分值 选择题(单选) 60% 20 每题3分 编程题 40% 4 每题10分 四、难度定义 难度 比例(分数) 低 20% 中 60% 高 20% 五、通过标准 职级 通过分数 02级初级软件工程师 30 03级软件工程师 39 04级高级软件工程师 48 六、参考资料 1. 《Java语言程序设计》(基础篇 原书第10版)机械工业出版社 作者:Y.Daniel Liang 2. 《Java核心技术·卷I》(原书第12版)机械工业出版社 作者:Cay S.Horstmann 3. 《On Java 中文版 基础卷》人民邮电出版社 作者:Bruce Eckel。 这是我的考试大纲。我属于04级高级软件工程师。按照题型设置选择题(单选)20,每题3分。编程题4,每题10分。和参考资料。帮我生成1套练习题。
最新发布
10-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值