static是一个Java的关键字,也是一个修饰符,可以用于修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
static变量
static成员变量:其实可以看做一个伪全局的成员变量,因为这个变量不属于具体的某个实例化对象,而是统一的存在内存中在main函数加载的时候跟随者main一起加载,static变量可以通过类名直接访问也可以通过实例化对象来访问。
public class Student {
public static int age = 20;
public String gender = "Man";
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.gender); //Man
System.out.println(s.age); //20
System.out.println(Student.age); //20
// System.out.println(Student.gender); //报错,因为gender是非static变量不能直接通过类名访问
System.out.println("=========================");
Student.age += 1; //改动即为全局改动;
System.out.println(s.age); //21
System.out.println(Student.age); //21
}
}
用法:对于一些特殊的变量例如一个班级的学生,那么可以将班级写死因为大家都是一个班的没有必要每个变量都存一份。这样可以加快程序运行时间并且减小内存占用。
static方法
static成员方法: static修饰的方法一般称作静态方法,静态方法和静态变量一样不属于任何对象因此对于静态方法来说是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,静态方法中不允许访问非静态的方法 或者 非静态的成员变量。虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量的。
/*
静态方法与非静态方法
1. 静态方法可以通过类名直接调用
2. 非静态方法通过实例化对象来调用不能通过类名调用
3. 静态方法不能调用非静态方法或者非静态变量,
因为静态方法是和main一起加载,加载之前还没有实例化对象~
4. 非静态方法可以调用静态方法或者变量,理由同上
*/
public class Student {
public static int age = 20;
public String gender = "Man";
public void run(){
System.out.println("Student: run"); //Student: run
eat(); //Student: eat
}
public static void eat(){
System.out.println("Student: eat"); //Student: eat
System.out.println(age); //20,静态方法可以访问静态变量或者方法
// System.out.println(gender); //报错,eat是静态成员方法不允许访问非静态的成员变量
// run(); //报错,eat是静态成员方法不允许访问非静态的成员方法
}
public static void main(String[] args) {
Student s = new Student();
s.run(); //Student: run
s.eat(); //Student: eat
Student.eat(); //Student: eat
// Student.run(); //报错,main是静态的不能访问非静态的成员方法
}
}
static块
static块 和 匿名块 以及 构造函数 可以写在任何地方没有代码顺序要求。但是他们的执行机制和顺序有先后之分
-
static代码块在加载类的时候就会加载执行,而且只执行一次;
-
匿名块在实例化对象的时候每次都会执行一次
-
构造函数同上
-
在无继承状态下执行顺序 static块 > 匿名块 > 构造函数。
-
继承状态下先执行父类static块,再执行子类static块,然后是父类的匿名块和父类的构造函数,最后是子类的匿名块子类的构造函数。为什么会是这样?首先JVM调用了main方法,main方法进栈然后遇到了B b1 = new B();语句会将父类和子类分别加载进内存然后才创建对象,我们知道静态代码块的执行优先于对象的创建,所以在父类加载进内存的时候父类静态代码块就执行了然后是子类加载进内存,与此同时子类的静态代码块也被执行了,然后就是子类的构造方法了,因为Java是分层初始化的,要想初始化子类必须先把父类初始化完毕后才可以(先有爸爸再有孩子),所以先初始化父类,父类中又有构造代码块,构造代码块的执行又优先于构造方法,所以第三条输出的语句是父类的构造代码块,然后是父类的空参构造方法,至此父类初始化完毕,最后初始化子类,所以有了下面的结果。
class A{
{
System.out.println("A: 匿名代码块");
}
static{
System.out.println("A: static块");
}
public A() {
System.out.println("A: 构造函数");
}
}
class B extends A{
{
System.out.println("B: 匿名代码块");
}
static{
System.out.println("B: static块");
}
public B() {
System.out.println("B: 构造函数");
}
}
public class Main {
public static void main(String[] args) {
B b1 = new B();
System.out.println("=================");
B b2 = new B();
}
}
/*
A: static块
B: static块
A: 匿名代码块
A: 构造函数
B: 匿名代码块
B: 构造函数
=================
A: 匿名代码块
A: 构造函数
B: 匿名代码块
B: 构造函数
*/
static库类
但是一般不会这么用
//导入一个static方法
import static java.lang.Math.PI; //细节
public class Main {
public static void main(String[] args) {
// System.out.println(Math.PI); //单纯的导入Math类
System.out.println(PI); //导入静态的Math之后可以直接用PI
}
}
愿你走出半生,归来仍是少年~