Java-EE-关键字(final、static及初始化顺序)
final
final
是一个关键字,它具有几种不同的含义,具体取决于它所修饰的元素:
-
最终变量:当
final
修饰一个变量时,这个变量就变成了常量。一旦被初始化后,它的值就不能被改变。final int MAX_COUNT = 10;
在这个例子中,
MAX_COUNT
是一个常量,它被初始化为10,之后不能被重新赋值。 -
最终方法:当
final
修饰一个方法时,这个方法不能被子类覆盖。public final void doSomething() { // 方法体 }
在这个例子中,
doSomething
方法是最终的,因此子类不能重写这个方法。 -
最终类:当
final
修饰一个类时,这个类不能被继承。public final class MyClass { // 类体 }
在这个例子中,
MyClass
是一个最终类,其他类不能继承它。final
关键字的使用可以提高程序的安全性和性能,因为它限制了某些元素的修改,从而减少了潜在的错误和性能开销。
static
在Java编程语言中,static
是一个关键字,它有多种用途,主要包括:
-
静态变量(类变量):当
static
修饰一个变量时,该变量属于类本身,而不是类的某个特定实例。所有实例共享同一个静态变量。public class MyClass { public static int staticVariable; }
在这个例子中,
staticVariable
是一个静态变量,可以通过类名直接访问,如MyClass.staticVariable
。 -
静态方法:当
static
修饰一个方法时,这个方法也属于类,而不是类的某个特定实例。静态方法只能直接访问类的静态成员,不能访问非静态成员。public class MyClass { public static void staticMethod() { System.out.println("This is a static method."); } }
在这个例子中,
staticMethod
是一个静态方法,可以通过类名直接调用,如MyClass.staticMethod()
。 -
静态初始化块:
static
关键字可以用于创建一个静态初始化块,它在类加载到JVM时执行一次。public class MyClass { static { // 初始化代码 } }
在这个例子中,花括号内的代码构成了一个静态初始化块。
-
静态内部类:
static
关键字可以用于创建静态内部类,这种内部类不需要外部类的实例就可以被创建。public class OuterClass { public static class StaticInnerClass { // 类体 } }
在这个例子中,
StaticInnerClass
是一个静态内部类,可以通过OuterClass.StaticInnerClass
直接创建实例。 -
静态导入:Java 5引入了静态导入特性,允许导入类的静态成员,使得在使用时不必指定类名。
import static java.lang.Math.PI;
在这个例子中,导入了
Math
类的PI
静态常量,之后可以直接使用PI
而不需要Math.PI
。
static
关键字的使用可以提高代码的组织性和可访问性,它允许将与特定实例无关的成员与类关联起来。此外,由于静态成员在类加载时初始化,它们还可以提高程序的性能。
初始化顺序
在Java中,对象的初始化顺序是一个重要的概念,因为它决定了对象的各个组成部分是按照什么顺序被创建和初始化的。以下是Java对象初始化的一般顺序:
-
静态变量初始化:在任何实例变量或实例方法之前,类的静态变量(也称为类变量)首先按照它们在类中出现的顺序进行初始化。
-
静态代码块:如果类中存在静态代码块(使用
static { ... }
定义),它将在静态变量初始化之后执行。静态代码块也是按照它们在类中出现的顺序执行的。 -
实例变量初始化:在创建类的实例时,类的实例变量(非静态变量)会按照它们在类中声明的顺序进行初始化。
-
实例代码块:如果类中存在实例代码块(使用
{ ... }
定义,并且不是静态的),它将在实例变量初始化之后执行。实例代码块也是按照它们在类中出现的顺序执行的。 -
构造器:最后,当使用
new
关键字创建对象时,会调用相应的构造器(构造函数)。如果存在多个构造器,那么调用哪一个取决于使用的是哪一个。
下面是一个简单的例子来说明这个顺序:
public class MyClass {
static int staticVar = 1; // 1. 静态变量初始化
static {
// 2. 静态代码块执行
}
int instanceVar = 2; // 3. 实例变量初始化
{
// 4. 实例代码块执行
}
public MyClass() {
// 5. 构造器执行
}
}
当你创建MyClass
的一个新实例时,上述的步骤会按照编号的顺序执行。
此外,如果一个类继承了另一个类,那么父类的初始化会在子类之前进行。这意味着父类的静态变量和静态代码块会先于子类的,父类的实例变量和实例代码块也会在子类的相应部分之前初始化,最后是子类的构造器。
public class ParentClass {
static int parentStaticVar = 3; // 父类的静态变量
static {
// 父类的静态代码块
}
int parentInstanceVar = 4; // 父类的实例变量
{
// 父类的实例代码块
}
public ParentClass() {
// 父类的构造器
}
}
public class ChildClass extends ParentClass {
static int childStaticVar = 5; // 子类的静态变量
static {
// 子类的静态代码块
}
int childInstanceVar = 6; // 子类的实例变量
{
// 子类的实例代码块
}
public ChildClass() {
// 子类的构造器
}
}
创建ChildClass
的一个实例时,初始化顺序将是:
ParentClass
的静态变量ParentClass
的静态代码块ChildClass
的静态变量ChildClass
的静态代码块ParentClass
的实例变量ParentClass
的实例代码块ChildClass
的实例变量ChildClass
的实例代码块ParentClass
的构造器ChildClass
的构造器
这个顺序确保了当一个对象被创建时,它的所有依赖都已经准备就绪。