以下是小编收集整理的方法重载与重写,本文共5篇,希望对大家有所帮助。

篇1:方法重载与重写
方法重载与重写
一、方法的重写。
1、重写只能出现在继承关系之中。当一个类继承它的父类方法时,都有机会重写该父类的方法。一个特例是父类的方法被标识为final。重写的主要优点是能够定义某个子类型特有的行为。
class Animal {
public void eat{
System.out.println (“Animal is eating.”);
}
}
class Horse extends Animal{
public void eat(){
System.out.println (“Horse is eating.”);
}
}
2、对于从父类继承来的抽象方法,要么在子类用重写的方式设计该方法,要么把子类也标识为抽象的。所以抽象方法可以说是必须要被重写的方法。
3、重写的意义。
重写方法可以实现多态,用父类的引用来操纵子类对象,但是在实际运行中对象将运行其自己特有的方法。
public class Test {
public static void main (String[] args) {
Animal h = new Horse();
h.eat();
}
}
class Animal {
public void eat(){
System.out.println (“Animal is eating.”);
}
}
class Horse extends Animal{
public void eat(){
System.out.println (“Horse is eating.”);
}
public void buck(){
}
}
一个原则是:使用了什么引用,编译器就会只调用引用类所拥有的方法。如果调用子类特有的方法,如上例的h.buck(); 编译器会抱怨的。也就是说,编译器只看引用类型,而不是对象类型。
4、重写方法的规则。
若想实现一个合格重写方法,而不是重载,那么必须同时满足下面的要求!
A、重写规则之一:重写方法不能比被重写方法限制有更严格的访问级别。
(但是可以更广泛,比如父类方法是包访问权限,子类的重写方法是public访问权限。)
比如:Object类有个toString()方法,开始重写这个方法的时候我们总容易忘记public修饰符,编译器当然不会放过任何教训我们的机会。出错的原因就是:没有加任何访问修饰符的方法具有包访问权限,包访问权限比public当然要严格了,所以编译器会报错的。
B、重写规则之二:参数列表必须与被重写方法的相同。
重写有个孪生的弟弟叫重载,也就是后面要出场的。如果子类方法的参数与父类对应的方法不同,那么就是你认错人了,那是重载,不是重写。
C、重写规则之三:返回类型必须与被重写方法的返回类型相同。
父类方法A:void eat(){} 子类方法B:int eat(){} 两者虽然参数相同,可是返回类型不同,所以不是重写。
父类方法A:int eat(){} 子类方法B:long eat(){} 返回类型虽然兼容父类,但是不同就是不同,所以不是重写。
D、重写规则之四:重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。
import java.io.*;
public class Test {
public static void main (String[] args) {
Animal h = new Horse();
try {
h.eat();
}
catch (Exception e) {
}
}
}
class Animal {
public void eat() throws Exception{
System.out.println (“Animal is eating.”);
throw new Exception();
}
}
class Horse extends Animal{
public void eat() throws IOException{
System.out.println (“Horse is eating.”);
throw new IOException();
}
}
这个例子中,父类抛出了检查异常Exception,子类抛出的IOException是Exception的子类,也即是比被重写的方法抛出了更有限的异常,这是可以的。如果反过来,父类抛出IOException,子类抛出更为宽泛的Exception,那么不会通过编译的。
注意:这种限制只是针对检查异常,至于运行时异常RuntimeException及其子类不再这个限制之中。
E、重写规则之五:不能重写被标识为final的方法。
F、重写规则之六:如果一个方法不能被继承,则不能重写它。
比较典型的.就是父类的private方法。下例会产生一个有趣的现象。
public class Test {
public static void main (String[] args) {
//Animal h = new Horse();
Horse h = new Horse();
h.eat();
}
}
class Animal {
private void eat(){
System.out.println (“Animal is eating.”);
}
}
class Horse extends Animal{
public void eat(){
System.out.println (“Horse is eating.”);
}
}
这段代码是能通过编译的。表面上看来违反了第六条规则,但实际上那是一点巧合。Animal类的eat()方法不能被继承,因此Horse类中的eat()方法是一个全新的方法,不是重写也不是重载,只是一个只属于Horse类的全新的方法!这点让很多人迷惑了,但是也不是那么难以理解。
main()方法如果是这样:
Animal h = new Horse();
//Horse h = new Horse();
h.eat();
编译器会报错,为什么呢?Horse类的eat()方法是public的啊!应该可以调用啊!请牢记,多态只看父类引用的方法,而不看子类对象的方法!
二、方法的重载。
重载是有好的,它不要求你在调用一个方法之前转换数据类型,它会自动地寻找匹配的方法。方法的重载是在编译时刻就决定调用哪个方法了,和重写不同。最最常用的地方就是构造器的重载。
1、基本数据类型参数的重载。
public class Test {
static void method(byte b){
System.out.println (“method:byte”);
}
static void method(short s){
System.out.println (“method:short”);
}
static void method(int i){
System.out.println (“method:int”);
}
static void method(float f){
System.out.println (“method:float”);
}
static void method(double d){
System.out.println (“method:double”);
}
public static void main (String[] args) {
method((byte)1);
method('c');
method(1);
method(1L);
method(1.1);
method(1.1f);
}
}
输出结果:
method:byte
method:int
method:int
method:float
method:double
method:float
可以看出:首先要寻找的是数据类型正好匹配方法。如果找不到,那么就提升为表达能力更强的数据类型,如上例没有正好容纳long的整数类型,那么就转换为float类型的。如果通过提升也不能找到合适的兼容类型,那么编译器就会报错。反正是不会自动转换为较小的数据类型的,必须自己强制转换,自己来承担转变后果。
char类型比较特殊,如果找不到正好匹配的类型,它会转化为int而不是short,虽然char是16位的。
2、重载方法的规则。
A、被重载的方法必须改变参数列表。
参数必须不同,这是最重要的!不同有两个方面,参数的个数,参数的类型,参数的顺序。
B、被重载的方法与返回类型无关。
也就是说,不能通过返回类型来区分重载方法。
C、被重载的方法可以改变访问修饰符。
没有重写方法那样严格的限制。
D、被重载的方法可以声明新的或者更广的检查异常。
没有重写方法那样严格的限制。
E、方法能够在一个类中或者在一个子类中被重载。
3、带对象引用参数的方法重载。
class Animal {}
class Horse extends Animal{}
public class Test {
static void method(Animal a){
System.out.println (“Animal is called.”);
}
static void method(Horse h){
System.out.println (“Horse is called.”);
}
public static void main (String[] args) {
Animal a = new Animal();
Horse h = new Horse();
Animal ah = new Horse();
method(a);
method(h);
method(ah);
}
}
输出结果是:
Animal is called.
Horse is called.
Animal is called.
前两个输出没有任何问题。第三个方法为什么不是输出“Horse is called.”呢?还是那句老话,要看引用类型而不是对象类型,方法重载是在编译时刻就决定的了,引用类型决定了调用哪个版本的重载方法。
4、重载和重写方法区别的小结。
如果能彻底弄明白下面的例子,说明你对重载和重写非常了解了,可以结束这节的复习了。
class Animal {
public void eat(){
System.out.println (“Animal is eating.”);
}
}
class Horse extends Animal{
public void eat(){
System.out.println (“Horse is eating.”);
}
public void eat(String food){
System.out.println (“Horse is eating ” + food);
}
}
public class Test {
public static void main (String[] args) {
Animal a = new Animal();
Horse h = new Horse();
Animal ah = new Horse();
a.eat();
h.eat();
h.eat(“apple”);
ah.eat();
//a.eat(“apple”);
//ah.eat(“apple”);
}
}
四个输出分别是什么?被注释的两条语句为什么不能通过编译?
第一条:a.eat(); 普通的方法调用,没有多态,没什么技术含量。调用了Animal类的eat()方法,输出:Animal is eating.
第二条:h.eat(); 普通的方法调用,也没什么技术含量。调用了Horse类的eat()方法,输出:Horse is eating.
第三条:h.eat(“apple”); 重载。Horse类的两个eat()方法重载。调用了Horse类的eat(String food)方法,输出:Horse is eating apple
第四条:ah.eat(); 多态。前面有例子了,不难理解。输出:Horse is eating.
第五条:a.eat(“apple”); 低级的错误,Animal类中没有eat(String food)方法。因此不能通过编译。
第六条:ah.eat(“apple”); 关键点就在这里。解决的方法还是那句老话,不能看对象类型,要看引用类型。Animal类中没有eat(String food)方法。因此不能通过编译。
小结一下:多态不决定调用哪个重载版本;多态只有在决定哪个重写版本时才起作用。
重载对应编译时,重写对应运行时。够简洁的了吧!
三、构造方法。
构造方法是一种特殊的方法,没有构造方法就不能创建一个新对象。实际上,不仅要调用对象实际类型的构造方法,还要调用其父类的构造方法,向上追溯,直到Object类。构造方法不必显式地调用,当使用new关键字时,相应的构造方法会自动被调用。
1、构造方法的规则。
A、构造方法能使用任何访问修饰符。包括private,事实上java类库有很多都是这样的,设计者不希望使用者创建该类的对象。
B、构造方法的名称必须与类名相同。这样使得构造方法与众不同,如果我们遵守sun的编码规范,似乎只有构造方法的首字母是大写的。
C、构造方法不能有返回类型。
反过来说,有返回类型的不是构造方法
public class Test {
int Test(){
return 1;
}
}
这个方法是什么东西?一个冒充李逵的李鬼而已,int Test()和其他任何普通方法没什么两样,就是普通的方法!只不过看起来很恶心,类似恶心的东西在考试卷子里比较多。
D、如果不在类中创建自己的构造方法,编译器会自动生成默认的不带参数的构造函数。
这点很容易验证!写一个这样简单的类,编译。
class Test {
}
对生成的Test.class文件反编译:javap Test,可以看到:
D:“JavaCode”bin>javap Test
Compiled from “Test.java”
class Test extends java.lang.Object{
Test();
}
看到编译器自动添加的默认构造函数了吧!
E、如果只创建了带参数的构造方法,那么编译器不会自动添加无参的构造方法的!
F、在每个构造方法中,如果使用了重载构造函数this()方法,或者父类的构造方法super()方法,那么this()方法或者super()方法必须放在第一行。而且这两个方法只能选择一个,因此它们之间没有顺序问题。
G、除了编译器生成的构造方法,而且没有显式地调用super()方法,那么编译器会插入一个super()无参调用。
H、抽象类有构造方法。
四、静态方法的重载与重写(覆盖)。
1、静态方法是不能被覆盖的。可以分两种情况讨论:
A、子类的非静态方法“覆盖”父类的静态方法。
这种情况下,是不能通过编译的。
class Father{
static void print(){
System.out.println (“in father method”);
}
}
class Child extends Father{
void print(){
System.out.println (“in child method”);
}
}
static方法表示该方法不关联具体的类的对象,可以通过类名直接调用,也就是编译的前期就绑定了,不存在后期动态绑定,也就是不能实现多态。子类的非静态方法是与具体的对象绑定的,两者有着不同的含义。
B、子类的静态方法“覆盖”父类静态方法。
这个覆盖依然是带引号的。事实上把上面那个例子Child类的print方法前面加上static修饰符,确实能通过编译!但是不要以为这就是多态!多态的特点是动态绑定,看下面的例子:
class Father{
static void print(){
System.out.println (“in father method”);
}
}
class Child extends Father{
static void print(){
System.out.println (“in child method”);
}
}
class Test{
public static void main (String[] args) {
Father f =new Child();
f.print();
}
}
输出结果是:in father method
从这个结果可以看出,并没有实现多态。
但是这种形式很迷惑人,貌似多态,实际编程中千万不要这样搞,会把大家搞懵的!
它不符合覆盖表现出来的特性,不应该算是覆盖!
总而言之,静态方法不能被覆盖。
2、静态方法可以和非静态方法一样被重载。
这样的例子太多了,我不想写例程了。看看java类库中很多这样的例子。
如java.util.Arrays类的一堆重载的binarySearch方法。
在这里提一下是因为查资料时看到这样的话“sun的SL275课程说,静态方法只能控制静态变量(他们本身没有),静态方法不能被重载和覆盖……”
大家不要相信啊!可以重载的。而且静态与非静态方法可以重载。
从重载的机制很容易就理解了,重载是在编译时刻就决定的了,非静态方法都可以,静态方法怎么可能不会呢?
篇2:方法重载与方法重写
方法重载:就是在同一个类中,方法的名字相同,但参数个数、参数的类型或返回值类型不同!
方法重写:它是指子类和父类的关系,子类重写了父类的方法,但方法名、参数类型、参数个数必须相同!
Java语言中的概念就是多,这回又有两个概念,重载和重写。这是两个新概念,也是两个令初学者容易混淆的概念。他们的概念截然不同,只不过都有个“重”字,就以为是很像的。
下面解释一下这两个概念:
方法重载(overloading method) 是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。
方法重写(overiding method) 子类不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。如果还是搞混的话,就把“重写覆盖”,这个词多念几遍吧。知道是覆盖的话,就知道是子类覆盖父类的方法了。
实践: 重载的例子
public class MethodOverloading {
void recieve(int i) {
System.out.println(“接收一个int数据”);
System.out.println(“i=”+i);
}
void recieve(float f) {
System.out.println(“接受一个float型的`数据”);
System.out.println(“f=”+f);
}
void recieve(String s) {
System.out.println(“接受一个String型数据”);
System.out.println(“s=”+s);
}
public static void main(String[] args){
MethodOverloading m = new MethodOverloading();
m.recieve(3456);
m.recieve(34.56);
m.recieve(“......“);
}
}
大家看到了上面的例子方法receive()有三个,名字相同参数不同。这样的话,在main()调用的时候,参数用起来就很方便了。重写的例子似乎不用举了,记不住的话,就和“覆盖”。
有时候,重载和重写的方式有些复杂,在jdk5里面。有一些方式能简化一些。我们来看看吧,jdk5的可变参数。 如果把相同参数类型的方法重载好几遍真的是很烦。就一个方法,pri(String args), pri(String arg0 ,String arg1), pri(String arg0,String arg1,String arg2), pri(String arg0,String arg1,String arg2,String arg3)。这样的话会写很多烦琐的代码。现在jdk5可以,用“…”来代替这些参数。
实践:
public class overload {
//若干个相同类型的参数,用“...”代替
public void pri(String... strings ){
for (String str : strings) //for这个循环语句也有迭代的意思
System.out.print(str);
}
public static void main(String[] args){
new overload().pri(“100jq”,“ );
}
}
jdk5的方法重写,比以前多了一个叫做协变返回的概念。在以往jdk的版本中,还有一个比较让人讨厌的地方。方法重写确实是比较不错的机制,如果想用父类的方法,写个super就可以了,如果不想用父类的方法就重写覆盖。但是重写覆盖的返回类型不能覆盖,父类的类型不够用怎么办,我们想在子类重写它的类型可以吗?现在可以了。
看下面的例子:
class Point2D { //定义二维的点
protected int x, y;
public Point2D() {
this.x=0;
this.y=0;}
public Point2D(int x, int y) {
this.x = x;
this.y = y;
}}
//定义三维的点,继承二维
class Point3D extends Point2D {
protected int z;
public Point3D(int x, int y) {
this(x, y, 0);
}
public Point3D(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}}
//定义二维的位置
class Position2D {
Point2D location;
public Position2D() {
this.location = new Point2D();
}
public Position2D(int x, int y) {
this.location = new Point2D(x, y);
}
public Point2D getLocation() {
return location;
}}
//定义三维的位置,继承二维的位置
class Position3D extends Position2D {
Point3D location; //在这里已经变成Point3D的类型了
public Position3D(int x, int y, int z) {
this.location = new Point3D(x, y, z);
}
@Override //注释是重写方法
public Point3D getLocation() {
return location; //返回是子类的类型而不是原来的类型了
}
}
篇3:方法重写和方法重载
方法重写和方法重载
在Java中,以下关于方法重载和方法重写描述正确的是?
方法重载和方法的重写实现的功能相同
方法重载出现在父子关系中,方法重写是在同一类中
方法重载的返回值类型必须一致,参数项必须不同
方法重写的返回值类型必须相同或相容。(或是其子类)
方法重载与方法重写、this关键字和super关键字
1、方法重载
重载能够使具有相同名称但不同数目和类型参数的类传递给方法。
注:
一是重载方法的参数列表必须与被重载的方法不同,并且这种不同必须足以清楚地确定要调用哪一个方法;
二是重载方法的返回值类型可以与被重载的方法相同,也可以不同,但是只有返回值类型不同不能表示为重载。
例如,最常用的println方法在JDK的java.io.PrintStream中定义了十几种形式的重载,常用格式如下:
public void println(int i) {....}
public void println(double i) {....}
public void println(String i) {....}
/**********************************************************
* ”方法重载“的使用方法及其示例代码*/
public class OverloadRewrite
{
void num(int i){
System.out.println(”接收到的是int类型参数,其值=“+i);
}
void num(float j){
System.out.println(”接收到的是float类型参数,其值=“+j);
}
void num(String k){
System.out.println(”接收到的是String类型参数,其值=“+k);
}
public static void main(String [] args) {
OverloadRewrite a=new OverloadRewrite();
a.num(100);
a.num(100.20f);
a.num(”Hello!“);
}
}
2、方法重写
当子类继承父类时,可以拥有父类中的成员方法和成员变量,在子类创建独有的成员即可。但是,如果创建一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写或方法覆盖。
注:
一是返回值类型与参数列表必须与被重写的返回值类型和参数列表相同,否则不能称为重写;
二是访问修饰符的限制一定要大于被重写方法的访问修饰符(public >protected >default >private);
三是重写方法一定不能抛出新的检查异常或者比被重写方法声明更加宽泛的检查型异常。例如,父类的一个方法声明了一个检查异常IOException,在重写这个方法时,就不能抛出Exception,只能抛出IOException的`子类异常,可以抛出非检查异常。
/**********************************************************
* ”方法重写“的使用方法及其示例代码*/
public class OverloadRewrite
{
public String Name;
public String Address;
public OverloadRewrite(String name,String address) { //构造方法
this.Name=name;
this.Address=address;
}
public String toString() { //重写toString
return ”我的名字是:“+Name+”,产地是:“+Address;
}
public class sichuancai extends OverloadRewrite{ // 继承
public String Detail;
public sichuancai(String name,String address,String detail){ //构造方法
super(name, address); //super调用父类相应的构造方法
this.Detail=detail;
}
}
// public String toString() { //重写toString
// return ”我的名字是:“+Name+”,产地是:“+Address+”,特点是:“+this.Detail;
// }
public static void main(String [] args) {
OverloadRewrite food=new OverloadRewrite(”面条“, ”意大利“);
System.out.println(food);
sichuancai sichuan=food.new sichuancai(”麻辣烫“, ”成都“, ”麻辣和香辣“);
System.out.println(sichuan);
}
}
3、this关键字
this关键字可用于在任何实例方法内指向当前对象,也可指向对其调用当前方法的对象,或者在需要当前类型对象引用时使用。
注:当一个类的属性名或成员变量与访问该属性的方法参数名相同时,则需要使用this关键字来访问类中的属性,以区分类的属性和方法中的参数。
4、super关键字
由于子类不能继承父类的构造方法,因此要调用父类的构造方法,必须在子类的构造方法的第一行使用super()。
super()方法会调用父类相应的构造方法,完成子类对象的部分初始化工作。
注:程序在以下两种情况下需要使用super()关键字:
一是在类的构造方法中,通过super语句调用该类的父类的构造方法。
二是在子类中访问父类中的成员。
篇4:方法重载和方法重写的概念和区别
方法重载和方法重写的概念和区别
重载:一个类中有一个方法A,你又在这个类中创建了一个方法B,方法B的名字和A一样,返回值类型也一样,但是参数的类型或个数不同,此时B重载了A。
例如:
public class TestClass{
public int test(int i){return 1;}
public int test(float f){return 1;}
}
重写:一个类M继承另一个类N,N中有一个方法A,这时你在M写了一个方法B,方法B的名字、返回值以及参数都和A一样,此时B重写了A。
例如:
public class TestClass1{
public int test(int i){return 1;}
}
public class TestClass2 extends TestClass1{
public int test(int i){return 2;}
}
方法重载:同一个类中,方法名相同,参数不同,方法体不同;
方法重写:不同的类中,方法体不同,参数相同,方法名也相同。
(两同两不同)
方法重载的'作用: 在有多个同名参数的类中,在调用时通过不同的参数来调用不同的方法。
方法重写的作用: 在继承中,子类可用方法重写,对父类进行扩充或改造。
篇5:方法重载和方法覆盖
方法重载和方法覆盖
重载与覆盖的区别【一】
1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系。
2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
3、覆盖要求参数列表相同;重载要求参数列表不同。
4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
override可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
overload对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点:
1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int, float), 但是不能为fun(int, int));
2、不能通过访问权限、返回类型、抛出的异常进行重载;
3、方法的异常类型和数目不会对重载造成影响;
4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果
class A {
protected int method1(int a, int b) { return 0; }
}
public class B extends A{
public int method1(int a, int b) { return 0; } //正确,重写父类方法,可以扩大访问权限
//private int method1(int a, int b) { return 0; } //错误,重写父类方法,不能降低了访问权限
//private long method1(int a, int b) { return 0; } //错误,重写父类方法,不能改变返回值类型
public short method1(int a, long b) { return 0; }//正确,重载自身的方法,可以有不同的访问权限和返回值类型
private int method1(int a, long b) { return 0; }//正确,重载自身的方法,可以有不同的访问权限和返回值类型
}
方法覆盖(overwrite)与方法重载(overload)详解【二】
方法重载(Overload)
有时候,类的同一种功能有多种实现方式,到底采用哪种实现方式,取决于调用者给定的参数。例如杂技师能训练动物,对于不同的动物有不同的训练方式。
public void train(Dog dog){
//训练小狗站立、排队、做算术
…
}
public void train(Monkey monkey){
//训练小猴敬礼、翻筋斗、骑自行车
…
}
再例如某个类的一个功能是比较两个城市是否相同,一种方式是按两个城市的名字进行比较,另一种方式是按两个城市的名字,以及城市所在国家的名字进行比较。
public boolean isSameCity (String city1,String city2){
return city1.equals(city2);
}
public boolean isSameCity(String city1,String city2,String country1,String country2){
return isSameCity(city1, city2) && country1.equals(country2);
}
再例如java.lang.Math类的max()方法能够从两个数字中取出最大值,它有多种实现方式。
public static int max(int a,int b)
public static int max(long a,long b)
public static int max(float a,float b)
public static int max(double a,double b)
以下程序多次调用Math类的max()方法,运行时,Java虚拟机先判断给定参数的类型,然后决定到底执行哪个max()方法。
//参数均为int类型,因此执行max(int a,int b)方法
Math.max(1,2);
//参数均为float类型,因此执行max(float a,float b)方法
Math.max(1.0F, 2.0F);
//参数中有一个是double类型,自动把另一个参数2转换为double类型,
//执行max(double a,double b)方法
Math.max(1.0,2);
对于类的方法(包括从父类中继承的方法),如果有两个方法的方法名相同,但参数不一致,那么可以说,一个方法是另一个方法的重载方法。
重载方法必须满足以下条件:
l 方法名相同。
l 方法的参数类型、个数、顺序至少有一项不相同。
l 方法的返回类型可以不相同。
l 方法的修饰符可以不相同。
在一个类中不允许定义两个方法名相同,并且参数签名也完全相同的方法。因为假如存在这样的两个方法,Java虚拟机在运行时就无法决定到底执行哪个方法。参数签名是指参数的类型、个数和顺序。
例如以下Sample类中已经定义了一个amethod()方法。
public class Sample{
public void amethod(int i, String s){}
//加入其他方法
}
下面哪些方法可以加入到Sample类中,并且保证编译正确呢?
A)public void amethod(String s, int i){} (可以)
B)public int amethod(int i, String s){return 0;} (不可以)
C)private void amethod(int i, String mystring){} (不可以)
D)public void Amethod(int i, String s) {} (可以)
E)abstract void amethod(int i); (不可以)
选项A的amethod()方法的参数顺序和已有的不一样,所以能作为重载方法加入到Sample类中。
选项B和选项C的amethod()方法的参数签名和已有的一样,所以不能加入到Sample类中。对于选项C,尽管String类型的参数的名字和已有的不一样,但比较参数签名无须考虑参数的具体名字。
选项D的方法名为Amethod,与已有的不一样,所以能加入到Sample类中。
选项E的方法的参数数目和已有的不一样,因此是一种重载方法。但由于此处的Sample类不是抽象类,所以不能包含这个抽象方法。假如把Sample类改为抽象类,就能把这个方法加入到Sample类中了。
再例如,以下Sample类中已经定义了一个作为程序入口的main()方法。
abstract public class Sample{
public static void main( String[] s){}
//加入其他方法
}
下面哪些方法可以加入到Sample类中,并且保证编译正确呢?
A)abstract public void main(String s, int i); (可以)
B)public final static int main( String[] s){} (不可以)
C)private void main(int i, String mystring){} (可以)
D)public void main( String s) throws Exception{} (可以)
作为程序入口的main()方法也可以被重载。以上选项A、C和D都可以被加入到Sample类中。选项B与已有的main()方法有相同的方法签名,因此不允许再加入到Sample类中。
方法覆盖(Override)
假如有100个类,分别为Sub1,Sub2…Sub100,它们的一个共同行为是写字,除了Sub1类用脚写字 外,其余的类都用手写字。可以抽象出一个父类Base,它有一个表示写字的方法write(),那么这个方法到底如何实现呢?从尽可能提高代码可重用性的 角度看,write()方法应该采用适用于大多数子类的实现方式,这样就可以避免在大多数子类中重复定义write()方法。因此Base类的 write()方法的定义如下:
public void write(){ //Base类的write()方法
//用手写字
…
}
由于Sub1类的写字的实现方式与Base类不一样,因此在Sub1类中必须重新定义write()方法。
public void write(){ //Sub1类的write()方法
//用脚写字
…
}
如果在子类中定义的一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配,那么可以说,子类的方法覆盖了父类的方法。
覆盖方法必须满足多种约束,下面分别介绍。
(1)子类方法的名称、参数签名和返回类型必须与父类方法的名称、参数签名和返回类型一致。例如以下代码将导致编译错误。
public class Base {
public void method() {…}
}
public class Sub extends Base{
public int method() { //编译错误,返回类型不一致
return 0;
}
}
Java编译器首先判断Sub类的method()方法与Base类的method()方法的参数签名,由于两者一 致,因此Java编译器认为Sub类的method()方法试图覆盖父类的方法,既然如此,Sub类的method()方法就必须和被覆盖的方法具有相同 的返回类型。
以下代码中子类覆盖了父类的一个方法,然后又定义了一个重载方法,这是合法的。
public class Base {
public void method() {…}
}
public class Sub extends Base {
public void method(){…} //覆盖Base类的method()方法
public int method(int a) { //重载method()方法
return 0;
}
}
(2)子类方法不能缩小父类方法的访问权限。例如以下代码中子类的method()方法是私有的,父类的method()方法是公共的,子类缩小了父类方法的访问权限,这是无效的方法覆盖,将导致编译错误。
public class Base {
public void method() {…}
}
public class Sub extends Base {
private void method() {…} //编译错误,子类方法缩小了父类方法的访问权限
}
为什么子类方法不允许缩小父类方法的访问权限呢?这是因为假如没有这个限制,将会与Java语言的多态机制发生冲突。例如对于以下代码:
Base base=new Sub(); //base变量被定义为Base类型,但引用Sub类的实例
base.method();
Java编译器认为以上是合法的代码。但在运行时,根据动态绑定规则,Java虚拟机会调用base变量所引用的 Sub实例的method()方法,如果这个方法为private类型,Java虚拟机就无法访问它。所以为了避免这样的矛盾,Java语言不允许子类方 法缩小父类中被覆盖方法的访问权限。本章第6.6节(多态)对多态做了进一步的阐述。
(3)子类方法不能抛出比父类方法更多的异常,关于异常的概念参见第9章(异常处理)。子类方法抛出的异常必须和父类方法抛出的异常相同,或者子类方法抛出的异常类是父类方法抛出的异常类的子类。
例如,假设异常类ExceptionSub1和ExceptionSub2是ExceptionBase类的子类,则以下的代码是合法的:
public class Base {
void method()throws ExceptionBase{}
}
public class Sub1 extends Base {
void method()throws ExceptionSub1{}
}
public class Sub2 extends Base {
void method()throws ExceptionSub1,ExceptionSub2{}
}
public class Sub3 extends Base {
void method()throws ExceptionBase{}
}
以下代码不合法:
public class Base {
void method() throws ExceptionSub1{ }
}
public class Sub1 extends Base {
void method()throws ExceptionBase {} //编译出错
}
public class Sub2 extends Base {
void method()throws ExceptionSub1,ExceptionSub2 {} //编译出错
}
为什么子类方法不允许抛出比父类方法更多的异常呢?这是因为假如没有这个限制,将会与Java语言的多态机制发生冲突。例如对于以下代码:
Base base=new Sub2(); //base变量被定义为Base类型,但引用Sub2类的实例
try{
base.method();
}catch(ExceptionSub1 e){ … } //仅仅捕获ExceptionSub1异常
Java编译器认为以上是合法的代码。但在运行时,根据动态绑定规则,Java虚拟机会调用base变量所引用的 Sub2实例的method()方法。假如Sub2实例的method()方法抛出ExceptionSub2异常,由于该异常没有被捕获,将导致程序异 常终止。
(4)方法覆盖只存在于子类和父类(包括直接父类和间接父类)之间。在同一个类中方法只能被重载,不能被覆盖。
(5)父类的静态方法不能被子类覆盖为非静态方法。例如以下的'代码将导致编译错误:
public class Base {
public static void method() { }
}
public class Sub extends Base {
public void method() { } //编译出错
}
(6)子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。在编译时,子类定义的静态方法也必须满足与方法覆盖类似的约束:方法的参数签名一致,返回类型一致,不能缩小父类方法的访问权限,不能抛出更多的异常。例如以下代码是合法的:
public class Base {
static int method(int a) throws BaseException{ return 0; }
}
public class Sub extends Base{
public static int method(int a) throws SubException { return 0; }
}
子类隐藏父类的静态方法和子类覆盖父类的实例方法,这两者的区别在于:运行时,Java虚拟机把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。下面举例来解释这一区别。在例程6-1中,Base类和它的子类即Sub类中都定义了实例方法method()和静 态方法staticMethod()。
例程6-1 Sub.java
package hidestatic;
class Base{
void method(){ //实例方法
System.out.println(”method of Base“);
}
static void staticMethod(){ //静态方法
System.out.println(”static method of Base“);
}
}
public class Sub extends Base{
void method(){ //覆盖父类的实例方法method()
System.out.println(”method of Sub“);
}
static void staticMethod(){ //隐藏父类的静态方法staticMethod()
System.out.println(”static method of Sub“);
}
public static void main(String args[]){
Base sub1=new Sub(); //sub1变量被声明为Base类型,引用Sub实例
sub1.method(); //打印 method of Sub
sub1.staticMethod(); //打印 static method of Base
Sub sub2=new Sub(); //sub2变量被声明为Sub类型,引用Sub实例
sub2.method(); //打印 method of Sub
sub2.staticMethod(); //打印 static method of Sub
}
}
运行Sub类的main()方法,程序将输出:
method of Sub
static method of Base
method of Sub
static method of Sub
引用变量sub1和sub2都引用Sub类的实例,Java虚拟机在执行sub1.method()和sub2.method()时,都调用Sub实例的method()方法,此时父类Base的实例方法method()被子类覆盖。
引用变量sub1被声明为Base类型,Java虚拟机在执行sub1. staticMethod()时,调用Base类的staticMethod()方法,可见父类Base的静态方法staticMehtod()不能被子类覆盖。
引用变量sub2被声明为Sub类型,Java虚拟机在执行sub2. staticMethod()时,调用Sub类的staticMethod()方法,Base类的staticMehtod()方法被Sub类的staticMehtod()方法隐藏。
(7)父类的非静态方法不能被子类覆盖为静态方法。例如以下代码是不合法的:
public class Base {
void method() { }
}
public class Sub extends Base {
static void method() { } //编译出错
}
(8)父类的私有方法不能被子类覆盖。例如在例程6-2中,子类Sub中定义了一个和父类Base中的方法同名、参 数签名和返回类型一致,但访问权限不一致的方法showMe(),父类中showMe()的访问权限为private,而子类中showMe()的访问权 限为public。尽管这在形式上和覆盖很相似,但Java虚拟机对此有不同的处理机制。子类方法覆盖父类方法的前提是,子类必须能继承父类的特定方法, 由于Base类的private类型的showMe()方法不能被Sub类继承,因此Base类的showMe()方法和Sub类的showMe()方法 之间并没有覆盖关系。
例程6-2 Sub.java
package privatetest;
class Base {
private String showMe() {
return ”Base“;
}
public void print(){
System.out.println(showMe()); //到底调用Base类的showMe()还是Sub类的showMe()?
}
}
public class Sub extends Base {
public String showMe(){
return ”Sub“;
}
public static void main(String args[]){
Sub sub=new Sub();
sub.print();
}
}
执行以上Sub类的main()方法,会打印出结果“Base”,这是因为print()方法在Base类中定义,因此print()方法会调用在Base类中定义的private类型的showMe()方法。
但是如果把Base类的showMe()方法改为public类型,其他代码不变:
public class Base {
public String showMe() {
return ”Base";
}
…
}
再执行以上Sub类的main()方法的代码,会打印出结果“Sub”,这是因为此时Sub类的showMe()方 法覆盖了Base类的showMe()方法。因此尽管print()方法在Base类中定义,Java虚拟机还是会调用当前Sub实例的showMe() 方法。
(9)父类的抽象方法可以被子类通过两种途径覆盖:一是子类实现父类的抽象方法;二是子类重新声明父类的抽象方法。例如以下代码合法:
public abstract class Base {
abstract void method1();
abstract void method2();
}
public abstract class Sub extends Base {
public void method1(){…} //实现method1()方法,并且扩大访问权限
public abstract void method2(); //重新声明method2()方法,仅仅扩大访问权限,但不实现
Tips
狭义的理解,覆盖仅指子类覆盖父类的具体方法,即非抽象方法,在父类中提供了方法的默认实现方式,而子类采用不同的实现方式。在本书中,为了叙述方便,把子类实现父类的抽象方法也看做方法覆盖。
例如以下代码不合法:
public abstract class Base {
abstract void method1();
abstract void method2();
}
public abstract class Sub extends Base {
private void method1(){…} //编译出错,不能缩小访问权限
private abstract void method2(); //编译出错,不能缩小访问权限
}
(10)父类的非抽象方法可以被覆盖为抽象方法。例如以下代码合法:
public class Base {
void method(){ }
}
public abstract class Sub extends Base {
public abstract void method(); //合法
}
图6-2 Sub类继承Base类
在本书提供的UML类框图中,在子类中只会显示子类特有的方法及覆盖父类的方法,而不会显示直接从父类中继承的方 法。例如,图6-2表明Base类是抽象类(Base名字用斜体字表示),method1()为抽象方法(method1名字用斜体字表 示),method2()和method3()为具体方法。Sub类是Base类的子类,Sub类实现了Base类的method1()方法,覆盖了 Base类的method2()方法,直接继承Base类的method3()方法,此外Sub类还有自己的method4()方法。
方法覆盖与方法重载的异同
方法覆盖和方法重载具有以下相同点:
·都要求方法同名。
·都可以用于抽象方法和非抽象方法之间。
·方法覆盖和方法重载具有以下不同点:
·方法覆盖要求参数签名必须一致,而方法重载要求参数签名必须不一致。
· 方法覆盖要求返回类型必须一致,而方法重载对此不做限制。
·方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类的所有方法(包括从父类中继承而来的方法)。
·方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
·父类的一个方法只能被子类覆盖一次,而一个方法在所在的类中可以被重载多次。
以下子类Sub覆盖了父类Base的method(int v)方法,并且提供了多种重载方法。
public class Base{
protected void method(int v){}
private void method(String s){} //重载
}
public abstract class Sub extends Base {
public void method(int v){} //覆盖
public int method(int v1,int v2){return 0;} //重载
protected void method(String s) throws Exception{} //重载
abstract void method(); //重载
文档为doc格式