带你深刻认识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方法(实例方法),但是实例方法内部可以调用静态方法