说明
Static 关键字是在编写代码的时候遇到的较难以理解的一个知识点 ,面试官也喜欢提这方面的问题。比如说主函数main的写法:
public static void main(String[] args){
}
这里为什么要用static呢?稍后再解答这个问题。
1. 静态变量
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块。
按照是否静态对类成员变量进行分类可分两种:
- 一种是被static修饰的变量,叫静态变量或类变量;
- 另一种是没有被static修饰的变量,叫实例变量。
-实例变量是绑定到类的某个特定实例的,它是不能被同一个类的不同对象所共享的。例如,创建了如下的两个对象:
class Person{
int i;
}
...
Person p1 = new Person();
Person p2 = new Person();
p1和p2是不相关的,它们存储在不同的内存位置。如图所示:
p1.i的变化不会影响到p2.i, 反之亦然.
如果想要一个类中的所有实例都共享一个数据,就要使用静态变量,也称之为类变量。
- 静态变量的定义
要声明一个静态变量,就要在这个变量的声明中加上修饰符static。静态变量能够被对象调用,也能被类直接调用。类中的常量是被该类的所有对象所共享的,如图所示:
所以一般在需要实现以下两个功能时使用静态变量:
在对象之间共享值时
方便访问变量时
静态变量的调用
public class Person{
static int i;
}
public class Test{
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();
Person.i = 10;
System.out.println("p1的值为" + p1.i);
System.out.println("p2的值为" + p2.i);
}
}
输出结果:
p1的值为10;
p2的值为10;
也可以用对象名调用:
public class Person{
static int i;
}
public class Test{
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();
Person.i = 10;
System.out.println("p1的值为" + p1.i);
System.out.println("p2的值为" + p2.i);
p1.i = 20;
System.out.println("p1的值为" + p1.i);
System.out.println("p2的值为" + p2.i);
}
}
p1的值为10;
p2的值为10;
p1的值为20;
p2的值为20;
2.静态方法
静态方法可以直接通过类名调用,任何的实例也都可以调用。
因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。
public class Person{
String name1;
static String name2;
static void talk(){
System.out.println("my name is " + name1);
System.out.println("my name is " + name2);
fun();
}
void fun(){
System.out.println("my name is " + name1);
System.out.println("my name is " + name2);
talk();
}
}
为什么在静态方法中不能调用非静态变量。我想是因为在面向对象中,调用成员变量必须是对象.成员变量,name1实际是this.name1;this指代的是调用这个函数的那个对象,但是静态函数咱们直接用类名来调用,不存在对象,因此会报错。
我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
3.静态代码块
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,就会按照static块的顺序来执行每个static块,并且只会执行一次。
静态代码块的用法
public Person{
static{
System.out.println("我是静态代码块");
}
}
public Test{
public static void main(){
System.out.println("Test");
}
}
输出结果:
我是静态代码块
Test
下面看个例子:
class Person{
private Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
Date startDate = Date.valueOf("1946");
Date endDate = Date.valueOf("1964");
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
isBornBoomer是用来这个人是否是1946-1964年出生的,而每次isBornBoomer被调用的时候,都会生成startDate和endDate两个对象,造成了空间浪费,如果改成这样效率会更好:
class Person{
private Date birthDate;
private static Date startDate,endDate;
static{
startDate = Date.valueOf("1946");
endDate = Date.valueOf("1964");
}
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。