static关键字
思维导图:
在 Java 中,static
是一个非常重要的关键字,它可以用来修饰类的成员,包括变量、方法、代码块以及内部类。下面为你详细介绍 static
关键字的各种用法和特点。
一.修饰内部类
静态内部类:当 static 修饰内部类时,这个内部类就成为了静态内部类。静态内部类不依赖于外部类的实例,可以直接创建。它只能访问外部类的静态成员,因为静态内部类在加载时可能还没有创建外部类的实例。
代码示例:
class Outer {static int outerStaticVariable = 10;int outerInstanceVariable = 20;// 静态内部类static class StaticInner {public void display() {// 可以访问外部类的静态成员System.out.println("Outer static variable: " + outerStaticVariable);// 以下代码会编译报错,因为静态内部类不能访问外部类的实例成员// System.out.println("Outer instance variable: " + outerInstanceVariable); }}
}public class Main {public static void main(String[] args) {// 可以直接创建静态内部类的实例Outer.StaticInner inner = new Outer.StaticInner();inner.display();}
}
说明:
StaticInner是Outer 类的静态内部类。在 StaticInner 类的 display 方法里,可以访问外部类的静态变量 outerStaticVariable,但不能访问外部类的实例变量 outerInstanceVariable在 Main 类的 main 方法中,可以直接创建 Outer.StaticInner 类的实例,无需先创建 Outer 类的实例。
static关键字不能修饰顶层类(直接定义在包中的类)。
代码示例:
// 以下代码会编译报错,因为 static 不能修饰顶层类
// static class TopLevelClass {
// public void method() {
// System.out.println("This is a top - level class.");
// }
// }
原因:顶层类代表一个独立的实体,其设计目的是为了被其他类继承或者实例化,从而实现不同的功能。而 static 关键字的作用是将成员和类相关联,而非和类的实例相关联。若将顶层类用 static 修饰,就会违背顶层类的设计初衷,所以 Java 语法不允许使用 static 修饰顶层类。
二.修饰成员变量和成员方法
2.1修饰成员变量
public class StaticTest01 {//修饰成员变量,此时是静态变量private static int age;private static String name;private static int number1 = 10;//实例变量private int number2 = 20;public static void main(String[] args) {StaticTest01.setAge(18);StaticTest01.setName("张三");//访问成员变量 类名.变量名System.out.println(StaticTest01.getAge() + "->" + StaticTest01.getName());}
}
2.2修饰成员方法同理
访问方式: 类名.方法名
2.3修饰代码块
public class StaticTest01 {private static int number1 = 10;private int number2 = 20;static {System.out.println(number1); //number1是静态的
// System.out.println(number2); //编译错误 静态不能访问实例变量}
注意:在实例方法,实例代码块中可以访问类变量,类方法;但是类变量,类方法,静态代码块不可以访问实例方法,实例变量,实例代码块。
原因:
- 静态方法/变量/代码块和类强相关;它不依赖于对象,在类加载时,静态方法/变量/代码块就被加载到内存中,无需创建类的实例就能调用。
- 实例方法/变量/代码块和实例对象强相关。调用实例方法/变量时,必须通过类的实例来调用,(对象的引用来调用)。
2.4static不能修饰构造方法
public class StaticTest01 {// public static StaticTest01() {
// static不能修饰构造方法
// }
}
不能用 static
修饰构造方法的原因
- 语义冲突:构造方法的目的是创建并初始化对象,而静态成员是属于类本身,不依赖于实例。如果构造方法被
static
修饰,这就与构造方法创建实例的初衷相违背。因为静态方法可以在没有创建任何实例的情况下被调用,若构造方法是静态的,那么就无法通过它来初始化特定的实例。 - 逻辑矛盾:构造方法在对象创建时自动调用,并且与具体的对象实例相关联。而静态方法没有
this
引用,this
引用指向调用该方法的对象实例。如果构造方法是静态的,就不存在this
引用,也就无法对特定的对象实例进行初始化操作。 - 设计原则:从设计角度来看,构造方法和静态方法有着不同的职责。构造方法负责对象的创建和初始化,而静态方法通常用于执行与类相关的通用操作,不涉及特定对象的状态。将两者区分开来可以使代码更加清晰和易于维护。
三.构造方法,静态代码块,普通代码块的执行流程
执行流程
当创建一个类的对象时,执行流程如下:
- 先进行类的加载,此时会执行静态代码块。静态代码块在类加载时只执一次。
- 创建对象实例,在调用构造方法之前,会先执行实例代码块。
- 执行构造方法完成对象的初始化。
代码示例:
public class Parent {public Parent() {System.out.println("父类构造方法执行");}{System.out.println("父类普通代码块执行");}static {System.out.println("父类静态执行");}
}public class Child extends Parent {public Child() {System.out.println("子类构造方法执行");}{System.out.println("子类普通代码块执行");}static {System.out.println("子类静态执行");}public static void main(String[] args) {Parent parent = new Child();System.out.println("=========证明static修饰的代码只会执行一次============");Parent parent2 = new Child();}
}
运行结果:
四.static成员的继承
4.1静态成员变量的继承
静态成员变量属于类,而非类的某个实例。当子类继承父类时,会继承父类的静态成员变量。不过,子类和父类实际上共享同一份静态成员变量,这意味着无论通过父类还是子类来访问或修改静态成员变量,操作的都是同一个变量。
代码示例:
class Parent {static int staticVariable = 10;
}class Child extends Parent {// 继承了 Parent 类的 staticVariable
}public class Main {public static void main(String[] args) {System.out.println(Parent.staticVariable); // 输出 10System.out.println(Child.staticVariable); // 输出 10Child.staticVariable = 20;System.out.println(Parent.staticVariable); // 输出 20System.out.println(Child.staticVariable); // 输出 20}
}
在上述代码中,Child类继承了Parent类的staticVariable 静态成员变量。当通过 Child 类修改staticVariable 的值后,再通过 Parent 类访问该变量,得到的是修改后的值,这表明它们共享同一份静态成员变量。
4.2静态方法的继承
静态方法同样属于类,子类可以继承父类的静态方法。子类可以直接使用父类的静态方法,也可以定义与父类同名的静态方法,不过这并非方法重写(Override
),而是方法隐藏(Hide
)。方法隐藏是指子类定义了与父类同名、同参数列表的静态方法,此时通过子类调用该方法时,调用的是子类的方法;通过父类调用该方法时,调用的是父类的方法。
代码示例:
class Parent {static void staticMethod() {System.out.println("Parent's static method");}
}class Child extends Parent {static void staticMethod() {System.out.println("Child's static method");}
}public class Main {public static void main(String[] args) {Parent.staticMethod(); // 输出 "Parent's static method"Child.staticMethod(); // 输出 "Child's static method"Parent parent = new Child();parent.staticMethod(); // 输出 "Parent's static method",因为静态方法根据引用类型调用}
}
在上述代码中,Child 类定义了与 Parent类同名的静态方法 staticMethod,这是方法隐藏。当通过 Parent 类引用调用 staticMethod 时,调用的是 Parent 类的方法;当通过 Child 类引用调用 staticMethod 时,调用的是 Child 类的方法。
总结:
Java类中包含了成员变量、成员方法、构造方法、初始化块和内部类(包括接口、枚举)5种成员,static关键字可以修饰除了构造方法外的其他4种成员。static关键字修饰的成员被称为类成员。类成员属于整个类,不属于单个对象。 static关键字有一条非常重要的规则,即类成员不能访问实例成员,因为类成员属于类的,类成员的作用域比实例成员的作用域更大,很容易出现类成员初始化完成时,但实例成员还没被初始化,这时如果类成员访问实力成员就会引起大量错误。
static修饰的部分会和类同时被加载。被static修饰的成员先于对象存在,因此,当一个类加载完毕,即使没有创建对象也可以去访问被static修饰的部分。 静态方法中没有this关键词,因为静态方法是和类同时被加载的,而this是随着对象的创建存在的。静态比对象优先存在。也就是说,静态可以访问静态,但静态不能访问非静态,而非静态可以访问静态。