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("静态代码块执行了");

    }

}

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

posted @ 2013-11-29 01:47  lee笔记  阅读(990)  评论(0编辑  收藏  举报