Pour les langages de programmation orientés objet, les cours constituent sans aucun doute la base la plus importante. Les quatre caractéristiques majeures de l'abstraction, de l'encapsulation, de l'héritage et du polymorphisme sont indissociables des classes. Seule l'existence de classes peut refléter les caractéristiques de la programmation orientée objet. Aujourd'hui, nous allons apprendre quelques connaissances sur les classes et l'héritage. Parlons d’abord des choses liées à l’initialisation des classes, puis nous expliquerons la grande fonctionnalité de l’héritage sous plusieurs aspects. Voici le sommaire de la table des matières de cet article :
1. Comprenez-vous les cours ?
2. Comprenez-vous l'héritage ?
3. Questions courantes d'entretien écrit
1. Comprenez-vous les catégories ?
En Java, un fichier de classe est un fichier de code avec le suffixe .java. Une seule classe publique est autorisée à apparaître dans chaque fichier de classe. Lorsqu'il existe une classe publique, le nom du fichier de classe doit être. identique à public. Les noms des classes sont les mêmes. S'il n'y a pas de public, le nom du fichier de classe peut être n'importe quel nom (bien sûr, les noms commençant par des chiffres ne sont pas autorisés).
Au sein de la classe, pour les variables membres, s'il n'y a pas d'initialisation d'affectation explicite lors de la définition, Java veillera à ce que chaque variable membre de la classe soit correctement initialisée :
1) Pour les variables de base les types de données tels que char, short, byte, int, long, float, double, etc., ils seront initialisés à 0 par défaut (les variables booléennes seront initialisées à false par défaut
2) ; types de référence Les variables seront initialisées à null par défaut.
Si le constructeur n'est pas explicitement défini, le compilateur créera automatiquement un constructeur sans argument, mais rappelez-vous que si le constructeur est explicitement défini, le compilateur n'ajoutera pas automatiquement le constructeur. Notez que tous les constructeurs sont statiques par défaut.
Ensuite, nous nous concentrons sur la séquence d'initialisation :
Lorsque le programme est exécuté, un objet d'une certaine classe doit être généré. Le moteur d'exécution Java vérifiera d'abord si cette classe est chargée. Sinon, alors la classe est chargée en premier puis l'objet est généré. S'il a été chargé, l'objet est généré directement.
Pendant le processus de chargement de la classe, les variables membres statiques de la classe seront initialisées. De plus, s'il y a un bloc d'instructions statiques dans la classe, le bloc d'instructions statiques sera exécuté. L'ordre d'exécution des variables membres statiques et des blocs d'instructions statiques est cohérent avec l'ordre dans le code. N'oubliez pas qu'en Java, les classes sont chargées à la demande. Cette classe ne sera chargée que lorsqu'elle sera nécessaire, et une seule fois. Regardez l'exemple suivant pour comprendre :
public class Test { public static void main(String[] args) throws ClassNotFoundException { Bread bread1 = new Bread(); Bread bread2 = new Bread(); } } class Bread { static{ System.out.println("Bread is loaded"); } public Bread() { System.out.println("bread"); } }
Lorsque vous exécutez ce code, vous constaterez que "Le pain est chargé" ne sera imprimé qu'une seule fois.
Pendant le processus de génération d'un objet, les variables membres de l'objet seront d'abord initialisées, puis le constructeur sera exécuté. Cela signifie que les variables de la classe seront initialisées avant qu'une méthode (y compris le constructeur) ne soit appelée, même si les variables sont intercalées entre les définitions de méthode.
public class Test { public static void main(String[] args) { new Meal(); } } class Meal { public Meal() { System.out.println("meal"); } Bread bread = new Bread(); } class Bread { public Bread() { System.out.println("bread"); } }
Le résultat de sortie est :
pain
repas
2. Comprenez-vous l'héritage ?
L'héritage est un élément indispensable de tous les langages POO. Le mot-clé extends est utilisé en Java pour exprimer la relation d'héritage. Lorsqu'une classe est créée, elle est toujours héritée. Si la classe à hériter n'est pas explicitement indiquée, elle est toujours implicitement héritée de la classe racine Object. Par exemple, le code suivant :
class Person { public Person() { } } class Man extends Person { public Man() { } }
La classe Man hérite de la classe Person Dans ce cas, la classe Person est appelée classe parent (classe de base), et la classe Man est appelée sous-classe. (classe dérivée). S'il existe une relation d'héritage entre deux classes, la sous-classe héritera automatiquement des méthodes et variables de la classe parent, et les méthodes et variables de la classe parent pourront être appelées dans la sous-classe. En Java, seul l'héritage unique est autorisé, ce qui signifie qu'une classe ne peut hériter explicitement que d'au plus une classe parent. Mais une classe peut être héritée par plusieurs classes, ce qui signifie qu’une classe peut avoir plusieurs sous-classes.
1. La sous-classe hérite des variables membres de la classe parent
Lorsque la sous-classe hérite d'une certaine classe, elle peut utiliser les variables membres de la classe parent, mais elle n'hérite pas complètement de la classe parent. Toutes les variables membres. Les principes spécifiques sont les suivants :
1) Vous pouvez hériter des variables membres publiques et protégées de la classe parent ; vous ne pouvez pas hériter des variables membres privées de la classe parent
2) Pour le package accède aux membres de la classe parent Variables, si la sous-classe et la classe parent sont dans le même package, la sous-classe peut hériter ; sinon, la sous-classe ne peut pas hériter
3) Pour les variables membres de la classe parent qui ; la sous-classe peut hériter, si elle est dans l'enfant. Si une variable membre du même nom apparaît dans la classe, un masquage se produira, c'est-à-dire que la variable membre de la sous-classe bloquera la variable membre de la classe parent du même nom. . Si vous souhaitez accéder à une variable membre portant le même nom dans la classe parent d'une sous-classe, vous devez utiliser le mot-clé super comme référence.
2. Les sous-classes héritent des méthodes de la classe parent
De même, les sous-classes n'héritent pas complètement de toutes les méthodes de la classe parent.
1) Peut hériter des méthodes membres publiques et protégées de la classe parent ; ne peut pas hériter des méthodes membres privées de la classe parent
2) Pour les méthodes membres d'accès au package de la classe parent ; , si la sous-classe Si elle est dans le même package que la classe parent, la sous-classe peut hériter ; sinon, la sous-classe ne peut pas hériter
3)对于子类可以继承的父类成员方法,如果在子类中出现了同名称的成员方法,则称为覆盖,即子类的成员方法会覆盖掉父类的同名成员方法。如果要在子类中访问父类中同名成员方法,需要使用super关键字来进行引用。
注意:隐藏和覆盖是不同的。隐藏是针对成员变量和静态方法的,而覆盖是针对普通方法的。(后面会讲到)
3.构造器
子类是不能够继承父类的构造器,但是要注意的是,如果父类的构造器都是带有参数的,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。看下面这个例子就清楚了:
class Shape { protected String name; public Shape(){ name = "shape"; } public Shape(String name) { this.name = name; } } class Circle extends Shape { private double radius; public Circle() { radius = 0; } public Circle(double radius) { this.radius = radius; } public Circle(double radius,String name) { this.radius = radius; this.name = name; } }
这样的代码是没有问题的,如果把父类的无参构造器去掉,则下面的代码必然会出错:
改成下面这样就行了:
4.super
super主要有两种用法:
1)super.成员变量/super.成员方法;
2)super(parameter1,parameter2....)
第一种用法主要用来在子类中调用父类的同名成员变量或者方法;第二种主要用在子类的构造器中显示地调用父类的构造器,要注意的是,如果是用在子类构造器中,则必须是子类构造器的第一个语句。
三.常见的面试笔试题
1.下面这段代码的输出结果是什么?
public class Test { public static void main(String[] args) { new Circle(); } } class Draw { public Draw(String type) { System.out.println(type+" draw constructor"); } } class Shape { private Draw draw = new Draw("shape"); public Shape(){ System.out.println("shape constructor"); } } class Circle extends Shape { private Draw draw = new Draw("circle"); public Circle() { System.out.println("circle constructor"); } }
shape draw constructor
shape constructor
circle draw constructor
circle constructor
这道题目主要考察的是类继承时构造器的调用顺序和初始化顺序。要记住一点:父类的构造器调用以及初始化过程一定在子类的前面。由于Circle类的父类是Shape类,所以Shape类先进行初始化,然后再执行Shape类的构造器。接着才是对子类Circle进行初始化,最后执行Circle的构造器。
2.下面这段代码的输出结果是什么?
public class Test { public static void main(String[] args) { Shape shape = new Circle(); System.out.println(shape.name); shape.printType(); shape.printName(); } } class Shape { public String name = "shape"; public Shape(){ System.out.println("shape constructor"); } public void printType() { System.out.println("this is shape"); } public static void printName() { System.out.println("shape"); } } class Circle extends Shape { public String name = "circle"; public Circle() { System.out.println("circle constructor"); } public void printType() { System.out.println("this is circle"); } public static void printName() { System.out.println("circle"); } }
shape constructor
circle constructor
shapethis is circle
shape
这道题主要考察了隐藏和覆盖的区别(当然也和多态相关,在后续博文中会继续讲到)。
覆盖只针对非静态方法(终态方法不能被继承,所以就存在覆盖一说了),而隐藏是针对成员变量和静态方法的。这2者之间的区别是:覆盖受RTTI(Runtime type identification)约束的,而隐藏却不受该约束。也就是说只有覆盖方法才会进行动态绑定,而隐藏是不会发生动态绑定的。在Java中,除了static方法和final方法,其他所有的方法都是动态绑定。因此,就会出现上面的输出结果。
更多Java : classes et héritage相关文章请关注PHP中文网!