关于对象和 this

本贴最后更新于 366 天前,其中的信息可能已经事过境迁

前言

最新看到一篇关于对象和 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 类型的,自然调用不了子类的方法

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3167 引用 • 8207 回帖 • 1 关注
  • 对象
    11 引用 • 44 回帖
  • this
    2 引用 • 7 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...