Java 面向对象/Java 多态
在任何面向对象的编程语言中,Overriding是一个允许子类或子类提供由其超类或父类之一提供的方法的特定实现的功能。当一个子类中的方法与其超类中的方法具有相同的名称,相同的参数或签名以及相同的返回类型(或子类型)时,则称该子类中的方法覆盖超类中的方法,类。
方法重写是java实现运行时多态的一种方式。执行的方法的版本将由用于调用它的对象决定。如果父类的对象用于调用方法,则会执行父类中的版本,但如果使用子类的对象调用该方法,则将执行子类中的版本。换句话说,它是被引用的对象的类型(不是引用变量的类型),它确定将执行重写方法的哪个版本。
// A Simple Java program to demonstrate
// method overriding in java
// Base Class
class Parent
{
void show() { System.out.println("Parent's show()"); }
}
// Inherited class
class Child extends Parent
{
// This method overrides show() of Parent
@Override
void show() { System.out.println("Child's show()"); }
}
// Driver class
class Main
{
public static void main(String[] args)
{
// If a Parent type reference refers
// to a Parent object, then Parent's
// show is called
Parent obj1 = new Parent();
obj1.show();
// If a Parent type reference refers
// to a Child object Child's show()
// is called. This is called RUN TIME
// POLYMORPHISM.
Parent obj2 = new Child();
obj2.show();
}
}
输出:
Parent's show()
Child's show()
重写方法的规则:
1. 重写和访问修饰符:
重写方法的访问修饰符可以允许比重写的方法更多(但不是更少)的访问。例如,超类中的受保护的实例方法可以在子类中公开,但不是私有的。这样做会产生编译时错误。
// A Simple Java program to demonstrate
// Overriding and Access-Modifiers
class Parent
{
// private methods are not overridden
private void m1() { System.out.println("From parent m1()");}
protected void m2() { System.out.println("From parent m2()"); }
}
class Child extends Parent
{
// new m1() method
// unique to Child class
private void m1() { System.out.println("From child m1()");}
// overriding method
// with more accessibility
@Override
public void m2() { System.out.println("From child m2()");}
}
// Driver class
class Main
{
public static void main(String[] args)
{
Parent obj1 = new Parent();
obj1.m2();
Parent obj2 = new Child();
obj2.m2();
}
}
输出:
From parent m2()
From child m2()
2. 最终的方法不能被覆盖:
如果我们不想重写方法,我们将其声明为final。
// A Java program to demonstrate that
// final methods cannot be overridden
class Parent
{
// Can't be overridden
final void show() { }
}
class Child extends Parent
{
// This would produce error
void show() { }
}
输出:
13: error: show() in Child cannot override show() in Parent void show() { } ^ overridden method is final
3. 无法重写静态方法(方法重写与方法隐藏):
当您在基类中定义一个静态方法时,它具有与静态方法相同的签名,这称为方法隐藏。下表总结了当你定义一个与超类中的方法具有相同签名的方法时发生的情况。
超类实例方法
超类静态方法
子类实例方法
覆盖
生成编译时错误
子类静态方法
生成编译时错误
隐藏
/* Java program to show that if static method is redefined by
a derived class, then it is not overriding,it is hiding */
class Parent
{
// Static method in base class which will be hidden in subclass
static void m1() { System.out.println("From parent static m1()");}
// Non-static method which will be overridden in derived class
void m2() { System.out.println("From parent non-static(instance) m2()"); }
}
class Child extends Parent
{
// This method hides m1() in Parent
static void m1() { System.out.println("From child static m1()");}
// This method overrides m2() in Parent
@Override
public void m2() { System.out.println("From child non-static(instance) m2()");}
}
// Driver class
class Main
{
public static void main(String[] args)
{
Parent obj1 = new Child();
// As per overriding rules this should call to class Child static
// overridden method. Since static method can not be overridden, it
// calls Parent's m1()
obj1.m1();
// Here overriding works and Child's m2() is called
obj1.m2();
}
}
输出:
From parent static m1()
From child non-static(instance) m2()
4. 私有方法不能被覆盖:
私有方法在编译期间被绑定时不能被覆盖。因此,我们甚至不能覆盖子类私有方法。
5. 重写方法必须具有相同的返回类型(或子类型):
从Java 5.0起,可以在子类中为重写方法使用不同的返回类型,但子类的返回类型应该是父类型的返回类型的子类型。这种现象被称为协变返回类型。
6. 从子类中调用重写的方法:
我们可以使用super关键字在重写方法中调用父类方法。
// A Java program to demonstrate that overridden
// method can be called from sub-class
// Base Class
class Parent
{
void show()
{
System.out.println("Parent's show()");
}
}
// Inherited class
class Child extends Parent
{
// This method overrides show() of Parent
@Override
void show()
{
super.show();
System.out.println("Child's show()");
}
}
// Driver class
class Main
{
public static void main(String[] args)
{
Parent obj = new Child();
obj.show();
}
}
输出:
Parent's show()
Child's show()
7. 重写和构造函数:
我们不能重写构造函数,因为父类和子类永远不能具有相同名称的构造函数(构造函数名称必须始终与类名称相同)。
8. 重写和异常处理:重写与异常处理相关的方法时,需要注意以下两条规则。
规则1:如果超类重写方法不引发异常,则子类重写方法只能抛出未检查的异常,抛出检查异常将导致编译时错误。
/* Java program to demonstrate overriding when
superclass method does not declare an exception
*/
class Parent
{
void m1() { System.out.println("From parent m1()");}
void m2() { System.out.println("From parent m2()"); }
}
class Child extends Parent
{
@Override
// no issue while throwing unchecked exception
void m1() throws ArithmeticException
{ System.out.println("From child m1()");}
@Override
// compile-time error
// issue while throwin checked exception
void m2() throws Exception{ System.out.println("From child m2");}
}
输出:
error: m2() in Child cannot override m2() in Parent
void m2() throws Exception{ System.out.println("From child m2");}
^
overridden method does not throw Exception
规则2:如果超类重写方法抛出异常,则子类重写方法只能抛出相同的子类异常。在Exception层次结构中抛出父异常将导致编译时错误。如果子类重写方法没有抛出任何异常,则不存在问题。
/* Java program to demonstrate overriding when
superclass method does declare an exception
*/
class Parent
{
void m1() throws RuntimeException
{ System.out.println("From parent m1()");}
}
class Child1 extends Parent
{
@Override
// no issue while throwing same exception
void m1() throws RuntimeException
{ System.out.println("From child1 m1()");}
}
class Child2 extends Parent
{
@Override
// no issue while throwing subclass exception
void m1() throws ArithmeticException
{ System.out.println("From child2 m1()");}
}
class Child3 extends Parent
{
@Override
// no issue while not throwing any exception
void m1()
{ System.out.println("From child3 m1()");}
}
class Child4 extends Parent
{
@Override
// compile-time error
// issue while throwing parent exception
void m1() throws Exception
{ System.out.println("From child4 m1()");}
}
error: m1() in Child4 cannot override m1() in Parent
void m1() throws Exception
^
overridden method does not throw Exception
9. 重写和抽象方法:
接口或抽象类中的抽象方法意味着在派生的具体类中被重写,否则会引发编译时错误。
10. 重写和synchronized / stricfp方法:
synchronized和stricfp修饰符和方法的存在对覆盖规则没有影响,也就是说,synchronized / stricfp方法可能覆盖非同步/ stricfp方法,反之亦然。
注意 :
在C ++中,我们需要虚拟关键字来实现覆盖或运行时多态。在Java中,默认情况下方法是虚拟的。
我们可以有多层次的方法覆盖。
// A Java program to demonstrate
// multi-level overriding
// Base Class
class Parent
{
void show() { System.out.println("Parent's show()"); }
}
// Inherited class
class Child extends Parent
{
// This method overrides show() of Parent
void show() { System.out.println("Child's show()"); }
}
// Inherited class
class GrandChild extends Child
{
// This method overrides show() of Parent
void show() { System.out.println("GrandChild's show()"); }
}
// Driver class
class Main
{
public static void main(String[] args)
{
Parent obj1 = new GrandChild();
obj1.show();
}
}
输出:
GrandChild's show()
重写与重载:
重载是相同的方法有不同的签名。重写是相同的方法,相同的签名,但通过继承连接不同的类。
重载是编译器时多态的一个例子,重写是运行时多态的一个例子。
为什么要重写方法?
如前所述,重写的方法允许Java支持运行时多态性。多态性对于面向对象编程来说是必不可少的:出于某种原因,它允许一般类指定对所有派生类都通用的方法,同时允许子类定义某些或所有这些方法的特定实现。重载方法是另一种方法Java实现多态的“一个接口,多个方法”方面。
动态方法调度是面向对象设计带来的代码重用和健壮性最强大的机制之一。现有代码库在不需要重新编译的情况下调用新类实例的能力,同时保持干净的抽象接口,这是一个非常强大的工具。
重写的方法允许我们调用任何派生类的方法,而无需知道派生类对象的类型。
什么时候应用方法覆盖?(举例)
重写和继承:成功应用多态的关键之一在于理解超类和子类形成了一个从较小到较大特殊化的层次结构。正确使用,超类提供了子类可以直接使用的所有元素。它还定义派生类必须自行实现的那些方法。这允许子类灵活地定义它自己的方法,但仍然强制执行一致的接口。因此,通过将继承与重写方法相结合,超类可以定义将由其所有子类使用的方法的一般形式。
我们来看一个更实用的使用方法覆盖的例子。考虑一个组织的员工管理软件,让代码具有一个简单的基类Employee,该类具有诸如raiseSalary(),transfer(),promote()等..等不同类型的员工,如经理,工程师等。 .etc可能有自己的基类Employee中存在的方法的实现。在我们的完整软件中,我们只需要传递各地员工的名单,并在不知道员工类型的情况下调用适当的方法。例如,通过遍历员工列表,我们可以轻松提高所有员工的工资。每种类型的员工都可能在其类中有自己的逻辑,我们不必担心,因为如果raiseSalary()对于特定的员工类型存在,那么只会调用该方法。
// A Simple Java program to demonstrate application
// of overriding in Java
// Base Class
class Employee
{
public static int base = 10000;
int salary()
{
return base;
}
}
// Inherited class
class Manager extends Employee
{
// This method overrides show() of Parent
int salary()
{
return base + 20000;
}
}
// Inherited class
class Clerk extends Employee
{
// This method overrides show() of Parent
int salary()
{
return base + 10000;
}
}
// Driver class
class Main
{
// This method can be used to print salary of
// any type of employee using base class refernce
static void printSalary(Employee e)
{
System.out.println(e.salary());
}
public static void main(String[] args)
{
Employee obj1 = new Manager();
// We could also get type of employee using
// one more overridden method.loke getType()
System.out.print("Manager's salary : ");
printSalary(obj1);
Employee obj2 = new Clerk();
System.out.print("Clerk's salary : ");
printSalary(obj2);
}
}
输出:
Manager's salary : 30000
Clerk's salary : 20000