Un survol de java

Cette page propose une première approche de java, sans être exhaustif.

Un premier programme java

Un programme java est constitué de classes, par exemple :
/** 
    Premier exemple
**/
public class HelloWorld {

    /**
        Point d'entrée du programme
        @param  args Tableau de chaînes passé au programme
    **/
    public static void main(String[] args) {
        System.out.println("Hello World!"); // Affiche une chaîne de caractères
    }
}
Ce code contient plusieurs choses : C'est à l'intérieur des méthodes qu'on écrit le code java qui va être exécuté. Un programme java va être une suite d'instructions, qui vont être exécutées séquentiellement (l'une après l'autre).

A retenir :

Exécution

Avant d'exécuter ce programme, on doit le compiler : on se place dans le répertoire contenant HelloWorld.java et on le compile :
javac HelloWorld.java
Cette opération produit un nouveau fichier, HelloWorld.class, qui est exécutable.
On peut maintenant l'exécuter :
java HelloWorld
Bien noter qu'on utilise un premier outil (javac) pour compiler un fichier, et qu'on utilise un autre outil (java) pour exécuter une classe (et non pas un fichier).

Manipuler des variables

Pour faire des choses plus élaborées, on va pouvoir créer des variables et leur affecter des valeurs.

En java, toute variable qu'on utilise doit être déclarée et associée à un type, par exemple :
int a;
String s;
double d;
boolean b;
Une fois qu'une variable est déclarée, on peut lui affecter une valeur :
a = 17;
s = "Bonjour";
b = true;
On peut déclarer et donner une valeur initiale à une variable dans une seule instruction :
int a = 17;
double d = 45.87;
Les principaux types disponibles sont :
boolean Peut prendre 2 valeurs : true ou false
int Nombre entier - il existe aussi d'autres types : byte, short, long
double Nombre à virgule - il existe aussi le type float
String Représente une chaîne de caractères
Une fois qu'une variable est déclarée, on peut lui affecter des valeurs différentes en utilisant des opérateurs.
int a = 17;
a = a + 3; // maintenant a vaut 20

String nom = "toto";
String affichage;
affichage = "Bonjour " + nom;
Les opérateurs disponibles dépendent du type de la variable.
OpérateursTypes concernésCommentaire
+
-
*
/
Nombres Opérateurs arithmétiques habituels
% Nombres entiers Modulo : reste de la division d'un nombre par un autre.
4 % 3 = 1
+ String Concaténation
A noter, deux raccourcis syntaxiques souvent utilisés :
++ i++ signifie i = i + 1
+= i+=3 signifie i = i + 3
Les mêmes raccourcis existent aussi avec - (l'opérateur moins).

Effectuer des tests

Deux mots-clés du langages permettent de tester des conditions : if et else.
if(une condition){
    faire des choses
}
else {
    faire autre chose
}
Les conditions sont exprimées par des expressions ayant une valeur de type boolean (expressions booléennes).
boolean a = true;
if(a) { ... }
Les expressions booléennes utilisées dans les tests sont souvent le résultat d'opérateurs de comparaison :
== Renvoie true si deux variables sont égales, false sinon.
!= Renvoie true si deux variables sont différentes, false sinon.
>
>=
<
<=
int a = 10, b = 12;
if(a == b) {
    System.out.println("a et b sont égaux");
}
else {
    System.out.println("a et b sont différents");
}
Les instructions if et else peuvent s'enchaîner :
int a = 10, b = 12;
if(a == b) {
    System.out.println("a et b sont égaux");
}
else if(a > b) {
    System.out.println("a est supérieur à b");
}
else {
    System.out.println("a est inféreieur à b");
}
Elles peuvent aussi s'emboîter :
int a = 10, b = 12;
if(a == b) {
    System.out.println("a et b sont égaux");
}
else {
    if(a > b){
        System.out.println("a est supérieur à b");
    }
    else {
        System.out.println("a est inféreieur à b");
    }
}
Les conditions (expressions booléennes) peuvent être combinées avec des opérateurs booléens.
&& et and Evalué à true si les deux opérandes sont évaluées à true.
if(12 > 10 && "a" < "b"){ ... }
|| ou or Evalué à true si une des deux opérandes sont évaluées à true.
if(12 > 10 || "a" > "b"){ ... }
! négation not Evalué à true si l'opérande est évaluée à false.
if(!(12 > 14)){ ... }
Exercice Test de l'âge

Ecrire des boucles

On peut demander d'exécuter certaines instructions plusieurs fois.
L'instruction for est la plus fréquemment utilisée.
for(int i=0; i < 10; i++){
    System.out.println(i);
}
Signification :
for(initialisation; condition d'arrêt; update){ ... }
Exercice : Ecrire un programme qui affiche les nombres pairs, de 2 à 20.
On peut aussi écrire des boucles de deux autres manières :
int i = 0;
while(i < 10){
    System.out.println(i);
    i++;
}
int i = 0;
do{
    System.out.println(i);
} while(i < 10)
Deux instructions importantes permettent de modifier le cours d'une boucle : break et continue.

break

break permet de sortir de l'instruction qui la contient.
for(int i = 0; i < data.length; i++) {
    if (data[i] == target) {
        index = i;
        break;
    }
} // l'interpreteur arrive ici après le break

continue

Passe à l'itération suivante de la boucle
for(int i = 0; i < data.length; i++) {
    if(data[i] == false){
        continue;
    }
    process(data[i]);
}
Exercice Multiples1

Créer et appeler des méthodes

La très grande majorité du code qu'on écrit se trouve dans des méthodes (terminologie objet pour désigner une fonction).
Les méthodes se trouvent à l'intérieur de classes, comme la méthode main() qu'on a déjà vu.
Au sein d'une classe, une méthode doit avoir une signature unique. Voir page Méthodes.

Application : L'exemple HelloWorld pourrait être modifié pour déporter l'affichage dans une méthode :
Voir l'exemple HelloWorld2.java
Une variante pourrait passer en agrument la chaîne à afficher.
Voir l'exemple HelloWorld3.java
Exercice : Reprendre l'exercice Test de l'âge et écrivez une méthode qui effectue le test.
En entrée, elle prend en paramètre un int.
En sortie, elle renvoie un boolean.

Utiliser des tableaux

Voir page Tableaux
Exercice Max et moyenne
Exercice : Voitures
Exercice : Nombres premiers

Créer des objets

Membres de classe

Jusqu'à présent, on a créé des classes qui ne contenaient que des méthodes de classe (déclarées static).
Travailler uniquement avec des méthodes static revient finalement à programmer de manière classique (impératif non-objet), et les classes fournissent juste un moyen commode de "ranger" les méthodes.
class HelloWorld2 {
    
    public static void main(String[] args) {
        echo();
    }
    
    public static void echo() {
        System.out.println("Hello World!");
    }
    
}
Ici, main() ou echo() sont des méthodes static de la classe HelloWorld2.
Pour les appeler depuis l'extérieur de la classe, on utilise le nom de la classe, suivi d'un point, suivi de l'appel de la méthode :
class TestHelloWorld2() {
    public static void main(String[] args) {
        HelloWorld2.echo();
    }
}

Variable de classe

De la même manière qu'une classe peut avoir des méthodes, elle peut aussi avoir des variables :
class HelloWorld2 {
    
    public static uneVariable = 4;

    public static uneAutreVariable;

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

class TestHelloWorld2() {
    public static void main(String[] args) {
        HelloWorld2.echo();
        System.out.println(HelloWorld2.uneVariable);
    }
}
Les méthodes et les variables d'une classe sont appelés les membres de la classe.

Objets

Une classe peut aussi être vue comme un patron, une template pour créer des objets de cette classe :
public class Chat {
    
    // variables non static
    // Ont une valeur différente pour chaque objet
    private String nom;
    private int age;
    
    // constructeur
    public Chat(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
    
    // Méthode non static
    // A un résultat différent pour chaque objet
    public String direBonjour() {
        return "Bonjour, je m'appelle " + this.nom + ", j'ai " + this.age + " ans";
    }
}

Les membres non static de la classe sont les variables et méthodes d'instance, qui vont dépendre d'un chat en particulier.
La valeur des variables d'instance constituent l'état d'un objet.
Les méthodes d'instance définissent le comportement de l'objet.

On peut affecter une valeur initiale aux variables de classe ou d'instance.
public class Chat {
    private String nom = "?";
}
Les variables de classe ou d'instance qui n'ont pas encore reçu de valeur ont une valeur par défaut ("" pour String, 0 pour les nombres...).

A noter: une classe peut à la fois avoir des membres d'instance (non static) et d'instance (static).

Constructeur, mot-clé new

Pour créer un nouveau chat, on introduit une méthode particulière, un constructeur. C'est une méthode qui a exactement le même nom que la classe qui le contient.

Pour créer un chat, on utilise le mot-clé new :
public class TestChat {
    public static void main(String[] args) {
        Chat minou = new Chat("Minou", 3);
    }
}
On dit qu'on a instancié la classe Chat.
La variable minou représente une instance de la classe Chat.

Une classe peut avoir plusieurs constructeurs, qui diffèrent par leurs paramètres.
public class Chat {
    private String nom;
    private int age;
    
    public Chat(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
    
    // en utilisant ce constructeur, l'âge n'est pas initialisé,
    // this.age = 0 (valeur par défaut)
    public Chat(String nom) {
        this.nom = nom;
    }
}
A l'utilisation :
Chat c1 = new Chat("chat 1", 2);
Chat c2 = new Chat("chat 2");
Le compilateur peut déterminer quel constructeur appeler, en se basant sur les paramètres utilisés.

Mot-clé this

Lorsqu'on se trouve dans le code de la classe, à l'intérieur d'une méthode d'instance (non static), on peut désigner l'objet courant en utilisant le mot-clé this.

L'emploi de ce mot-clé est optionnel, sauf pour résoudre une ambiguïté :
    // constructeur
    public Chat(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
Ici, le compilateur doit pouvoir faire la différence entre la variable locale passée en paramètre, nom, et la variable d'instance nom. Donc ici, this est obligatoire.

Mais on aurait pu écrire aussi :
    // constructeur
    public Chat(String nomDuChat, int ageDuChat) {
        nom = nomDuChat;
        age = ageDuChat;
    }
Ici, pas d'ambiguïté donc this est facultatif, on peut écrire indifféremment nom ou this.nom pour référer à la variable d'instance nom.

Visibilité

On peut décider de la visibilité des membres d'une classe, en les déclarant (entre autres) public ou private. La possibilité de rendre privés les membres d'une classe s'appelle l'encapsulation (voir cette page).

Dans la classe Chat, les variables d'instance nom et age ont été déclarées private, donc on ne peut pas les utiliser à l'extérieur :
public class TestChat {
    public static void main(String[] args) {
        Chat minou = new Chat("Minou", 3);
        // === NON : la ligne suivante génère une erreur de compilation ===
        System.out.println(minou.nom);
    }
}
On souvent intérêt à réduire le plus possible la visibilité des membres d'une classe.
Par exemple pour la classe Chat, si l'âge était public, on pourrait écrire :
public class Chat {
    // ...
    public int age;
    // ...
}

public class TestChat {
    public static void main(String[] args) {
        Chat minou = new Chat("Minou", 3);
        minou.age = -3;
    }
}
Déclarer que l'âge est privé permet de contrôler la cohérence de l'état d'un objet.

Getters, setters

Afin de garder privées les variables d'une classe tout en permettant d'y accéder, on définit souvent des méthodes pour les lire (getter) et les modifier (setter).
public class Chat {
    
    private int age;
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        if(age < 0) {
            // Envoyer une exception
        }
        this.age = age;
    }
}
Exercice : Personnes

Travailler avec des packages

Voir page "Packages, application java