module | title | subtitle | language | compilation | build | noFormalities | noCleanRepo | noBonusDir | noErrorMess | author | version |
---|---|---|---|---|---|---|---|---|---|---|---|
B-INN-000 |
Introduction au Java |
Programmation Orienté Objet |
java |
javac, gradle, automated compilation with IDE |
gradle |
false |
false |
true |
true |
Alwyn |
1.0 |
-
Qu'est-ce que le Java ?
- Java est un langage de programmation orienté objet, typé, "compilé" qui fonctionne grâce à une Java Virtual Machine (JVM)
-
Qu'est-ce que la JVM ?
-
La JVM est une machine virtuelle qui permet l'interprétation du code java compilé (.class), et aussi des archives java (.jar)
-
C'est l'un des avantages du Java ! Grâce à la JVM le code est très portable, il peut être exécuté partout tant qu'une JVM est présente !
-
D'autres langages peuvent aussi fonctionner sur la JVM c'est le cas du Scala et du Kotlin
-
Si vous n'avez pas déjà installé les outils nécessaires à ce workshop, suivez les instructions situées ici : #br https://github.com/alwyn974/workshop-java/blob/master/REQUIREMENTS.md
#newpage
En java, on peut changer la visibilité d'une variable, d'une classe, d'une fonction, pour cela il faut utiliser des keyword spécifique
- Les modifieurs de visibilité
private
personne ne peut modifier/accéder à cette variable à part la classe actuelleprotected
uniquement les classes enfants ou appartenant au même package peuvent modifier/accéder à cette variablepublic
tout le monde peut modifier/accéder à cette variable
#hint(Par défaut une variable, classe ou fonction déclarée sans modifieur de visibilité est en privé)
- Les modifieurs d'accessibilité
static
une variable ou une fonction statique peut être accédée sans instancier la classe, elle est aussi unique à cette classefinal
pour faire simple, c'est l'équivalent duconst
en C/C++super
bon techniquement, ce n'est pas un modifieur d'accessibilité, au contraire il permet d'accéder à des méthodes/variables de la classe parente, il permet surtout d'appeler le constructeur de la classe parente
#warn(Les variables statiques sont toutes les mêmes peu importe l'instance de la classe)
#hint(Le keyword super est pratique lorsque l'on veut utiliser la méthode de la classe parente et y ajouter quelque chose, ou bien pour modifier une variable de la classe parente à la place d'une variable déclaré dans la classe enfant)
#newpage
Vu que le Java est basé sur le C++ et donc le C, les types restent à peu près les mêmes, pour certains ils sont améliorés ou ajoutés#br
boolean
permet de stocker les étatstrue
etfalse
(comme bool en C/C++)byte
permet de manipuler des entiers codés sur 1 octet (comme char)double
,int
,long
,float
,short
restent les mêmes- Vos objets à vous
#warn(Pour double
, int
, long
, float
, short
se sont des types primitifs, et non pas des objets.
Ils possèdent donc leur équivalent en tant d'objet Double
, Integer
, Long
, Float
, Short
)
enum
les enums en java sont beaucoup plus complets que ceux en C/C++, vu que ce sont des objets, on peut y assigner plus qu'un simple Integer comme valeurinterface
permet de créer une interfaceabstract class
permet de créer une classe abstraiteclass
permet de créer une classe normale
#newpage
L'héritage en java fonctionne comme en C++ à quelques différences près. On ne peut hériter que d'une seule classe, néanmoins on peut hériter de plusieurs interfaces
Exemple:
public class Animal {
public void eat() {
System.out.prinln("Animal can't eat");
}
}
public interface ILiveable {
void live();
}
public interface IMovable {
void move(int x, int y);
}
public class Cat extends Animal implements ILiveable, IMovable {
private final String name;
public Cat(String name) {
this.name = name;
}
//Overriden function from interface IMovable
@Override
public void move(int x, int y) {
System.out.println("X: " + x + " Y:" + y);
}
//Overriden function from interface IMovable
@Override
public void live() {
System.err.println("I'm dead bruh");
}
//Overriden method from Animal class
@Override
public void eat() {
System.out.println("I'm hungry !!!");
}
}
#hint(@Override est une annotation, elle permet de préciser qu'une méthode est override, cette annotation spécifique n'est pas obligatoire. On peut créer nos propres annotations aussi. Les annotations seront très utiles dans les prochains workshop)
Comme en C++ on peut surcharger une méthode avec différents types de paramètre tout en ayant le même nom
Exemple:
public class Main {
public static void main(String[] args) {
System.out.println("coucou"); //type of parameter is String
System.out.println(1); //type of parameter is Integer
System.out.println(0.0); //type of parameter is Double
System.out.println(0.0f); //type of parameter is Float
System.out.println(5 < 0); //type of parameter is Boolean
System.out.println(new Object()); //type of parameter is Object, print an Object call the method toString() of the object
}
}
Comme en C++, si l'on hérite d'une classe, d'une interface ou d'une classe abstraite, on peut surcharger les méthodes présentes dans la classe parente sauf si les méthodes ont le modifieur private
ou final
#hint(Contrairement au C++ on ne peut pas hériter de toutes les variables/méthodes d'une classe, uniquement des méthodes et variables public
et protected
)
C'est presque comme en C++, sauf qu'il faut utiliser new partout. #br En java, on ne s'occupe pas de la gestion mémoire, c'est le Garbage Collector (GC) de la JVM qui s'en occupe automatiquement
#warn(Il faut quand même penser à faire un code un minimum optimisé)
Exemple:
public class Main {
public static void main(String[] args) {
Object object = new Object(); //create object variable with Object type
String str = new String("Hey"); //create str variable with String type
String string = "toto"; //the good way to create a String
int[] array = new int[42]; //create array with int[] type
}
}
Les projets Java sont principalement générés par des builds tools, comme gradle
qu'on va utiliser dans ce workshop.
La structure reste plutôt basique un dossier src
où l'on mettra tout notre code, néanmoins en utilisant gradle dans ce dossier vous aurez un dossier test
et un dossier main
.
Le dossier test
est pour les tests unitaires et le dossier main
pour mettre votre code.
En Java pour organiser votre code, on va créer ce qu'on appelle des packages
, ce sont juste des sous dossiers qui seront dans src/main
. Cela permet d'avoir un code organisé selon des catégories. #br
Généralement un package se construit sous la forme d'un ndd (nom de domaine) inversé + le nom du projet et différents sous packages.
Exemple:
re.alwyn974.monsuperworkshop
re.alwyn974.monsuperworkshop.subpackage
re.alwyn974.monsuperworkshop.epitech.workshop
#warn(Il y a une convention de nommage pour les packages, ils doivent être en minuscules. On n'est pas obligé de respecter cette convention, mais c'est mieux)
En java si vous voulez avoir accès à d'autres classes, il vous faut utiliser les import
, l'import est l'équivalent d'un include de header contenant les prototypes en C/C++.
Exemple:
import re.alwyn974.monsuperworkshop.Marvin;
import re.alwyn974.monsuperworkshop.*;
import static re.alwyn974.monsuperworkshop.Marvin.sayHello;
import static re.alwyn974.monsuperworkshop.Marvin.*;
Il existe 2 types d'import :
- L'import static, permet d'accéder à des variables et méthodes statiques sans préciser le nom de la classe
- L'import normal, permet d'accéder à une classe et ses méthodes qui se trouvent dans un autre package
L'import avec wildcard *
permet d'importer toutes les classes d'un package ou toutes les méthodes/variables d'une classe avec l'import static.
#hint(En utilisant Intellij Idea et l'autocomplétion la plupart des imports se feront automatiquement, sauf en cas de conflits là il faudra choisir l'import spécifique. Les classes contenues dans le même package sont accessibles directement)
#newpage
En Java tout code doit être dans une classe/interface/enum. On ne peut pas déclarer une méthode hors d'une classe comme en C/C++.
Pour créer une classe/interface/enum, il y a une structure à suivre :
- On commence par
public
ou rien (dans ce cas la classe sera en privée) - Ensuite, on précise le type d'objet que l'on crée (
class
,interface
,enum
,abstract class
) - On précise le nom de l'objet
- Si on veut faire de l'héritage, on utilise
extends
et/ouimplements
pour les interfaces et on précise le nom de la classe parente - Et après on ouvre des accolades
Exemple :
public class Marvin {
//TODO: add code here
}
class ImNotAccessible {
//TODO: add code here
}
public interface ITakeALotOfSpace {
//TODO: add code here
}
public abstract class AbstractChooseAName {
//TODO: add code here
}
public enum EnumTest {
//TODO: add code here
}
#warn(Comme pour les packages, les noms d'objets doivent respecter une convention de nommage, pour les objets, on suit le PascalCase, qui consiste à mettre en majuscule la première lettre de chaque mot.)
#warn(Le nom d'un objet doit être aussi le même que le fichier Java correspondant)
#newpage
Comme en C++ on peut ajouter un constructeur à une classe. Par contre, il n'y a pas de destructeur. Pour créer un constructeur il y a une structure à suivre :
- On commence par un modifieur de visibilité (
public
,private
,protected
) ou rien (dans ce cas le constructeur sera en privé) - On précise le nom de la classe
- On précise les paramètres du constructeur
- Et enfin on ouvre des accolades
Exemple:
public class Marvin {
public Marvin(String name) {
}
}
public class PrivateConstructor {
private PrivateConstructor() {
}
}
public class ProtectedConstructor {
protected ProtectedConstructor() {
}
}
public class ClassWithFinalVariable {
final String toto;
public ClassWithFinalVariable(String toto) {
this.toto = toto;
}
}
#warn(Si vous avez déclaré des variables final
dans votre objet, sans les initialiser, il faudra les initialiser dans le constructeur)
#newpage
Pour créer une variable ou une méthode, il faut quelle soit dans une classe/interface/enum/abstract class. Il y a aussi certaines structures à suivre :
- On commence par un modifieur de visibilité (
public
,private
,protected
) ou rien (dans ce cas la variable sera en privée) - On précise le type de la variable (String, int, boolean, etc.)
- On précise le nom de la variable
- On peut assigner la variable à une valeur ou non
- On commence par un modifieur de visibilité (
public
,private
,protected
) ou rien (dans ce cas la méthode sera en privée) - On précise le type de retour de la méthode (String, int, boolean, etc.)
- On précise le nom de la méthode
- On précise les paramètres de la méthode
Exemples:
public class Main {
private static final String NAME = "Marvin";
private boolean isMarvin = true;
private int age = 42;
private void method() {
}
private String getName() {
return NAME;
}
private boolea isMarvin() {
return isMarvin;
}
}
#warn(Ici on suit la convention de nommage du camelCase
qui consiste à mettre en minuscule la première lettre du mot et en mettre une majuscule à chaque mot suivant.)
#warn(Une petite spécification pour les variables static final
on les écrit en SCREAMING_SNAKE_CASE
)
#newpage
Si vous venez d'installer Intellij Idea, cliquez sur New Project
sinon aller dans File > New > Project
#imageLeft(new_project.png, 200px, 9)
Choisissez le nom du projet (ex : WorkshopJava
)
Sélectionnez Java
Sélectionnez Gradle
Sélectionnez la version du JDK (11 si vous êtes sur le dump)
Dans Advanced Settings
mettez votre package dans GroupID
(ex : com.github.username.workshop
)
Dans ArtifactId
mettez le nom du jar (ex : WorkshopJava
)
Il faudra alors attendre la fin de la création du projet gradle (Quand le dossier src
apparait, c'est fini)
Pour lancer un projet Java, à coter du main
vous aurez un petit logo de lancement, il suffit de cliquer dessus et c'est parti !
Cela va créer une configuration sur Intellij pour lancer le programme avec ce main
précisément, car en Java on peut avoir plusieurs mains et en choisir un en lançant un .jar
Si vous voulez générer un jar avec gradle il suffit de faire gradle build
ou ./gradlew build
. Le jar sera généré dans le dossier build/libs
Vous pourrez le lancer avec java -jar nomdufichier.jar
.
Vous aurez remarqué qu'en faisant cette commande il y a une erreur, c'est tout simplement, car aucun main n'a été précisé lors de la compilation #br
Pour résoudre ce problème, il suffit de mettre le main
à utiliser dans le build.gradle
:
Ajoutez donc ce bloc à la fin du build.gradle
jar {
manifest {
attributes "Main-Class" : "re.alwyn974.monsuperworkshop.Main"
}
}
#newpage
- Créez votre propre package (ex:
com.github.username.workshop
) - Dans ce package, créez une classe
HelloWorld.java
- Créez une méthode main en
public static
et en prenant en argument unString... args
- Trouvez comment print un
Hello World !\\n
#hint(Concernant le paramètre que prend le main, le main peut prendre soit String... args
, soit String[] args
.
Les méthodes statiques ne peuvent accéder qu'à des variables statiques et des méthodes statiques, sans utiliser d'instance)
#terminal(Lancement avec intellij, parce que c'est plus rapide Hello World!)
Fichier : FizzBuzz.java Spécification : Contient un main #br
Faites une boucle qui va de 1 => 200 et pour chaque nombre suivez ces instructions :
- Si le nombre est divisible par 3 : on écrit Fizz
- Si le nombre est divisible par 5 : on écrit Buzz
- Si le nombre est divisible par 3 et par 5 : on écrit Fizzbuzz
- Sinon, on écrit le nombre
#terminal(Lancement avec intellij, parce que c'est plus rapide 1 2 3 -> Fizz 4 5 -> Buzz 6 -> Fizz 7 8 9 -> Fizz 10 -> Buzz 11 12 -> Fizz 13 14 15 -> Fizzbuzz)
Fichier : GuessANumber.java Spécification : Contient un main #br
Vous connaissez Guess a number
? C'est un jeu de devinette. Ou vous devez trouver le nombre qui a été créé aléatoirement, à l'aide d'indication du programme si votre nombre est inférieur ou supérieur au nombre créé.
Vous allez devoir le recréer, en faisant vos propres recherches.
Il faudra récupérer la valeur minimum et maximum en argument, si un argument n'est pas mis il faudra alors mettre en valeur par défaut: 1 et 100
Il faudra aussi afficher le nombre de tentatives à la fin du jeu.
#terminal(Lancement avec intellij, parce que c'est plus rapide Your guess? 50 Too low! Your guess? 100 Too high! Your guess? 60 Too low! Your guess? 70 Too high! Your guess? 65 Too low! Your guess? 69 Too high! Your guess? 67 Too low! Your guess? 68 You win! It took you 7 tries.)
#hint(Utilisez la classe Scanner et Random du package java.util)
#hint(Une petite doc pour ajouter les arguments sur la configuration Intellij Idea : Configuration)
#newpage
Fichier: Fibonacci.java Spécification: Contient un main #br
Réimplémenter la suite de Fibonacci :
- En récursive (
recursiveFibonacci(int n)
) - En itérative (
iterativeFibonacci(int n)
) - Récupérer la valeur de n en argument dans le main, sinon mettre 10 par défaut
#terminal(Lancement avec intellij parce que build, c'est long. Suite de Fibonacci de 10: Recursive: 55 Iterative: 55 )
#hint(Lien vers la suite de Fibonacci)
Sur les prochains exercices, on va faire un peu d'héritage et utiliser chaque type d'objet.
Pour cela vous allez créer un sous package inheritance
#br
On va commencer par créer une class AbstractVehicule
qui sera abstraite et devra contenir :
- Une méthode publique
void move()
qui sera abstraite - Une variable privée et finale
String name
qui sera le nom du véhicule et aura un Getter - Créer un constructeur protégé qui prendra
String name
en paramètre et initialisera la variable finalename
- Une méthode publique
double getSpeed()
qui sera abstraite - Une surcharge de la méthode
String toString()
qui retournera le nom du véhicule et sa vitesse
#terminal(La méthode toString() devrait renvoyer quelque chose comme ça: AbstractVehicule: {name: UnNomRandom, speed: 0} )
#newpage
On va créer un petit enum VehiculeType
pour notre classe AbstractVehicule
#br
L'enum VehiculeType
devra contenir :
- Un constructeur qui prendra une
String
en argument et qui sera le type du véhicule. - Une
String
en privée et finale qui sera le nom du type de véhicule - Un getter
String getType()
qui retournera le type du véhicule #br Comme type de véhicule, il nous faudra au moins 3 types de véhicule différents : - Car
- Plane
- Boat
#br
Maintenant, il faut modifier la classe abstraite
AbstractVehicule
pour qu'elle contienne un getter abstraitVehiculeType getType()
#br
Maintenant qu'on a un type de Véhicule, ce serait bien d'ajouter un peu de couleur non ?
On va donc modifier notre classe AbstractVehicule
pour ajouter :
- Une variable privée de type
Color
avec un getter/setter - Ajouter un autre constructeur de
AbstractVehicule
qui prendra le nom et la couleur en paramètre toujours en protected - Modifiez aussi la méthode
toString()
pour maintenant afficher la couleur en plus !
#hint(Vous connaissez sprintf ? Trouvez l'équivalent en Java pour facilité la méthode toString())
Créer 3 sous packages à inheritance
: fly
, drive
, floaty
Dans chaque package correspondant créer une interface IFlyable
, IDriveable
et IFloatyable
:
IFlyable
devra contenir une méthode publiquevoid fly()
IDriveable
devra contenir une méthode publiquevoid drive()
IFloatyable
devra contenir une méthode publiquevoid floaty()
#newpage
Créer 3 classes qui hériteront de AbstractVehicule
et d'une interface IFlyable
, IDriveable
et IFloatyable
:
Car
,Plane
,Boat
- Chaque constructeur prendra le nom, la vitesse et la couleur en paramètre et sera publique
- Déclarer une variable protégée pour la vitesse
- Dans chaque méthode abstraite implémentée, afficher un message correspondant dans la console
- La méthode
getType()
devra retourner le type de véhicule correspondant
#terminal(Exemples: move => "Moved" drive => "Driving" fly => "Flying" floaty => "Floating")
Fichier: Main.java #br
Créer une méthode qui prendra en paramètre un véhicule et qui affichera son nom, son type, sa couleur et sa vitesse (showVehicule
)
- Faites une liste d'
AbstractVehicule
et ajouter une dizaine de véhicules dans cette liste. (Différents Véhicules) - Pour chaque élément dans la liste, appeler la méthode
showVehicule
- Afficher le nombre de véhicules dans la liste
- Afficher le nombre de véhicules ayant le type
Car
- Afficher le nombre de véhicules ayant le type
Plane
- Afficher le nombre de véhicules ayant le type
Boat
#terminal(Exemple: Name: HydroJet Color: java.awt.Color[r=255,g=0,b=0] Speed: 300.0 Type: PLANE ... Vehicules: 10 Cars: 4 Planes: 3 Boats: 3)
#hint(Javadoc de l'interface List : lien)
Vous avez sûrement utilisé getType()
pour compter le nombre de véhicules de chaque type. On va utiliser l'équivalent du dynamic_cast
(C++) maintenant. (Uniquement pour vérifer le type d'une classe)
Vous allez devoir créer 3 listes de véhicules différentes :
- Une liste de
Car
- Une liste de
Plane
- Une liste de
Boat
En utilisant la liste d'avant, vous devez ajouter les véhicules de chaque type dans les listes correspondantes. #br En fonctions des listes, appeler la méthode correspondante à chaque type. (Méthode de l'interface)
#hint(Je vous laisse chercher l'équivalent du dynamic_cast. C'est un keyword spécifique à Java)
#terminal(Exemple: Driving Driving Driving Driving Floaty Floaty Floaty Flying Flying Flying)
#hint(Psst regardez les Stream
de java 8: lien)
#newpage
Comme en C++ le Java possède des exceptions. On va donc créer une Exception pour l'enum VehiculeType
.
Créez une exception : VehiculeTypeNotFound
qui sera dérivée d'exception et devra prendre un paramètre String message
Ajoutez une méthode fromString
qui prendra en paramètre une String
, qui retournera un VehiculeType
et sera publique et statique
Si la String
ne correspond à aucun type de véhicule, la méthode devra lancer l'exception qu'on vient de créer
Testez votre code voir si l'exception est bien lancée en cas d'erreur.
#hint(Javadoc de l'exception : lien
Javadoc d'Enum : lien, psst, regardez la méthode values()
N'oubliez pas d'ajouter throws
à la méthode)
#warn(Interdiction de faire des conditions pour chaque type.)
Java dispose de plusieurs bibliothèques pour faire des tests unitaires, on va utiliser la plus connue: JUnit
Dans le dossier src/test/
recréer votre package, mais en rajoutant test
à la fin
On va tester le package inheritance.
Créez une classe InheritanceTest
Importez statiquement toutes les méthodes de classe Assertions
de junit.
Syntaxe d'un test unitaire :
public class InheritanceTest {
@Test
public void testVehiculeTypes() {
assertEquals("Car", VehiculeType.CAR.getType()); //assertEquals is a static method from "Assertions"
}
}
Tester l'égalité entre différent String et le type d'un véhicule. Faites-le pour chaque type de véhicule.
Il faudra aussi tester la non-égalité entre un String et un type de véhicule.
#newpage
Maintenant, on va tester notre méthode fromString
. Testez-la avec différents types de véhicule.
Des types valides et non valides.
Il faudra tester avec la méthode spéciale de JUnit pour gérer les exceptions !
#hint(Toutes les méthodes d'assertions de JUnit : lien)
Fichier: WorkshopTest.java #br
On va tester notre méthode itérative et récursive de Fibonacci. Testez que chaque méthode renvoie les mêmes valeurs pour les nombres de 0 à 20.
Fichier: FizzBuzzTest.java
On va aussi pouvoir tester la sortie standard System.out
, pour faire cela il faudra :
- Créer une variable finale
ByteArrayOutputStream
qui sera le nouveau flux de sortie standard - Créer une variable finale
ByteArrayOutputStream
qui sera le nouveau flux de sortie d'erreur - Créer une variable finale
PrintStream
qui sera l'ancien flux de sortie standard - Créer une variable finale
PrintStream
qui sera l'ancien flux d'erreur
Vous allez devoir changer le flux de sortie standard et d'erreur pour que les tests unitaires puissent fonctionner, il faudra avant chaque test et le restauré après.
Vous allez pouvoir tester l'output que produit le Main de FizzBuzz. (Oui, oui, on peut tester un main directement)
#hint(Cherchez l'annotation qui vous facilitera la vie ici: Javadoc JUnit)
#newpage
Beaucoup de site propose des cours sur le Java, en voici quelques un :
Merci d'avoir suivi ce workshop, n'hésitez pas à me contacter si vous avez des questions ou des remarques.