Static变量

static关键字

  • 在类中用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时初始化,对于该类的所有对象来说,static成员变量只有一份。
  • 可以通过引用或者类名访问静态成员

      原来一个类里面的成员变量,每new一个对象,这个对象就有一份自己的成员变量,因为这些成员变量都不是静态成员变量。对于static成员变量来说,这个成员变量只有一份,而且这一份是这个类所有的对象共享。

◆在类中,用static声明的成员变量为静态变量,或者叫:类属性、类变量。

(注意:静态变量是从属于类,在对象里面是没有这个属性的;成员变量是从属于对象的,有了对象才有那个属性)

  • 它为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显示初始化。
  • 对于该类所有对象来说,static成员变量只有一份.被该类的所有对象共享!!
  • 可以使用“对象.类属性”来调用。不过,一般都是用”类名.类属性”。
  • static变量置于方法区中。
  • 在静态的方法里面不可以调用非静态的方法或变量;但是在非静态的方法里可以调用静态的方法或变量。

Static方法

◆用static声明的方法为静态方法。

  • 不需要对象,就可以调用(类名.方法名
  • 在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员

【实例1】

student类:

测试:

静态初始化块

静态初始化块是在类被加载的时候就执行的一块程序,并且一直存在直到程序关闭。也就是说当程序被执行,即classloader将该java程序编译后的class文件加载后,就能执行到静态初始化块这段程序;当程序关闭,我的个人理解也就是java.exe进程被结束的时候,静态初始化块结束(例如在静态初始化块里对一个类的静态变量进行赋值,该变量一直存在到程序关闭)。

下面我们来举例说明:

     public class Test {//静态变量public static String testStatic = "testStatic";//静态初始化块static {System.out.println(testStatic);System.out.println("Proc begin");testStatic = "testProc";System.out.println("Proc end");}//主方法public static void main(String[] args) {System.out.println(testProc);System.out.println("main begin");System.out.println("main end");}}

执行main方法输出结果:
testStatic
Proc begin
Proc end
testProc
main begin
main end

也就是说当JVM将要执行main方法的时候,先要将Test.class加载到JVM中,此时就执行了静态初始化块中的程序;然后再执行执行main方法中的程序。这个例子没有将这个类实例化,所以没有用到构造函数。倘若需要实例化该类的时候,则构造方法的执行顺序也是在静态初始化块之后的。

最后我们可以得出这么一个结论:Java类的执行优先顺序

该类的静态变量->该类的静态初始化块->该类的构造方法

若存在父类的情况下则是:

父类的静态变量->父类的静态初始化块->子类的静态变量->子类的静态初始化块

内存分析Static

静态成员变量与非静态成员变量的区别:

【示例1】

package cn.galc.test;public class Cat {/*** 静态成员变量*/private static int sid = 0;private String name;int id;Cat(String name) {this.name = name;id = sid++;}public void info() {System.out.println("My Name is " + name + ",NO." + id);}public static void main(String[] args) {Cat.sid = 100;Cat mimi = new Cat("mimi");Cat pipi = new Cat("pipi");mimi.info();pipi.info();}
}

通过画内存分析图了解整个程序的执行过程:

执行程序的第一句话:Cat.sid = 100;时,这里的sid是一个静态成员变量,静态变量存放在数据区(data seg),所以首先在数据区里面分配一小块空间sid,第一句话执行完后,sid里面装着一个值就是100。

此时的内存布局示意图如下所示

  

 接下来程序执行到:

 Cat  mimi = new Cat(“mimi”);

  这里,调用Cat类的构造方法Cat(String name),构造方法的定义如下:

 Cat ( String name){this.name = name;id=sid++;}

  调用时首先在栈内存里面分配一小块内存mm,里面装着可以找到在堆内存里面的Cat类的实例对象的地址,mm就是堆内存里面Cat类对象的引用对象。这个构造方法声明有字符串类型的形参变量,所以这里把“mimi”作为实参传递到构造方法里面,由于字符串常量是分配在数据区存储的,所以数据区里面多了一小块内存用来存储字符串“mimi”。此时的内存分布如下图所示:

  

当调用构造方法时,首先在栈内存里面给形参name分配一小块空间,名字叫name,接下来把”mimi”这个字符串作为实参传递给name,字符串也是一种引用类型,除了那四类8种基础数据类型之外,其他所有的都是引用类型,所以可以认为字符串也是一个对象。所以这里相当于把”mimi”这个对象的引用传递给了name,所以现在name指向的是”mimi”。所以此时内存的布局如下图所示:

接下来执行构造方法体里面的代码:

this.name=name;

  这里的this指的是当前的对象,指的是堆内存里面的那只猫。这里把栈里面的name里面装着的值传递给堆内存里面的cat对象的name属性,所以此时这个name里面装着的值也是可以找到位于数据区里面的字符串对象“mimi”的,此时这个name也是字符串对象“mimi”的一个引用对象,通过它的属性值就可以找到位于数据区里面的字符串对象“mimi”。此时的内存分布如下图所示:

  

接下来执行方法体内的另一句代码:

 id=sid++;

  这里是把sid的值传递给id,所以id的值是100,sid传递完以后,自己再加1,此时sid变成了101。此时的内存布局如下图所示。

  

到此,构造方法调用完毕,给这个构造方法分配的局部变量所占的内存空间全部都要消失,所以位于栈空间里面的name这块内存消失了。栈内存里面指向数据区里面的字符串对象“mimi”的引用也消失了,此时只剩下堆内存里面的指向字符串对象“mimi”的引用没有消失。此时的内存布局如下图所示:

  

接下来执行:Cat pipi = new Cat(“pipi”);

  这里是第二次调用构造方法Cat(),整个调用过程与第一次一样,调用结束后,此时的内存布局如下图所示:

  

  最后两句代码是调用info()方法打印出来,打印结果如下:

My name is mimi,NO.100
My name is pipi,NO.101
  

通过这个程序,看出来了这个静态成员变量sid的作用,它可以计数。每当有一只猫new出来的时候,就给它记一个数。让它自己往上加1。

  程序执行完后,内存中的整个布局就如上图所示了。一直持续到main方法调用完成的前一刻。

  这里调用构造方法Cat(String name) 创建出两只猫,首先在栈内存里面分配两小块空间mimi和pipi,里面分别装着可以找到这两只猫的地址,mimi和pipi对应着堆内存里面的两只猫的引用。这里的构造方法声明有字符串类型的变量,字符串常量是分配在数据区里面的,所以这里会把传过来的字符串mimi和pipi都存储到数据区里面。所以数据区里面分配有存储字符串mimi和pipi的两小块内存,里面装着字符串“mimi”和“pipi”,字符串也是引用类型,除了那四类8种的基础数据类型之外,其他所有的数据类型都是引用类型。所以可以认为字符串也是一个对象。

  这里是new了两只猫出来,这两只猫都有自己的id和name属性,所以这里的id和name都是非静态成员变量,即没有static修饰。所以每new出一只新猫,这只新猫都有属于它自己的id和name,即非静态成员变量id和name是每一个对象都有单独的一份。但对于静态成员变量来说,只有一份,不管new了多少个对象,哪怕不new对象,静态成员变量在数据区也会保留一份。如这里的sid一样,sid存放在数据区,无论new出来了多少只猫在堆内存里面,sid都只有一份,只在数据区保留一份。

  静态成员变量是属于整个类的,它不属于专门的某个对象。那么如何访问这个静态成员变量的值呢?首先第一点,任何一个对象都可以访问这个静态的值,访问的时候访问的都是同一块内存。第二点,即便是没有对象也可以访问这个静态的值,通过“类名.静态成员变量名”来访问这个静态的值,所以以后看到某一个类名加上“.”再加上后面有一个东西,那么后面这个东西一定是静态的,如”System.out”,这里就是通过类名(System类)再加上“.”来访问这个out的,所以这个out一定是静态的。

【示例2】

package cn.galc.test;public class Cat {/*** 这里面的sid不再是静态成员变量了,因为没有static修饰符,* 此时它就是类里面一个普通的非静态成员变量,和id,name一样,* 成为每一个new出来的对象都具有的属性。*/private  int sid = 0;private String name;int id;Cat(String name) {this.name = name;id = sid++;}public void info() {System.out.println("My Name is " + name + ",NO." + id);}public static void main(String[] args) {//Cat.sid = 100;这里不能再使用“类.静态成员变量”的格式来访问sid了,因为sid现在变成了非静态的成员变量了。所以必须要把这句话注释掉,否则无法编译通过。Cat mimi = new Cat("mimi");Cat pipi = new Cat("pipi");mimi.info();pipi.info();}
}

这段代码与上一段代码唯一的区别是把声明sid变量的static修饰符给去掉了,此时的sid就不再是静态成员变量,而是非静态成员变量了,此时每一个new出来的cat对象都会有自己单独的sid属性。所以这段代码执行完成后,内存中的布局如下图所示:

  

  由于sid变成了非静态成员变量,所以不再有计数的功能了。sid与id和name属性一样,成为每一个new出来的对象都具有的属性,所以每一个new出来的cat都加上了一个sid属性。由于不能再使用”类名.静态成员对象名”的格式访问sid,所以代码的第一句”Cat.sid =100;”不能这样使用,否则编译会出错,必须把这句话注释掉才能编译成功。既然无法访问得到sid的值,所以sid的值就一直都是初始化时赋给的值0。直到调用构造方法时,执行到方法体内的代码id=sid++;时,sid首先把自身的值0赋值给id,所以id的值是0,然后sid自己加1,所以sid变成了1。

  所以静态变量和非静态变量的区别就在于静态变量可以用来计数,而非静态变量则不行。

  理解了内存,就理解了一切,就理解了各种各样的语言。所有的语言无非都是这样:局部变量分配内存永远在栈里面,new出来的东西分配内存永远是在堆里,静态的东西分配内存永远是在数据区。剩下的代码肯定是在代码区。所有的语言都是这样。

在一个静态方法里,如果想访问一个非静态的成员变量,是不能直接访问的,必须在静态方法里new一个对象出来才能访问。如果是加了static的成员变量,那么这个成员变量就是一个静态的成员变量,就可以在main方法里面直接访问了。

  main方法是一个静态的方法,main方法要执行的时候不需要new一个对象出来。

  动态方法是针对于某一个对象调用的,静态方法不会针对某一个对象来调用,没有对象照样可以用。所以可以使用”classname.method()”.的形式来调用静态方法。所以想在main方法里面访问非静态成员变量是不可以的,想在main方法里面访问非静态方法也是不可以的,因为非静态方法只能针对于某个对象来调用,没有对象,就找不到方法的执行者了。

  成员变量只有在new出一个对象来的时候才在堆内存里面分配存储空间。局部变量在栈内存里面分配存储空间。

  静态方法不再是针对某一个对象来调用,所以不能访问非静态的成员。

  非静态成员专属于某一个对象,想访问非静态成员必须new一个对象出来才能访问。

  静态的变量可以通过对象名去访问,也可以通过类名去访问,两者访问的都是同一块内存。

总结

类只能使用类的方法和属性,对象既可以使用类的方法,又可以使用对象的方法。

static的方法只能调用static的变量和方法,非static的方法既可以调用static的,又可以调用非static的。

参考资料
  • http://www.cnblogs.com/xdp-gacl/p/3637407.html
  • http://www.cnblogs.com/Qian123/p/5171407.html
  • https://blog.csdn.net/deadpanda/article/details/5307932

文章有不当之处,欢迎指正,你也可以关注我的微信公众号:好好学java,获取优质学习资源,也可以加入QQ技术交流群:766946816,咋们来聊聊java。

java基础提升篇:Static关键字相关推荐

  1. java基础提升篇:深入分析Java的序列化与反序列化

    初遇 序列化是一种对象持久化的手段.普遍应用在网络传输.RMI等场景中.本文通过分析ArrayList的序列化来介绍Java序列化的相关内容.主要涉及到以下几个问题: 怎么实现Java的序列化 为什么 ...

  2. 你所需要的java基础提升篇大总结

    java基础篇深入解析大总结 java基础(一) 深入解析基本类型 java基础(二) 自增自减与贪心规则 java基础(三) 加强型for循环与Iterator java基础(四) java运算顺序 ...

  3. java基础提升篇:Java中Native关键字的作用

    初遇 初次遇见 native是在 java.lang.Object 源码中的一个hashCode方法: public native int hashCode(); 为什么有个native呢?这是我所要 ...

  4. 【通知】+ java基础提升篇:Java 序列化的高级认识

    点击上方"好好学java",选择"置顶公众号" 优秀学习资源.干货第一时间送达! 好好学java java知识分享/学习资源免费分享 关注 精彩内容 你所需要的 ...

  5. java基础提升篇:深入浅出Java多线程

    初遇 Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别的形式,但多线程 ...

  6. java基础提升篇:Java 序列化的高级认识

    如果你只知道实现 Serializable 接口的对象,可以序列化为本地文件.那你最好再阅读该篇文章,文章对序列化进行了更深一步的讨论,用实际的例子代码讲述了序列化的高级认识,包括父类序列化的问题.静 ...

  7. java基础提升篇:synchronized同步块和volatile同步变量

    初遇 Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量.这两种机制的提出都是为了实现代码线程的安全性.其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低 ...

  8. Java基础提升篇:equals()与hashCode()方法详解

    概述 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) public int hashCode() Object类是类继承 ...

  9. Java基础提升篇:equals()方法和“==”运算符

    点击上方"好好学java",选择"置顶公众号" 优秀学习资源.干货第一时间送达! 好好学java java知识分享/学习资源免费分享 关注 精彩内容 你所需要的 ...

最新文章

  1. wordpress 常用函数-wpdb类
  2. IPV4报头格式详解
  3. 推荐系统XDeepFM模型--DeepFM和DCN升级版
  4. .NET 6 预览版 7:新功能已完成 ,将专注于改进
  5. QT [005] 数据库设计 - 一个被忽略的数据库show类 - ConnectionWidget Class
  6. 材料成型过程计算机控制系统,华南理工大学
  7. TurboMail打造雀氏(中国)日用品邮件系统
  8. openstack社区_OpenStack社区中发生了什么?
  9. PL/0语言编译器扩展 编译原理课程实践(1)
  10. Spring Boot配置文件application.properties
  11. WPF datagrid 加入图片
  12. c++ new操作符(new operator)、operator new、placement new 、operator new[] 及相对应的delete 操作符、operator delete
  13. 前端常见面试题-css篇
  14. 很值得看看的中文翻译The Django Book
  15. RHCE-ansible第三次实验,通过ansible自动化部署web服务
  16. 平板电脑怎么打开HTML,为什么平板电脑和笔记本电脑打开网页不一样,感觉平板电脑就是手机?...
  17. 推荐几个很实用的网址
  18. 让oracle开机自动启动,设置CentOS下开机自动启动Oracle
  19. Windows添加自定义开机启动项
  20. 03 graphx 从 SSSP 来看 pregel

热门文章

  1. MVC --.Routing
  2. ESP32 SDK OTA Demo升级流程
  3. 简单数据分布分析及python实现
  4. 从零开始掌握Python机器学习(附资源)
  5. Boost Asio总结(12)class basic_socket_acceptor
  6. Html 教程 (8)表单
  7. (chap6 Http首部) 传输方式的分类
  8. (chap4 Http状态码) 5XX
  9. [armv9]-Introducing-Arm-Confidential-Compute-Architecture
  10. Android keymaster4.0- device集成笔记