文章目录
- 一、super关键字的使用
- (1)为什么需要super?
- (2)super的理解
- (3)super可以调用的结构
- 1、super调用方法
- 举例1
- 举例2
- 举例3
- 小结
- 2、super调用属性
- 举例1
- 举例2
- 举例3
- 小结
- 3、super调用构造器
- 引入
- 举例1
- 举例2
- 举例3
- 举例4
- 4、总结
- super调用方法、属性
- super调用构造器
- (4)小结:this与super
- 1、this和super的意义
- 2、this和super的使用格式
- (5)练习
- 1、练习1
- 2、练习2
- 3、练习3
- (6)面试题
- 1、第一题
- 2、第二题
- 3、第三题
- 4、第四题
- 二、子类对象实例化全过程
- (1)介绍
- (2)举例
一、super关键字的使用
(1)为什么需要super?
举例1:子类继承父类以后,对父类的方法进行了重写,那么在子类中,是否还可以对父类中被重写的方法进行调用?
可以!
举例2:子类继承父类以后,发现子类和父类中定义了同名的属性(若子类造对象,就会有两个同名属性),是否可以在子类中区分两个同名的属性?(方法可以覆盖,属性不能覆盖)
可以!
如何调用? 使用super
关键字即可。
(2)super的理解
super的理解:父类的
在子类中,若想调用父类中被重写的方法,就用super.方法
即可;若想调用父类中的属性,就用super.属性
即可。
若没有写super,调用的就是子类中重写的方法和子类里面声明的属性。
在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造器中调用父类的构造器
注意:
- 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
- super的追溯不仅限于直接父类
- super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
(3)super可以调用的结构
super可以调用的结构:属性、方法、构造器
具体的:
1、super调用方法
- 如果子类没有重写父类的方法,只要权限修饰符允许,在子类中完全可以直接调用父类的方法;
- 如果子类重写了父类的方法,在子类中需要通过
super.
才能调用父类被重写的方法,否则默认调用的子类重写的方法
举例1
观察下面代码的输出结果。
【Person.java】
package yuyi01;public class Person {//属性String name;private int age;//方法public void eat(){System.out.println("人吃饭");}public void sleep(){System.out.println("人睡觉");}
}
【Student.java】
package yuyi01;public class Student extends Person {//属性String school;//方法public void study(){System.out.println("学生学习");}//重写public void eat(){System.out.println("学生多吃有营养的食物");}public void sleep(){System.out.println("学生保证每天不低于七小时睡眠");}
}
【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s1=new Student();s1.eat();s1.sleep();}
}
输出结果:
举例2
如何在子类方法(Student.java里面,还能够调用父类中被重写的方法呢?
如果此时在子类方法里面调用eat()
方法,毫无疑问,这个eat()方法指的是自己类里面重写的方法。如下:
当然,使用eat()
调用和this.eat()
调用效果一样,前者只是省略了this.
而已。
若现在想调用父类中的eat()方法,很简单,只需要在前面写super.
即可。(以不影响封装性为前提)
【Student.java】
package yuyi01;public class Student extends Person {//属性String school;//重写public void eat(){System.out.println("学生多吃有营养的食物");}//...public void show(){eat(); //省略了thisthis.eat();super.eat(); //父类中的eat()方法}
}
this.eat();
直接在本类找,找到了,就直接调用本类的重写方法即可。
super.eat();
直接在直接父类中找,找到了,就直接调用父类被重写的方法即可。
eat();
是省略了this.
,所以本质上也是调用本类中的方法,若本类中找不到,才会去父类中找。
测试类【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s1=new Student();//...s1.show();}
}
运行结果:
举例3
父类【Person.java】
package yuyi01;public class Person {//...public void doSport(){System.out.println("人运动");}
}
子类【Student.java】
package yuyi01;public class Student extends Person {//...public void show1(){doSport();}
}
此时子类中调用的doSport()
毫无疑问是父类中的方法,因为子类中没有重写它。
这时候它的前缀是啥呢?
若在本类中调用方法,前缀都会省略this.
。调用show1()
方法的时候,它会在本类中找doSport()
方法,找不到就会去父类中找。
此时本类中没有doSport()方法,就会去父类中找,找到并调用。若父类中还没有,就会继续往上找,直到Object,还没有找到就会报错了。
画个图看看:
此时Sutdent类里面没有重写doSport(),所以只有一个父类Person中的doSport()而已,只能调用它。
从结果上说,this.doSport()
和super.doSport()
一致;但是从过程上来说,this.doSport()
先从本类开始找,super.doSport()
直接向直接父类中找。
【Student.java】
package yuyi01;public class Student extends Person {//...public void show1(){doSport();this.doSport();super.doSport();}
}
测试类【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s1=new Student();//...s1.show1();}
}
运行结果:
小结
- 方法前面没有super.和this.
- 先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
- 方法前面有this.
- 先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
- 方法前面有super.
- 从当前子类的直接父类找,如果没有,继续往上追溯
2、super调用属性
- 如果实例变量与局部变量重名,可以在实例变量前面加this.进行区别
- 如果子类实例变量和父类实例变量重名,并且父类的该实例变量在子类仍然可见,在子类中要访问父类声明的实例变量需要在父类实例变量前加super.,否则默认访问的是子类自己声明的实例变量
- 如果父子类实例变量没有重名,只要权限修饰符允许,在子类中完全可以直接访问父类中声明的实例变量,也可以用this.实例访问,也可以用super.实例变量访问
举例1
暂且不考虑权限的事情。此时父类Person和子类Student中有同名的属性id
。
父类【 Person.java】
package yuyi01;public class Person {//属性String name;private int age;int id; //身份证号}
子类【Student.java】
package yuyi01;public class Student extends Person {//属性String school;int id; //学号}
若此时创建子类Student的对象,那么它拥有几个属性呢?和父类同名的属性会不会被干掉?
来Debug一下:
所以,属性没有方法那样有覆盖之说。
属性不会覆盖,而方法可以覆盖。
举例2
既然有两个同名的属性,那么该如何区分它们呢?
此时将父类和子类中的属性id都赋值。
父类【 Person.java】
package yuyi01;public class Person {//属性String name;private int age;int id=1001; //身份证号}
在子类中写一个show2()方法,输出id的结果是什么呢?
子类【Student.java】
package yuyi01;public class Student extends Person {//属性String school;int id=1002; //学号public void show2(){System.out.println(id); //?}
}
此时输出语句并没有报错,这里遵循一个就近原则。
就和之前说的get
方法一样,比如:
再比如:
当时解决办法是这样:
具体关于this的讲解在这一篇博客:https://blog.csdn.net/m0_55746113/article/details/134089173?spm=1001.2014.3001.5502
所以此时的id会就近找一个一样的,然后就找到了本类的id。
若想要父类中的id,就需要加一个super.
。
子类【Student.java】
package yuyi01;public class Student extends Person {//属性String school;int id=1002; //学号public void show2(){System.out.println(id); //1002System.out.println(this.id); //1002System.out.println(super.id); //1001}
}
测试类【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s1=new Student();//...System.out.println();s1.show2();}
}
运行结果:
举例3
父类【Person.java】
package yuyi01;public class Person {//属性String name;//...
}
子类【Student.java】
package yuyi01;/*** ClassName: Student* Package: yuyi04* Description:** @Author 雨翼轻尘* @Create 2023/10/29 0029 16:40*/
public class Student extends Person {//属性String school;int id=1002; //学号//方法public void show3(){System.out.println(name); //这样写就相当于省略了thisSystem.out.println(this.name);System.out.println(super.name);}
}
name
先在当前类里面找,若没有找到,就去父类中找。
从结果上,三个输出值是一样的;但从过程上来说,name
先找本类再找父类,super.name
直接找父类。
测试类【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s1=new Student();s1.show3();}
}
输出结果:
加super与this,就是区分重名的属性,重写的方法。
若没有重名属性和重写的方法,this与super加不加无所谓,只不过一个本类找,一个直接父类找,过程上有区别,结果上没有区别。
若是父类中没有的属性(本类中有),用super直接父类中找是会报错的。
小结
总结:起点不同(就近原则)
- 变量前面没有super.和this.
- 在构造器、代码块、方法中如果出现使用某个变量,先查看是否是当前块声明的
局部变量
, - 如果不是局部变量,先从当前执行代码的
本类去找成员变量
- 如果从当前执行代码的本类中没有找到,会往上找
父类声明的成员变量
(权限修饰符允许在子类中访问的)
- 在构造器、代码块、方法中如果出现使用某个变量,先查看是否是当前块声明的
- 变量前面有this.
- 通过this找成员变量时,先从当前执行代码的本类去找成员变量
- 如果从当前执行代码的本类中没有找到,会往上找父类声明的成员变量(权限修饰符允许在子类中访问的)
- 变量前面super.
- 通过super找成员变量,直接从当前执行代码的直接父类去找成员变量(权限修饰符允许在子类中访问的)
- 如果直接父类没有,就去父类的父类中找(权限修饰符允许在子类中访问的)
特别说明:应该避免子类声明和父类重名的成员变量
3、super调用构造器
引入
为何要调用构造器?
比如在Student类里new了一个Student对象,可以通过s1调用属性、方法。
【Student.java】
package yuyi01;public class Student extends Person {//属性String school;int id=1002; //学号}
若此时父类Person里面的name属性没有限制,那么在测试里面可以调用name属性。
这里我们知道内存中有name属性,才直接调用它。
可我们new的是Student,相当于调用的是Student的构造器,会加载Student的结构,那为啥还会加载父类的结构呢(为啥内存中有
name属性)?这里就涉及到super调用构造器的问题。
举例1
① 子类继承父类时,不会继承父类的构造器(构造器只有在同名的类里面才有)。只能通过“super(形参列表)
”的方式调用父类指定的构造器。
父类【Person.java】
package yuyi01;public class Person {//属性String name;private int age;int id=1001; //身份证号//构造器public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public Person(String name, int age, int id) {this.name = name;this.age = age;this.id = id;}
}
子类【Student.java】
package yuyi01;public class Student extends Person {//属性String school;int id=1002; //学号//测试super调用父类的构造器public Student(){}public Student(String name,int age){}
}
比如在子类中调用父类中的空参构造器:
举例2
父类【Person.java】
package yuyi01;public class Person {//构造器public Person() {System.out.println("Person()...");}
}
子类【Student.java】
package yuyi01;public class Student extends Person {//测试super调用父类的构造器public Student(){super(); //调用父类中空参的构造器System.out.println("Student()...");}
}
测试类【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s2=new Student();}
}
输出结果:
当我们调用子类构造器创建对象的时候,先调用父类构造器,输出Person()...
,然后再输出Student()...
。如下:
可以在子类构造器中调用父类的构造器,格式就是super(形参列表)。
举例3
② 规定:“super(形参列表)”,必须声明在构造器的首行。(和this很像)
如下:
③ 我们前面讲过,在构造器的首行可以使用"this(形参列表)",调用本类中重载的构造器, 结合②,结论:在构造器的首行,“this(形参列表)” 和 "super(形参列表)"只能二选一。
比如:
④ 如果在子类构造器的首行既没有显示调用"this(形参列表)“,也没有显式调用"super(形参列表)”, 则子类此构造器默认调用"super()",即调用父类中空参的构造器。
父类【Person.java】
package yuyi01;public class Person {//构造器public Person() {System.out.println("Person()...");}}
子类【Student.java】
package yuyi01;public class Student extends Person {public Student(String name,int age){//没有显示调用父类中空参的构造器}
}
测试类【StudentTest.java】
package yuyi01;public class StudentTest {public static void main(String[] args) {Student s3=new Student("Tom",13);}
}
输出结果:
在Student(String name,int age)
构造器首行,并没有写this(形参列表),也没有写"super(形参列表)"。此时会默认是Super()
,而且是空参的。
来Debug看一下:
进入构造器了,如下:
再下一步:
所以,在调Student构造器的时候,没有写this,也没有super语句,会默认父类空参构造器。
若此时将空参构造器注释掉,会发现子类中两个构造器都会报错。第一个是显示调用,但没有找到空参构造器,就会报错;第二个虽然没有显示调用谁,但是默认调用了空参构造器,也没有找到,所以也会报错。如下:
⑤ 由③和④得到结论:子类的任何一个构造器中,要么会调用本类中重载的构造器,要么会调用父类的构造器。 只能是这两种情况之一。
画个图瞅瞅:
⑥ 由⑤得到:一个类中声明有n个构造器,最多有n-1个构造器中使用了"this(形参列表)
“(若有n个就会形成一个环,递归了),则剩下的那个一定使用"super(形参列表)
”(显示或者默认)。
子类中的任何一个构造器,都会直接或间接地调用父类的构造器。如下:
开发中常见错误:
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有空参的构造器,则编译出错。
调用父类构造器不是为了造对象,造对象需要搭配new,调用父类构造器是为了初始化信息–比如将父类的属性、方法加载到内存中。
举例4
父类【Person.java】
package yuyi01;public class Person {//属性String name;private int age;int id=1001; //身份证号//方法public int getAge() {return age;}public void setAge(int age) {this.age = age;}//构造器public Person() {System.out.println("Person()...");}
}
子类【Student.java】
package yuyi01;public class Student extends Person {//属性String school;int id=1002; //学号public Student(String name,int age){//没有显示调用父类中空参的构造器setAge(age);super.name=name; //当前类里面没有name属性,这里super也可以写成this}
}
上面的写法很Low,若父类Person中有这样的构造器:
package yuyi01;public class Person {//属性String name;private int age;int id=1001; //身份证号//构造器public Person() {System.out.println("Person()...");}public Person(String name, int age) {this.name = name;this.age = age;}}
子类可以这样写:
package yuyi01;public class Student extends Person {//属性String school;int id=1002; //学号public Student(String name,int age){super(name,age);}
}
那么子类就可以直接调用父类中的构造器,如下:(若是没有写,调用的就是父类中的空参构造器Person()
)
以后声明子类构造器的时候,构造器的首行就可以调用父类指定的结构了。
4、总结
子类继承父类以后(super关键字使用的前提是基于继承),我们就可以在子类的方法或构造器中,调用父类中声明的属性或方法。(满足封装性的前提下)
super调用方法、属性
🗃️如何调用呢?
需要使用"super.
"的结构,表示调用父类的属性或方法。
一般情况下,我们可以考虑省略"super."的结构。
但是,如果出现子类重写了父类的方法或子父类中出现了同名的属性时,则必须使用"super.
"的声明,显式地调用父类被重写的方法或父类中声明的同名的属性。
特别说明:应该避免子类声明和父类重名的成员变量(方法没法避开)
在阿里的开发规范等文档中都做出明确说明:
super调用构造器
① 子类继承父类时,不会继承父类的构造器。只能通过“super(形参列表)”的方式调用父类指定的构造器。
② 规定:“super(形参列表)”,必须声明在构造器的首行。
③ 我们前面讲过,在构造器的首行可以使用"this(形参列表)",调用本类中重载的构造器,
结合②,结论:在构造器的首行,“this(形参列表)” 和 "super(形参列表)"只能二选一。
④ 如果在子类构造器的首行既没有显示调用"this(形参列表)“,也没有显式调用"super(形参列表)”,
则子类此构造器默认调用"super()",即调用父类中空参的构造器。
⑤ 由③和④得到结论:子类的任何一个构造器中,要么会调用本类中重载的构造器,要么会调用父类的构造器。
只能是这两种情况之一。
⑥ 由⑤得到:一个类中声明有n个构造器,最多有n-1个构造器中使用了"this(形参列表)“,
则剩下的那个一定使用"super(形参列表)”。
–> 我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接的调用到父类的构造器。
也正因为调用过父类的构造器,我们才会将父类中声明的属性或方法加载到内存中,供子类对象使用。
情景举例1:
class A{}
class B extends A{}class Test{public static void main(String[] args){B b = new B();//A类和B类都是默认有一个无参构造,B类的默认无参构造中还会默认调用A类的默认无参构造//但是因为都是默认的,没有打印语句,看不出来}
}
情景举例2:
class A{A(){System.out.println("A类无参构造器");}
}
class B extends A{}
class Test{public static void main(String[] args){B b = new B();//A类显示声明一个无参构造,//B类默认有一个无参构造,//B类的默认无参构造中会默认调用A类的无参构造//可以看到会输出“A类无参构造器"}
}
情景举例3:
class A{A(){System.out.println("A类无参构造器");}
}
class B extends A{B(){System.out.println("B类无参构造器");}
}
class Test{public static void main(String[] args){B b = new B();//A类显示声明一个无参构造,//B类显示声明一个无参构造, //B类的无参构造中虽然没有写super(),但是仍然会默认调用A类的无参构造//可以看到会输出“A类无参构造器"和"B类无参构造器")}
}
情景举例4:
class A{A(){System.out.println("A类无参构造器");}
}
class B extends A{B(){super();System.out.println("B类无参构造器");}
}
class Test{public static void main(String[] args){B b = new B();//A类显示声明一个无参构造,//B类显示声明一个无参构造, //B类的无参构造中明确写了super(),表示调用A类的无参构造//可以看到会输出“A类无参构造器"和"B类无参构造器")}
}
情景举例5:
class A{A(int a){System.out.println("A类有参构造器");}
}
class B extends A{B(){System.out.println("B类无参构造器");}
}
class Test05{public static void main(String[] args){B b = new B();//A类显示声明一个有参构造,没有写无参构造,那么A类就没有无参构造了//B类显示声明一个无参构造, //B类的无参构造没有写super(...),表示默认调用A类的无参构造//编译报错,因为A类没有无参构造}
}
情景举例6:
class A{A(int a){System.out.println("A类有参构造器");}
}
class B extends A{B(){super();System.out.println("B类无参构造器");}
}
class Test06{public static void main(String[] args){B b = new B();//A类显示声明一个有参构造,没有写无参构造,那么A类就没有无参构造了//B类显示声明一个无参构造, //B类的无参构造明确写super(),表示调用A类的无参构造//编译报错,因为A类没有无参构造}
}
情景举例7:
class A{A(int a){System.out.println("A类有参构造器");}
}
class B extends A{B(int a){super(a);System.out.println("B类有参构造器");}
}
class Test07{public static void main(String[] args){B b = new B(10);//A类显示声明一个有参构造,没有写无参构造,那么A类就没有无参构造了//B类显示声明一个有参构造, //B类的有参构造明确写super(a),表示调用A类的有参构造//会打印“A类有参构造器"和"B类有参构造器"}
}
情景举例8:
class A{A(){System.out.println("A类无参构造器");}A(int a){System.out.println("A类有参构造器");}
}
class B extends A{B(){super();//可以省略,调用父类的无参构造System.out.println("B类无参构造器");}B(int a){super(a);//调用父类有参构造System.out.println("B类有参构造器");}
}
class Test8{public static void main(String[] args){B b1 = new B();B b2 = new B(10);}
}
(4)小结:this与super
1、this和super的意义
this:当前对象
- 在构造器和非静态代码块中,表示正在new的对象
- 在实例方法中,表示调用当前方法的对象
super:引用父类声明的成员
2、this和super的使用格式
- this
- this.成员变量:表示当前对象的某个成员变量,而不是局部变量
- this.成员方法:表示当前对象的某个成员方法,完全可以省略this.
- this()或this(实参列表):调用另一个构造器协助当前对象的实例化,只能在构造器首行,只会找本类的构造器,找不到就报错
- super
- super.成员变量:表示当前对象的某个成员变量,该成员变量在父类中声明的
- super.成员方法:表示当前对象的某个成员方法,该成员方法在父类中声明的
- super()或super(实参列表):调用父类的构造器协助当前对象的实例化,只能在构造器首行,只会找直接父类的对应构造器,找不到就报错
(5)练习
1、练习1
🌋题目描述
修改方法重写的练习2中定义的类Kids中employeed()方法,在该方法中调用父类ManKind的employeed()方法,
然后再输出"but Kids should study and no job."
【Kids.java】
package yuyi02;/*** ClassName: Kids* Package: yuyi05* Description:修改继承内容的练习1中定义的类Kids,在Kids中重新定义employeed()方法,覆盖父类ManKind中定义的employeed()方法,输出"Kids should study and no job."* @Author 雨翼轻尘* @Create 2023/10/30 0030 10:56*/
public class Kids extends Mankind { //父类中声明的属性和方法都被继承到子类了,构造器就不提了。后边提super关键字的时候会提到,在子类当中调用父类中的构造器private int yearOld;public int getYearOld() {return yearOld;}public void setYearOld(int yearOld) {this.yearOld = yearOld;}public void printAge(){System.out.println("I am "+yearOld+" years old");}@Overridepublic void employeed() {System.out.println("Kids should study and no job.");}//构造器public Kids(){}public Kids(int yearOld){this.yearOld=yearOld;}//把父类中的属性也做一个赋值,包括自己的属性public Kids(int sex, int salary,int yearOld){this.yearOld=yearOld;//sex、salary两个 属性是父类继承过来的,怎么给他们赋值?setSex(sex);setSalary(salary);}
}
【Mankind.java】
package yuyi02;/*** ClassName: Mankind* Package: yuyi05* Description:* (1)定义一个ManKind类,包括* 成员变量int sex和int salary;* - 方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);** - 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。* @Author 雨翼轻尘* @Create 2023/10/30 0030 10:32*/
public class Mankind {//属性private int sex;private int salary;//方法public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}public int getSalary() {return salary;}public void setSalary(int salary) {this.salary = salary;}public void manOrWoman(){if(sex==1){System.out.println("man");} else if (sex==0) {System.out.println("woman");}}public void employeed(){if(salary==0){System.out.println("no job");} else {System.out.println("job");}}//构造器public Mankind() {}public Mankind(int sex, int salary) {this.sex = sex;this.salary = salary;}
}
【KidsTest.java】
package yuyi02;/*** ClassName: KidsTest* Package: yuyi05* Description:*(3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。* @Author 雨翼轻尘* @Create 2023/10/30 0030 10:58*/
public class KidsTest {public static void main(String[] args) {Kids someKid=new Kids();someKid.setSex(1);someKid.setSalary(100);someKid.setYearOld(12);//Kids类自己声明的方法someKid.printAge();//来自于父类中声明的方法someKid.manOrWoman();someKid.employeed();//System.out.println("*************");someKid.employeed();}
}
🤺代码
【 Kids.java】
package yuyi02;/*** ClassName: Kids* Package: yuyi05* Description:修改方法重写的练习2中定义的类Kids中employeed()方法,在该方法中调用父类ManKind的employeed()方法,然后再输出"but Kids should study and no job."* @Author 雨翼轻尘* @Create 2023/11/4 0030 10:56*/
public class Kids extends Mankind { //父类中声明的属性和方法都被继承到子类了,构造器就不提了。后边提super关键字的时候会提到,在子类当中调用父类中的构造器//...@Overridepublic void employeed() {//在子类中调用父类中被重写的方法super.employeed(); //先去调用父类中的employeed()方法System.out.println("but Kids should study and no job.");}//...
}
【Mankind.java】
package yuyi02;public class Mankind {//...public void employeed(){if(salary==0){System.out.println("no job");} else {System.out.println("job");}}
}
【KidsTest.java】
package yuyi02;public class KidsTest {public static void main(String[] args) {//...Kids someKid=new Kids();someKid.employeed();}
}
👻输出结果
2、练习2
🌋题目描述
在Cylinder类中修改求表面积的方法findArea()和求体积的方法findVolume(),使用上super。
【Circle.java】
package yuyi03;/*** ClassName: Circle* Package: yuyi06* Description:** @Author 雨翼轻尘* @Create 2023/10/31 0031 10:07*/
public class Circle {//属性private double radius; //半径//方法public void setRadius(double radius){this.radius=radius;}public double getRadius(){return radius;}//求圆的面积public double findArea(){return Math.PI*radius*radius;}//构造器public Circle(){radius=1;}
}
【Cylinder.java】
package yuyi03;/*** ClassName: Cylinder* Package: yuyi06* Description:* 圆柱类* @Author 雨翼轻尘* @Create 2023/10/31 0031 10:19*/
public class Cylinder extends Circle {//属性private double length; //高//方法public void setLength(double length){this.length=length;}public double getLength(){return length;}//求圆柱的体积public double findVolume(){return Math.PI*getRadius()*getRadius()*getLength(); //底面积*高//return findArea()*getLength(); //错误的}//构造器public Cylinder(){length=1;}//求表面积@Overridepublic double findArea() {return Math.PI*getRadius()*getRadius()*2+2*Math.PI*getRadius()*getLength();}
}
【CylinderTest.java】
package yuyi03;/*** ClassName: CylinderTest* Package: yuyi06* Description:** @Author 雨翼轻尘* @Create 2023/10/31 0031 10:29*/
public class CylinderTest {public static void main(String[] args) {Cylinder cy=new Cylinder();cy.setRadius(2.3);cy.setLength(1.4);System.out.println("圆柱的体积为: "+cy.findVolume());System.out.println("圆柱的表面积为: "+cy.findArea());}
}
🤺代码
【Cylinder.java】
package yuyi03;public class Cylinder extends Circle {//...//求圆柱的体积public double findVolume(){//return Math.PI*getRadius()*getRadius()*getLength(); //底面积*高 正确的//return findArea()*getLength(); //错误的return super.findArea()*getLength();}//...//求表面积@Overridepublic double findArea() {return Math.PI*getRadius()*getRadius()*2+2*Math.PI*getRadius()*getLength();}
}
【Circle.java】
package yuyi03;public class Circle {//...//求圆的面积public double findArea(){return Math.PI*radius*radius;}
}
【CylinderTest.java】
package yuyi03;public class CylinderTest {public static void main(String[] args) {Cylinder cy=new Cylinder();cy.setRadius(2.3);cy.setLength(1.4);System.out.println("圆柱的体积为: "+cy.findVolume());System.out.println("圆柱的表面积为: "+cy.findArea());}
}
👻输出结果
3、练习3
🌋题目描述
①写一个名为Account的类模拟账户。该类的属性和方法如下图所示。
该类包括的属性:账号id,余额balance,年利率annualInterestRate;
包含的方法:访问器方法(getter和setter方法),返回月利率的方法getMonthlyInterest(),取款方法withdraw(),存款方法deposit()。
写一个用户程序测试Account类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%的Account对象。
使用withdraw方法提款30000元,并打印余额。
再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。
提示:在提款方法withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。
运行结果如图所示。
②创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。
在CheckAccount类中重写withdraw方法,其算法如下:
————————————————————————————————————————
如果(取款金额<账户余额),
可直接取款
如果(取款金额>账户余额),
计算需要透支的额度
判断可透支额overdraft是否足够支付本次透支需要,如果可以
将账户余额修改为0,冲减可透支金额
如果不可以
提示用户超过可透支额的限额
————————————————————————————————————————
要求:写一个用户程序测试CheckAccount类。
在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。
使用withdraw方法提款5000元,并打印账户余额和可透支额。
再使用withdraw方法提款18000元,并打印账户余额和可透支额。
再使用withdraw方法提款3000元,并打印账户余额和可透支额。
提示:
(1)子类CheckAccount的构造方法需要将从父类继承的3个属性和子类自己的属性全部初始化。
(2)父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw方法中需要修改它的值,因此应修改父类的balance属性,定义其为protected。
运行结果如下图所示。
🤺代码①
【Account.java】
package yuyi04;/*** ClassName: Account* Package: yuyi04* Description:** @Author 雨翼轻尘* @Create 2023/11/4 0004 8:29*/
public class Account {//属性private int id; //账户private double balance; //余额private double annualInterestRate; //年利率//构造器public Account(int id,double balance,double annualInterestRate){//super();this.id=id;this.balance=balance;this.annualInterestRate=annualInterestRate;}//方法public void setId(int id) {this.id = id;}public void setBalance(double balance) {this.balance = balance;}public int getId() {return id;}public double getBalance() {return balance;}public void setAnnualInterestRate(double annualInterestRate) {this.annualInterestRate = annualInterestRate;}/*** 获取月利率* @return*/public double getMonthlyInterest(){return annualInterestRate / 12;}/*** 取钱曹操作* @param amount 要取的钱数*/public void withdraw(double amount){if(balance>=amount){balance-=amount;}else{System.out.println("余额不足!");}}/*** 存钱操作* @param amount 要存的额度*/public void deposit(double amount){if(amount>0){balance+=amount;}}
}
【AccountTest.java】
package yuyi04;/*** ClassName: AccountTest* Package: yuyi04* Description:* 写一个用户程序测试Account类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%的Account对象。* 使用withdraw方法提款30000元,并打印余额。* 再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。* @Author 雨翼轻尘* @Create 2023/11/4 0004 10:46*/
public class AccountTest {public static void main(String[] args) {Account acct=new Account(1122,20000,0.045);acct.withdraw(30000);System.out.println("您的账户余额为:"+acct.getBalance());acct.withdraw(2500);acct.deposit(3000);System.out.println("您的账户余额为: "+acct.getBalance());System.out.println("月利率为: "+acct.getMonthlyInterest());}
}
👻运行结果①
🤺代码②
【Account.java】
package yuyi04;/*** ClassName: Account* Package: yuyi04* Description:** @Author 雨翼轻尘* @Create 2023/11/4 0004 8:29*/
public class Account {//属性private int id; //账户private double balance; //余额private double annualInterestRate; //年利率//构造器public Account(int id,double balance,double annualInterestRate){//super();this.id=id;this.balance=balance;this.annualInterestRate=annualInterestRate;}//方法public void setId(int id) {this.id = id;}/*public void setBalance(double balance) {this.balance = balance;}*/public int getId() {return id;}public double getBalance() {return balance;}public void setAnnualInterestRate(double annualInterestRate) {this.annualInterestRate = annualInterestRate;}/*** 获取月利率* @return*/public double getMonthlyInterest(){return annualInterestRate / 12;}/*** 取钱曹操作* @param amount 要取的钱数*/public void withdraw(double amount){if(balance>=amount){balance-=amount;}else{System.out.println("余额不足!");}}/*** 存钱操作* @param amount 要存的额度*/public void deposit(double amount){if(amount>0){balance+=amount;}}
}
【CheckAccount.java】
package yuyi04;/*** ClassName: CheckAccount* Package: yuyi04* Description:* 创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。* @Author 雨翼轻尘* @Create 2023/11/4 0004 14:51*/
public class CheckAccount extends Account{//属性private double overdraft; //可透支限额//方法public double getOverdraft() {return overdraft;}public void setOverdraft(double overdraft) {this.overdraft = overdraft;}//重写withdraw方法/*** 针对于可透支的账户的取钱操作* @param amount 要取的钱数*/public void withdraw(double amount){if(getBalance()>=amount){//错误的:(左右结果都是一个值,何来赋值一说)//getBalance()=getBalance()-amount;//正确的super.withdraw(amount); //super别去掉了,这里调用的是父类的withdraw方法} else if (getBalance()+overdraft>=amount) {overdraft-=amount-getBalance(); //可透支的限额剩余量super.withdraw(getBalance()); //把原本账户的钱取光}else{System.out.println("超过可透支限额");}}//构造器public CheckAccount(int id,double balance,double annualInterestRate){super(id,balance,annualInterestRate);}public CheckAccount(int id,double balance,double annualInterestRate,double overdraft){super(id,balance,annualInterestRate);this.overdraft=overdraft;}
}
【CheckAccountTest.java】
package yuyi04;/*** ClassName: CheckAccountTest* Package: yuyi04* Description:* 要求:写一个用户程序测试CheckAccount类。* 在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。** 使用withdraw方法提款5000元,并打印账户余额和可透支额。* 再使用withdraw方法提款18000元,并打印账户余额和可透支额。* 再使用withdraw方法提款3000元,并打印账户余额和可透支额。** @Author 雨翼轻尘* @Create 2023/11/4 0004 22:05*/
public class CheckAccountTest {public static void main(String[] args) {CheckAccount checkAccount=new CheckAccount(1122,20000,0.045,5000);checkAccount.withdraw(5000);System.out.println("您的账户余额为: "+checkAccount.getBalance());System.out.println("您的可透支额为: "+checkAccount.getOverdraft());checkAccount.withdraw(18000);System.out.println("您的账户余额为: "+checkAccount.getBalance());System.out.println("您的可透支额为: "+checkAccount.getOverdraft());checkAccount.withdraw(3000);System.out.println("您的账户余额为: "+checkAccount.getBalance());System.out.println("您的可透支额为: "+checkAccount.getOverdraft());}
}
👻输出结果
⚡注意
【注意一】
当我们写完第一问的时候,第二问写CHeckAccount继承于Account,就会报错。
如下:
这是为哈呢?
在声明一个类没有显示写构造器的时候,会默认有一个空参的构造器。任何一个构造器的首行,要么写this(形参列表),要么是super(形参列表)。若现在构造器也没有写,那就是默认super()。
而现在父类Account里面根本没有提供空参构造器,所以会报错。
有两种解决办法。
第一种是给父类提供空参的构造器:
package yuyi04;public class Account {//...//构造器//空参构造器public Account(){}public Account(int id,double balance,double annualInterestRate){//super();this.id=id;this.balance=balance;this.annualInterestRate=annualInterestRate;}
}
第二种可以直接调用有参的构造器:
【注意二】
CheckAccount类里面重写的withdraw
方法。
package yuyi04;public class CheckAccount extends Account{//...//重写withdraw方法/*** 针对于可透支的账户的取钱操作* @param amount 要取的钱数*/public void withdraw(double amount){if(getBalance()>=amount){//错误的:(左右结果都是一个值,何来赋值一说)//getBalance()=getBalance()-amount;//正确的super.withdraw(amount); //super别去掉了,这里调用的是父类的withdraw方法} else if (getBalance()+overdraft>=amount) {overdraft-=amount-getBalance(); //可透支的限额剩余量super.withdraw(getBalance()); //把原本账户的钱取光}else{System.out.println("超过可透支限额");}}
}
这里很容易弄混:
【小Tips】
按住Ctrl+Shift+向上方向键
:本行与上一行互换
【Super的使用】
①子类重写的方法里面,调用父类被重写的方法:
②
(6)面试题
1、第一题
如下代码输出结果是多少?
package com.atguigu05._super.interview;/*** 判断运行结果** @author 雨翼轻尘* @create 2023/11/5*/
public class Interview01 {public static void main(String[] args) {new A(new B());}
}class A {public A() {System.out.println("A");}public A(B b) {this();System.out.println("AB");}
}class B {public B() {System.out.println("B");}
}
最终输出结果是:
分析一下,看图:
2、第二题
如下代码输出结果是多少?
package com.atguigu05._super.interview;/*** 判断运行结果** @author 雨翼轻尘* @create 2023/11/5*/
public class Interview01 {public static void main(String[] args) {new A(new B());}
}class A {public A() {System.out.println("A");}public A(B b) {this();System.out.println("AB");}
}class B extends A{public B() {System.out.println("B");}
}
最终输出结果是:
分析一下,看图:
3、第三题
如下代码输出结果是多少?
package yuyi05;/*** @author 雨翼轻尘* @create 2023/11/5*/
public class Interview02{public static void main(String[] args) {Father f = new Father();Son s = new Son();System.out.println(f.getInfo());//atyuyiSystem.out.println(s.getInfo()); //atyuyis.test();//atyuyi atyuyiSystem.out.println("-----------------");s.setInfo("轻尘");System.out.println(f.getInfo());//atyuyiSystem.out.println(s.getInfo());//轻尘s.test(); //轻尘 轻尘}
}
class Father{private String info = "atyuyi";public void setInfo(String info){this.info = info;}public String getInfo(){return info;}
}
class Son extends Father{private String info = "雨翼轻尘";public void test(){System.out.println(this.getInfo());System.out.println(super.getInfo());}
}
最终输出结果是:
分析一下,看图:
①
②
③
④
4、第四题
如下代码输出结果是多少?
package yuyi05;/*** @author 雨翼轻尘* @create 2023/11/5*/
public class Interview02{public static void main(String[] args) {Father f = new Father();Son s = new Son();System.out.println(f.getInfo());//返回最近的info,即本类的"atyuyi"System.out.println(s.getInfo()); //返回最近的info,即本类的"雨翼轻尘"s.test();//“雨翼轻尘” atyuyiSystem.out.println("-----------------");s.setInfo("轻尘");System.out.println(f.getInfo());//atyuyiSystem.out.println(s.getInfo());//雨翼轻尘s.test(); //雨翼轻尘 轻尘}
}
class Father{private String info = "atyuyi";public void setInfo(String info){this.info = info;}public String getInfo(){return info;}
}
class Son extends Father{private String info = "雨翼轻尘";public void test(){System.out.println(this.getInfo());System.out.println(super.getInfo());}//重写public String getInfo(){return info;}
}
最终输出结果是:
分析一下,看图:
①
②
二、子类对象实例化全过程
(1)介绍
调用子类构造器去创建对象的时候,会直接或间接地调用父类地构造器。
整个过程其实可以理解为关于某个类的对象,在创建这个对象的过程当中是什么样的场景。
Dog dog = new Dog("小花","小红");
(2)举例
代码举例:
class Creature{ //生物类//声明属性、方法、构造器
}class Animal extends Creature{ //动物类}class Dog extends Animal{ //狗类}class DogTest{public static void main(String[] args){//子类对象DOg在实例化的过程当中整个过程是怎样的呢?Dog dog = new Dog(); //创建DOg的对象中,涉及到父类、父类的父类加载的过程?//通过dog调用属性、方法,只要是在Animal或Creature里面定义的属性、方法dog.xxx();dog.yyy = ...;}
}
- 从结果的角度来看:体现为类的继承性
当我们创建子类对象后,子类对象就获取了其父类(所有父类,包括直接父类、间接父类)中声明的所有的属性和方法,在权限允许的情况下,可以直接调用。
- 从过程的角度来看:(继承性它在内存层面是怎么保证它是能够调用的)
当我们通过子类的构造器创建对象时,子类的构造器一定会直接或间接地调用到其父类的构造器,而其父类的构造器同样会直接或间接地调用到其父类的构造器,…,直到调用了Object
类中的构造器为止。
正因为我们调用过子类所有的父类的构造器,所以我们就会将父类中声明的属性、方法加载到内存中,供子类的对象使用。
问题:在创建子类对象的过程中,一定会调用父类中的构造器吗? yes!
先有父类的加载,才有子类的加载。
- 问题:创建子类的对象时,内存中到底有几个对象?
就只有一个对象(只new了一次)!即为当前new后面构造器对应的类的对象。
①造对象–>new
②构造器–>初始化<init>
叨叨:
这一篇写累死我了,战线拉得很长,不过值得,有任何错误的地方欢迎指正