静态与非静态详解

静态与非静态详解静态与static关键字的分析前言本文将对静态与非静态的关系,以及与之密切相关的关键字static做一些总结分析。这里的静态,指以static关键字修饰的,包括类,方法,块,字段。非静态,指没有用static修饰的。由于静态变量都只能在方法之外声明,所以这里不再考虑局部变量的问题。总览静态

大家好,欢迎来到IT知识分享网。静态与非静态详解"

静态与static关键字的分析

前言

  本文将对静态与非静态的关系,以及与之密切相关的关键字static做一些总结分析。这里的静态,指以static关键字修饰的,包括类,方法,块,字段。非静态,指没有用static 修饰的。由于静态变量都只能在方法之外声明,所以这里不再考虑局部变量的问题。

总览

静态有一些特点:

  • 全局唯一,任何一次的修改都是全局性的影响。
  • 只加载一次,优先于非静态。
  • 使用方式上不依赖于实例对象。
  • 生命周期属于类级别,从JVM 加载开始到JVM卸载结束。

我们先来讨论静态方法、静态变量、静态块,静态类将留在最后。

静态方法

  毫无疑问,当一个方法被static修饰时,它便成为了一个静态方法。

  静态方法在可见的情况下(访问修饰符限定可访问)可以由类名直接调用。利用这一特性,我们可以将一些经常要使用的功能封装成静态方法放在工具类中,以便随时调用。

  静态方法只能访问静态的成员变量和方法,不能访问非静态的方法和变量。

静态变量

  被static修饰的变量被称为静态变量,他和静态方法一样可以使用类名在可见的情况下直接调用。利用这一特性,我们可以在类中设置静态变量,可以实现以下操作:

  • 所有可见类都可以访问修改静态变量而且不需要实例化对象,使用最多的还是工具类中设定的静态常量,他在工具类创建的时候就设定好数值并无法更改,从而将一些比较难以表示的量,如π,使用更简单的方法表示和使用。
  • 定义private静态变量,所有该类的对象在实例化时都有该静态变量的一份拷贝(注意拷贝的是地址),从而实现所有对象都可以访问和修改这个变量,实现共用。

静态块

  由于从属于类,声明在方法之外,所以也称为静态初始化块,此处需要和普通初始化块加以区别。

  静态初始化块在类没有实例化时也会执行。例如,若静态初始化块所在类中有main方法,,运行main方法时都会先将本类中所有静态初始化块从上到下先运行完毕,然后后再运行main方法中的语句。而普通初始化块就没有这待遇了,它只有在类实例化的时候才会执行。

  关于运行的先后顺序,初始化一个类时,会先将所有的静态初始化块从上到下执行完毕,然后再执行普通初始化块的语句,最后才执行构造方法中的语句。而若该类有父类时,需要先执行父类构造器再执行子类构造器,这时情况就更为复杂了,具体流程为

  1. 父类的静态初始化块
  2. 子类的静态初始化块
  3. 父类的初始化块
  4. 父类的构造函数
  5. 子类的初始化块
  6. 子类的构造函数

测试一下

 1 public class Test {
 2     public static void main(String[] args) {
 3         Son son = new Son();
 4     }
 5 }
 6 class Father{
 7     {
 8         System.out.println("这是爸爸的非静态初始块~~");
 9     }
10     static{
11         System.out.println("这是爸爸的静态初始块~~");
12     }
13     public Father(){
14         System.out.println("这是爸爸的构造器~~");
15     }
16 }
17 class Son extends Father{
18     {
19         System.out.println("这是儿子的非静态初始块~~");
20     }
21     static{
22         System.out.println("这是儿子的静态初始块~~");
23     }
24     public Son(){
25         System.out.println("这是儿子的构造器~~");
26     }
27 }
这是爸爸的静态初始块~~
这是儿子的静态初始块~~
这是爸爸的非静态初始块~~
这是爸爸的构造器~~
这是儿子的非静态初始块~~
这是儿子的构造器~~

结果显而易见,至于为什么是这个结果,我们先不讨论,先来想一下这段代码具体的执行过程,在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Son类,而在加载Son类的时候发现Son类继承自Father类,因此会转去先加载Father类,在加载Father类的时候,发现有static块,便执行了static块。在Father类加载完成之后,便继续加载Son类,然后发现Son类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。至于以上所有非静态初始化代码块的执行顺序都和构造函数绑定并先于构造函数执行。(注:执行main方法之前要先找到并加载Test类,所以Teat类中的静态初始化块才是最前面的)。

静态内部类。

  显然,普通类是不能被static修饰的,但内部类可以。和静态方法一样,它可以在可见的情况下使用外部类类名来调取并初始化。而非静态内部类被死死的限制在了外部类内部,只能在外部类中初始化非静态内部类,但好像这里能在外面被初始化也不是一件能拿出来炫耀的事。

  非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。

  静态内部类可以单独初始化,而普通内部类初始化必须在外部类初始化后使用外部类对象来调用并实例化。

测试一下

public class Test {
    public static void main(String[] args) {
        //来看看在外面的情况
        Out.StaticIn staticIn = new Out.StaticIn();//正常调取,正常实例化
        Out out = new Out();
        Out.SimpleIn simpleIn = out.new SimpleIn();//Out带领,也能实例化
    }
}
class Out{
    class SimpleIn{ }
    static class StaticIn{ }
    public static void main(String[] args) {
        StaticIn staticIn = new StaticIn();//显然可以直接实例化
        //SimpleIn simpleIn = new SimpleIn();直接初始化编译器就不愿意了~~
        Out out = new Out();
        SimpleIn simpleIn = out.new SimpleIn();//还得Out实例化一下你才肯出来
    }
}

题外话(静态导入)

  关于工具类中的静态变量和静态方法在其他类中的调用有一个需要注意的地方。当在文件中类的外部import static jav.lang.Math.*;时,所有Math类中的静态方法和静态变量都可以直接使用而不需要用Math这个类名去调取。但显然的一点是,此文件的所有类都不能再出现和Math类中被使用的静态变量和静态方法同名的变量和方法。这样在书写代码的时候确实能省一点代码,但是会影响代码可读性,所以一般情况下不建议这么使用。

测试一下

1 package cn.Test;
2 import static java.lang.Math.*; 3 public class Test { 4 public static void main(String[] args) { 5  System.out.println(PI); 6 System.out.println(max(3,6)); 7  } 8 }
3.141592653589793
6

完美输出~~

 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/32298.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信