java必学必会之static关键字


Posted in Javascript onDecember 03, 2015

一、static关键字

java必学必会之static关键字

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

1.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。

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

java必学必会之static关键字

接下来程序执行到:

  Cat  mimi = new Cat(“mimi”);

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

Cat ( String name){

this.name = name;

id=sid++;

}

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

java必学必会之static关键字

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

java必学必会之static关键字

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

this.name=name;

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

java必学必会之static关键字

接下来执行方法体内的另一句代码:id=sid++;

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

java必学必会之static关键字

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

java必学必会之static关键字

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

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

java必学必会之static关键字

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

java必学必会之static关键字

通过这个程序,看出来了这个静态成员变量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一定是静态的。

再看下面的这段代码

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属性。所以这段代码执行完成后,内存中的布局如下图所示:

java必学必会之static关键字

由于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一个对象出来才能访问。

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

以上就是本文的全部内容,信息量很大,需要大家耐心阅读,从而真正的学会java static关键字。

Javascript 相关文章推荐
JavaScript在网页中画圆的函数arc使用方法
Nov 13 Javascript
JS DOM实现鼠标滑动图片效果
Sep 17 Javascript
动态加载JavaScript文件的两种方法
Apr 22 Javascript
JavaScript直播评论发弹幕切图功能点集合效果代码
Jun 26 Javascript
深入浅析JS是按值传递还是按引用传递(推荐)
Sep 18 Javascript
前端js弹出框组件使用方法
Aug 24 Javascript
js中开关变量使用实例
Feb 24 Javascript
jQuery插件zTree实现删除树子节点的方法示例
Mar 08 Javascript
微信小程序利用swiper+css实现购物车商品删除功能
Mar 06 Javascript
Element-ui自定义table表头、修改列标题样式、添加tooltip、:render-header使用
Apr 11 Javascript
JS实现打字游戏
Dec 17 Javascript
vue实现简单跑马灯效果
May 25 Javascript
详解页面滚动值scrollTop在FireFox与Chrome浏览器间的兼容问题
Dec 03 #Javascript
继续学习javascript闭包
Dec 03 #Javascript
解决js页面滚动效果scrollTop在FireFox与Chrome浏览器间的兼容问题的方法
Dec 03 #Javascript
jQuery 1.9.1源码分析系列(十五)之动画处理
Dec 03 #Javascript
ztree获取选中节点时不能进入可视区域出现BUG如何解决
Dec 03 #Javascript
jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween
Dec 03 #Javascript
JS使用post提交的两种方式
Dec 03 #Javascript
You might like
微盾PHP脚本加密专家php解密算法
2020/09/13 PHP
php性能优化分析工具XDebug 大型网站调试工具
2011/05/22 PHP
php中判断数组是一维,二维,还是多维的解决方法
2013/05/04 PHP
php实现memcache缓存示例讲解
2013/12/04 PHP
PHP检测字符串是否为UTF8编码的常用方法
2014/11/21 PHP
php中随机函数mt_rand()与rand()性能对比分析
2014/12/01 PHP
php使用SAE原生Mail类实现各种类型邮件发送的方法
2016/10/10 PHP
php封装的数据库函数与用法示例【参考thinkPHP】
2016/11/08 PHP
Mac下关于PHP环境和扩展的安装详解
2019/10/17 PHP
(JS实现)MapBar中坐标的加密和解密的脚本
2007/05/16 Javascript
js form action动态修改方法
2008/11/04 Javascript
js打印纸函数代码(递归)
2010/06/18 Javascript
汉化英文版的Dreamweaver CS5并自动提示jquery
2010/11/25 Javascript
javascript中的Base64、UTF8编码与解码详解
2015/03/18 Javascript
基于JS实现新闻列表无缝向上滚动实例代码
2016/01/22 Javascript
javascript自动切换焦点控制效果完整实例
2016/02/02 Javascript
关于ES6的六个小特性(二)
2017/02/20 Javascript
vue2.0实战之基础入门(1)
2017/03/27 Javascript
js字符限制(字符截取) 一个中文汉字算两个字符
2017/09/12 Javascript
Vue-cli 使用json server在本地模拟请求数据的示例代码
2017/11/02 Javascript
Node.js中sequelize时区的配置方法
2017/12/10 Javascript
vue使用Google地图的实现示例代码
2018/12/19 Javascript
基于Layui自定义模块的使用方法详解
2019/09/14 Javascript
Vue常用的全选/反选的示例代码
2020/02/19 Javascript
js实现的订阅发布者模式简单示例
2020/03/14 Javascript
Python环境下安装使用异步任务队列包Celery的基础教程
2016/05/07 Python
Python pickle模块实现对象序列化
2019/11/22 Python
Python通过4种方式实现进程数据通信
2020/03/12 Python
为什么说python更适合树莓派编程
2020/07/20 Python
Under Armour安德玛中国官网:美国高端运动科技品牌
2018/03/09 全球购物
实习自我评价怎么写
2013/12/02 职场文书
应届生简历中的自我评价
2014/01/13 职场文书
《冬阳童年骆驼队》教学反思
2014/04/15 职场文书
2014大学班主任工作总结
2014/11/08 职场文书
《鲁滨逊漂流记》之六读后感(4篇)
2019/09/29 职场文书
《乙女游戏世界对路人角色很不友好》OP主题曲无字幕动画MV公开
2022/04/05 日漫