一、static 关键字的含义

static是Java50个关键字之一。static关键字可以用来修饰代码块表示静态代码块,修饰成员变量表示全局静态成员变量,修饰方法表示静态方法。(注意:不能修饰普通类,除了内部类,这是为什么?)

class A {static {System.out.println("A : 静态代码块");}static int i ;  // 静态变量static void method() {System.out.println("A: 静态方法");}
}

简而言之,被static关键字修饰的内容都是静态的。
静态是相对于动态的,动态是指Java程序在JVM上运行时,JVM会根据程序的需要动态创建对象并存储对象(分配内存),对象使命结束后,对象会被垃圾回收器销毁,即内存回收由JVM统一管理并分配给其他新创建的对象;静态是指Java程序还没有运行时,JVM就会为加载的类分配空间存储被static关键字修饰的内容;如静态成员变量,Java类加载到JVM中,JVM会把类以及类的静态成员变量存储在方法区,我们知道方法区是线程共享且很少发生GC的区域,所以被static关键字修饰的内容都是全局共享的,且只会为其分配一次存储空间。
所以当类的某些内容不属于对象,而由对象共享即属于类的时候,就可以考虑是否用static关键字进行修饰。

二、static 关键字的用途

1、修饰代码块

类中用static关键字修饰的代码块称为静态代码,反之没有用static关键字修饰的代码块称为实例代码块。

实例代码块会随着对象的创建而执行,即每个对象都会有自己的实例代码块,表现出来就是实例代码块的运行结果会影响当前对象的内容,并随着对象的销毁而消失(内存回收);而静态代码块是当Java类加载到JVM内存中而执行的代码块,由于类的加载在JVM运行期间只会发生一次,所以静态代码块也只会执行一次。

因为静态代码块的主要作用是用来进行一些复杂的初始化工作,所以静态代码块跟随类存储在方法区的表现形式是静态代码块执行的结果存储在方法区,即初始化量存储在方法区并被线程共享。

2、修饰成员变量

类中用static关键字修饰的成员变量称为静态成员变量,因为static不能修饰局部变量(为什么?),因此静态成员变量也能称为静态变量。静态变量跟代码块类似,在类加载到JVM内存中,JVM会把静态变量放入方法区并分配内存,也由线程共享。访问形式是:类名.静态成员名。

public class StaticTest {public static void main(String[] args) {System.out.println(D.i);System.out.println(new D().i);}
}
class D {static {i = 2;System.out.println("D : 静态代码块1");}static int i;
}

运行结果:

D : 静态代码块1
2
2

静态变量存储在类的信息中,且可以在线程间共享,那么它当然也属于该类的每个对象,因此可以通过对象访问静态变量,但编译器并不支持这么做,且会给出警告。

注意:

  • 一个类的静态变量和该类的静态代码块的加载顺序。类会优先加载静态变量,然后加载静态代码块,但有多个静态变量和多个代码块时,会按照编写的顺序进行加载。

    public class Main {public static void main(String[] args) {System.out.println(new D().i);}
    }
    class D {static {i = 2;System.out.println("D : 静态代码块1");}static {i = 6;System.out.println("D : 静态代码块2");}static int i;
    }
    

    可以想一下运行的结果。

  • 静态变量可以不用显式的初始化,JVM会默认给其相应的默认值。如基本数据类型的byte为0,short为0,char为\u0000,int为0,long为0L,float为0.0f,double为0.0d,boolean为false,引用类型统一为null。

  • 静态变量既然是JVM内存中共享的且可以改变,那么对它的访问会引起线程安全问题(线程A改写的同时,线程B获取它的值,那么获取的是修改前的值还是修改后的值呢?),所以使用静态变量的同时要考虑多线程情况。如果能确保静态变量不可变,那么可以用final关键字一起使用避免线程安全问题;否则需要采用同步的方式避免线程安全问题,如与volatile关键字一起使用等。

  • static关键不能修饰局部变量,包括实例方法和静态方法,不然就会与static关键字的初衷-共享相违背。

3、修饰方法

static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。

但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。举个简单的例子:

在上面的代码中,由于print2方法是独立于对象存在的,可以直接用过类名调用。假如说可以在静态方法中访问非静态方法/变量的话,那么如果在main方法中有下面一条语句:

MyObject.print2();

此时对象都没有,str2根本就不存在,所以就会产生矛盾了。同样对于方法也是一样,由于你无法预知在print1方法中是否访问了非静态成员变量,所以也禁止在静态成员方法中访问非静态成员方法。

而对于非静态成员方法,它访问静态成员方法/变量显然是毫无限制的。

因此,如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。

三、static 关键字的误区

1、static关键字会改变类中成员的访问权限吗?

有些初学的朋友会将java中的static与C/C++中的static关键字的功能混淆了。在这里只需要记住一点:与C/C++中的static不同,Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。看下面的例子就明白了:

提示错误"Person.age 报红",这说明static关键字并不会改变变量和方法的访问权限。

2、能通过this访问静态成员变量吗?

虽然对于静态方法来说没有this,那么在非静态方法中能够通过this访问静态成员变量吗?先看下面的一个例子,这段代码输出的结果是什么?

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

这里面主要考察队this和static的理解。this代表什么?this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。

3、static能作用于局部变量么?

在C/C++中static是可以作用域局部变量的,但是在Java中切记:static是不允许用来修饰局部变量。不要问为什么,这是Java语法的规定。

具体原因可以参考这篇博文的讨论:http://www.debugease.com/j2se/178932.html

4、static关键字虽然不能修饰普通类,但可以用static关键字修饰内部类使其变成静态内部类。static关键字本身的含义就是共享,而Java类加载到JVM内存的方法区,也是线程共享的,所以没必要用static关键字修饰普通类。

四、常见的笔试面试题

下面列举一些面试笔试中经常遇到的关于static关键字的题目,仅供参考,如有补充欢迎下方留言。

1、下面这段代码的输出结果是什么?

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

至于为什么是这个结果,我们先不讨论,先来想一下这段代码具体的执行过程,在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。

2、这段代码的输出结果是什么?

public class Test {Person person = new Person("Test");static{System.out.println("test static");}public Test() {System.out.println("test constructor");}public static void main(String[] args) {new MyClass();}
}class Person{static{System.out.println("person static");}public Person(String str) {System.out.println("person "+str);}
}class MyClass extends Test {Person person = new Person("MyClass");static{System.out.println("myclass static");}public MyClass() {System.out.println("myclass constructor");}
}

输出结果:

test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

类似地,我们还是来想一下这段代码的具体执行过程。首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器。

3、这段代码的输出结果是什么?

public class Test {static{System.out.println("test static 1");}public static void main(String[] args) {}static{System.out.println("test static 2");}
}

输出结果:

test static 1
test static 2

虽然在main方法中没有任何语句,但是还是会输出,原因上面已经讲述过了。另外,static块可以出现类中的任何地方(只要不是方法内部,记住,任何方法内部都不行),并且执行是按照static块的顺序执行的。

五、static关键字的缺点

封装是Java类的三大特性之一,也是面向对象的主要特性。因为不需要通过对象,而直接通过类就能访问类的属性和方法,这有点破坏类的封装性;所以除了Utils类,代码中应该尽量少用static关键字修饰变量和方法。

static 关键字用法总结相关推荐

  1. c语言中external,static关键字用法

    static用法: 在C中,static主要定义全局静态变量.定义局部静态变量.定义静态函数. 1.定义全局静态变量:在全局变量前面加上关键字static,该全局变量变成了全局静态变量.全局静态变量有 ...

  2. static关键字用法

    static关键字 1.修饰成员变量 在我们平时的使用当中,static最常用的功能就是修饰类的属性和方法,让他们成为类的成员属性和方法,我们通常将用static修饰的成员称为类成员或者静态成员,这句 ...

  3. c++ static 关键字用法

    http://www.cnblogs.com/hucn/archive/2011/03/25/1995386.html 一. static 变量 static变量大致分为三种用法 1. 用于局部变量中 ...

  4. Java的static关键字用法及原理

    文章目录 1.静态变量: 2.静态方法: 3.静态代码块: 4.静态内部类 5.主要用法: 6.本质问题: 1.静态变量: 定义了一个static关键字,用于修饰类的成员,如成员变量,成员方法,以及代 ...

  5. C++ static关键字用法

    前言:C++是C语言的扩展,C++兼容C语言的所有语法,所以static关键字在C语言中的用法对于C++同样适用:二者不同的是C++扩展出来的特性,而这些特性与C++面向对象有关. C语言中stati ...

  6. C++中static关键字用法

    原文参考:https://zhuanlan.zhihu.com/p/347930016 1.面向过程设计中的static 1.1静态全局变量 在全局变量前,加上关键字static,该变量就被定义成为一 ...

  7. asp.net(c#) static关键字用法小结

    转自http://www.cnblogs.com/dragonlhf/archive/2007/10/19/930664.html 静态变量 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并 ...

  8. c语言中static关键字用法详解

    概述 static关键字在c语言中比较常用,使用恰当能够大大提高程序的模块化特性,有利于扩展和维护. 但是对于c语言初学者,static由于使用灵活,并不容易掌握.本文就static在c语言中的应用进 ...

  9. C/C++中static关键字用法汇总

    1. 函数内static局部变量:变量在程序初始化时被分配,直到程序退出前才被释放,也就是static是按照程序的生命周期来分配释放变量的,而不是变量自己的生命周期.多次调用,仅需一次初始化. 2. ...

最新文章

  1. 分库分表这样玩,可以永不迁移数据、避免热点
  2. python【蓝桥杯vip练习题库】ADV-136大数加法(高精度加法)
  3. Java应用全链路启动速度提升至15s,阿里云SAE能力再升级
  4. 2015-01-14
  5. codeforces-1176 (div3)
  6. pearson特征选择matlab,使用sklearn做特征选择
  7. Ubuntu16.04中安装stlink驱动
  8. 企业管理--盖洛普Q12测评法
  9. 机器学习原理与实战 | SVM(支持向量机)实践
  10. google guava Joiner 示列
  11. 教你一步一步用VPS
  12. c语言 函数拟合,曲线拟合成Y=a*(X^b)+c*(X^d)函数 - 数学 - 小木虫 - 学术 科研 互动社区...
  13. Binary Hypotheses
  14. 【阿里云2020校招提前批】中间件核心部门疯狂直推中!
  15. 深度学习应用在图像分割上的网络模型概述
  16. 简单计算一下,发现炒房一点不划算
  17. 结构化思维模型的构建(多种思维参考)
  18. 支持增删查改的简单Java Web通讯录详细教程【基于Mac OS+IDEA+Servlet+JDBC+Tomcat】
  19. activiti 文档_那些可多人协作编辑的在线文档工具
  20. 微信or支付宝交易限额

热门文章

  1. 图07 --- 关键路径
  2. session反序列化
  3. 算法题:给出一组数字,拼接一个最大的值
  4. 99%中小企业对数字化的误区,你中招了吗?
  5. 如何求first集与follow集
  6. 计算机类书籍阅读记录
  7. Android 锁屏图标的大小修改
  8. [ USACO 2007 FEB ] Lilypad Pond (Silver)
  9. element ui的布局el-container布满整个页面
  10. 【JAVASE(7)】JAVASE学习--注解及小众常用类篇