1、概述
static关键字用于描述静态变量,静态方法,静态块,静态类,静态内部类,静态import,本文详细描述各静态“对象”与非静态“对象”的区别。
2.1、 静态变量与非静态变量
静态变量在类的实例化之前完成初始化,在整个程序运行过程中只初始化一次,并且该类的所有实例都共享该变量,public下可以通过类名直接访问到该变量(可以通过实例对象访问该变量,但编译器会提示warning信息),等同于全局变量,全局相对于整个程序运行期而不是该类的所有实例对象。非静态的成员变量相当于类实例所有方法的全局变量。静态变量在程序运行期间不能被系统回收。
非静态变量在类的实例化过程中完成初始化,每次新建一个对象时就会初始化类中的非静态变量,即为其分配对应的内存空间。只能通过实例化的对象访问该变量(不同通常情况下将非静态变量设为private或者protected,从而通过类方法访问该变量),对该变量的修改只在该对象内可见,等同于局部变量。
public class StaticTest {
public static String staticVar="abc";
private String firstName;
private String lastName;
public StaticTest() {
System.out.println("类的默认初始化");
}
public StaticTest(String firstName,String lastName) {
this.firstName=firstName;
this.lastName=lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
public class Test { public static void main(String[] args) { //通过类名直接访问静态变量 System.out.println(StaticTest.staticVar); StaticTest test=new StaticTest("李", "四"); //通过对象实例访问静态变量,编译器提示警告信息 System.out.println(test.staticVar); //通过类方法访问非静态变量 System.out.println(test.getFirstName()); //修改静态变量后全局可见 StaticTest.staticVar="def"; System.out.println(test.staticVar); }<strong>}</strong>
2.2、 静态变量的使用场景
当类的多个实例需要共享某个变量及表示一些公共的常量时采用静态变量,后者通常配合final关键字使用。
public class StaticTest2 {
public static int count=0;
public StaticTest2() {
count++;
}
public static int getCount(){
return count;
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
new StaticTest2();
}
System.out.println("该类被实例化了"+StaticTest2.getCount()+"次");
}
public class StaticTest3 { private static final String MON="Mondey"; private static final String TUE="Tuesday"; private static final String WED="Wedsday"; private static final String THU="Thursday"; private static final String FRI="Friday"; private static final String SAR="Saturday"; private static final String SUN="Sunday";}3.1 静态方法与非静态方法
静态方法同静态变量一样,可以通过类名直接访问,相关程序代码在虚拟机加载class文件的过程中就被分配了内存空间,不需要在此之前执行类的实例化。因为执行静态方法时类的非静态成员并没有初始化,所以静态方法只能访问静态成员变量或者静态方法,不允许访问非静态成员变量或者非静态方法。因为this关键字代表的是某一个具体的实例化对象,通过this可以访问该对象的非静态成员变量,所以在静态方法中不允许使用this关键字,同理也不允许使用super关键字。非静态方法可以访问静态或者非静态的成员,只能通过类的实例化对象调用。
public abstract class StaticTest4 {
private static String name="shl";
private int age;
public static void test(){
//不能访问非静态变量
// System.out.println(age);
//不能访问非静态方法
// test2();
//不能使用this关键字
// System.out.println(this.age);
System.out.println(name);
System.out.println(test3());
}
public void test2(){
System.out.println(age);
}
public static String test3(){
return name;
}
//抽象方法不能是static的
//public static abstract String test4();
}
3.2、 静态方法的适用场景
因为静态方法跟类实例无关,所以静态方法等同于一个函数,当某些方法需要被频繁调用且该方法执行过程中与类实例的状态无关即不引用非静态成员时可将该方法设为静态方法,方便调用。比如Math中的方法都是静态方法,这些方法在执行过程中与Math这个类本身的状态没有任何关联。
4、 静态方法与静态变量的继承与覆盖
先看代码:
public class StaticTest51 {
public static String a="a";
public static String b="b";
public static void privateA(){
System.out.println(a);
}
public static void privateB(){
System.out.println(b);
}
}
public class StaticTest52 extends StaticTest51{
public static String a="c";
public static String b="d";
public static void privateA(){
System.out.println(a);
}
}
public class StaticTest53 extends StaticTest51 { }
public class Test5 { public static void main(String[] args) { //52“覆写了”51中的静态成员a,b和printA()方法 //printA()的代码原父类中的printA()一样,具体执行时打印子类中的a //printB()继承自父类,打印父类中的b StaticTest52.privateA();//c StaticTest52.privateB();//b //父类中的a并没有因为子类的覆写而改变 StaticTest51.privateA();//a //53直接继承自51,没有做任何的修改 StaticTest53.a="s"; StaticTest53.privateA();//s StaticTest52.privateA();//c //父类中的a随着子类中a的改变而改变,说明子类中的a在没有被覆写的条件下实际上还是跟父类共享 //同一个a,即并没有真正的“继承”一个a。真正的继承应该是子类a的改变与父类中a的改变互不影响。 StaticTest51.privateA();//s }}总结:静态方法和静态变量并不是真正意义上的“继承”,子类没有“覆写”父类的变量和方法时,子类跟父类共享静态方法和静态变量。 子类与父类中的非静态成员的改变是互不影响的,即子类与父类在内存空间是完全独立的。
5、静态代码块与非静态代码块
静态代码块同静态方法一样不能访问非静态变量和非静态方法,不能含有this或者super关键字,与静态方法不同的是,静态块会在类加载时自动执行,不需要也无法通过其他方式调用,且在整个程序运行过程中只执行一次。因此静态代码块通常适用于初始化某些系统资源如数据库连接池或者加载配置文件等。
非静态代码块在main方法中没有实际意义,跟普通代码一样按照先后顺序依次执行。处于类中的非静态代码块在表现上等同于“构造方法”,每次执行构造方法时会先执行非静态代码块,按照非静态代码块的先后顺序依次执行,最后再执行构造方法。两者在执行顺序上如下代码所示:
public class StaticTest71 {执行结果:
private String name="shl";
private static int age=23;
private boolean sex;
{
System.out.println(name);
System.out.println(age);
test();
test2();
System.out.println("第一非static代码块");
}
static{
//不能访问非静态变量
// System.out.println(name);
System.out.println(age);
test2();
//不能执行非静态方法
// test();
System.out.println("第一static代码块");
}
{
System.out.println("第二非static代码块");
}
static{
System.out.println("第二static代码块");
}
public StaticTest71() {
System.out.println("构造块");
}
public void test(){
System.out.println("非static方法");
}
public static void test2(){
System.out.println("static方法");
}
public static void main(String[] args) {
System.out.println("执行主方法");
{
System.out.println("主方法中的代码块一");
}
System.out.println("执行主方法二");
{
System.out.println("主方法中的代码块二");
}
System.out.println("===========================");
new StaticTest71();
System.out.println("===========================");
new StaticTest71();
System.out.println("===========================");
new StaticTest71();
}
//先依次执行静态代码块再执行main方法
23
static方法
第一static代码块
第二static代码块
执行主方法
//执行main方法中的代码块,与执行普通代码一样
主方法中的代码块一
执行主方法二
主方法中的代码块二
===========================
//执行构造方法,会先依次执行非静态代码块,静态代码块已经在main方法执行完毕,不会再执行
shl
23
非static方法
static方法
第一非static代码块
第二非static代码块
构造块
===========================
shl
23
非static方法
static方法
第一非static代码块
第二非static代码块
构造块
===========================
shl
23
非static方法
static方法
第一非static代码块
第二非static代码块
构造块
public class StaticTest72 extends StaticTest71{ static{ System.out.println("子类static块1"); } { System.out.println("子类非static块1"); } public StaticTest72() { System.out.println("子类构造块"); } { System.out.println("子类非static块2"); } static{ System.out.println("子类static块2"); } public static void main(String[] args) { System.out.println("主方法执行"); new StaticTest72(); System.out.println("======================"); new StaticTest71(); System.out.println("======================"); new StaticTest72(); }执行结果:
//父类的静态代码块先执行,其次是子类的静态代码块,最后是main方法执行顺序总结:父类中的静态块,子类中的静态块,main方法,父类中的非静态块,父类构造块,子类非静态块,子类构造块,父类与子类中的静态块只执行一次,静态块与非静态块都是按照声明的先后顺序依次执行。
23
static方法
第一static代码块
第二static代码块
//子类静态代码块
子类static块1
子类static块2
主方法执行
//先执行父类中的非静态代码块,构造块,再执行子类中的非静态块和构造块
shl
23
非static方法
static方法
第一非static代码块
第二非static代码块
构造块
子类非static块1
子类非static块2
子类构造块
======================
shl
23
非static方法
static方法
第一非static代码块
第二非static代码块
构造块
======================
shl
23
非static方法
static方法
第一非static代码块
第二非static代码块
构造块
子类非static块1
子类非static块2
子类构造块
6、静态内部类与静态类
静态内部类与普通内部类之间的区别与静态变量和非静态变量的区别有很大不同,具体参考另一篇博客《java内部类》。静态类是不存在的,不允许用static或者private,protected关键字修饰普通的类,只能用final,abstract,public关键字修饰普通的类,原因也很简单,用static或者private、protected来修饰普通的类没有实际意义。
7、静态import
有些对象比如Junit中的Assert对象的几乎所有方法都是static方法,引用时可以直接通过类名引用,但是如果该类的静态方法被频繁引用时,可以使用静态import,比如import static org.junit.Assert.*;,然后可以直接使用Assert中的静态方法,无需添加类名。