static关键字
主要用法
static
主要有四种用法:
- 用来修饰成员变量,将其变为类的成员,从而实现该成员对于所有对象的共享;
- 用来修饰成员方法,将其变为类方法,可以直接使用
类名.方法名
的方式调用,常用于工具类(Math.max()
); - 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
- 静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。
静态变量与实例变量的区别
- 语法上的区别:静态变量前面需要加
static
关键字,而实例变量不需要。 - 实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
- 静态变量不属于某个实例对象,而是属于整个类。只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
public class Solution {
static String solution = "chenzf";
public static void main(String[] args) {
System.out.println(Solution.solution);
// 不建议使用该方式访问静态变量
Solution result = new Solution();
System.out.println(result.solution);
solution = "chenzufeng";
System.out.println(Solution.solution);
}
}
/*
chenzf
chenzf
chenzufeng
*/
修饰成员变量
static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。而如果代码尝试不用实例,来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。执行new来创建对象时,数据存储空间才被分配,其方法才供外界调用。
public class StaticTest
{
static int i = 47;
public static void main(String[] args)
{
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
System.out.println("st1: " + st1 + " st2: " + st2);
// st1: Object.StaticTest@3b07d329 st2: Object.StaticTest@41629346
System.out.println("st1:" + st1.i + " st2:" + st2.i); // st1:47 st2:47
System.out.println(StaticTest.i); // 47
}
}
即使创建了两个StaticTest
对象,StaticTest.i
也只有一份存储空间,这两个对象共享同一个i
。因此,st1.i
和st2.i
指向同一存储空间。
引用static变量有两种方法:可以通过一个对象去定位它,如st1.i
。也可以通过其类名直接引用(StaticTest.i
),这对于非静态成员则不行。使用类名是引用static
变量的首选方式,这不仅是因为它强调了变最的static结构,而且在某些情况下它还为编译器进行优化提供了更好的机会。
static变量在内存中的位置
public class Person {
String name;
int age;
public String toString() {
return "Name:" + name + ", Age:" + age;
}
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Name:zhangsan, Age:10
* Name:lisi, Age:12
*///~
}
两个Person对象的方法实际上只是指向了同一个方法定义。这个方法定义是位于内存中的一块不变区域——元空间(包括类元信息、字段、静态属性、方法、常量等[JDK8])。这一块存储区不仅存放了方法的定义,实际上从更大的角度而言,它存放的是各种类的定义。当通过new
来生成对象时,会根据这里定义的类的定义去创建对象。
多个对象仅会对应同一个方法,不管多少个对象,它们的方法总是相同的,尽管最后的输出会有所不同。
public class Person {
String name;
static int age;
/* 其余代码不变... */
/**Output
* Name:zhangsan, Age:12
* Name:lisi, Age:12
*/
}
给age
属性加了static
关键字之后,Person对象
就不再拥有age属性了,age属性会统一交给Person类
去管理,即多个Person对象
只会对应一个age属性
,一个对象如果对age属性做了改变,其他的对象都会受到影响。此时的age和toString()
方法一样,都是交由类去管理。
虽然static
可以让对象共享属性,但是实际中很少这么用,也不推荐这么使用。这样会让该属性变得难以控制,因为它在任何地方都有可能被改变。如果想共享属性,一般会采用其他的办法:
public class Person {
private static int count = 0;
int id;
String name;
int age;
public Person() {
id = ++count;
}
public String toString() {
return "Id:" + id + ", Name:" + name + ", Age:" + age;
}
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Id:1, Name:zhangsan, Age:10
* Id:2, Name:lisi, Age:12
*///~
}
上面的代码起到了,给Person的对象创建一个唯一id以及记录总数的作用。其中count由static修饰,是Person类的成员属性,每次创建一个Person对象,就会使该属性自加1,然后赋给对象的id属性。这样,count属性记录了创建Person对象的总数,由于count使用了private修饰,所以从类外面无法随意改变。
修饰成员方法
static修饰成员方法最大的作用,就是可以使用类名.方法名
的方式操作方法,避免了先要new出对象的繁琐和资源消耗:
public class PrintHelper {
public static void print(Object o){
System.out.println(o);
}
public static void main(String[] args) {
PrintHelper.print("Hello world");
}
}
在static方法的内部不能调用非静态方法,反之可以。
静态块
class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成员变量初始化");
static Book book2;
static {
book2 = new Book("static成员book2成员变量初始化");
book4 = new Book("static成员book4成员变量初始化");
}
public Person(String msg) {
System.out.println(msg);
}
Book book3 = new Book("book3成员变量初始化");
static Book book4;
public static void funStatic() {
System.out.println("static修饰的funStatic方法");
}
public static void main(String[] args) {
System.out.println("****************");
Person p1 = new Person("p1初始化");
Person.funStatic();
}
/**Output
* static成员book2成员变量初始化
* static成员book4成员变量初始化
* ***************
* book1成员变量初始化
* book3成员变量初始化
* p1初始化
* static修饰的funStatic方法
*/
}