static关键字以及内存分析

此文为网上资源收集而写。
今天我们来看一下static关键字
这里写图片描述
static表示全局或静态的意思,用来修饰成员变量或者成员方法,也可以形成static代码块,但是在java语言中其实是没有所谓全局变量的概念的。
 被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
  用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。

 static 变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用–废话),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。
static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:
类名.静态方法名(参数列表…)
类名.静态变量名
用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块(用处非常大,呵呵)。
static变量
 按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:
 对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
 用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。

 static 变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用–废话),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。

 static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:
类名.静态方法名(参数列表…)
类名.静态变量名

 用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块(用处非常大,呵呵)。

static变量
 按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:
 对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
 对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
 static方法
 静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法 (就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!
 因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
 static final 用来修饰成员变量和成员方法,可简单理解为“全局常量”!
对于变量,表示一声明就无法改变值,可以通过类名访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。
 说了这么多理论的东西,下面我们用一个实例来分析下static以及内存存储位置。
 

package wangcc.learnStatic;

class User {
    private String name;
    private int id;
    public static int uid = 0;

    User(String name) {
        this.name = name;
        id = uid++;
    }

    @Override
    public String toString() {
        return "User [name=" + name + ", id=" + id + "]";
    }
}

public class TestStatic {
    public static void main(String[] args) {
        User.uid = 100;
        User kobe = new User("kobe");
        User james = new User("james");
        System.out.println(kobe.toString());
        System.out.println(james.toString());
    }
}

下面我们通过画内存图来分析程序的执行

User.uid = 100;
执行这句话时:uid为static修饰的变量(静态成员变量),静态变量存储在数据区(data seg),这句话执行完后uid的值就为100。
这里写图片描述
接下来执行User kobe = new User(“kobe”);
这句话会调用User类的构造函数
User(String name) {
this.name = name;
id = uid++;
}
调用时首先会在栈内存中分配一块内存,这块内存空间中有一个变量kobe,kobe中装着一系列的值,通过这些值我们可以找到位于堆内存中new出来的User对象,即kobe是User对象的一个引用。这个构造方法声明有字符串类型的形参,这里把”kboe”作为实参传入构造函数中。由于字符串常量是分配在数据区存储的,所以数据区里面多了一小块内存用来存储字符串“kobe”。此时的内存分布如下图所示:
这里写图片描述
这时对象已经被new出来了,所以堆内存中会存在id,name,但是此时还没有被赋值
当调用构造方法时,首先在栈内存里面给形参name分配一小块空间,名字叫name,接下来把”kobe”这个字符串作为实参传递给name,字符串也是一种引用类型,除了那四类8种基础数据类型之外,其他所有的都是引用类型,所以可以认为字符串也是一个对象。所以这里相当于把”kobe”这个对象的引用传递给了name,所以现在name指向的是”kobe”。所以此时内存的布局如下图所示:
这里写图片描述
接下来进入构造方法
this.name=name;
 这里的this指的是当前的对象,指的是堆内存里面的那个用户。这里把栈里面的name里面装着的值传递给堆内存里面的User对象的name属性,所以此时这个name里面装着的值也是可以找到位于数据区里面的字符串对象”kobe”的,此时这个name也是字符串对象”kobe”的一个引用对象,通过它的属性值就可以找到位于数据区里面的字符串对象”kobe”。此时的内存分布如下图所示:
 这里写图片描述
 接下来执行id = uid++;
  这里是把uid的值传递给id,所以id的值是100,sid传递完以后,自己再加1,此时uid变成了101。此时的内存布局如下图所示:
  这里写图片描述
  当结束构造方法的调用时,所有给这个构造方法分配的局部变量所占的内存空间全部都要消失,所以位于栈内存中的name的内存空间将要消失,栈内存里面指向数据区里面的字符串对象”kobe”的引用也消失了,此时只剩下堆内存里面的指向字符串对象”kobe”的引用没有消失。此时的内存布局如下图所示:
  这里写图片描述
  理解了内存,就理解了一切,就理解了各种各样的语言。所有的语言无非都是这样:局部变量分配内存永远在栈里面,new出来的东西分配内存永远是在堆里,静态的东西分配内存永远是在数据区。剩下的代码肯定是在代码区。所有的语言都是这样。

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

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

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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值