static关键字总结

 static 关键字主要用来修饰方法 ,类 ,成员变量以及代码块。
 
1、修饰成员变量和成员方法
被 static 修饰的成员属于类,不属于这个类的某个对象,而是被类中所有对象共享,可以并且建议通过类名调用。被static 声明的成员变量属于静态成员变量,静态变量存放在 Java 内存区域的方法区,方法区与Java的堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。调用格式:类名.静态变量名或者类名.静态方法名();
 
2、静态代码块
静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.静态代码块的格式是
static {    
语句体;   
}
一个类中的静态代码块可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果静态代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。注意:静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问.比如这样就是错误的:
class demo{
staic {
int i =3;
system.out.println(i); //错误
}
private static int i;
}
 
 
3、静态内部类(static修饰类的话只能修饰内部类)
静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个外部类引用,该引用是指向创建它的外部类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。
public class Singleton {
    
    声明为 private 避免调用默认构造方法创建对象
    private Singleton() {
    }
    
    声明为 private 表明静态内部该类只能在该 Singleton 类中被访问
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用方法getUniqueInstance()从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
 
4、静态导包(用来导入类中的静态资源,JDK1.5之后的新特性):
格式为:import static 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
Math. --- 将Math中的所有静态资源导入,这时候可以直接使用里面的静态方法,而不用通过类名进行调用
  如果只想导入单一某个静态方法,只需要将换成对应的方法名即可
import static java.lang.Math.;
  换成import static java.lang.Math.max;具有一样的效果
public class Demo {
  public static void main(String[] args) {
    int max = max(1,2);
    System.out.println(max);
  }
}
 
5、静态方法和非静态方法
静态方法属于类本身,非静态方法属于从该类生成的每个对象。 如果类中方法执行的操作不依赖于其类的各个变量和方法,可以设置为静态(这将使程序的占用空间更小)。 否则,它应该是非静态的。
很简单的一个栗子:
class Foo {
    int i;
    public Foo(int i) {
       this.i = i;
    }
    public static String method1() {
       return "An example string that doesn't depend on i (an instance variable)";
    }
    public int method2() {
       return this.i + 1;  Depends on i
    }
}
像这样调用静态方法:Foo.method1(),但是不能这种调用 method2()。
 
总结:
  • 在外部调用静态方法时,可以使用”类名.方法名”的方式,也可以使用”对象名.方法名”的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
  • 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制
6、static{}静态代码块与{}非静态代码块(构造代码块)
 
相同点: 都是在JVM加载类时且在构造方法执行之前执行,在类中都可以定义多个,定义多个时按定义的顺序执行,一般在代码块中对一些static变量进行赋值。
不同点: 静态代码块在非静态代码块之前执行(静态代码块—非静态代码块—构造方法)。静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new一次就执行一次。 非静态代码块可在普通方法中定义(不过作用不大);而静态代码块不行。注意静态代码块不能存在任何方法体中,即使静态方法也不行,在类加载的时候,静态方法也已经加载了,但是我们必须要通过类名或者对象名才能访问,也就是说相比于静态代码块,静态代码块是主动运行的,而静态方法是被动运行的。不管是哪种方法,我们需要明确静态代码块的存在在类加载的时候就自动运行了,而放在不管是普通方法还是静态方法中,都是不能自动运行的。
 
构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。需要注意的是,听名字我们就知道,构造代码块不是优先于构造函数执行,而是依托于构造函数,也就是说,如果你不实例化对象,构造代码块是不会执行的。和构造函数的作用类似,都能对对象进行初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)。利用每次创建对象的时候都会提前调用一次构造代码块特性,我们可以做诸如统计创建对象的次数等功能。
 
非静态代码块与构造函数的区别是: 非静态代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。
 
普通代码块和构造代码块的区别是:构造代码块是在类中定义的,而普通代码块是在方法体中定义的。且普通代码块的执行顺序和书写顺序一致。
一般情况下,如果有些代码比如一些项目最常用的变量或对象必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的。如果我们想要设计不需要创建对象就可以调用类中的方法,例如:Arrays类,Character类,String类等,就需要使用静态方法, 两者的区别是 静态代码块是自动执行的而静态方法是被调用的时候才执行的。
上述执行顺序:静态代码块>构造代码块>构造函数>普通代码块 
一个例子:
public class TestStaticCodeBlock {
    public TestStaticCodeBlock() {
        System.out.println("默认构造函数方法");
    }
    static int a = 0;//用来测试   静态代码块>构造代码块>构造函数>普通代码块执行顺序
    //构造代码块1
    {
        a++;
        System.out.println("构造代码块1!");
    }
    //构造代码块2
    {
        a++;
        System.out.println("构造代码块2!");
    }
    //静态代码块1
    static {
        a++;
        System.out.println("静态代码块1!");
    }
    //静态代码块2
    static {
        a++;
        System.out.println("静态代码块2!");
    }
    public static void statictest(){
        {
            System.out.println("静态方法中的代码块!");
        }
        System.out.println("静态方法中的内容!");
    }
    public void test(){
        System.out.println("普通方法中的内容!");
        {
            System.out.println("普通方法中的代码块!");
            System.out.println("普通方法中a的值 = " + a);
        }
    }
    public static void main(String[] args) {
        System.out.println("执行main方法");
        System.out.println("main方法中a值等于= " + a);
        TestStaticCodeBlock testStaticCodeBlock = new TestStaticCodeBlock();
        testStaticCodeBlock.test();
        TestStaticCodeBlock testStaticCodeBlock1 = new TestStaticCodeBlock();
        testStaticCodeBlock1.test();
        TestStaticCodeBlock.statictest();
    }
}
输出结果:
静态代码块1!
静态代码块2!
执行main方法
main方法中a值等于= 2
构造代码块1!
构造代码块2!
默认构造函数方法
普通方法中的内容!
普通方法中的代码块!
普通方法中a的值 = 4
构造代码块1!
构造代码块2!
默认构造函数方法
普通方法中的内容!
普通方法中的代码块!
普通方法中a的值 = 6
静态方法中的代码块!
静态方法中的内容!
上述代码中,很明显,静态代码块只是调用了一次,而非静态的代码块每创建一个对象就会调用一次。
 
 
7、父类子类执行顺序
 
    首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有构造代码块,如果有就执行父类的构造代码块,父类的构造代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有构造代码块,如果有就执行子类的构造代码块。子类的构造代码块执行完毕再去执行子类的构造方法。
  总之一句话,静态代码块内容先执行,接着执行父类构造代码块和构造方法,然后执行子类构造代码块和构造方法。
原文地址:https://www.cnblogs.com/liujiarui/p/12499716.html