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 相关文章推荐
js弹出层(jQuery插件形式附带reLoad功能)
Apr 12 Javascript
使用vue.js制作分页组件
Jun 27 Javascript
基于Layer+jQuery的自定义弹框
May 26 Javascript
JS三目运算(三元运算)方法详解
Mar 01 Javascript
JS+Canvas绘制动态时钟效果
Nov 10 Javascript
微信小程序picker组件简单用法示例【附demo源码下载】
Dec 05 Javascript
Vue.js更改调试地址端口号的实例
Sep 19 Javascript
JavaScript设计模式之代理模式实例分析
Jan 16 Javascript
Vue.set 全局操作简单示例
Sep 19 Javascript
解决vue scoped scss 无效的问题
Sep 04 Javascript
vue 中的动态传参和query传参操作
Nov 09 Javascript
vue中使用mockjs配置和使用方式
Apr 06 Vue.js
详解页面滚动值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的字符串用法小结
2010/06/08 PHP
PHP新建类问题分析及解决思路
2015/11/19 PHP
PHP进行批量任务处理不超时的解决方法
2016/07/11 PHP
JS学习之一个简易的日历控件
2010/03/24 Javascript
Dojo 学习要点
2010/09/03 Javascript
JQuery操作iframe父页面与子页面的元素与方法(实例讲解)
2013/11/20 Javascript
按钮接受回车事件的三种实现方法
2014/06/06 Javascript
jQuery中attr()和prop()在修改checked属性时的区别
2014/07/18 Javascript
使用jQuery实现验证上传图片的格式与大小
2014/12/03 Javascript
javascript replace()第二个参数为函数时的参数用法
2016/12/26 Javascript
引入JavaScript时alert弹出框显示中文乱码问题
2017/09/16 Javascript
javaScript实现鼠标在文字上悬浮时弹出悬浮层效果
2020/04/12 Javascript
微信小程序自定义组件实现tabs选项卡功能
2018/07/14 Javascript
微信小程序实现提交input信息到后台的方法示例
2019/01/19 Javascript
关于ckeditor在bootstrap中modal中弹框无法输入的解决方法
2019/09/11 Javascript
vue父子组件间引用之$parent、$children
2020/05/20 Javascript
详解JavaScript之ES5的继承
2020/07/08 Javascript
python opencv设置摄像头分辨率以及各个参数的方法
2018/04/02 Python
python 输出上个月的月末日期实例
2018/04/11 Python
python多行字符串拼接使用小括号的方法
2020/03/19 Python
Python设计模式之桥接模式原理与用法实例分析
2019/01/10 Python
numpy concatenate数组拼接方法示例介绍
2019/05/27 Python
Django后台管理系统的图文使用教学
2020/01/20 Python
Python select及selectors模块概念用法详解
2020/06/22 Python
canvas实现高阶贝塞尔曲线(N阶贝塞尔曲线生成器)
2018/01/10 HTML / CSS
英国第一的购买便宜玩具和游戏的在线购物网站:Bargain Max
2018/01/24 全球购物
Bally澳大利亚官网:瑞士奢侈品牌
2018/11/01 全球购物
英国门销售网站:Green Tree Doors
2020/01/07 全球购物
Why do we need Unit test
2013/01/03 面试题
商场消防安全责任书
2014/07/29 职场文书
2014年局领导班子自身建设情况汇报
2014/11/21 职场文书
复试通知单模板
2015/04/24 职场文书
JavaScript实现队列结构过程
2021/12/06 Javascript
SQL Server内存机制浅探
2022/04/06 SQL Server
CentOS下安装Jenkins的完整步骤
2022/04/07 Servers
vue实力踩坑之push当前页无效
2022/04/10 Vue.js