前言
最新看到一篇关于对象和 this 的分析,挺有意思的,结合自己的经验,浅浅理解了一下,并做一下记录。
Self 和 this
灵感来自于 python,python 中定义方法,都会默认给个 self,比如:
def __init__(self, name, age):
self.__name = name
self.__age = age
这个 self 其实是实例的引用,相当于是 java 中的 this,不过 java 中通常是隐式的。
但是通过观察 python 的代码,我们可以发现,我们通过实例调用方法的时候,其实除了给需要的参数外,我们还需要将当前实例的引用传过去。只不过在 java 中这个引用不需要我们手动传。
那么为什么一定要传这个实例的引用呢?能不能不传呢?
答案是不能,至于为什么?那跟 java 的设计有关系了。
我问了 AI 几个问题,一起来看一下:
对象是被创建到堆中的,但是好像没有方法?没有方法,那我们能调用对象的方法呢?
噢~,方法属于类,就是公共的,只有一份,jvm 把方法存放在了方法区。
看第一句描述,每个对象都有一个指向它所属类的指针,这个指针指向该类的方法区的方法表。这就是我们使用 对象.方法 能正确执行的原因~
简单总结一下:方法是属于类的,不是属于对象的,所以只有一份。jvm 创建的对象只包含对象头、实例数据,并不包含方法,类中定义的方法存放在方法区,每个对象都有一个指向它所属的类指针(就是 this 呗~),这个指针指向了该类的方法区的方法表,当对象调用方法的时候,会去方法表中查找对应的方法,然后把方法加载到内存中执行。
再来看上面的问题,因为类的方法是共用的,调用方法的时候是不是需要 java 将 this(this 代表实例的引用)传递过去啊?你不传过去的话,jvm 怎么知道这个方法是 a 对象调用还是 b 对象调用的?
Static 与 this
static 修饰的属性和方法是属于类的,是所有实例共享的。我们反过来想想,问什么要把属性或者方法定义为 static?
- static 变量:大家共有的,大家都一样
- static 方法:这个方法不处理差异化数据
也就是说 static 注定与差异化无关,即与具体对象的数据无关。
以静态方法为例,当我们确定一个方法只处理通用流程,而与实例无关时,我们就可以把它设置为静态方法,比如我们经常写的工具类,里面的方法都是静态方法,因为跟具体的实例无关嘛~
关于 this 的一个小例子
public class Demo {
public static void main(String[] args) {
Son son = new Son();
Daughter daughter = new Daughter();
}
}
class Father {
public Father() {
System.out.println(this.getClass().getName());
}
}
class Son extends Father {
}
class Daughter extends Father {
}
结果如下:
com.example.test.Son
com.example.test.Daughter
为什么?父类创建的时候,其实根据不知道有哪些子类,但是却在父类中得到了子类的名字。
我们知道子类实例化的时候会隐私调用父类构造器,所以上面的代码等同于如下:
class Son extends Father {
public Son() {
super();
}
}
class Daughter extends Father {
public Daughter() {
super();
}
}
所以其实就是调用方法调用罢了, 调用的时候也隐式传入了 this,new Son() 的时候,调用了 super() ,其实传入了 this,就是 Son 实例的引用,然后父类里面的 this 其实就是 Son 的实例,自然就能打印出儿子的名称了,Daughter 同理。
另一个小例子
public class ThisDemo {
public static void main(String[] args) {
// TODO
}
@Getter
@Setter
@Accessors(chain = true)
static class Father {
private String fatherName;
}
@Getter
@Setter
@Accessors(chain = true)
static class Son extends Father {
private String sonName;
}
}
问题一:可以 new Son().setSonName("").setFatherName(),却不能 new Son().setFatherName("").setSonName(),为什么?
问题二:无论怎么调整 setter 顺序,返回值始终是 Father 类型,为什么?
Father father = new Son("").setSonName("").setFatherName("");
Father father = new Son("").setFatherName("");
@Getter、@Setter、@Accessors 是 lombook 的注解, 其中 @Accessors 用来实现链式调用。
我们来看下 IDEA 编号的代码:
public class Demo2 {
public Demo2() {
}
public static void main(String[] args) {
Demo2.Father father = (new Demo2.Son()).setSonName("大头儿子").setFatherName("小头爸爸");
System.out.println(father);
}
static class Son extends Demo2.Father {
private String sonName;
Son() {
}
public String getSonName() {
return this.sonName;
}
public Demo2.Son setSonName(final String sonName) {
this.sonName = sonName;
return this;
}
}
static class Father {
private String fatherName;
Father() {
}
public String getFatherName() {
return this.fatherName;
}
public Demo2.Father setFatherName(final String fatherName) {
this.fatherName = fatherName;
return this;
}
}
}
看一下 setSon 和 setFather 方法的返回值
回顾下我们上面的分析,调用方法的时候,其实会隐式传递 this(就是当前实例的引用),所以:
new Son().setSonName("") 后是 Son 类型的,自然可以调用父类的方法
new Son().setFatherName("")后是 Father 类型的,自然调用不了子类的方法
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于