Static介绍以及内存加载


在Java中,我们在定义类的时候,类中都有相应的属性和行为。而这些属性和行为都是通过创建本类对象调用的。当在调用对象的某个行为时,这个行为没有访问到对象特有的数据时,方法创建这个对象就显得有点多余了。可是不创建对象,我们就无法调用在定义在类中的行为。举个简单的例子:

复制代码
/*
定义一个类 person
*/
class Person
{

   int age;
   String  name;
   String addr;
   //说话行为
   void speak(){
     System.out.println(age+"+"+name+"+"+addr);
    }
   /吃饭行为
   void eat(){
     System.out.println("吃饭....");  
    }  
}
/*创建Person的对象,并且调用定义在类中的行为
*/
class Demo { public static void main ( String[] agrs){ Person p = new Person();new一个对象 p. eat(); 调用吃饭行为 } }
复制代码

我们都知道,在以上的代码中,只要调用eat的行为,我们都需要创建一个新的对象,如果我们调用多次eat的行为,我们就需要创建多个对象,这显得太多余了。

如果创建对象调用方法,发现这个方法中没有使用到对象中特有的数据,仅仅是调用类中的方法,这个时候我们可以用static关键字来休息我们的方法。 这个时候,被static修饰的方法是静态的。所以,eat行为可以定义为:

static void eat(){
     System.out.println("吃饭....");  
    }  

这个时候eat是静态方法,属于类的方法,可以使用类名直接调用。这个时候我们就不需要创建一个对象来调用eat了,就直接地写成:Person.sleep(); 就可以了。那么我们来尝试一下,用static来修饰speak的行为如下:

复制代码
static speak(){

System.out.println(age+"+"+name+"+"+addr);

}
............
............
//在主函数中直接调用speak行为
Person.speak();

复制代码

来编译一下,结构如下:

按照错误提示:非静态的变量不能被静态的方法引用。这是为什么呢?原因有一下几点:

  • 静态时随着类的加载就加载了,也是随着类的消失而消失了。
  • 静态时优先于对象而存在内存里面的,被对象共享
  • 类中的变量是随着对象的加载而加载的,而静态时优先出现在内存里面的,所以静态无法后来出现在内存中的数据也就理所当然了。进一步讲,在静态方法里面无法使用this关键字,因为在静态方法里面,对象还没出现,所以this就没有任何指向了。

在上诉的例子中,我们在类中定义的成员变量age,name, addr是非静态的的,而且被static修饰的方法无法访问非静态的成员属性和行为。如果我们要在静态方法中访问成员属性呢?我们可以就直接用static修饰成员变量,那么被修饰的那个成员变量就变为静态的了,同样地,随着类的加载而加载了。优先于对象出现了。我们都知道,定义的类中,非静态的成员变量随着对象的出现而加载,如果成员属性有一个是所有的对象共同的呢?比如输入很多个对象的属性,国籍。大家的国籍都是Chinese。那么每次new一个对象,都会加载一个国籍的属性,但是这么多国籍属性都是一样的,确实浪费了内存空间,通过以上的的例子我们知道,使用静态修饰一个方法,我们可以让众多对象共享一个方法。同样的道理,使用static修饰一个成员变量,我们让对个对象共享这个数据。继续上面的例子,我们把这个代码改为:

复制代码
/*
定义一个类 person
*/
class Person
{

   int age;
   String  name;
   String addr;
   Static  String nation = Chinese;
   //说话行为
   void speak(){
     System.out.println(age+"+"+name+"+"+addr+"+"+nation);
    }
   /吃饭行为
  static void eat(){
     System.out.println("吃饭....");  
    }  
}
/*创建Person的对象,并且调用定义在类中的行为
*/
class Demo
{
   public static void main ( String[] agrs){
      Person p = new Person();new一个对象
       p. eat(); 调用吃饭行为
    }
}
复制代码

nation这个成员变量是大家共同的,所以呢,我们可以使用static修饰这个变量来让所有的对象共享这个数据。static修饰的变量是类共有的,比如chinese国籍是所有中国人这一类人共有的,所以是属于类的,也成为静态变量也称为类变量。

我们通过以上的总结,我们了解了static的引入以及作用,那么静态在内存中使怎么样加载的呢?

当输入java Demo之后,JVM加载Demo.class文件到方法区,分配内存空间,然后执行main方法,但是main方法是静态的,所以main方法被加载到方法区内的静态代码区域里面。之后执行main,main在栈内压栈,遇到Person的类,同样JVM加载person.class的文件,首先Person类文件的静态变量(nation)和静态方法(eat)也被加载到方法区的静态代码区里面,静态成员初始化nation = chinese。非静态的变量的模板被加载到方法区内。开始加载Person这个类,首先静态成员变量初始化,nation = null,加载这个到栈内存里面去,这样完成类的加载。之后,new 一个对象Person被加载到堆内存里面开辟一个物理地址(我们假设), 成员变量默认初始化 :age = 0 name = null addr = null,加载speak到栈内存。之后调用默认的构造函数,所以将对象的内存空间赋值给p,p指向0x45。回到主函数,执行p.eat,所以p到方法区里面找eat函数,执行eat的行为,打印之后,main函数执行完毕,弹栈。这个程序执行完毕,内存加载过程如上图所示。

关于static,有时候还用来修饰一段代码块,我们称为静态代码块。如下代码所示:

public class Person {

    private String name;

    private int age;

    static{

       System.out.println("静态代码块执行了");

    }

}

在类中,不管创建多少个对象,静态代码块都只执行一次。可以用来给变量进行初始化和给类进行初始化。

Stay hungry,stay foolish!
Java中的静态变量和静态方法都是属于类的,而不是属于对象的。因此,当类被加载内存中时,静态变量和静态方法也会被加载内存中。 下面是一个简单的Java类,可以用来说明静态变量和静态方法的内存图: ```java public class MyClass { private static int count = 0; public static int getCount() { return count; } public static void incrementCount() { count++; } } ``` 当这个类被加载内存中时,会创建一个Class对象用来表示这个类。这个Class对象包含了类的一些元信息,如类名、父类、接口等。 同时,静态变量count也会在内存中分配一块空间,用来存储它的值。在这个例子中,count的初始值为0。 静态方法getCount()和incrementCount()也会被加载内存中,但是它们不会被实例化对象所持有。相反,它们属于类本身,因此可以通过类名来直接调用。 当我们调用静态方法incrementCount()时,它会将count的值加1。因为count是静态变量,所以它的值会在所有的实例对象之间共享。这意味着,如果我们创建了多个MyClass的实例对象,并调用了多次incrementCount()方法,count的值也会随之增加。 总结一下,Java静态变量和静态方法的内存图包括以下几个部分: 1. Class对象,用来表示这个类的元信息; 2. 静态变量,用来存储静态变量的值; 3. 静态方法,用来表示属于类本身的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值