重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
package One;
public class Animal {
public String name;
public String gender;
public int age;
public Animal(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public void eat(){
System.out.println(name+"在吃饭");
}
public void sleep(){
System.out.println(name+"在睡觉");
}
public void bark(){
System.out.println(name+"在叫");
}
}
package One;
public class Animal {
public String name;
public String gender;
public int age;
public Animal(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public void eat(){
System.out.println(name+"在吃饭");
}
public void sleep(){
System.out.println(name+"在睡觉");
}
public void bark(){
System.out.println(name+"在叫");
}
}
package One;
public class Dog extends Animal{
public String color;
public Dog(String name, String gender, int age, String color) {
super(name, gender, age);
this.color = color;
}
public void eat(){
System.out.println(name+"在干饭");
}
public void sleep(){
System.out.println(name+"在睡大觉");
}
public void bark(){
System.out.println(name+"在狗叫");
}
}
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代
表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体
调用那个类的方法
package One;
public class TestAnimal {
/*
在代码编写时,编译器此时不知道Animal要指向那个对象,只有在代码运行的时候,由于基类可以引用不
同的对象,对象确定好之后才能调用不同对象的方法;比如Animal可以引用Dog对象也可以引用Cat对象
只有在方法调用的时候需要传递实参,当方法被执行的时候对象才具体,此时eat,sleep,bark才能知道
调用的是那个类的方法
*/
public static void Test(Animal animal){
animal.eat();
animal.sleep();
animal.bark();
}
public static void main(String[] args) {
Dog d1=new Dog("小八","公",3,"黄色");
Test(d1);
System.out.println("---------------------------");
Cat c1=new Cat("小七","雌",3,"粘人型");
Test(c1);
}
}
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
package One;
public class TestAnimal {
public static void Test(Animal animal){
animal.eat();
animal.sleep();
animal.bark();
}
public static void method(){
Animal animal=new Animal("xxxx","xxx",0);
Animal a1=new Dog("小八","公",3,"黄色");
Animal a2=new Cat("小七","雌",3,"粘人型");
//向上转型,安全
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的
方法,此时:将父类引用再还原为子类对象即可,即向下转换。
package One;
public class TestAnimal {
public static void main(String[] args) {
Dog d1=(Dog)Animal;
Cat c1=(Cat)Animal;
//向下转型,编译器时禁止的,不安全,如果不让编译器崩溃可以使用instanceof
}
}
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入
了 instanceof ,如果该表达式为true,则可以安全转换。
package One;
public class TestAnimal {
public static void Test(Animal animal){
animal.eat();
animal.sleep();
animal.bark();
}
//引用instance类名N:引用实际引用的实体是否为N的对象,如果是返回true,如果不是返回false
public static void main(String[] args) {
Animal animal=new Animal("小八","公",3);
if(animal instanceof Dog){
Dog dog=(Dog)animal;
dog.bark();
}
if (animal instanceof Dog){
Cat cat=(Cat)animal;
cat.eat();
}
}
}
方法重写
package Two;
public class Base {
/*
//重写:子类只能重写基类重的方法,不能重写成员变量
public Base() {
System.out.println("Base()");
}
private void func1(){
System.out.println("func1()");
}
public final void func2(){
System.out.println("func2()");
}
public static void func3(){
System.out.println("func3()");
}
//以上方法类型都不可以被重写
public void func4(){
System.out.println("func4()");
}
//例外:协变,被重写的方法可以返回值类型不同,返回值有要求,要有父子关系,可以不在继承体系之中,基类方法返回基类引用,子类方法返回子类引用
public Base func5(){
System.out.println("func5()");
return this;
}
public A func6(){
return new A();
}
//访问权限可以不同但是基类的访问权限必须要比子类的访问权限高或者相同,但不能低
//public>default>protected>private
//一般情况下都设置public
protected void func7(){
System.out.println("func7");
}
*/
}
package Two;
public class Derive extends Base{
//编译报错:子类不能重写基类的构造方法
/*因为:构造方法作用在创建对象时,由编译器调用,用来初始化对象完整
构造方法没有调用,对象都不完整,也不可能实现多态
只有对象创建好之后,才能用其调用其他方法实现多态
但是对象创建好了之后,构造方法都已经调用完了,不可能在构造方法中实现多态
因此:在子类中重写基类的构造方法是没有任何意义的
*/
/*
@Override
public Base() {
System.out.println("Base()");
}
//方法重写编译器不报错但是并不代表重写成功
//可以在编译器加上@Override注解:编译器自动检测重写方法,编译器此时就会报错
//private所修饰的方法只能在类内调用所以报错
@Override
private void func1(){
System.out.println("func1()");
}
//final修饰方法表示方法不能被重写
public final void func2(){
System.out.println("func2()");
}
//实现多态时,编译器需要通过基类调用被重写的放法,此时编译器不知道要调用那个类中的方法
//只有当基类的引用指向具体的对象时,才能调用具体类中被重写的方法
//从实现条件来看:被重写的方法必需要被具体的对象来调用
//被重写的方法必需要是实例方法;实例方法中才有this引用,通过this引用来调用方法
//而类方法中没有this引用,所以不能调用被重写的方法
@Override
public static void func3(){
System.out.println("func3()");
}
//注意:在重写基类的方法时,子类的方法必须要和基类的方法原型一致,实现体不同
//原型一致:修饰相同 返回值类型形同方法名相同 参数列表相同
//即:外壳一样,实现不同
@Override
public void func4(){
System.out.println("func4()");
}
public Derive func5(){
System.out.println("func5()");
return this;
}
public B func6(){
return new B();
}
void func7(){
System.out.println("func7");
}
*/
}
package Two;
public class A {
}
package Two;
public class B extends A{
}
避免在构造方法中调用重写方法
package Three;
public class B {
public B() {
//返回子类调用子类中被重写的方法
this.func();
//避免在构造方法中调用重写方法,构造方法的作用是将成员变量初始化,如果构造方法没有调用完成,则对应的构造是不完整的
//则程序运行会有歧义
}
public void func(){
System.out.println("func()===");
}
}
package Three;
public class D extends B{
public D(){
super();
}
private int num;
public void func(){
System.out.println("func()");
System.out.println(num);
}
}
package Three;
public class TestFunc {
public static void main(String[] args) {
B b1=new D();
//创建一个子类的对象,先要调用子类对象中的构造方法
}
}