这篇说一下容易混淆的两个关键字:this与super.
this
简单的介绍
先看看<Java编程思想>上面的解释:
this关键字只能在方法内部使用,表示对"调用方法的那个对象"的引用.this的用法和其他对象引用并无不同.但要注意,如果在方法内部调用同一个类中的另一个方法,就不必使用this,直接调用即可.当前方法中的this引用会自动应用于同一个类中的其他方法.
也就是说,this用来指向同一个类的东西.
构造器中的this
各种代码中,this最多的地方就是构造器了,我们在使用IDE自动生成的时候就可以看到这样的代码:
class SuperClass {
//4个变量
private int id;
private int number;
private int load;
private String name;
//构造器
SuperClass(int id, int number, int load, String name){
this.id= id;
this.number = number;
this.load = load;
this.name = name;
}
}
this后面的变量是类中定义的变量,等号后面的方法传入的参数,解释一下就是,这个对象的这个变量等于(被)参数传入的这个变量(赋值).
再来看看稍微复杂一点的,其中加入了用来测试的输出语句:
SuperClass(int id, String name){ //构造器1
this.id = id;
this.name = name;
System.out.println("SuperClass 2个参数的构造器.");
}
SuperClass(int id, int number, int load, String name){ //构造器2
this(id, name);
this.number = number;
this.load = load;
System.out.println("SuperClass 4个参数的构造器.");
}
SuperClass(int id, int number, int load){ //构造器3
this(id, number, load, null);
System.out.println("SuperClass 3个参数的构造器1.");
}
SuperClass(int id, int number, String name){ //构造器4
this(id, number, 0, name);
System.out.println("SuperClass 3个参数的构造器2.");
}
现在我们要创建一个3个参数的SuperClass对象,会输出什么呢?实际操作一下看看:
public static void main(String[] args) {
SuperClass sc = new SuperClass(1, 2, 3);
}
结果:
SuperClass 2个参数的构造器.
SuperClass 4个参数的构造器.
SuperClass 3个参数的构造器1.
通俗的解释一下就是,传入的参数是(int, int, int),于是就寻找方法签名相同的方法,发现构造器3相同,就执行构造器3.构造器3中调用了这个对象(this)的四个参数的构造器(构造器2) ,构造器2中又调用了这个对象(this)中的两个参数的构造器(构造器1).
方法签名
方法签名是一个方法的方法名,传入参数的类型构成的,方法签名一样的方法在一个类中只能存在一个,比如上面的几个构造器,而下面的方法不能存在于同一个类中:
public void sign(int first, int second, String third){}
public void sign(int a, int b, String c){}
看起来传入参数的名字是不同的,但是是不能共存在一个类中的.
那跟返回值有没有关系,答案是没有,比如下面的方法也是不允许共存在同一个类中的:
public void sign(int first, int second, String third){}
public int sign(int first, int second, String third){ return 1;}
这与方法重载关系很大,首先想想我们使用的输出语句:
System.out.println();
它好像什么都能输出:给它一个小数,整数,字符,甚至是对象都可以,就是因为它有很多个重载版本:首先判断传入的参数(输出的内容)是什么类型,再调用方法签名(println+参数类型)符合的方法, 我们来看看这个方法有多少个重载版本:
在这条语句中,out是System类中的一个PrintStream类型常量:
public final static PrintStream out = null;
println()位于java.io.PrintStream中.
public void println() {}
public void println(boolean x) {}
public void println(char x) {}
public void println(int x) {}
public void println(long x) {}
public void println(float x) {}
public void println(char x[]) {}
public void println(String x) {}
public void println(Object x) {}
回到构造器
那么再来看看下面这个构造器可以不可以:
SuperClass(int id, int number, int load){
System.out.println("SuperClass 3个参数的构造器1.");
this(id, number, load, null);
}
答案是不可以,因为Java中规定this()调用本类构造器的语句在构造器中必须位于第一条语句.具体原因可以看看下面的代码:
class SubClass extends SuperClass{
SubClass(int id, int number, int load, String name){
this.load = load;
this.name = name;
System.out.println("SubClass 4个参数的构造器.");
}
SubClass(int id, int number){
super(id, number, null);
System.out.println("SubClass 2个参数的构造器.");
}
public static void main(String[] args){
new SubClass(1, 2, 3, "1");
}
}
运行结果是:
SuperClass 无参构造器.
SubClass 4个参数的构造器.
发现第一行出现的是:SuperClass.., 并没有执行另一个构造器,即使已经使用两个this语句对另一个构造器未指定的变量进行了赋值.再来看看加了this的情况:
SubClass(int id, int number, int load, String name){
this(id, number);
this.load = load;
this.name = name;
System.out.println("SubClass 4个参数的构造器.");
}
SubClass(int id, int number){
super(id, number, null);
System.out.println("SubClass 2个参数的构造器.");
}
public static void main(String[] args) {
new SubClass(1, 2, 3, "1");
}
运行结果:
SuperClass 2个参数的构造器.
SuperClass 4个参数的构造器.
SuperClass 3个参数的构造器2.
SubClass 2个参数的构造器.
SubClass 4个参数的构造器.
可以看到这次调用了SubClass中两个参数的构造器, 所以可以说明,如果未显式的指定this,默认就会走父类中的逻辑,也就是super.也就是说会自动在构造器中第一行加入super().
方法中的this
有了上面的铺垫其实要说的内容已经比较少了.需要指出的就是,方法中this可以放在任意位置,不像构造器严格限制在第一条语句,下面的代码就是合法的:
int out(){
return 1 ;
}
int otherOut(){
int i = 100;
return this.out() + i;
}
当然,都在同一个类中,也就不用加这个this了:
int out(){
return 1 ;
}
int otherOut(){
int i = 100;
return out() + i;
}
super
接下来来说说super.super用来指向离自己最近的父类的东西.
构造器
来看看下面的两个构造器:
class SuperClass{
SuperClass(){
System.out.println("SuperClass 无参构造器.");
}
}
class SubClass extends SuperClass{
SubClass(){
System.out.println("SubClass无参构造器.");
}
}
public class ThisAndSuper{
public static void main(String[] args){
new SubClass();
}
}
运行结果:
SuperClass 无参构造器.
SubClass 无参构造器.
这里在子类构造器中隐式的加入了一行代码:
super();
下面来看看有参构造器:
SuperClass(int id, int number, int load, String name){
this.id = id;
this.name = name;
this.number = number;
this.load = load;
}
SubClass(int id, int number, int load, int gen, String name){
super(id, number, load, name);
this.gen = gen;
}
这么做的前提是,父类确实存在这样的构造器,如果不存在就会报错.
这么做也是可以的 :
class SuperClass{
int id;
int number;
int load;
String name;
SuperClass(){}
}
class SubClass extends SuperClass{
private int gen;
SubClass(int id, int number, int load, int gen, String name){
super.id = id;
super.number = number;
super.name = name;
super.load = load;
this.gen = gen;
}
}
这种情况下需要有默认无参构造器,如果没有声明其它有参构造器,就没必要写出来了,此时系统默认提供一个无参构造器.但是如果声明了有参构造器,那么就需要显式的把默认无参构造器写出来,因为你写的任何有参构造器都会覆盖掉系统默认提供的无参构造器.
根据前面this的介绍,这条语句在构造器中也是只能存在于第一条语句. 下面再看看有参数构造器的情况:
SuperClass(int id, String name){
this.id = id;
this.name = name;
System.out.println("SuperClass 2个参数的构造器.");
}
SubClass(int id, int number){
super(id, number, null);
System.out.println("SubClass 2个参数的构造器.");
}
测试类main()中创建一个SubClass对象,可以看到如下结果:
SuperClass 2个参数的构造器.
SubClass 2个参数的构造器.
可知子类构造器中的super确实调用了父类对应的构造器,至于编译器怎么知道的super()中是哪个构造器,还是方法签名的功劳. 需要注意的是,如果子类有参构造器没有显式的继承父类的构造器,会自动在第一行加上super().
方法
先来看看下面的代码:
class SuperClass{
private int id;
private int number;
private int load;
private String name;
SuperClass(int id, int number, int load, String name){
this.id = id;
this.name = name;
this.number = number;
this.load = load;
}
@Override
public String toString() {
return id+ "," + name +"," + number + "," + load;
}
}
class SubClass extends SuperClass{
private int gen;
SubClass(int id, int number, int load, int gen, String name){
super(id, number, load, name);
this.gen = gen;
}
@Override
public String toString() {
return super.toString() + "," + gen;
}
}
然后在测试类中编写如下语句:
public static void main(String[] args) {
SubClass sc = new SubClass(1, 2, 3, 4, "name");
System.out.println(sc);
}
运行后可以得到如下结果:
1,2,3,name,4
但是如果父类中不重写toString(),再运行会怎样?
keyword.SubClass@6d6f6e28,4
看起来像是Object默认的toString().
用ctrl+alt+H,同时鼠标点击子类的toString(),可以看到方法调用层次(也就是从这里到最根部都调用了什么方法),这里先看看父类有toString()的调用层次:
可以看到是父类的toString(),再看看父类没有重写toString()的调用层次:
也就是说直接重写了Object的toString().
其它
这里说一点this与super的一些注意事项
1.this()与super()不能同时的出现在同一个构造器中.
2.this()与super()必须都出现在构造方法的第一条语句.
3.this是调用本类中的方法,super是调用父类中的方法.
4.static环境中不可以使用this与super,包括static变量,static方法,static块.