Java >> Tutoriel Java >  >> Tag >> public

La différence entre privé et public en Java

Alors que j'écrivais mon premier semestre d'enseignement de la réflexion, j'ai eu l'idée de lancer une série de questions d'étudiants appelées Coding Tangents. Dans cette série, j'aborderai les questions des étudiants avec des explications claires et faciles à suivre qui démystifient la syntaxe courante du langage de programmation. En particulier, j'aimerais aborder la différence entre public et privé en Java aujourd'hui.

Le problème rencontré par les étudiants

Souvent, lorsque nous enseignons Java, nous sommes bloqués en laissant une grande partie de la syntaxe comme un processus mécanique. En d'autres termes, nous disons aux élèves que des mots clés comme public , statique , et privé leur sera expliqué plus tard. En attendant, ils n'ont qu'à croire que nous leur expliquerons ces concepts plus tard.

L'un de ces éléments de syntaxe qui est presque toujours laissé pour une discussion ultérieure est privé vs public . Ces mots clés sont connus sous le nom de modificateurs d'accès, et nous les approfondirons dans cet article.

Mais d'abord, regardons un exemple de code qui est presque certain de soulever des questions sur les modificateurs d'accès :

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
}

Afin d'enseigner Java, nous sommes souvent obligés d'introduire le langage en utilisant ces horribles cinq lignes de code. Après tout, c'est le strict minimum requis pour faire fonctionner un programme Java.

En conséquence, nous sommes souvent obligés de dire aux étudiants quelque chose du genre :

Ne vous inquiétez pas des quatre lignes extérieures. Placez simplement le code que vous souhaitez exécuter dans le bloc du milieu.

Professeurs d'informatique partout

Bien sûr, ce raisonnement laisse beaucoup à désirer si vous êtes un nouvel étudiant. Par exemple, que font chacune de ces quatre lignes extérieures ? Ce qui est public ? Que diriez-vous de statique , Chaîne[] , ou System.out.println ?

Heureusement, j'ai entendu parler de la partie modificateur d'accès aujourd'hui.

L'explication souhaitée par les étudiants

À ce stade, parlons des modificateurs d'accès à un niveau élevé.

Présentation du modificateur d'accès

En Java, les modificateurs d'accès sont un moyen de nous aider à ne pas marcher sur nos propres pieds. En général, ils sont utilisés pour définir un certain niveau d'accès à une classe, une méthode ou une variable.

Par exemple, si nous voulons modéliser quelque chose du monde réel (disons une voiture), il y a certains aspects de cet objet que nous ne voulons probablement pas exposer au public (par exemple contrôle individuel des balais d'essuie-glace). Peut-être que sous le capot, les essuie-glaces sont contrôlés individuellement, mais nous avons construit notre système de telle sorte que le commutateur donné à l'utilisateur ait encapsulé ce comportement. En d'autres termes, les deux essuie-glaces se déplacent ensemble comme prévu.

Si nous avions choisi d'exposer le contrôle individuel de chaque essuie-glace, nous pourrions constater que de nombreux utilisateurs cassent accidentellement la fonctionnalité de l'essuie-glace. Après tout, si les essuie-glaces ne sont pas parfaitement synchronisés, ils peuvent s'entrechoquer.

C'est l'idée de haut niveau derrière les modificateurs d'accès. Nous les utilisons pour exposer ou masquer certaines fonctionnalités afin d'améliorer l'expérience utilisateur globale.

Idées fausses

À ce stade, de nombreux étudiants commenceront à penser que les modificateurs d'accès sont un moyen de rendre le code plus sécurisé contre les pirates. Bien que cela soit en grande partie faux, il y a certains mérite dans l'argumentation. Bien sûr, rien n'empêche quelqu'un d'utiliser une fonctionnalité comme la réflexion pour accéder aux champs et méthodes privés. Cela dit, les modificateurs d'accès peuvent aider à protéger l'utilisateur moyen contre la corruption de l'état d'un objet.

Pensez à l'exemple de l'essuie-glace. Lorsque nous allumons nos essuie-glaces, nous nous attendons à ce qu'ils se déplacent tous les deux à la même vitesse. Sans accès restreint, nous pourrions modifier la vitesse par défaut de l'un des essuie-glaces. Ensuite, la prochaine fois que nous allions allumer les essuie-glaces… BAM ! Pour éviter ce problème, nous encapsulons (ou cacher) le fait que nous avons deux essuie-glaces individuels dans une seule méthode exposée (publique).

L'encapsulation est l'art de réduire un état complexe à un ensemble de comportements exposés. Si je vous demandais de lancer une balle, vous ne commenceriez certainement pas par demander un ensemble de transformations matricielles pour la rotation de votre bras. Vous n'avez qu'à lancer la balle. C'est l'idée derrière l'encapsulation (et l'abstraction).

Dans cet exemple, nous pouvons utiliser des modificateurs d'accès pour spécifier quels comportements sont exposés. Par exemple, nous voudrions probablement autoriser les utilisateurs à accéder au lancer commande mais peut-être pas la commande rotateArm ou pickUpBall commandes.

Maintenant que nous avons abordé certaines des idées fausses, entrons dans la syntaxe.

Mots clés

En Java, il existe en fait quatre modificateurs d'accès :public , privé , paquet-privé (par défaut) et protégé . Chaque mot-clé offre un niveau d'accès au code donné par le tableau suivant :

public privé paquet-privé protégé
Même classe T T T T
Classe différente dans le même package T F T T
Sous-classe dans le même package T F T T
Classe différente dans un package différent T F F F
Sous-classe dans un package différent T F F T

En d'autres termes, nous pouvons classer les mots clés par ordre de moindre accessibilité :

  1. privé
  2. package-privé (par défaut)
  3. protégé
  4. public

Pendant la durée de ce tutoriel, je n'explorerai pas le package-private ou protégé mots-clés car ils sont un peu plus nuancés, mais j'ai pensé qu'il était important de les mentionner.

Classer les actions comme publiques ou privées

En utilisant l'exemple de lancer de balle précédent, essayons de déterminer quel modificateur d'accès serait approprié dans diverses situations :

  • public
    • lancer
    • attraper
    • lancer
    • présentation
  • privé
    • rotateArm
    • translateVertices
    • PickUpBall
    • calculateVolume

Remarquez que toutes les actions de haut niveau sont publiques et que les actions de niveau inférieur sont privées. C'est parce que nous ne voulons pas nécessairement exposer les actions de niveau inférieur au public. Mais pourquoi pas? Prenons un autre exemple.

Disons que les fonctions de haut niveau reposent sur un état sous-jacent du système. Par exemple, lancer une balle repose sur la connaissance d'informations telles que la force de gravité et les propriétés de la balle. Si quelqu'un était en mesure d'accéder d'une manière ou d'une autre aux actions de niveau inférieur, il pourrait potentiellement manipuler ces hypothèses de base sur le monde.

Que se passerait-il si nous pouvions accéder à des actions telles que setGravity ? ou setBall ? Comment nos actions de haut niveau comme lancer ou attraper changer ?

Utilisation de setGravity commande, je pourrais vous dire que la gravité est en fait deux fois plus forte que vous ne le pensez avant de vous dire de lancer la balle. À ce stade, vous mettriez à jour votre modèle du monde avant d'augmenter considérablement la force de votre lancer pour tenir compte du changement de gravité. Cependant, en réalité, la gravité n'a pas vraiment changé, donc à la place, vous renversez la balle.

Ce scénario est souvent ce qui se produit lorsque nous exposons des fonctionnalités de niveau inférieur qui ne déclenchent pas de mises à jour automatiques des propriétés dépendantes. Dans de nombreux cas, les systèmes sont très compliqués et la modification d'un paramètre sous-jacent entraîne la défaillance du système. En conséquence, nous essayons d'encapsuler les fonctionnalités pour couvrir toutes nos bases.

Classes définies par l'utilisateur

Jusqu'à présent, nous avons surtout parlé de la philosophie des modificateurs d'accès, mais quelles sont les conséquences réelles et comment les utilisons-nous réellement ? Pour aider à clarifier ces questions, prenons un moment pour écrire certaines de nos propres classes qui tentent de démontrer les différences pratiques entre public et private .

Hello World Revisited

Maintenant que nous avons vu quelques explications de haut niveau, revenons à notre exemple Hello World.

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
}

Ici, nous pouvons voir que nous utilisons le mot-clé public deux fois :une fois pour la classe et une autre fois pour la méthode principale. En d'autres termes, nous avons choisi d'exposer à la fois la classe HelloWorld et la méthode main au public.

Pour rendre les choses un peu plus intéressantes, enveloppons l'impression dans sa propre méthode privée :

public class HelloWorld {
  public static void main(String[] args) {
    printHelloWorld();
  }

  private static void printHelloWorld() {
    System.out.println("Hello, World!"); 
  }
}

Si nous essayons d'exécuter cette solution, nous remarquerons que le comportement n'a pas du tout changé. C'est parce que les méthodes privées peuvent être utilisées dans leur propre classe. En dehors de HelloWorld , cependant, personne ne sait printHelloWorld() existe même. En fait, on pourrait essayer d'appeler la méthode directement depuis une autre classe du même dossier, et on se retrouverait avec une erreur :

public class CallPrivateMethod {
  public static void main(String[] args) {
    HelloWorld.printHelloWorld();  // ERROR
  }
}

Comme nous pouvons le voir, nous avons caché la fonctionnalité d'impression, afin qu'elle ne puisse être utilisée que par le HelloWorld classer. Si pour une raison quelconque nous avons fait le printHelloWorld() public, nous pourrions très bien l'exécuter.

Essuie-glaces

Maintenant, allons plus loin dans ce concept en implémentant réellement les essuie-glaces en Java (au moins à un niveau élevé). Pour commencer, nous allons créer une classe de voiture qui a un private méthode pour un essuie-glace et un public méthode pour les deux essuie-glace :

public class Car {
    private boolean[] wipers;

    public Car() {
        this.wipers = new boolean[2];
    }

    private void turnOnWiper(int index) {
        this.wipers[index] = true;
    }

    public void turnOnWipers() {
        for (int i = 0; i < this.wipers.length; i++) {
            this.turnOnWiper(i);
        }
    }
}

Ici, nous avons créé une classe Car qui stocke un private tableau d'états de l'essuie-glace. Pour chaque balai, leur état est soit allumé (true ) ou désactivé (false ). Pour activer un essuie-glace, nous avons écrit un private méthode qui vous permet d'activer un essuie-glace par son index. Ensuite, on rassemble tout ça avec un public méthode qui parcourt tous les essuie-glaces et les active tous.

Maintenant, en ignorant le problème réaliste ici, à savoir que les essuie-glaces sont activés en série et non en parallèle, nous avons une solution assez solide. Si quelqu'un devait instancier une voiture, il ne pourrait activer que tous les essuie-glaces à la fois.

public class CarBuilder {
    public static void main(String[] args) {
        Car car = new Car();
        car.turnOnWipers(); // Turns on wipers!
        car.turnOnWiper(1); // Compilation ERROR
        car.wipers[0] = false; // Compilation ERROR
    }
}

Fait amusant :l'utilisateur ne sait même pas comment les essuie-glaces sont implémentés, nous avons donc le contrôle total pour modifier l'architecture sous-jacente à tout moment. Bien sûr, nous devons toujours fournir les mêmes fonctionnalités, mais la façon dont nous y parvenons dépend de nous. En d'autres termes, nous pourrions potentiellement changer le tableau d'essuie-glace pour stocker des entiers. Ensuite, pour chaque essuie-glace, l'entier serait corrélé à la vitesse.

Maintenant, pourquoi n'essayez-vous pas d'élargir la classe vous-même. Par exemple, je recommande d'ajouter une méthode pour désactiver les essuie-glaces. Vous voudrez peut-être ensuite écrire une nouvelle méthode privée pour désactiver les essuie-glaces individuels, ou vous trouverez peut-être plus logique de refactoriser le turnOnWiper méthode pour prendre également un booléen. Étant donné que l'utilisateur ne voit jamais ces méthodes, vous avez un contrôle total sur l'implémentation sous-jacente. Bon codage !

Ouvrir le forum

J'espère que cela vous aide à comprendre la différence entre le privé et public mots-clés et pourquoi nous les utilisons. Sinon, je suis ouvert à tous les commentaires et questions que vous pourriez avoir. N'hésitez pas à utiliser les commentaires ci-dessous pour entamer un peu de dialogue. Et si cela vous a aidé, partagez-le avec vos amis. J'apprécie toujours le soutien !


Balise Java