第十二讲 回顾 static 与 this

第十一讲 回顾 static 与 this

1 关于 static

static:
	意为:静态的
关于类:
	类 = 属性 + 方法;一个java软件它是由很多类组成的,一个类中有两部分,属性和方法。
	属性:定义在类体中,方法外的变量或者是数据。在java中没有什么变量和方法是定义在类外面的。
		 	java是纯面向对象的语言,因为java程序最小的单位是类。
		属性分为两种,一种是没有被static关键字修饰的,一种是带有static关键字的
		不带static修饰的属性叫做成员属性(变量):private int num; 
		带有static修饰的属性叫做静态属性(变量):private static String country;
	方法分三种:有一种特殊的方法叫做构造方法,这种方法不用写返回值类型,方法名为类名;
		另外两种方法:一种是没有被static关键字修饰的,一种是带有static关键字的
		不带static修饰的方法叫做成员方法:public void test();
		带有static修饰的方法叫做静态方法:public static void main(String[] args);
-----------------------------------------------------------------------		
以上,是一些定义,大家记住就可。
.......................................................................

什么时候使用static:
	属性:成员属性和静态属性
	成员属性(变量): 这个属性(变量)是成员的,也就是说它是属于实例对象的,不同的对象有不同的属性值,
	或者是说变量的值不一样。
	例如:Person中有一个成员属性,private int idCard,这个属性是人的身份证号码,
	每个人身份证号都不一样,所以它的值依赖于实例对象,只有有具体的实例对象,
	这个属性的值才有具体的意义。
	比如说:小明还没出生,它就没有身份证号。只有小明诞生了,才会有身份证号。
	所以说,成员属性要有实际的意义,需要依赖实例对象,不同的对象,值代表的意思不一样。
	我们说这种属性,是对象相关的,它与对象息息相关,对象产生,它就产生,对象消亡,它就消亡。
	对象存在于堆内存空间中,对象一旦产生,它的成员属性就会在堆中被存储,且被赋值。
	对象一旦被GC回收,它的成员属性所占有的内存空间就会被释放。
	
public class Person {
    private int id;
    private String name;
    public Person() {}
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
        // 这里可以写很多java语句,但是一般情况下,不写。因为方法要功能单一。
        // 构造方法,功能很单一,就是给成员属性赋值。
    }
}

在这里插入图片描述

静态属性:被static修饰的属性(变量)
	上述讨论成员属性(变量),既然有一个要与成员变量区分的静态变量,那么它与成员变量肯定不是一回事。
	静态变量不存放于堆内存空间中,也不存放于栈内存空间中。
	堆内存中只存放成员变量,栈内存中只存放局部变量。
	现在只剩下方法区内存,静态属性(变量)就存放在这里。(记住就行)

	方法区内存的介绍:
		方法区内存存放的东西有哪些?代码片段的字节码,静态的数据,常量
		方法区内存是jvm中最先有内容的内存空间,这部分空间主要用来加载类的信息,
		加载类中代码片段的字节码,也就是各种.class文件。
		在类加载的过程中,遇到了static修饰的变量,就会在方法区内存空间中开辟空间存储并赋值。
		这是在main方法没有运行的时候,甚至可能连类的信息都还没有被加载完的时候,
		就已经给static修饰的变量开辟空间并赋值了。
		static修饰的变量,也就是静态变量,它是在类加载的时候开辟空间并赋值的,
		SUN公司这么做也就意味着,static修饰的变量是类级别的。它与对象无关。
		一个程序要运行,只有当与这个程序相关的所有的类都加载完毕以后,程序才会开始执行。
		因为我们说过,cpu要运算,寄存器就要有数据,寄存器要有数据,内存中就要有数据,
		否则,cpu不会运转。cpu都不工作,就不用谈程序的运行。所以,是先内存中有内容,
		然后通过总线给到寄存器,寄存器和CPU交互,再给回内存(或其他的设备内存),
		然后读写到硬盘(或者是输出到其他的设备)。
		java中jvm对类加载只进行一次,加载完毕后才会让程序正常执行。
		在加载的过程中,SUN公司为程序员准备了一些可以提前运行部分特殊代码的机制,
		这部分能在类加载的时候运行的代码是static修饰的属性的声明和赋值,以及static代码块。
		这种机制有一些好处,比如可以观测类加载的过程,可以在类加载的过程中输出日志,
		可以类加载完成之后程序运行的压力,提升程序运行的效率。
		比如Spring框架,Spring是管理对象的。未来我们的程序很大,有很多很多的对象,Spring来管,
		beanFactory,这是一个bean工厂,专门生产对象,
		有一些对象我们不需要在程序运行的时候再调用new 来产生,我们可以在类加载的时候就生产好。
		好比,zz同学他做电商,卖电脑,作为电商,一定要在店铺开好以后才能销售产品;
		但是zz同学在开通和装修店铺的过程中,就发起了预售。
		这个店铺开通和装修的过程就是JVM中的类加载到方法区内存的过程,
		预售就是静态属性和静态代码块的执行。
		栈内存空间:相当于每日发货的小仓库(活跃度最高的)
		堆内存空间:相当于库存产品的大仓库(活跃度相对不高)
		方法区内存:只活跃一次,就是相当于注册、开店
		开店注册的动作只有一次,这里面会产生一些数据,这些数据比如营业执照中的经营范围,
		店不倒,范围就一直在。它与具体的产品无关。
		
在java中,有一些数据是与对象无关的,也就是说,由同一个类产生的对象,
不论是谁,变量值都不会改变,对象不存在,变量的值也存在。
这些数据就是静态的,它随着类的产生而产生,类的消亡而消亡。
类是一个模板,只有当方法区内存被清空的时候就消失了。
比如,我们都是中国人,中国人这个类中有一个属性叫做国籍,每个中国人对象都有这个属性,
且属性值都是"中国",这样的属性我们不应该给每个对象都给一份,这样浪费堆内存的存储空间。
只要是中国人这个类存在,国籍就一定存在。这样的数据只要有一份就可以了,
这一份数据是类加载的时候就已经赋值完成了。这样的数据我们就可以定义为static的,静态的。
这样的变量,也叫作类级别的变量。它只与类相关,与对象无关。

静态的数据:我们使用"类名."的方式访问。因为它是类级别的,只要是这个类存在,
这个数据就存在,不依赖对象。当然也可以用"引用."的方式访问。因为"引用"也是类这个类型的。
比如 Chinese类 有国籍这个静态属性,那么任何一个中国人都有这个属性,值一样都是中国。
Chinese zhangsan = new Chinese();
zhangsan.country = "China"; 这样做当然可以。因为zhangsan的类型是Chinese
实质上上述代码是Chinese.country。与对象无关,对象的引用之所以能访问这个属性,
是因为对象是它所在的类的类型。
zhangsan = null;
zhangsan.country = "China"; 这也是可以的,因为不是对象的引用在访问静态的变量,
而是"类名."的方式在访问。
静态的变量与对象无关,不论对象是谁,只要它是这个类的类型,就可以了。
Chinese zhangsan = null; 这个zhangsan的类型还是Chinese,类型一旦被定义是不会改变的。
所以zhangsan.country 本质上就是 Chinese.country.

静态属性的生命周期:类加载的时候产生,程序运行结束时消亡。(作用域)
成员属性的生命周期:当new的时候产生,当引用为null消亡
局部变量的生命周期:压栈的时候产生,弹栈的时候消亡

静态方法:
	它和静态属性一样,是类级别的。这类方法与实例对象无关。只与类相关。
	"类名."的方式调用。使用"引用."的方式也可以,推荐使用"类名."的方式,不会产生空指针异常。
	例子就是昨天的bornCry(),人类出生都要哇哇哇的哭,这不随哪个对象而改变。是人类共有的方法。
	静态方法用来处理静态的数据。静态的方法中能不能访问成员变量?不能。

方法是指令的组织,它都是代码片段,都存放于方法区内存中,方法的执行都要压栈。

2 关于this

每一个实例对象在堆中被new出来的时候,都有一个属性叫做this,这个属性保存了对象在堆内存中的内存地址。

在这里插入图片描述

你们就没有疑问吗?
	this在哪里?怎么找到的?
	在Person()方法中就没有this,
	怎么找到了堆内存空间中的Person类型的对象的?
	this.id = id;
public class Person {
	private int id;
	private String name;
	
	public Person() {
		
	}
	// 构造函数的作用是new对象的时候给成员属性赋值。
	public Person(int id, String name) {
		// id = id;
		// name = name;
		// 这种方式不可以,因为java中有就近原则,也就是说id = id;
		// 这个语句,java会认为是形参中的id给形参中的id赋值,不会给成员属性id赋值
		this.id = id;
		this.name = name;
	}
	
	public static void main(String[] args) {
		Person p = new Person(1, "zhangsan");
	}
}
/*
Person p = new Person(1, "zhangsan");的执行过程如下:
    先执行赋值"="的右边,右边先执行new,执行new的时候在堆中开辟空间,用来存储对象。
    然后再调用Person的构造方法,给对象的成员变量赋值。
    然后,执行赋值"="左边,在栈中开辟一块空间,这块空间的名字叫做p,p是Person类型的引用
    最后,执行"=", 将new Person()返回的地址存入p所指向的栈内存空间。   
*/


在这里插入图片描述

第一步:Person()构造方法的参数列表中,一定要有0x1234这个对象作为参数。
问:一个方法是通过什么与其他的属性互动?只有我们在传参数的时候它能互动了。
传入的实参的类型一定要和方法的形参类型相同,位置也要相同,个数也要相同。
Person(int id, String name) {
	this.id = id;
	this.name = name;
} ----- 1
请问,这个this从哪里来?
这个this一定是从参数列表中来的。那么构造方法的原型应该是这个样子的:
	Person(Person this, int id, String name) {
		this.id = id;
		this.name = name;
	} ---- 2
只有这样,下面的代码才成立,否则不会成立:
Person(int id, String name) {
	this.id = id;
	this.name = name;
} ----- 1
Person this:这是什么,这是不是一个引用啊?是的!
1这段代码如果是所见即所得,看到的就是它本身的样子的话。请问,this从哪里来?
this也是一个引用,它在堆内存中对象内部存储了对象本身的地址,它一个引用。
类定义好以后就会产生引用吗?不可能的!!!!!
引用只能定义。
以下代码中全文都没有定义过this,请问this从哪里来?没有来由的。
所以推断:1这段代码,不是所见即所得。它的原型只能是2.这样才能解释得通。
只不过在1中,将2代码中Person(Person this, int id, String name)中的Person this省略了。

所有的成员方法中都有一个隐含的this参数。都有。
只有这样,方法才能被各个不同的实例对象调用,否则没法调用。
public class Person {
	private int id;
	private String name;

	public Person() {
		
	}
	// 构造函数的作用是new对象的时候给成员属性赋值。
	public Person(int id, String name) {
		this.id = id;
		this.name = name;
	}	
}

在这里插入图片描述

我们来看看底层,是不是真的有这个参数:
	以此来证明我不是瞎说:
E:\java基础\day06-1>javac -g:vars Person.java
E:\java基础\day06-1>javap -v Person.class	
  public Person(); // 无参构造
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1// 这里表示方法中有多少个参数
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LocalVariableTable:
      // 局部变量属性表,这里表示方法中有多少个局部变量,参数也是局部变量
        Start  Length  Slot  Name   Signature
            0       5     0  this   LPerson;

  public Person(int, java.lang.String);// 有参构造 int 基础数据类型 
    descriptor: (ILjava/lang/String;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iload_1
         6: putfield      #7                  // Field id:I
         9: aload_0
        10: aload_2
        11: putfield      #13                 // Field name:Ljava/lang/String;
        14: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      15     0  this   LPerson;
            0      15     1    id   I
            0      15     2  name   Ljava/lang/String;

   public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=2, args_size=1
         0: new           #8                  // class Person
         3: dup
         4: iconst_1
         5: ldc           #17                 // String zhangsan
         7: invokespecial #19                
        10: astore_1
        11: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      12     0  args   [Ljava/lang/String;
           11       1     1     p   LPerson;
}
public class Test 
{
	private int id;
	
	public Test(Test this){
		System.out.println(this.id);
	}
	
	public Test(int id) {}

	public void test01() {}
	
	public void test02() {
		int i = 0;
		int j = 0;
		String name = "";
		char c = 'a';
		boolean f = false;
	} 
	
	public void test03(int i, String s, char c, boolean f, double d) {
		
	}
	
	public static void test04() {
		
	}
}

  • 再次验证
E:\java基础\day06-1>javac -g:vars Test.java
E:\java基础\day06-1>javap -v Test.class
 public Test();
   descriptor: ()V
   flags: (0x0001) ACC_PUBLIC
   Code:
     stack=1, locals=1, args_size=1
        0: aload_0
        1: invokespecial #1             
        4: return
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0       5     0  this   LTest;

 public Test(int);
   descriptor: (I)V
   flags: (0x0001) ACC_PUBLIC
   Code:
     stack=1, locals=2, args_size=2
        0: aload_0
        1: invokespecial #1                  
        4: return
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0       5     0  this   LTest;
           0       5     1    id   I

 public void test01();
   descriptor: ()V
   flags: (0x0001) ACC_PUBLIC
   Code:
     stack=0, locals=1, args_size=1
        0: return
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0       1     0  this   LTest;

 public void test02();
   descriptor: ()V
   flags: (0x0001) ACC_PUBLIC
   Code:
     stack=1, locals=6, args_size=1
        0: iconst_0
        1: istore_1
        2: iconst_0
        3: istore_2
        4: ldc           #7                  
        6: astore_3
        7: bipush        97
        9: istore        4
       11: iconst_0
       12: istore        5
       14: return
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0      15     0  this   LTest;
           2      13     1     i   I
           4      11     2     j   I
           7       8     3  name   Ljava/lang/String;
          11       4     4     c   C
          14       1     5     f   Z

 public void test03(int, java.lang.String, char, boolean, double);
   descriptor: (ILjava/lang/String;CZD)V
   flags: (0x0001) ACC_PUBLIC
   Code:
     stack=0, locals=7, args_size=6
        0: return
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0       1     0  this   LTest;
           0       1     1     i   I
           0       1     2     s   Ljava/lang/String;
           0       1     3     c   C
           0       1     4     f   Z
           0       1     5     d   D

 public static void test04();
   descriptor: ()V
   flags: (0x0009) ACC_PUBLIC, ACC_STATIC
   Code:
     stack=0, locals=0, args_size=0
        0: return
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值