2.5 方法重写
各位同学,学习完继承之后,在继承的基础之上还有一个很重要的现象需要给大家说一下。
叫做方法重写。为了让大家能够掌握方法重写,我们先认识什么是方法重写,再说一下方法的应用场景。
什么是方法重写
当子类觉得父类方法不好用,或者无法满足父类需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
注意:重写后,方法的访问遵循就近原则。下面我们看一个代码演示
写一个A类作为父类,定义两个方法print1和print2
public class A {
public void print1(){
System.out.println("111");
}
public void print2(int a, int b){
System.out.println("111111");
}
}
再写一个B类作为A类的子类,重写print1和print2方法。
public class B extends A{
// 方法重写
@Override // 安全,可读性好
public void print1(){
System.out.println("666");
}
// 方法重写
@Override
public void print2(int a, int b){
System.out.println("666666");
}
}
接下来,在测试类中创建B类对象,调用方法
public class Test {
public static void main(String[] args) {
// 目标:认识方法重写,掌握方法重写的常见应用场景。
B b = new B();
b.print1();
b.print2(2, 3);
}
}
执行代码,我们发现真正执行的是B类中的print1和print2方法

知道什么是方法重写之后,还有一些注意事项,需要和大家分享一下。
- 1. 重写小技巧:使用Override注解,它可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
- 2. 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限
public > protected > 缺省
- 3. 重写的方法返回值类型,必须与被重写的方法返回值类型一样,或者范围更小
- 4. 私有方法、静态方法不能被重写,如果重写会报错。
关于这些注意事项,同学们其实只需要了解一下就可以了。实际上我们实际写代码时,只要和父类写的一样就可以( 总结起来就8个字:声明不变,重新实现)
方法重写的应用场景
学习完方法重写之后,接下来,我们还需要大家掌握方法重写,在实际中的应用场景。方法重写的应用场景之一就是:子类重写Object的toString()方法,以便返回对象的内容。
比如:有一个Student类,这个类会默认继承Object类。
public class Student extends Object{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
其实Object类中有一个toString()方法,直接通过Student对象调用Object的toString()方法,会得到对象的地址值。
public class Test {
public static void main(String[] args) {
Student s = new Student("胜仔", 20);
// System.out.println(s.toString());
System.out.println(s);
}
}

但是,此时不想调用父类Object的toString()方法,那就可以在Student类中重新写一个toSting()方法,用于返回对象的属性值。
public class Student extends Object{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
重新运行测试类,结果如下

好了,到这里方法什么是方法重写,以及方法重写的应用场景我们就学习完了。
1、方法重写是什么?
子类写了一个方法名称,形参列表与父类某个方法一样的方法去覆盖父类的该方法。
2、重写方法有哪些注意事项?
建议加上:@Override注解,可以校验重写是否正确,同时可读性好。
子类重写父类方法时,访问权限必须大于或者等于父类被重写的方法的权限。
重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
私有方法、静态方法不能被重写。
3、方法重写有啥应用场景?
当子类觉得父类的方法不好用,或者不满足自己的需求时,就可以用方法重写。
2.6 子类中访问成员的特点
各位同学,刚才我们已经学习了继承,我们发现继承至少涉及到两个类,而每一个类中都可能有各自的成员(成员变量、成员方法),就有可能出现子类和父类有相同成员的情况,那么在子类中访问其他成员有什么特点呢?
-
原则:在子类中访问其他成员(成员变量、成员方法),是依据就近原则的
-
先子类局部范围找。
-
然后子类成员范围找。
-
然后父类成员范围找,如果父类范围还没有找到则报错。
定义一个父类,代码如下
public class F {
String name = "父类名字";
public void print1(){
System.out.println("==父类的print1方法执行==");
}
}
再定义一个子类,代码如下。有一个同名的name成员变量,有一个同名的print1成员方法;
public class Z extends F {
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部名称
}
@Override
public void print1(){
System.out.println("==子类的print1方法执行了=");
}
public void showMethod(){
print1(); // 子类的
}
}
接下来写一个测试类,观察运行结果,我们发现都是调用的子类变量、子类方法。
public class Test {
public static void main(String[] args) {
// 目标:掌握子类中访问其他成员的特点:就近原则。
Z z = new Z();
z.showName();
z.showMethod();
}
}
-
如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加this或者super进行区分。
public class Z extends F {
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部名称
System.out.println(this.name); // 子类成员变量
System.out.println(super.name); // 父类的成员变量
}
@Override
public void print1(){
System.out.println("==子类的print1方法执行了=");
}
public void showMethod(){
print1(); // 子类的
super.print1(); // 父类的
}
}
1、在子类方法中访问成员(成员变量、成员方法)是什么特点?
就近原则,子类没有找子类、子类没有找父类、父类没有就报错!
2、如果子父类中出现了重名的成员,如果此时一定要在子类中使用父类的怎么办?
使用 super.父类成员变量/父类成员方法
2.7 子类中访问构造器的特点
各位同学,我们知道一个类中可以写成员变量、成员方法,还有构造器。在继承关系下,子类访问成员变量和成员方法的特点我们已经学过了;接下来再学习子类中访问构造器的特点。
我们先认识子类构造器的语法特点,再讲一下子类构造器的应用场景
子类中访问构造器的语法规则
-
首先,子类全部构造器,都会先调用父类构造器,再执行自己。
代码如下:
class F{
public F(){
System.out.println("===父类F的 无参数构造器 执行了===");
}
public F(String name, int age){
System.out.println("===父类F的 有参数构造器 执行了===" + name + " ==> " + age);
}
}
class Z extends F{
public Z(){
super(); // 默认存在的
System.out.println("===子类Z的 无参数构造器 执行了===");
}
public Z(String name){
//super(); // 默认存在的
super("柳岩", 17);
System.out.println("===子类Z的 有参数构造器 执行了===");
}
}
public class Test1 {
public static void main(String[] args) {
// 目标:先认识子类构造器的特点,再掌握这个特点的常见应用场景。
Z z = new Z();
Z z2 = new Z("柳岩");
}
}
注意: 子类构造器是如何实现调用父类构造器的?
默认情况下,子类全部构造器的第一行代码都是 super() (写不写都有),它会调用父类的无参数构造器。
如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(….),指定去调用父类的有参数构造器。
子类访问构造器的应用场景
-
如果不想使用默认的
super()方式调用父类构造器,还可以手动使用super(参数)调用父类有参数构造器。
public class Test2 {
public static void main(String[] args) {
// 目标:搞清楚子类构造器为什么要调用父类构造器,有啥应用场景。
// new对象调用Teacher类中3个参数的构造器
Teacher t = new Teacher("柳岩", 18, "Java");
System.out.println(t.getName());
System.out.println(t.getAge());
System.out.println(t.getSkill());
}
}
class Teacher extends People{
private String skill;
public Teacher(String name, int age, String skill){
// super(name,age)调用People类中2个参数的构造器
super(name , age);
this.skill = skill;
}
//...为了节省代码篇幅,这里省略了get和set方法...
}
class People{
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
//...为了节省代码篇幅,这里省略了get和set方法...
}
总结:
子类构造器可以通过调用父类构造器,把对象中包含父类这部分的数据先初始化赋值,
再回来把对象里包含子类这部分的数据也进行初始化赋值。
在本类中访问自己的构造方法
刚才我们学习了通过super()和super(参数)可以访问父类的构造器。有时候我们也需要访问自己类的构造器。语法如下:
this(): 调用本类的空参数构造器 this(参数): 调用本类有参数的构造器
public class Test3 {
public static void main(String[] args) {
// 目标:掌握在类的构造器中,通过this(...)调用兄弟构造器的作用。
Student s1 = new Student("柳岩", 18, "家里蹲大学");
System.out.println(s1.getName());
System.out.println(s1.getAge());
System.out.println(s1.getSchoolName());
System.out.println("=================");
// 需求:如果学生没有填写学校,那么学校默认就是胜雅教育
// 调用Student中2个参数的构造方法
Student s2 = new Student("刘涛", 20);
System.out.println(s2.getName());
System.out.println(s2.getAge());
System.out.println(s2.getSchoolName());
}
}
class Student{
private String name;
private int age;
private String schoolName;
public Student() {
}
public Student(String name, int age){
// 调用本类中3个参数的构造方法
this(name, age, "胜雅教育");
}
public Student(String name, int age, String schoolName) {
super();
this.name = name;
this.age = age;
this.schoolName = schoolName;
}
//...为了节省代码篇幅,这里省略了get和set方法...
}
this(...)和super(…)使用时的注意事项:
this(…) 、super(…) 都只能放在构造器的第一行,因此,有了this(…)就不能写super(…)了,反之亦然。
最后我们被this和super的用法在总结一下
this: 代表本类对象的引用(访问本类成员):
this.成员变量 //访问本类成员变量
this.成员方法 //调用本类成员方法
this() //调用本类空参数构造器
this(参数) //调用本类有参数构造器
super: 代表父类对象的引用(访问父类成员):
super.成员变量 //访问父类成员变量
super.成员方法 //调用父类成员方法
super() //调用父类空参数构造器
super(参数) //调用父类有参数构造器
注意:this和super访问构造方法,只能用到构造方法的第一行,否则会报错。
总结:
1、子类构造器有啥特点?
子类中的全部构造器,都必须先调用父类的构造器,再执行自己。
2、super(…)调用父类有参数构造器的常见应用场景是什么?
为对象中包含父类这部分的成员变量进行赋值。
3、this(…)的作用是什么?
在构造器中调用本类的其他构造器。
4、this(…)和super(…)的使用需要注意什么?
都必须放在构造器的第一行。

683

被折叠的 条评论
为什么被折叠?



