Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung des Überladens, Umschreibens und der Konstruktoren von Java

Detaillierte Erläuterung des Überladens, Umschreibens und der Konstruktoren von Java

巴扎黑
巴扎黑Original
2017-09-21 11:58:181902Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen zum Überladen und Umschreiben von Java sowie detaillierte Beispiele für Konstruktorfunktionen vorgestellt. Ich hoffe, dass jeder, der die objektorientierte Methode von Java versteht und beherrscht, darauf zurückgreifen kann >

Detaillierte Beispiele für Java-Überladung, Umschreiben und Konstruktoren

Methodenumschreiben

1 innerhalb des Erbverhältnisses. Wenn eine Klasse eine Methode von ihrer übergeordneten Klasse erbt, hat sie die Möglichkeit, die Methode der übergeordneten Klasse zu überschreiben. Ein Sonderfall liegt vor, wenn eine übergeordnete Klassenmethode als final markiert ist. Der Hauptvorteil des Überschreibens besteht in der Möglichkeit, für einen Subtyp spezifisches Verhalten zu definieren.


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. Entwerfen Sie für abstrakte Methoden, die von der übergeordneten Klasse geerbt werden, entweder die Methode, indem Sie sie in der Unterklasse überschreiben, oder markieren Sie die Unterklasse als abstrakt. Man kann also sagen, dass abstrakte Methoden Methoden sind, die neu geschrieben werden müssen.

3. Die Bedeutung des Umschreibens.


Überschreibende Methoden können Polymorphismus erreichen und Referenzen von der übergeordneten Klasse verwenden, um Unterklassenobjekte zu manipulieren, aber im tatsächlichen Betrieb führt das Objekt seine eigene einzigartige Methode aus.


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(){
    }
  }
Ein Prinzip ist: Welche Referenz auch immer verwendet wird, der Compiler ruft nur die Methoden auf, die der Referenzklasse gehören. Wenn Sie eine unterklassenspezifische Methode aufrufen, wie z. B. h.buck() im obigen Beispiel, wird sich der Compiler beschweren (Kompilierungsfehler). Das heißt, der Compiler betrachtet nur Referenztypen, keine Objekttypen.

4. Regeln zum Umschreiben von Methoden.


Wenn Sie eine qualifizierte Überschreibungsmethode anstelle einer Überladung implementieren möchten, müssen Sie gleichzeitig die folgenden Anforderungen erfüllen!

A. Eine der Umschreiberegeln: Die überschreibende Methode darf keine restriktivere Zugriffsebene haben als die überschriebene Methode.

(Es kann jedoch umfassender sein. Beispielsweise verfügt die übergeordnete Klassenmethode über Paketzugriffsrechte und die überschriebene Methode der Unterklasse über öffentliche Zugriffsrechte.)
Beispiel: Die Object-Klasse verfügt über eine toString()-Methode , beginnen Sie mit dem Überschreiben dieser Methode. Manchmal neigen wir dazu, den öffentlichen Modifikator zu vergessen, und der Compiler wird sicherlich keine Gelegenheit verpassen, uns eine Lektion zu erteilen. Der Grund für den Fehler liegt darin, dass Methoden ohne Zugriffsmodifikatoren natürlich über Paketzugriffsrechte verfügen, die strenger sind als öffentliche, sodass der Compiler einen Fehler meldet.

B. Regel 2 umschreiben: Die Parameterliste muss mit der der überschriebenen Methode übereinstimmen.


Rewrite hat einen Zwillingsbruder namens Overload, der später auftauchen wird. Wenn sich die Parameter der Unterklassenmethode von der entsprechenden Methode der übergeordneten Klasse unterscheiden, haben Sie die falsche Person. Das ist eine Überladung, keine Überschreibung.

C. Regel drei umschreiben: **Der Rückgabetyp muss mit dem Rückgabetyp der überschriebenen Methode identisch sein.


Methode A der übergeordneten Klasse: void eat(){} Methode der Unterklasse B: int eat(){} Obwohl die Parameter gleich sind, sind die Rückgabetypen unterschiedlich und werden daher nicht neu geschrieben. **


Methode A der übergeordneten Klasse: int eat(){} Methode der Unterklasse B: long eat(){} Obwohl der Rückgabetyp mit der übergeordneten Klasse kompatibel ist, ist der Unterschied unterschiedlich ist keine Überschreibung.

D. Regel 4 umschreiben: Die überschreibende Methode kann keine neuen Ausnahmen oder geprüften Ausnahmen auslösen, die breiter sind als die von der überschriebenen Methode deklarierten geprüften Ausnahmen. Es ist jedoch möglich, weniger, begrenztere oder keine Ausnahmen auszulösen.


import java.io.*;
  /**
  * Java学习交流QQ群:589809992 我们一起学Java!
  */
  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();
    }
  }
In diesem Beispiel löst die übergeordnete Klasse eine geprüfte Ausnahme Exception aus, und die von der Unterklasse ausgelöste IOException ist eine Unterklasse von Exception, das heißt, sie wird neu geschrieben .-Methode löst eine eingeschränktere Ausnahme aus, was in Ordnung ist. Wenn andererseits die übergeordnete Klasse eine IOException auslöst und die Unterklasse eine umfassendere Ausnahme auslöst, wird sie nicht kompiliert.

Hinweis: Diese Einschränkung gilt nur für geprüfte Ausnahmen. RuntimeException und ihre Unterklassen unterliegen dieser Einschränkung nicht mehr.

E. Regel 5 umschreiben: Als endgültig markierte Methoden können nicht überschrieben werden.

F. Regel 6 umschreiben: Wenn eine Methode nicht vererbt werden kann, kann sie nicht überschrieben werden.


Die typische Methode ist die private Methode der übergeordneten Klasse. Das folgende Beispiel erzeugt ein interessantes Phänomen.


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.");
    }
  }
Dieser Code kann kompiliert werden. Oberflächlich betrachtet scheint es ein Verstoß gegen Regel 6 zu sein, aber in Wirklichkeit war es eher ein Zufall. Die eat()-Methode der Animal-Klasse kann nicht vererbt werden, daher ist die eat()-Methode in der Horse-Klasse eine brandneue Methode, kein Umschreiben oder Überladen, sondern eine brandneue Methode, die nur zur Horse-Klasse gehört! Das verwirrt viele Menschen, ist aber nicht so schwer zu verstehen.


Wenn die main()-Methode so aussieht:


  Animal h = new Horse();
  //Horse h = new Horse();
  h.eat();
Der Compiler meldet einen Fehler, warum? Die eat()-Methode der Horse-Klasse ist öffentlich! Es sollte aufgerufen werden können! Bitte beachten Sie, dass Polymorphismus nur die Methoden berücksichtigt, auf die die übergeordnete Klasse verweist, nicht die Methoden des Unterklassenobjekts!

Überladung von Methoden

Überladung ist benutzerfreundlich, Sie müssen den Datentyp nicht konvertieren, bevor Sie eine Methode aufrufen, es findet automatisch eine Übereinstimmungsmethode . Das Überladen von Methoden bestimmt, welche Methode zur Kompilierungszeit aufgerufen werden soll, was sich vom Überschreiben unterscheidet. Der am häufigsten verwendete Ort ist die Überladung des Konstruktors.

1. Überladung grundlegender Datentypparameter.


/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
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);
    }
  }
Ausgabeergebnis:


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课程说,静态方法只能控制静态变量(他们本身没有),静态方法不能被重载和覆盖……”

大家不要相信啊!可以重载的。而且静态与非静态方法可以重载。

从重载的机制很容易就理解了,重载是在编译时刻就决定的了,非静态方法都可以,静态方法怎么可能不会呢?

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Überladens, Umschreibens und der Konstruktoren von Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn