相信大家对static关键字并不陌生,回顾一下我们在第一次课创建的第一个Java程序HelloWorld.java:
主方法的修饰符里就有static
,为什么主方法必须有static
修饰符呢?通过本讲的学习,你会找到答案的。
Java中的static
关键字,用于修饰类的成员,如成员变量、成员方法以及代码块等,被static
修饰的成员具备一些特殊性。比如被static
关键字修饰的成员变量、方法可以被类直接访问,而不需要预先构造类的实例化对象。
new
关键字创建该类的实例对象后,系统才会为每个对象分配内存空间,存储各自的数据。有时候,开发人员希望某些特定的数据在内存里只有一份,而且能被一个类的所有实例对象所共享。例如某个学校所有学生共享同一个学校名称,此时完全不必在每个学生对象所占用的内存空间中都声明一个变量来表示学校名称,而可以在对象以外的空间声明一个表示学校名称的变量让所有对象来共享。这种能被类的所有对象共享的变量就是静态变量。
从图可以看出,所有学生对象共享一个名称为schoolName的变量。在一个Java类中,要实现这种功能可以使用static
关键字来修饰成员变量,该变量被称作静态变量,它可以被所有实例所共享。
package net.hw.lesson12;
/**
* 功能:学生类
* 作者:华卫
* 日期:2020年5月1日
*/
public class Student {
static String schoolName; // 静态变量schoolName
private String name; // 实例变量name
private int age; // 实例变量age
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void speak() {
System.out.println("我叫" + name + ",今年" + age + "岁了,是" + schoolName + "的学生。");
}
}
package net.hw.lesson12;
/**
* 功能:测试Student类
* 作者:华卫
* 日期:2020年5月1日
*/
public class Example1201{
public static void main(String[] args) {
// 给静态变量赋值
Student.schoolName = "泸州职业技术学院";
// 创建第1个学生对象,并设置属性
Student stu1 = new Student();
stu1.setName("李晓红");
stu1.setAge(19);
// 创建第2个学生对象,并设置属性
Student stu2 = new Student();
stu2.setName("洪晓熙");
stu2.setAge(18);
// 调用两个学生对象的方法
stu1.speak();
stu2.speak();
// 通过对象名访问静态变量
System.out.println("stu1.schoolName = " + stu1.schoolName);
System.out.println("stu2.schoolName = " + stu2.schoolName);
// 通过类名访问静态变量
System.out.println("Student.schoolName = " + Student.schoolName);
}
}
运行程序,查看结果:
由于schoolName是静态变量,既可以通过Student.schoolName的方式进行调用,也可以通过Student的实例对象进行调用,比如stu1.schoolName和stu2.schoolName。第11行代码将变量schoolName赋值为“泸州职业技术学院”后,两个学生对象stu1和stu2的schoolName属性值均为“泸州职业技术学院”。
注意,static关键字只能用于修饰成员变量,不能用于修饰局部变量,否则编译会报错。
普通成员方法也叫实例方法,表明只有在实例化对象之后才能调用,如果想不创建对象就能调用某个方法,那么此时可以考虑使用静态方法。
在一个类中,被static
关键字修饰的方法称为静态方法。
package net.hw.lesson12;
/**
* 功能:调用静态方法
* 作者:华卫
* 日期:2020年5月1日
*/
public class Example1202 {
public static void main(String[] args) {
// 设置静态属性值
Student.schoolName = "泸州职业技术学院";
// 用“类名.方法”方式调用静态方法
Student.tell();
// 实例化对象
Student stu = new Student();
// 用“对象名.方法”方式调用静态方法
stu.tell();
}
}
在一个静态方法中只能访问用static
修饰的成员,原因是没有被static
修饰的成员需要先创建对象才能访问,而静态方法在被调用时可以不创建任何对象。
static
关键字修饰的代码块称为静态代码块。当类被加载时,静态代码块会执行,并且只会执行一次。
在程序中,经常使用静态代码块来对类的成员变量进行初始化。
package net.hw.lesson12;
/**
* 功能:静态代码块
* 作者:华卫
* 日期:2020年5月1日
*/
public class Person {
static String country;
static {
country = "中国";
System.out.println("执行Person类的静态代码块");
}
public void speak() {
System.out.println("我是" + country + "人。");
}
}
package net.hw.lesson12;
/**
* 功能:测试Person类
* 作者:华卫
* 日期:2020年5月1日
*/
public class Example1203 {
static {
System.out.println("执行Example1203的静态代码块");
}
public static void main(String[] args) {
// 实例化两个Person对象
Person p1 = new Person();
Person p2 = new Person();
// 调用对象方法
p1.speak();
p2.speak();
}
}
上述案例,我们输出了Math的静态属性E和PI,调用了Math的静态方法sqrt()。
如何能查看一个类的源码呢?很简单,将鼠标移到该类上,左手按着Ctrl键,同时点击鼠标左键,即可打开该类的源码文件进行查看。
package java.lang;
import java.util.Random;
import sun.misc.FloatConsts;
import sun.misc.DoubleConsts;
/**
* @author unascribed
* @author Joseph D. Darcy
* @since JDK1.0
*/
public final class Math {
/**
* Don't let anyone instantiate this class.
*/
private Math() {}
/**
* The {@code double} value that is closer than any other to
* e, the base of the natural logarithms.
*/
public static final double E = 2.7182818284590452354;
/**
* The {@code double} value that is closer than any other to
* pi, the ratio of the circumference of a circle to its
* diameter.
*/
public static final double PI = 3.14159265358979323846;
/**
* Returns the trigonometric sine of an angle. Special cases:
* - If the argument is NaN or an infinity, then the
* result is NaN.
*
- If the argument is zero, then the result is a zero with the
* same sign as the argument.
*
* The computed result must be within 1 ulp of the exact result.
* Results must be semi-monotonic.
*
* @param a an angle, in radians.
* @return the sine of the argument.
*/
public static double sin(double a) {
return StrictMath.sin(a); // default impl. delegates to StrictMath
}
}