Java面向对象(三)
方法的重写
方法的重写与之前所学中方法的重载好像名字的相似,但是两者之间可以说毫不相干。
方法的重写是当父类中的方法已经没办法满足子类的需求时。子类就需要将方法进行重写。
方法重写的要求
父类的方法并不是所有的方法都可以被重写,重写也是需要遵守一定的规则:
①重写的方法形参,方法名,返回类型都必须相同,因为你是要去重写父类的方法,就必须与方法的所有除代码内容的东西一致才可以
②重写方法的访问修饰符的限制一定要大于被重写方法的访问修饰符
③重写的方法不能抛出新的异常或比被重写方法声明的异常要广的异常
举个例子:
需求: 1. 学生类: 属性: name, age, school; 方法: introduction 自我介绍。 2. 老师类: 属性: name,age, lesson 方法: introduction 自我介绍。eat() 吃饭 根据上面两个类抽象一个父类,把公共的内容放入父类。打印如下: "我叫:张三,今年26,所在学校为清华" "我叫:李四,今年28,所教课程为java"
思路:
①两个类中都抽取出相同的成员(属性和方法)比如属性中:name,age。方法中introduction。里面相同的内容作为一个它们的父类
②其他子类特有的成员,就写在自己类中。
③父类方法中introduction方法每个子类的自我介绍都不一样,所以这个方法需要用到重写
Person类:
1 public class Person { 2 //姓名 3 public String name; 4 //年龄 5 public int age; 6 //自我介绍的方法 7 public void introduction(){ 8 System.out.println("自我介绍的内容"); 9 } 10 }
Student类:
1 public class Student extends Person { 2 public String school; 3 4 //由于Person类中的introduction方法无法满足Student类中的需求,所以需要重写 5 //重写要求: 6 //1. 方法名相同 2.形参列表相同 3.返回类型相同 7 //4. 访问修饰符要大于被重写方法的访问修饰符 5. 异常不能比重写方法中异常要大/广 8 @Override 9 public void introduction() { 10 System.out.println("我叫:"+name+"今年"+age+"所在学校为"+school); 11 } 12 }
Teacher类:
1 public class Teacher extends Person{ 2 public String lesson; 3 4 //由于Person类中的introduction方法无法满足Teacher类中的需求 5 @Override 6 public void introduction() { 7 System.out.println("我叫:"+name+",今年"+age+",所教课程为"+lesson); 8 } 9 }
Super关键字
super关键字与this关键字类似,不过this关键字表示本类中的对象,而super关键字表示父类中的对象。
在子类中使用super关键字,可以调用父类的成员,比如:属性,方法,构造器。
①使用super关键字,调用父类属性
1 //Test测试类 2 public class Test{ 3 public static void main(String[] args) { 4 //测试使用super调用父类构造器 5 Son son=new Son(125); 6 //测试使用super调用父类的属性 7 son.money(); 8 //测试使用super调用父类的方法 9 son.info(); 10 } 11 } 12 //父类 13 class Father { 14 public double money=134.2; 15 16 public Father(double money){ 17 System.out.println("这是父类的有参构造器"); 18 this.money=money; 19 } 20 21 public void show(){ 22 System.out.println("调用父类的方法"); 23 } 24 } 25 //子类 26 class Son extends Father{ 27 public void money(){ 28 double money1 = super.money; 29 System.out.println("通过调用super关键字获取父类属性money值为:"+money1); 30 } 31 32 public Son(double money){ 33 //使用super关键字调用父类的构造器 34 super(money); 35 } 36 //使用super关键字,调用父类的方法 37 public void info(){ 38 super.show(); 39 } 40 }
效果展示:
主要讲解构造子类时各种方法的执行顺序。
一个类中往往会存在静态成员;非静态成员;构造器;当一个子类继承父类时,他执行的顺序是怎样的?
举个例子:
代码:
1 public class Test { 2 public static void main(String[] args) { 3 Son son=new Son(); 4 } 5 } 6 7 class Father{ 8 public int age; 9 public static double money; 10 Father(){ 11 System.out.println("这是父类的无参构造器"); 12 } 13 //静态代码块 14 static { 15 System.out.println("这是父类的静态代码块"); 16 } 17 { 18 System.out.println("这是父类的代码块"); 19 } 20 public static void show(){ 21 System.out.println("这是一个静态的show方法"); 22 } 23 public void info(){ 24 System.out.println("这是一个静态的"); 25 } 26 } 27 class Son extends Father { 28 public int score; 29 public static String name; 30 31 static { 32 System.out.println("这是子类的静态代码块"); 33 } 34 { 35 System.out.println("这是子类的代码块"); 36 } 37 Son(){ 38 System.out.println("这是一个子类的构造器"); 39 } 40 }
效果展示:
从效果展示中可以看出,想要生成子类的对象之前要先实例化父类的对象,而整体的先后顺序为:
类的初始化顺序
①运行父类的静态成员(包括静态变量,静态代码块),并且按照先后顺序执行
②运行子类的静态成员(包括静态变量,静态代码块),并且按照先后顺序执行
实例化对象的顺序
③运行父类非静态成员(包括非静态变量,非静态代码块),并且按照先后顺序执行
④运行父类的构造器,生成父类的对象
⑤运行子类非静态成员(包括非静态变量,非静态代码块),并且按照先后顺序执行
⑥运行子类的构造器,生成子类的对象
final是最终的意思,它可以修饰属性,方法,类,修饰这些成员会有什么变化?
①final修饰属性,表示这个属性是一个常量,常量代表着只能赋值一次,不可以随便修改。
②final修饰方法,表示这个方法是一个完美的方法,不能被重写。
③final修饰类,表示这个类是一个十分完美的类,不能被别人继承。
举个例子:
1 final class A{ 2 //final修饰的变量表示常量,常量用大写 3 public static final int ANIMALS= 20; 4 5 //常量赋值后不能修改值 6 //ANIMALS=10;出错 7 8 9 public final void show(){ 10 System.out.println("这是一个父类的show()方法"); 11 } 12 } 13 14 //final修饰的类不能有子类 15 /* 16 出错原因是A使用final修饰,所以不能继承 17 class B extends A{ 18 19 //出错原因是final修饰的方法不能被重写 20 @Override 21 public void show() { 22 System.out.println("这是子类的show()方法"); 23 } 24 } 25 */
Object类
Object类是所有类的父类,因为所有的类都是直接或间接的继承Object类。如果一个类没有指定继承某个类,默认继承Object类。
Object类需要学习三种方法:
①toString方法:打印对象的地址值
举个例子:
Person类:
1 public class Person { 2 public int age; 3 public String name; 4 public Person(int age,String name){ 5 this.name=name; 6 this.age=age; 7 } 8 9 }
Test类:
1 public class Test { 2 public static void main(String[] args) { 3 Person people=new Person(18,"张三"); 4 System.out.println(people.toString()); 5 } 6 }
效果展示:
可以看到,Object中的toString方法打印出来的内容是对象的地址值,如果我们想让他打印出对象的内容要如何实现呢?
前面的我们讲到子类可以重写父类的方法,而每个类都是直接或间接继承父类,所以我们可以通过重写toString方法从而达到我们想要的目的。
举个例子:
Person类:
1 public class Person { 2 public int age; 3 public String name; 4 public Person(int age,String name){ 5 this.name=name; 6 this.age=age; 7 } 8 9 //重写父类的toString方法,让打印出对象的相关属性内容 10 @Override 11 public String toString() { 12 return "Person{" + 13 "age=" + age + 14 ", name='" + name + '\'' + 15 '}'; 16 } 17 }
Test类:(与前者测试类相同)
1 public class Test { 2 public static void main(String[] args) { 3 Person people=new Person(18,"张三"); 4 System.out.println(people.toString()); 5 } 6 }
效果展示:
②equals方法:比较两者之间的地址值是否相同。
以前字符串的比较好像也用到equals方法,但为什么不是比较两者之间的值吗?
因为Java中String类型重写equals方法,所以我们如果想让两个对象比较它们的属性是否相同,也可以重写equals方法。