Agile Software Craftsman
Software Anarchist
blog : https://blog.crafting-labs.fr
twitter : @avernois
Vous attendez quoi de cette formation ?
4.0.0
fr.craftinglabs.training
my-app
jar
1.0-SNAPSHOT
My awesome project
my-app
+- pom.xml
+- src
+- main
| +- java
| +- resources
+- test
+- java
+- resources
junit
junit
4.11
test
3 types de scope
Note: avoir son propre repository manager en interne peut être une bonne idée :)
Il y en a bien d'autres : Doc Maven
Remplacer un variable (ex: ${nom.variable}
) par sa valeur définie dans le pom.
src/main/resources
true
${my.properties}
hello
4.0.0
fr.craftinglabs.training
app
1.0-SNAPSHOT
pom
my-app
my-webapp
+- pom.xml // le pom parent
+- my-app
| +- pom.xml
| +- src
| +- main
| +- java
+- my-webapp
| +- pom.xml
| +- src
| +- main
| +- webapp
Dans les poms des modules, on rajoute un lien vers le pom parent
fr.craftinglabs.training
app
1.0-SNAPSHOT
Iterable
est l'interface des Collection
public interface Iterable<T> {
Iterator<T> iterator();
}
public interface Iterator<T> {
boolean hasNext();
T next();
void remove(); // à implémenter uniquement si on le supporte
}
Iterable<AType> iterable = .. ;
for(AType item : iterable) {
doThings();
}
String anElement = "an element";
Collection collection = new HashSet();
boolean collectionChanged = collection.add(anElement);
boolean elementRemoved = collection.remove(anElement);
collection.addAll(aSet); // aSet is a Set :)
collection.removeAll(aSet);
collection.retainAll(aSet); // ne garde dans collection que les éléments présent dans aSet.
List<String> listA = new ArrayList<>();
listA.add("element 1");
listA.add("element 2");
listA.add(0, "element 0"); // qu'est ce qui se passe ?
listA.remove(0);
listA.remove("element 1");
Set<String> setA = new HashSet<>();
setA.add("element 0");
setA.add("element 1");
setA.add("element 2");
setA.remove("element 0");
Un type de Set trié automatiquement.
SortedSet<String> setA = new TreeSet<>();
Comparator<String> comparator = new MyComparator<>();
SortedSet<String> setB = new TreeSet<String>(comparator);
NavigableSet<String> original = new TreeSet<>();
original.add("1");
original.add("2");
original.add("3");
original.add("4");
NavigableSet<String> reverse = original.descendingSet();
NavigableSet<String> headset = original.headSet("3"); // contient 1 et 2
NavigableSet<String> subset = original.subSet("1", "3"); // contient 1, et 2
Comme une liste, sauf que l'objectif est d'ajouter les élements à la fin, et de les rétirer au début.
Queue<String > queue = new LinkedList<>();
queue.add("element 0");
queue.add("element 1");
queue.add("element 2");
queue.remove(); // retire et retourne "element 0"
queue.peek(); // retourne "element 0" sans le retirer
Comme une Queue, sauf qu'on peut y accéder des deux côté
DeQue<String > dequeue = new LinkedList<>();
deque.add("element 1"); // ajout à la fin
deque.addFirst("element 0"); // ajout au début
deque.addLast("element 2"); // ajout à la fin
deque.remove(); // retire et retourne "element 0"
deque.peek(); // retourne "element 0" sans le retirer
Une stack est une implémentation de List, dont l'objectif est d'être une LIFO.
Stack<String> stack = new Stack<>();
stack.push("1");
stack.push("2");
stack.push("3");
String top = stack.peek(); // retourne "3" sans le retirer
int index = stack.search("3"); // retourne 1 ("3" est en tête)
// yep, l'index des stack commence à 1 :)
stack.pop(); //retourne "3" et le retire
stack.pop(); //retourne "2" ...
stack.pop(); //retourne "1"
Map<Integer, String>> map = new HashMap<>();
map.put(1, "1");
String value = map.get(1);
Set<Map.Entry<K, V>> entries = map.entrySet();
Set< keys = map.keySet();
Collection<V> values = map.values();
Comme les SortedSet, mais avec des Map :)
idem
Pour que equals() et contains() fonctionne comme on peut s'y attendre sur les listes, il est indispensable de que equals et hashcode des objets contenus soit correctement défini !
equals et hashcode doivent toujours être surchargé ensemble !
Collections.sort(List) ou List.sort depuis 1.8
List<String> list = Arrays.asList("1", "3", "2");
Collections.sort(list); // tri selon l'ordre naturel
Comparator<String> comparator = new MyStringComparator();
Collections.sort(list, comparator);
list.sort(comparator); // 1.8
public interface Comparator<T> {
int compare(T object1, T object2);
}
public class MyStringComparator implements Comparator<String> {
public int compare(String s1, String s2){
int compare;
// si s1 > s2, compare > 0
// si s1 == s2, compare = 0
// si s1 < s2, compare < 0
return compare;
}
}
public interface StateChangeListener {
public void onStateChange(State oldState, State newState);
}
--
public class StateOwner {
public void addStateListener(StateChangeListener listener) { ... }
}
--
StateOwner stateOwner = new StateOwner();
stateOwner.addStateListener(new StateChangeListener() {
public void onStateChange(State oldState, State newState) {
System.out.println("State changed")
}
});
stateOwner.addStateListener(new StateChangeListener() {
public void onStateChange(State oldState, State newState) {
System.out.println("State changed")
}
});
devient
stateOwner.addStateListener(
(oldState, newState) -> System.out.println("State changed"));
On passe par les streams pour relier les deux.
List<String> items = new ArrayList<String>();
items.add("un");
items.add("deux");
items.add("trois");
Stream<String> stream = items.stream();
Ensuite la manipulation se passe en deux phases :
stream.filter(item -> item.startWith("u"));
stream.map(item -> item.toUpperCase());
List<String> strings = stream.map(item -> item.toUpperCase())
.collect(Collectors.toList());
String reduced = items.stream()
.reduce((acc, item) -> acc + " " + item)
.orElse("");
String shortest = items.stream()
.min(Comparator.comparing(item -> item.length()))
.orElse("");
Class helloClass = HelloWorld.class;
Method[] methods = helloClass.getMethods();
for(Method method: methods) {
System.out.println(methods.getName());
}
Class helloClass = HelloWorld.class;
String fullClassName = helloClass.getName(); // fully qualified
// sans le package
Sring simpleClassName = helloClass.getSimpleName();
int modifiers = helloClass.getModifiers();
// les modifiers sont encodés dans un int.
// il faut passer par Modifier pour le décoder
Modifier.isPublic(modifiers);
Modifier.isFinal(modifiers);
...
Constructor[] constructors = helloClass.getConstructors();
//le constructeur avec une String en paramêtre
Constructor<HelloWorld> constructor = helloClass.getConstructors(new Class[]{String.class});
HelloWorld hello = constructor.newInstance("myArgs");
Field[] fields = helloClass.getFields();
Field field = helloClass.getField("myField");
Class fieldType = field.getType();
HelloWorld hello = new HelloWorld();
field.get(hello);
field.set(hello, value);
//pour un champ static
someStaticField.set(null, value);
Method[] methods = helloClass.getMethods();
// si HelloWorld a une methode myMethodName(String param);
Method method = helloClass.getMethod("myMethodName", String.class);
Class returnType = method.getReturnType();
Class[] paramTypes = method.getParameterTypes();
method.invoke(hello, "first param");
Les méthodes getFields, getMethods précédentes ne retourne que les méthodes publiques. Pour toutes les avoir, il faut passer par:
helloClass.getDeclaredMethods();
helloClass.getDeclaredMethod("myPrivateMethodName", String.class);
helloClass.getDeclaredFields();
helloClass.getDeclaredField("myPrivateField");
Pour invoquer une méthode privée ou changer une valeur d'un champ privé, il faut d'abord le rendre visible
aPrivateField.setAccessible(true);
aPrivateMethod.setAccessible(true);
Array
int[] array = (int[]) Array.newInstance(int.class, 5);
Array.set(array, 0, 42);
Array.get(array, 0):
String[] strings = new String[3];
Class stringArrayClass = strings.getClass();
Class stringArrayComponentType = stringArrayClass.getComponentType();
Toutes les classes sont chargées par une instance de ClassLoader (pas forcemment la même pour toutes les classes).
Les différent ClassLoader d'une application sont organisé de façon hierarchique.
public class MyClass {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classLoader = MyClass.class.getClassLoader();
Class clazz = classLoader.loadClass("fr.craftinglabs.training.java.ToLoad");
}
Vu que par défaut, un ClassLoader ne recharge pas une classe qu'il a déjà chargée, pour faire du reloading, il faut créer son propre ClassLoader
Sauf que. Une même classe, chargée par deux ClassLoader différents est vu comme deux classes distinctes (et non compatible).
La réflexion permet de créer dynamiquement des implmentations d'interface.
//le truc vers qui seront transférer tous les appels
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[] { MyInterface.class }, // la liste des interfaces à implémenter
handler);
public interface InvocationHandler{
Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Vous voyez quatre carte
___ ___ ___ ___ | | | | | | | | | D | | 7 | | 5 | | K | |___| |___| |___| |___|
Sur chaque carte, il y a un chiffre d'un côté, une lettre de l'autre
.Quelle(s) carte(s) faut-il retourner pour savoir si la règle suivante est vérifiée :
S'il y a un D d'un côté, alors il y a 5 de l'autre ?
Quatres personnes sont dans un bar. Vous savez :
Quelle(s) personne(s) faut-il intéroger pour savoir si la règle suivante est vérifiée :
Si un personne boit de l'alcool, elle doit avoir plus de 18 ans ?
@Author(name="Antoine", date="2016/04/04")
public class MyAnnotatedClass {
@MyAnnotation
public void myAnnotatedMethod(@MyParamAnnotation String param) {
...
}
}
public @interface Author {
String name() default "Antoine";
String date();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CLASS})
@Inherited
public @interface Author {
String name() default "Antoine";
String date();
}
...
Class classe = MyAnnotatedClass.class;
Author author = (Author) classe.getAnnotation(Author.class);
if (author != null) {
System.out.println("Author:" + author.name());
}
...
Pour cela il faut créer un Processor qui s'occupera de gérer notre annotation
AbstractProcessor fait une partie du boulot pour vous.
@SupportedAnnotationTypes({"fr.craftinglabs.training.java.annotation.Print"})
public class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
//process your annotation here
return true;
}
}
On précise les annotation supportées par ce processor via une annotation
@SupportedAnnotationTypes({"fr.craftinglabs.training.java.annotation.Print"})
Pour être utilisé, il faut déclarer le processor. Il faut créer une fichier
META-INF/services/javax.annotation.processing.Processor
Il doit contenir le nom des classes de processor.
lecture d'un ou plusieurs octets depuis un stream.
pas de mise en cache.
impossible de remonter le flux.
lecture depuis un buffer que l'on accède comme on veux.
le buffer contient il assez d'info pour être pertinent ?
le buffer courant est écrasé à chaque lecture.
les opérations d'écriture/lecture sont non bloquantes. Il est donc possible de gérer des sources simultanés depuis un unique thread.
Peu de connexions.
Beacoup de données.
Beaucoup de connexionx
Peu de données.
Java 7 simplifie grandement l'utilisation de NIO pour la manipulation de fichier via Files et FileSystem.
Pendant longtemps, la gestion des dates/times en java était un enfer.
Depuis Java 7 et la JSR310, les choses se sont améliorées.
Toutes les objets Date/Time sont immutables.
Local* représentent la moment du point de vue de celui qui la demande. Il n'y a aucune info de timezone. C'est l'heure/date, à l'endroit où le système s'exécute.
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.parse("2015-12-14T16:12:17");
LocalTime time = LocalTime.of(17,18);
time.getHour();
date.getMonth(); retourne un enum
date.getMonthValue();
date.getDayOfYear();
LocalDateTime another = dateTime.withDayOfMonth(15).withYear(2016);
LocalDateTime yetAnother = dateTime.plusWeeks(3).plus(4, WEEKS);
// import static java.time.temporal.TemporalAdjusters.*;
LocalDateTime ldt = dateTime.with(lastInMonth(TUESDAY));
ZonedDateTime zoned = ZonedDateTime.of(dateTime, id);
ZonedDateTime.parse("2016-04-14T22:30:30+02:00[Europe/Paris]");
OffsetDateTime time = OffsetDateTime.now(ZoneId.of("Europe/Paris"));
time = time.withOffsetSameInstant(ZoneOffset.of("+04:00"));
Par défaut, la JVM utilise les ZoneId défini par l'IANA. Il en existe 2 autres : les ZoneId de l'IATA (basé sur les code aéroport) et ceux de Microsoft.
La config semble se faire du côté de ZoneRulesProvider
La plupart des méthodes qui manipulent des String peuvent prendre un Charset en paramètre.
JPA est une spécification dont l'objectif est d'uniformiser l'accès aux ORM.
Il s'agit d'un ensemble d'interface du package javax.persistence
EntityManagerFactory managerFactory = Persistence.createEntityManagerFactory("persistenceUnit");
EntityManager entityManager = managerFactory.createEntityManager();
Employee employee = new Employee("John");
entityManager.persist(employee);
select e from Employee e order by e.nom asc
public class Employee {
@Id
@GeneratedValue
private Long id;
private String name;
// Accessors
}
Récupère les infos des Persistence Unit pour pouvoir créer des EntityManager.
Dans un contexte JEE, il est fourni par le serveur d'application.
gère les échanges entre le code et la BD. Ses actions sont généralement englobé dans des transactions.
public class Employee {
//...
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "adresse", unique = true, nullable = false)
private Adresse adresse;
//...
public class Adresse {
// optionnel
@OneToOne(mappedBy = "adresse")
private Employee personne;
//...
public class Employee {
//...
@ManyToOne
private Department department;
//...
public class Departement {
@OneToMany
private List<Employee> employees = new ArrayList<>();
//...
C'est faire tourner plusieurs programme ou partie d'un programme en parallèle.
Avec le nombre de processeur/croissant dans nos machines, ne pas en tirer partie semble dommage.
Il est dépendant du processus qui l'a crée.
Il peut partager des éléments avec d'autres Thread (provenant du même processus).
Chaque Thread à son propre cache mémoire pour les données partagées.
Thread t = new Thread(new MyThreadedCode());
t.start();
public class MyThreadedCode implements Runnable {
public void run() {
System.out.println("Runs in a thread.");
}
}
Mot clé du langage, il s'applique à des blocs de code.
Un bloc synchronized ne peut être accéder que par un seul thread à la fois.
Un thread qui entre dans un bloc synchronized est sur de voir les modifications faites par celui qui vient d'en sortir.
Mot clé du langage, il s'applique à aux variables de classe.
Tout thread lisant une variable est sur d'en voir la dernière version.
Ensemble de wrapper de type primitives Thread safe et sans lock.
Propose des constructions Thread safe pour les Collections.
Note : si elles sont Thread safe, les opérations ne sont pas forcemment atomique.
Les Threads sont coûteux à créer et démarrer. Les Executors sont des pools de Thread.
List<Runnable> runnables = new ArrayList<Runnable>();
// mettre des runables dans runnables
ExecutorService execute = Executors.newFixedThreadPool(10);
for(Runnable r : runnables){
service.execute(r);
}
service.shutdown();
La méthode run() d'un Runnable, ne retourne rien,
public interface Callable<V> {
public V call() throws Exception;
}
ExecutorService execute = Executors.newSingleThreadExecutor();
Future<Integer> future = execute.submit(new MonCallable());
future.get(); // bloquant, attend que le Callable est rendu son résultat.
un des grosses difficultés de la concurrence est la gestion des changements d'états des données partagées. Si ces données deviennent immutable, les problèmes disparaissent, non ?
Une API standardisée pour créer des services REST.
Jersey : l'implementation de référence de JAX RS.