Java中static的使用
原文链接https://www.cnblogs.com/dolphin0520/p/10651845.html
作者Matrix海 子
本文为笔记
0. 概述
static既可以修饰方法,也可以修饰成员变量,也可以用来修饰代码块。
static最大的好处是可以在不创建对象的情况下直接使用被static修饰的方法或者变量。
1. static修饰方法
被static修饰的方法被称为静态方法。静态方法和普通方法最大的区别在于,静态方法是没有this的。这也就解释了为什么在静态方法内不能访问类的非静态成员变量和方法,因为非静态成员变量和方法都依赖于具体的对象,而静态方法都没有this一说,更不用谈访问对象里的东西了。
但是要注意,非静态方法中是可以访问到静态成员方法和变量的。
假设此时有一段代码
class Student {
private String name = "Cayman";
private static String s_name = "static Cayman";
public Student() {
}
//这段代码是正确的
public void printName() {
System.out.println(name);
System.out.println(s_name);
printStaticName();
}
public static void printStaticName() {
System.out.println(name);
//在静态方法中无法访问非静态成员方法
System.out.println(s_name);
//在静态方法中无法访问非静态成员变量
printName();
}
private static void main(String[] args){
Student s = new Student();
//下面这行会报错,因为此时都没有对象,更不用谈访问成员变量name了
Student.printStaticName();
}
}
所以归根结底,不能在静态方法中使用非静态成员变量和方法的根本原因是,在调用静态方法的时候,都没有具体的对象来供它访问非静态成员变量和方法。
而对于非静态成员方法来说,当然可以访问本身就不依赖于对象的静态方法。
所以说,只要我们在编码时发现某个方法需要我们不创建对象就可以被使用,就要把这个方法声明为static的,这也是为什么main方法需要被声明为static,因为程序运行main方法时,是没有具体对象的。
2. static修饰成员变量
被static修饰的变量被称为静态变量,和非静态变量的区别在于
-
静态变量是在类视角下的一个变量,而非静态变量是在对象视角下的一个变量,也就是说,类中声明的静态变量只有一份副本,这份副本被所有该类的对象共同调用,而非静态变量对于每个具体的对象而言都有它自己的一份副本,互不影响。
-
静态变量在类加载的时候就被初始化了,且只会初始化一次!
-
static变量位于方法区;普通成员变量位于堆区。
-
static变量生命周期与类的生命周期相同;普通成员变量和其所属的对象的生命周期相同。
-
在对象序列化时(Serializable),static变量会被排除在外(因为static变量是属于类的,不属于对象)
静态变量有几个常见的使用场景
-
在不创建对象的情况下使用变量
-
作为共享变量,例如计数器
-
单例模式
3. static修饰代码块
在Java语言中,用花括号{}括住的一段代码就是代码块,static可以直接用来修饰一个代码块,例如
class Student() {
static {
System.out.println("Hello!")
}
// field
...
// constructor
...
//method
...
}
此时,在Student类在被初次加载且仅当初次加载时,才会输出Hello字符串。且static代码块可以放在程序的任何地方。
static代码块往往可以提高代码的效率,因此用来实现那些在程序运行中需要且只需要完成一次的代码段。
4. static还可以静态导包
例如
// 如果不加上static就会报错
import static java.lang.Math.random()
5. static的一些注意事项
-
static是不会改变域或方法的访问权限的,在Java中能够影响到访问权限的只有private、public、protected这几个关键字。
-
static不允许修饰局部变量。
6. 含有static的代码分析
public class Main {
static int value = 33;
public static void main(String[] args) throws Exception{
new Main().printValue();
}
private void printValue(){
int value = 3;
System.out.println(this.value);
}
// 33
}
在printValue中使用this.value访问到的其实就是Main类中的静态变量value,这是因为在Java中,this关键字指向的是当前对象的引用,和局部对象没有关系,而在main函数中,在new Main()之后其实就已经生成了一个Main对象了,而这个对象是拥有value这个域的(每个Main的对象都有这个域,且指向同一个地方)。
public class Test extends Base{
static{
System.out.println("test static");
}
public Test(){
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test();
}
}
class Base{
static{
System.out.println("base static");
}
public Base(){
System.out.println("base constructor");
}
}
/*
base static
test static
base constructor
test constructor
*/
上述代码可以看到,Test类是Base的子类。于是在程序运行之前,父类先被加载,于是Base类被加载,运行其中的static块,之后加载Test类,运行其中的static块。进入main方法后会先调用父类的构造器,再调用子类Test的构造器。
//1.加载Test类,输入test static
public class Test {
//5.初始化Myclass的父类的成员变量,加载Person类
Person person = new Person("Test");
static{
System.out.println("test static");
}
//8.person域初始化完毕后,开始构造Test对象,输出test constructor
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
//2.尝试new一个Myclass对象,此时要加载Myclass类
new MyClass();
}
}
//6.加载Person类,输出static块,初始化Person对象,完成构造
class Person{
static{
System.out.println("person static");
}
//7.初始化完毕,输出person test
public Person(String str) {
System.out.println("person "+str);
}
}
//3.加载Myclass类,发现是Test的子类,就先加载Test,但是Test已经加载过了,所以直接进入类,输出myclass static,
//4.加载完毕后,开始通过构造器生成对象,但是要先初始化父类的成员变量
class MyClass extends Test {
//9.父类初始化完毕,开始初始化子类的成员变量,再次进入Person构造器,输出person MyClass
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
//10.通过构造器构造MyClass对象,输出myclass constructor
public MyClass() {
System.out.println("myclass constructor");
}
}
/*
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
*/

浙公网安备 33010602011771号