目录
1、super是什么
java小写关键字,通常可以理解为用于指代父类
2、用法
和this做对比
this super 备注 可用范围 实例方法和构造方法中 实例方法和构造方法中 语法结构 this·、this() super ·、super() XX( )作用位置 构造方法第一行,用于调用当前类中与其参数结构相同的构造方法 构造方法首行,用于调用父类中与其参数结构相同的构造方法 所以二者注定不能共存 不可用 静态方法中 静态方法中 是否可省略 可以 可以 什么情况下不可省略 用于区分全局变量和局部变量的时候 ? 实现代码复用 this指代当前对象,super指代当前对象的父类对象,所以二者只能用于有对象存在的地方,即实例方法和构造方法中,静态方法是类类型的,不是对象类型的,其内部不存在对象的概念,所以,两种关键字都不可以应用于静态方法中。
两种关键字都是在简化代码并尽可能调用已有代码的程度上实现的,所以能够有效的实现代码复用,简化代码编写,更易于程序员对代码的理解。
①“super ·”
在存在继承关系的两个类中,当子类和父类有同名属性或者方法的时候,如果想要在子类中访问父类中的成分,可以利用该关键字对其进行访问。
例如:Animal父类
package animal;
public class Animal {
private String name; //name属性
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Dog子类:
package animal;
public class Dog extends Animal{
public String name; //name属性
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void superTest(){
System.out.println(super.getName());
}
}
测试类:
package animal;
public class test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.setName("陈玄"); //调用子类中的set方法,设置name="陈玄";
System.out.println(dog.getName()); //获取子类中的name属性值;
dog.superTest(); //调用superTest方法,输出父类中的name属性值;
}
}
输出如下:正常的输出了dog对象的name值,以及其父类中name元素的值
陈玄
null
Process finished with exit code 0
怎么证明是父类中的值呢? 将Dog中的代码修改如下:
package animal;
public class Dog extends Animal{
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void worf(){
System.out.println("汪汪汪");
}
public void superTest(){
System.out.print("设置父类中的name属性值为瑶");super.setName("瑶");
System.out.println("父类中name属性值"+super.getName());
System.out.println("当前类对象name属性值"+this.getName());
}
}
输出结果如下:很明显在通过super设置name属性的值后,通过this关键字访问到的当前类对象的name属性的值并没有发生改变,而通过super访问到的是其父类的属性,很明显发生了改变。
设置父类中的name属性值为瑶
父类中name属性值瑶
当前类对象name属性值陈玄
Process finished with exit code 0
②super()
第二种用法,和父类的构造方法有关。通过该方法,可以在子类的构造方法中实现对父类构造方法的调用,其实就相当于在子类中包含了一个父类型的特征,即要想有儿子,必须先有父亲。
通常可以利用这个方法,通过调用父类中的构造方法,完成对属性元素的初始化操作。如下:
Animal父类:
package animal;
public class Animal extends Object{
private String name;
private String id;
private int age;
//无参的构造方法
public Animal(){
}
//有参的构造方法
public Animal(String name,String id,int age){
this.id=id;
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Dog子类:如下,在子类中,去访问父类的属性,并对其进行赋值和控制台输出,我们可以通过super+“·”的方式进行设置,但是这样代码编写量可能会很大,增强代码编写的复杂程度,也不便于代码的阅读,也没有充分的利用继承带给我们的良好特性。
package animal;
public class Dog extends Animal{
private String address;
public Dog(){
super();
}
//关键代码部分
public Dog(String address){
super.setName("百里玄策");
super.setId("001");
super.setAge(15);
this.address=address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void test(){
System.out.println(this.getName());
System.out.println(this.getId());
System.out.println(this.getAge());
System.out.println(this.getAddress());
}
}
对Dog子类的diamagnetic进行修改,如下:
package animal;
public class Dog extends Animal{
private String address;
public Dog(){
super();
}
//有参的构造方法
public Dog(String address){
super("百里玄策","001",15);
this.address=address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//测试方法
public void test(){
System.out.println(this.getName());
System.out.println(this.getId());
System.out.println(this.getAge());
System.out.println(this.getAddress());
}
}
测试类:
package animal;
public class test {
public static void main(String[] args) {
Dog dog=new Dog("天玄镇");
dog.test();
}
}
name、id、age属性在其父类中,子类中的属性是从父类中继承过来的,父类的构造方法完成对以上属性的初始化操作,super()方法位于子类构造方法的第一行,即在创建子类对象之前,会创建一个父类型的特征,调用相对应参数类型的父类中的构造方法,那么也就对当前子类对象的父类特征进行了初始化操作,这样在通过当前类访问属性的时候,是可以正确的输出值的。
如上代码,测试类创建子类dog对象,调用了子类中有参的构造方法,方法中第一个语句就是super("百里玄策","001",15);这个语句会调用父类中的有参的构造方法,并将参数赋值给name、id、age三个属性,完成属性的初始化,这样子类对象进行访问的时候,就可以访问到有值的属性。
父类中通常会将属性封装,设置为private类型的成分,意味着只能在当前类中使用,在继承关系中,该种类型的成分会被子类继承,但是鉴于访问限制,子类不能直接的进行使用,所以合理的利用super(可选参数)方法,就可以在子类中,借助父类的对应构造方法,完成对其父类所有的私有属性的初始化操作,这也是访问父类私有属性的一个方法。
例如上述代码的输出如下,很明显成功的完成了赋值初始化的操作。
百里玄策
001
15
天玄镇
Process finished with exit code 0
注意
在类中,如果没有手动的进行构造方法的编写,那么系统默认提供一个无参的构造方法,通常我们在编写构造方法的时候,会人为的提供有参的构造方法和无参的构造方法,这样避免存在继承关系或者创建实例化对象的时候,导致程序报错。
java中所有的类如果没有显示的继承一个类,那么他们都会默认继承Object类。
在子类的构造方法中,如果在构造方法中如果没有手动的写Super()方法,那么默认有一个无参的super()方法,即会默认调用父类的无参的构造方法,一定要保障父类中存在无参的构造方法。如果手动的写了有参的super();方法行,那么一定要保证父类中有相同参数类型的构造方法存在。即调用super(实参列表)方法的时候父类中一定要有对应的构造方法与之相对应,否则就会出现错误。如果想要调用父类中有参的构造方法,一定要手动的写好super(实际参数)。
所以,在父类中存在无参的构造函数的时候,super()方法可以省略不写,但是如果只有有参的构造函数或者我们只需要有参的构造函数实现某些功能,那么一定要人为的手写super(实际参数)方法。
这个机制是“继承”能够得以实现的最根本原因。
所以说无论如何,父类的构造方法一定会执行,因为执行子类并对其进行实例化的时候,一定会先去调用父类的构造方法,而Object类作为最终的父类,java类归根结底一定执行了object类中的无参构造方法,但是object类中不会再执行相关语句,因为它是顶级的类,它没有父类了。
3、原理分析
super代表的是当前对象的父类型特征,所以一定要先有当前对象,才会存在父类型特征,才会存在super关键字,所以在静态方法中不存在super关键字。
super()方法会去调用父类的构造方法,但是其实最终只创造了最底层的子类这样一个对象,并没有创建父类对象,而是只从父类那边获取了特征,然后在子类中组织成一个特征模块,(拿来主义),拿过来就是子类的了,所以其实只创建了底层的子类对象一个。
也就是说从根本上,在调用super()的时候并没有创建新的父类对象,而是在调用的时候初始化当前对象的父类型特征块。
例如:子类:
public class 子类A extends 父类B{
public A(){
super();
}
}
父类:
public class 父类B{
public B(){
}
}
那么在内存中,子类和这个super()方法调用父类的构造方法形成的父类特征块之间的关系如下:
也是因为是父类特征块不是父类对象,所以super不算指向对象的引用,不能直接输出,但是this是指向对象的引用,存储的是对象的地址,可以直接的打印输出。如下所示:
修改子类中的test方法:
package animal;
public class Dog extends Animal{
private String address;
public Dog(){
super();
}
public Dog(String address){
super.setName("百里玄策");
super.setId("001");
super.setAge(15);
this.address=address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//test方法
public void test(){
System.out.println(this);
System.out.println(super);
}
}
运行后,控制台报如下错误 ,错误位置即为super所在输出行。
java: 需要'.'
但是如果注释掉错误行,直接输出this引用是可以正确输出地址的。
animal.Dog@4554617c
Process finished with exit code 0
在输出引用的时候,会自动的调用toString方法,输出一个字符串,字符串以@符号分割,符号左边是类名,符号的右边是引用所存储的内存地址。
很明显,super如果不是以一个方法的形式存在于子类的构造方法中,就一定要和“·”运算符配合使用,否则会报错。
为了方便记忆,其实也可以理解为在子类的内部包含了一个它的父类对象。
4、思考
4.1、java中允许子类和父类之间存在同名的属性和方法,那么当父类和子类中有同名的属性或者方法的时候,系统会访问哪个?
举个例子:父类Animal
package animal;
public class Animal extends Object{
private String name="瑶";
//无参的构造方法
public Animal(){
}
//有参的构造方法
public Animal(String name){
this.id=id;
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
子类Dog类
package animal;
public class Dog extends Animal{
private String name;
public Dog(){
super();
}
public Dog(String name){
super(name);
}
}
测试类:我们在测试类中创建Dog子类的对象,然后输出name的值。
package animal;
public class test {
public static void main(String[] args) {
Dog dog=new Dog("天玄镇");
System.out.println(dog.getName());
}
}
输出结果如下:
天玄镇
Process finished with exit code 0
所以,当父类和子类中存在同名的属性的时候,通过子类对象访问到的属性是子类中的属性,感觉某些意义上也可以认为是一种覆盖。
4.2、this、super和缺省的区别
已知通过某对象的this指针可以访问到自己,通过super可以访问到对象内部包含的父特征模块中的可访问的成分(非private),4.1中也可以看出,如果缺省不写的情况下,具有相同的属性访问到的是自身内存单元中的成分,那么也就是说在缺省的情况下,默认是"this·"去调用那些成分的.
例如:Animal父类:
package animal;
public class Animal extends Object{
private String name="瑶"; //父类中的name属性的值为"瑶"
//无参的构造方法
public Animal(){
}
//有参的构造方法
public Animal(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
将子类Dog类修改成如下:
package animal;
public class Dog extends Animal{
private String name;
public Dog(){
super();
}
public Dog(String name){
this.name=name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
//主体代码
public void test(){
System.out.println("this:"+this.getName());
System.out.println("super:"+super.getName());
System.out.println("缺省:"+getName());
}
}
测试类如下:
package animal;
public class test {
public static void main(String[] args) {
Dog dog=new Dog("天玄镇");
dog.test();
}
}
看输出结果:
this:天玄镇
super:瑶
缺省:天玄镇
Process finished with exit code 0
很明显,this和缺省的输出是相同的,通过super引用的是子类对象内存单元中的父类型特征模块中的信息.
为了更好的理解如上的代码,绘制如下的内存分析图:
当创建子类dog对象的时候,会调用子类的public构造方法,会先去调用父类的构造方法,这样子类的内存单元中就有了父类型的特征模块,因为给了String类型的参数"天玄镇",那么子类对象所在的内存单元中的name就会被初始化为"天玄镇",之后子类对象调用test()方法,进行控制台的输出的时候,this调用当前对象中的getname方法,super调用的是父类型特征模块中的getName()方法,缺省的情况下,默认是"this·"的,调用的是当前对象中的成分.