什么!少年你居然以为this关键字很简单?还不看我博客?

带你深刻认识This关键字

先总结一下重点笔记,方便大家学习
1、this是个啥玩意儿?
   this是一个关键字, 但凡学过英语的都知道,意思为:“这个”。
 
2、this在写代码的时候充当啥角色?
   this是一个引用,表示对“调用方法的那个对象”的引用。this也是一个变量,this变量中保存了内存地址指向自身,this存储在JVM堆内存java对象内部。
 
3、this需要创建吗?
   当我们创建一个对象的时候,会自动生成该对象对应的this变量。创建100个Java对象,就有100个不同的this。
 
4、学了大半天this,this应该出现在那里?
   this可以出现在“实例方法”中,this指向当前正在执行这个动作的对象。(this代表当前对象)
 
5、多数情况下this可以省略不写的?
   没错,可以省略不写。如果在方法内部调用同一个类的另一个方法或者是某一个变量,就不必使用this,直接调用即可。当前方法中的this会自动应用到同一个类的其他方法或者变量。(编译器会自动帮你添加上。)
 
6、什么情况下this不可以省略?
   只有当需要明确指出对当前对象的引用时,才需要使用this关键字。
例如:
   return this; // 当需要返回对当前对象的引用
   this.name = name;// 此处this.name为当前对象的实例变量,name为局部变量,只是重名了。
 
7、使用this有啥忌讳?
   this只能在方法内部使用,但是不能使用在带有static的方法当中。就是想使用也使用不了,语法压根不支持。
 
8、杂乱补充
   this的用法与其他对象引用基本差不多,除了5中提到的那点。
   当一个行为/动作执行的过程当中是需要对象参与的,那么这个方法一定要定义为“实例方法”,不要带static关键字。


Note1:不知道起啥标题

看一下代码:两个类

  • Note.class:
    * 实例变量:name(名字)
    * 构造方法:Note()
    * 实例方法:doNote()
    * 抽象方法:doSome()
  • Test.class
    * main()主方法

带着问题看代码

  • 1、回顾一下,什么是 “实例变量”?例如:private String name;
  • 2、doNote(){ System.out.println( name + "在背着你偷偷做笔记学习!"); } 去掉this运行结果一样,为什么?
  • 3、分析下代码中备注报错的代码,为什么会报错?
public class Note {
	private String name;	// 实例变量
	
	public Note() {}
	public void doNote() {
		System.out.println( this.name + "在背着你偷偷做笔记学习!");
		//System.out.println(name + "在背着你偷偷做笔记学习!"); 
	}
	
	public String getName(){
		return name;
	}
	
	public static void doSome() {
		System.out.println(name);	// 报错了
		System.out.println(this.name);	// 又报错了
		/* 分析一下为什么编译错误?
		 * doSome()方法调用不是对象去调用,是一个类名去调用,执行过程中没有“当前对象”。
		 * name是一个“实例变量”
		 * 而这个函数体代码的含义是:访问当前对象的name,而如今既然没有当前对象,自然不能访问当前对象的name
		 * 
		 * 小总结:static的方法的调用不需要对象,直接使用类名,所以执行过程没有当前对象,所以不能使用this。
		 */

		Note note = new Note();
		System.out.println(note.name);  // 没有报错
	}
}
public class Test {

	public static void main(String[] args) {
		Note note = new Note();
		System.out.println(note.name);  // 报错
		System.out.println(note.getName());	// 这里访问的name是note引用指向对象的name
	}		
	
}
  • 1、什么是实例变量?
    在类的声明中,属性是用变量来表示的。
    实例变量:定义在类中但在任何方法之外(New出来的均有初始化),是在类声明的内部但是在类的其他成员方法之外声明的。
    类的每个对象维护它自己的一份实例变量的副本。
    在堆内存的对象内部中存储,所以访问该数据的时候,必须先创建对象,通过“引用. "的方式访问。

  • 2、this是否可以省略?
    之前提到过:this可以出现在“实例方法”中,this指向当前正在执行这个动作的对象。(this代表当前对象),所以才在代码里加上“this. ".
    当我们到调用name,肯定是想调用当前对象中的成员变量name啊,不然干嘛花力气创建对象!所以在java的语法机制里默认是调用当前对象的成员变量。
    又由于name是一个实例变量,所以访问(name)的时候一定访问的是当前对象的name。

  • 3、静态方法doSome()为什么调用name或this.name会报错?
    没有static关键字的方法被称为:“实例方法”,实例方法怎么访问?——“引用. ”
    没有static关键字的变量被称为:“实例变量”
    带有static关键字的方法被称为:“静态方法”,静态方法怎么访问?——“类名. ”
    博客开头的笔记里说过:this不能使用在带有static的方法当中。现在解释一下这句话的意思:
    static方法不能使用this,不就代表static方法里没有"当前对象"。那为什么没有呢,因为带有static的方法是通过“类名. "的方式访问
    没听懂?那好吧,我引用下书中的解释,希望你会明白?
    为了能用简便、面向对象的语法来编写代码,编译器做了一些幕后工作。它暗自把“所操作对象的引用”作为第一个参数传递给对应的实例方法。
    例如:
    Note note = new Note();
    note.doNote(); // 你编写的代码,调用对象的实例方法
    Note.doNote(note); // 编译器后台代码,当然我们不能这么编写代码,会报错的。
    // 而静态方法的调用是这样的:
    Note.doSome(); // 压根就没有“对象”什么事儿,没有对象能有调用实例变量吗?

  • 如果非要在静态方法里调用成员变量该怎么办?
    没有"当前对象"无法直接调用,那只能间接调用了。
    Note note = new Note();
    System.out.println(note.name); // 就好比main方法

  • 为什么doSome()方法中System.out.println(note.name);没有报错,但是在Test中的main方法里就报错呢?
    这个我也没有合理的解释,哈哈哈!


Note2:静态(实例)方法能否访问静态(实例)变量

复习一下note1的知识

public class Note2 {
	int num = 10;	// 实例变量
	
	/** 带有static的方法
	 * JVM负责调用main方法,JVM是怎么调用的?
	 * 		—— Note2.main(String[] );
	 * 
	 */
	public static void main(String[] args) {
//		System.out.println(num);	// 编译报错,没有当前对象
		
		
		/** 再来看看上面的报错代码:
		 * 		代码意思:访问“当前对象”的num属性
		 */
		
		Note2 n2 = new Note2();
		System.out.println(n2.num);
	}

}

看完代码,你要明白,为什么静态方法和实例方法都是在一个类里面,但为啥带static的方法就是不能访问类的成员变量呢?
当有人问你你可别说啥:就是不能访问,一个是静态方法,一个是实例方法。实例可以,静态不行…… ———— 太没逼格了大兄弟!
如果你现在还没太明白的话,………………,我也没招了,都怪我语文不好没解释明白。我的错!只能建议你反复改看。
思考一下:静态方法不能直接访问实例变量,现在对调一下,实例方法里能否调用静态变量呢?
答案是可以的,无论是静态方法还是实例方法,都可以调用静态变量


Note3:关于实例方法的调用问题

看一下代码:就一个类!可别嫌麻烦不想看,那你想干嘛?想我吗?

  • Note3.class:
    * 实例方法:doNote()、run()
    * 抽象方法:doSome()
    * main()主方法

带着问题看代码
1、main方法中,注释部分为什么编译错误?
2、在run()方法体里调用doOther()方法是可以的,为什么呢?
我猜测你的回答是这样的:
* 1、语法不支持,这样写就是会报错,常识啊。
* 2、实例方法的方法体里就是可以调用另一个实例方法啊,常识啊。
你可别!别这么回答了,太磕碜了!

public class Note3 {

	// 带有static的主方法
	public static void main(String[] args) {
		// 调用doSome()方法
		// 1
		Note3.doSome();
		// 2
		doSome();
		
		
		// 调用doOther()方法
//		Note3.doOther();	// 编译错误
//		this.doOther();		// 编译错误
		
		Note3 n3 = new Note3();
		n3.doOther();
	}

	// 带有static
	public static void doSome() {
		System.out.println(" do some! ");
	}
	
	// 实例方法
	public void doOther() {
		System.out.println(" do other! ");
	}
	
	public void run() {
		System.out.println("run execute!");
		doOther();
	}
}

在主方法里,正确调用实例方法的方式是?

  • mian方法里没有this,因为是static的方法。故编译错误
  • 实例方法调用必须有对象的存在,与实例变量一样。
  • 没有对象的话,就先创建对象,通过引用. 的方式访问 。

在一个实例方法体里调用另一个实例方法是可以的,为什么呢?

  • 1、首先:run()是实例方法,调用run()方法的一定是有对象存在的。故一定要先创建了一个对象才能调用run()方法
  • 2、其次:在实例方法体里的代码执行过程中一定是存在“当前对象”的,也就是说这里一定是有“this”的。
  • 3、最后:doOther()是一个实例方法,实例方法调用必须有对象的存在 。
  • 故完整的写法:this.doOther(); 只是把this省略了。所以调用不报错。

static方法不能直接调用实例变量或方法,为什么?
带static的方法中不能“直接”访问实例变量和方法。 因为实例变量和实例方法的访问都需要对象的存在。
而static的方法是没有this的,也就是当前对象是不存在的。 自然也是无法访问当前对象的实例变量和实例方法。


Note4:this啥时候不能省略呢?

当然,还有其他情况下,this也不能省略。总结里也提到过另一种情况,这里就介绍下最最最常见的情况。
老规矩,代码结构:
Note4.class:

  • 成员变量:(int)id,(String)name
  • 无参构造方法
  • 实例方法:get()、set()
public class Note4 {
	private int id;
	private String name;
	
	public Note4() {}

	public int getId() {
		return id;		// 这里可以不写成:return this.id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

区分局部变量和实例变量的时候,"this"不能省略
如果省略this的话:
public void setId(int id) {
      id = id;      // 方法体里的id与实例变量的id没有关系:id =id,都是(int id)里的id
      // 可以看成 “就近原则”
}
这里就到介绍一个新概念,我把它命名为 “调用域” ,是不是感觉很专业啊!哈哈哈!

  • 当类中的一个实例方法要调用一个叫“name”的变量。那它要去找啊,但在找之前要先确定范围,也就是我说的“调用域”。它会先在方法体里找,如果该方法的形参刚好有一个叫做“name”、或者在方法体里定义了一个name变量。此时就找到了,那么调用域就是方法体。
  • 但要是没有的话,就要扩大寻找范围了,也就是整个类。就将目标锁定在成员变量上,看看有没有一个变量叫做“name”的。此时的调用域就是类。
  • 而在上述代码里,setId的方法体里要调用一个叫做id的变量,刚好方法形参叫id、成员变量也叫id。那到底是调用哪一个呢?这就要涉及到刚才说的例子了里提到的“调用域”,也就是搜索范围的意思。
  • 为了提高效率,这个搜索范围是由小变大,所以最开始的调用域为方法体,如果找到了那就收工回家,此时压根就不知道由个成员变量也叫id。
  • 可是setId方法的意图是将形参变量id给成员变量id赋值,没有了this就成了形参id给形参id自己赋值了。所以当发生重名的时候,为了区分局部变量和实例变量,需要使用this来表明身份。

也许有人会说了,直接形参不叫id,不就不会发现重名的状况吗?
确实,形参改名就能解决问题,但是不建议。原因很简单,为了代码的规范和可维护,这样编写代码更“官方”。哈哈哈,就是这样,但是你要是想独特也可以改。


Note5:在构造器使用this调用构造器(看起来提高逼格,实际很简单!)

看一下代码:还是就一个类!

  • Times.class:
    * 成员变量:(int)【year,month,day】
    * 构造方法:有参/无参
    * 实例方法:show()、get()、set()

现在有一需求:调用无参数构造方法的时候,默认创建的日期是"2020-6-18,该如何实现?
大致思路,在无参构造方法里默认初始化为2020-6-18——注意注释掉的错误写法!!!

public class Times {
	private int year;
	private int month;
	private int day;
	
	public Times() {
//		this.year = 2020;
//		this.month = 6;
//		this.day = 8;
		// 仔细一看是不是和有参构造方法里的代码很类似呢?
		// 所以就思考了:代码能不能通过调用另一个(有参)构造方法来完成呢?
		
//		new Times(2020,6,18);	// 错误写法,这就表示创建一个新对象了
		
		// 正确写法:
		this(20200,6,18);		// 小结论:构造方法的调用不一定要创造对象
	}
	public Times(int year, int month, int day) {
		this.year = year;
		this.month = month;
		this.day = day;
	}
	
	public int getYear() {
		return year;
	}
	public void setYear(int year) {
		this.year = year;
	}
	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {
		this.month = month;
	}
	public int getDay() {
		return day;
	}
	public void setDay(int day) {
		this.day = day;
	}
	
	public String Show() {
		return "Date [year=" + year + ", month=" + month + ", day=" + day + "]";
	}
	
	public static void main(String[] args) {
		Times n1 = new Times();
		Times n2 = new Times(2020,7,1);
		
		n1.Show();
		n2.Show();
	}
}

老方法
public Times() {
      this.year = 2020;
      this.month = 6;
      this.day = 8;
}
错误方法
public Times() {
      new Times(2020,6,18);
}
使用this的新方法
public Times() {
      this(20200,6,18);
}
敲黑板,记笔记了::"this"可以用在哪里?

  • 1、可以使用在实例方法当中,代表当前对象【语法格式:this.】
  • 2、可以使用在构造方法当中,通过当前的构造方法调用其他的构造方法【语法格式:this(实参);】
    重点:this(1970,1,1); 只能出现在构造方法第一行,不然会报错,也说明了只能调用一次。

你也许会疑问了,使用代码写的不挺好的吗?直接复制参构造方法里的代码改改就行了,看起来也挺整齐的,为啥非要使用别的方法来对参数初始化呢?
那如果你的参数有100个,那你的无参构造方法就要写100行代码,还是100行重复的代码!!!


Note6:在staic方法里调用static方法

虽然这部分内容应该是”staic“关键字的知识点,但为了与this做对照,就提前介绍一下。
代码结构
Note6.class:

  • 静态方法:doSome()
  • main()主方法

问题

  • 1、回顾下空指针异常,代码里会使用到。
  • 2、好没想到,哈哈哈。
public class Note6 {
	
	public static void doSome() {
		System.out.println("doSome!");
	}
	
	public static void main(String[] args) {
		Note6.doSome();
		
		doSome();
		
		Note6 n6 = new Note6();
		n6.doSome();	// 此处代码会显示警告,为什么警告呢?思考一下
		
		n6 = null;	// 引用为空
		n6.doSome();	// 未出现空指针异常,说明调用doSome()方法的时候未用到对象。
		// 所以可以得出结论:n6.doSome();的实例效果就是Note6.doSome();
		
	}
}

什么时候程序在运行的时候出现空指针异常呢?

  • 空引用访问实例相关的数据,因为实例相关的数据就是对象相关的数据
    • 实例相关的数据包括:
      • 实例变量【对象需要存在】
      • 实例方法【对象需要存在】
  • 这些数据在访问的时候,必须有对象的参与。
  • 当可引用的时候,对象不存在,此时访问这些实例数据一定会出现空指针异常。

结论:

  • 带static的方法,既可以采用·类名. 的方法访问,也可以采用引用. 的方式访问。
  • 虽然语言上允许采用 "引用. "的方法调用static方法,但实际执行过程中与引用指向的对象无关。 而且在使用引用的方式访问带有static的方法,程序会出现警告。
  • 所以带有static的方法建议使用 “类名.” 的方式访问。

补充
虽然在static方法内部无法调用非static方法(实例方法),但是实例方法内部可以调用静态方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值