Java static用法详解
Java 程序要运行就必须在某个类中定义一个 main 方法作为程序的入口,我们会发现在定义 main 方法时,有一个 static 关键字,那 static 表示什么呢?为什么要用 static 来修饰 main 方法?本节内容为你一一揭晓。
static 表示静态或全局,可以用来修饰成员变量和成员方法以及代码块。要访问类中的成员变量或者成员方法,必须首先创建该类的对象,然后通过对象才能访问成员变量和成员方法。也就是说,成员变量和成员方法的访问必须依赖于对象,这是常规的方式。
而使用 static 修饰的成员变量和成员方法,它们独立于该类的任何一个实例对象,访问时不依赖于该类的对象,可以理解为被该类的所有实例对象共用,所以说它是全局。
用 static 修饰的成员变量叫作静态变量也叫作类变量,static 修饰的成员方法叫作静态方法,也叫作类方法。
多个对象共用,内存中只有一份,没有被 static 修饰的成员变量叫实例变量,没有被 static 修饰的成员方法叫作实例方法,一个对象对应一个,内存中有多份,如下图所示。
图 1 静态成员变量和静态成员方法
既然静态成员变量和静态成员方法独立于任何一个实例对象,我们在访问这些静态资源时就无须创建对象,直接通过类即可访问,例如:
接下来带领大家分析几个实例。先分析下面这段程序(代码段 A):
再分析一段程序(代码段 B):
最后再分析一段程序(代码段 C):
我们在使用 static 修饰成员方法时需要注意,静态方法中不能使用 this 关键字,不能直接访问所属类的实例变量和实例方法,可直接访问类的静态变量和静态方法。若要访问类的实例变量和实例方法,必须先实例化类的对象,然后通过对象去访问,举个简单的例子:
静态代码块的特点是只执行一次,在什么时候执行呢?当该类被加载到内存时执行,不需要手动调用,它会自动执行。那么,首次加载类是什么时候呢?这里我们需要简单地阐述一下 Java 加载类的机制。
我们知道,Java 代码是由类构成的,但是真正运行时是通过对象和对象之间的相互调用关系来完成需求的。即程序运行时,需要在内存中创建多个对象,对象怎么创建?需要通过类来创建,类是模板,同时这个模板只需要加载一次,所以程序在运行时,首先会将程序中用到的类加载到内存中,并且只加载一次。然后通过类来创建多个对象以完成具体的业务。
被加载到内存中的类叫作运行时类,静态代码块就是在加载类的时候执行的,因为类只加载一次,所以静态代码块也只执行一次。简单理解,当代码中第一次出现某个类时,就会执行静态代码块,静态代码块只能访问静态变量和静态方法,下面的实例演示了如果定义一个静态代码块:
static 表示静态或全局,可以用来修饰成员变量和成员方法以及代码块。要访问类中的成员变量或者成员方法,必须首先创建该类的对象,然后通过对象才能访问成员变量和成员方法。也就是说,成员变量和成员方法的访问必须依赖于对象,这是常规的方式。
而使用 static 修饰的成员变量和成员方法,它们独立于该类的任何一个实例对象,访问时不依赖于该类的对象,可以理解为被该类的所有实例对象共用,所以说它是全局。
用 static 修饰的成员变量叫作静态变量也叫作类变量,static 修饰的成员方法叫作静态方法,也叫作类方法。
多个对象共用,内存中只有一份,没有被 static 修饰的成员变量叫实例变量,没有被 static 修饰的成员方法叫作实例方法,一个对象对应一个,内存中有多份,如下图所示。
图 1 静态成员变量和静态成员方法
既然静态成员变量和静态成员方法独立于任何一个实例对象,我们在访问这些静态资源时就无须创建对象,直接通过类即可访问,例如:
public class User { public static String name; public static void show(){ System.out.println("这是一个User对象"); } }测试类的 main 方法作为程序的入口,直接通过类访问静态成员变量和静态成员方法,例如:
public class Test { public static void main(String[] args) { User.name = "张三"; System.out.println(User.name); User.show(); } }程序运行结果为:
张三
这是一个User对象
接下来带领大家分析几个实例。先分析下面这段程序(代码段 A):
public class Test { private int id; public static void main(String[] args) { Test test = null; for(int i = 0; i < 10; i++) { test = new Test(); test.id++; } System.out.println(test.id); } }程序中的 id 为成员变量,每个实例化对象都拥有一个自己的 id。循环中创建了 10 个 Test 对象,内存中就会有 10 个 id,每个 id 都进行了加一操作,由 0 变为 1,并且每次循环都会将新创建的 Test 对象赋给变量 test,相当于更新了 10 次 test 的引用。因为每次的 id 值都为 1,所以最终的结果也为 1。
再分析一段程序(代码段 B):
public class Test { private static int id; public static void main(String[] args) { Test test = null; for(int i = 0; i < 10; i++) { test = new Test(); test.id++; } System.out.println(test.id); } }程序中的 id 是静态变量,属于类不属于对象,内存中只有一份,所以任何时候对 id 的修改都会作用于同一个变量,循环中共执行了 10 次 num++,所以 num 的结果为 10。
最后再分析一段程序(代码段 C):
public class Test { private static int id; public static void main(String[] args) { for(int i = 0; i < 10; i++) { Test.id++; } System.out.println(Test.id); } }同代码段 B 相比,唯一的区别在于 B 是通过实例化对象来访问静态变量的,代码 C 是通过类来访问静态变量的,运行结果没有区别。
我们在使用 static 修饰成员方法时需要注意,静态方法中不能使用 this 关键字,不能直接访问所属类的实例变量和实例方法,可直接访问类的静态变量和静态方法。若要访问类的实例变量和实例方法,必须先实例化类的对象,然后通过对象去访问,举个简单的例子:
public class User { public static String name; public int num; public void test(){} public static void show(){ System.out.println("这是一个User对象"); User user = new User(); user.num=1; user.test(); } }static 除了可以修饰成员变量和成员方法之外,还可以修饰代码块,被 static 修饰的代码块叫作静态代码块。
静态代码块的特点是只执行一次,在什么时候执行呢?当该类被加载到内存时执行,不需要手动调用,它会自动执行。那么,首次加载类是什么时候呢?这里我们需要简单地阐述一下 Java 加载类的机制。
我们知道,Java 代码是由类构成的,但是真正运行时是通过对象和对象之间的相互调用关系来完成需求的。即程序运行时,需要在内存中创建多个对象,对象怎么创建?需要通过类来创建,类是模板,同时这个模板只需要加载一次,所以程序在运行时,首先会将程序中用到的类加载到内存中,并且只加载一次。然后通过类来创建多个对象以完成具体的业务。
被加载到内存中的类叫作运行时类,静态代码块就是在加载类的时候执行的,因为类只加载一次,所以静态代码块也只执行一次。简单理解,当代码中第一次出现某个类时,就会执行静态代码块,静态代码块只能访问静态变量和静态方法,下面的实例演示了如果定义一个静态代码块:
public class User { public static int num; static{ num++; System.out.println("执行了静态代码块"); } }测试类的 main 方法作为程序的入口,代码如下所示:
public class Test { public static void main(String[] args) { User user = new User(); User user2 = new User(); User user3 = new User(); System.out.println(user.num); System.out.println(user2.num); System.out.println(user3.num); } }程序运行结果为:
执行了静态代码块
1
1
1