Σε Έργα ανάπτυξης Java , μια τυπική ροή εργασίας περιλαμβάνει την επανεκκίνηση του διακομιστή με κάθε αλλαγή τάξης και κανείς δεν παραπονιέται για αυτό. Αυτό είναι γεγονός για την ανάπτυξη της Java. Έχουμε εργαστεί έτσι από την πρώτη μας μέρα με την Java. Αλλά είναι δύσκολο να επιτευχθεί η επαναφόρτωση της κλάσης Java; Και θα μπορούσε να είναι τόσο δύσκολο όσο και συναρπαστικό να λυθεί αυτό το πρόβλημα ειδικευμένοι προγραμματιστές Java ; Σε αυτό το σεμινάριο κλάσης Java, θα προσπαθήσω να αντιμετωπίσω το πρόβλημα, να σας βοηθήσω να αποκτήσετε όλα τα οφέλη της επαναφόρτωσης κατηγορίας εν κινήσει και να αυξήσετε την παραγωγικότητά σας πάρα πολύ.
Η επαναφόρτωση τάξης Java δεν συζητείται συχνά και υπάρχει πολύ λίγη τεκμηρίωση που διερευνά αυτήν τη διαδικασία. Είμαι εδώ για να το αλλάξω αυτό. Αυτό το σεμινάριο τάξεων Java θα παρέχει μια βήμα προς βήμα εξήγηση αυτής της διαδικασίας και θα σας βοηθήσει να αποκτήσετε αυτήν την απίστευτη τεχνική. Λάβετε υπόψη ότι η εφαρμογή επαναφόρτωσης τάξης Java απαιτεί μεγάλη προσοχή, αλλά η εκμάθηση πώς να το κάνει θα σας βάλει στα μεγάλα πρωταθλήματα, τόσο ως προγραμματιστής Java όσο και ως αρχιτέκτονας λογισμικού. Επίσης, δεν θα βλάψει να καταλάβει πώς να αποφύγετε τα 10 πιο κοινά λάθη Java .
Όλος ο πηγαίος κώδικας για αυτό το σεμινάριο μεταφορτώνεται στο GitHub εδώ .
Για να εκτελέσετε τον κώδικα ενώ ακολουθείτε αυτό το σεμινάριο, θα χρειαστείτε Μέβεν , Πηγαίνω και είτε Εκλειψη ή IntelliJ IDEA .
mvn eclipse:eclipse
για τη δημιουργία αρχείων έργου του Eclipse.target/classes
.pom
αρχείο.Alt+B E
run_example*.bat
. Ορίστε την αυτόματη μεταγλώττιση του μεταγλωττιστή IntelliJ σε true. Στη συνέχεια, κάθε φορά που αλλάζετε οποιοδήποτε αρχείο java, το IntelliJ θα το μεταγλωττίζει αυτόματα.Το πρώτο παράδειγμα θα σας δώσει μια γενική κατανόηση του φορτωτή κλάσης Java. Εδώ είναι ο πηγαίος κώδικας.
Δεδομένων των παρακάτω User
ορισμός τάξης:
public static class User { public static int age = 10; }
Μπορούμε να κάνουμε τα εξής:
public static void main(String[] args) { Class userClass1 = User.class; Class userClass2 = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example1.StaticInt$User'); ...
Σε αυτό το παράδειγμα εκμάθησης, θα υπάρχουν δύο User
τάξεις φορτωμένες στη μνήμη. userClass1
θα φορτωθεί από τον προεπιλεγμένο φορτωτή κλάσης της JVM και userClass2
χρησιμοποιώντας το DynamicClassLoader
, έναν προσαρμοσμένο φορτωτή κλάσης του οποίου ο πηγαίος κώδικας παρέχεται επίσης στο έργο GitHub και το οποίο θα περιγράψω λεπτομερώς παρακάτω.
Εδώ είναι το υπόλοιπο του main
μέθοδος:
out.println('Seems to be the same class:'); out.println(userClass1.getName()); out.println(userClass2.getName()); out.println(); out.println('But why there are 2 different class loaders:'); out.println(userClass1.getClassLoader()); out.println(userClass2.getClassLoader()); out.println(); User.age = 11; out.println('And different age values:'); out.println((int) ReflectUtil.getStaticFieldValue('age', userClass1)); out.println((int) ReflectUtil.getStaticFieldValue('age', userClass2)); }
Και η έξοδος:
Seems to be the same class: qj.blog.classreloading.example1.StaticInt$User qj.blog.classreloading.example1.StaticInt$User But why there are 2 different class loaders: [email protected] [email protected] And different age values: 11 10
Όπως μπορείτε να δείτε εδώ, αν και το User
Τα μαθήματα έχουν το ίδιο όνομα, είναι στην πραγματικότητα δύο διαφορετικές τάξεις και μπορούν να διαχειρίζονται και να χειρίζονται ανεξάρτητα. Η ηλικιακή τιμή, αν και δηλώνεται ως στατική, υπάρχει σε δύο εκδόσεις, προσαρμόζεται ξεχωριστά σε κάθε τάξη και μπορεί να αλλάξει και αυτόνομα.
Σε ένα κανονικό πρόγραμμα Java, ClassLoader
είναι η πύλη που φέρνει μαθήματα στο JVM. Όταν μια τάξη απαιτεί να φορτωθεί μια άλλη τάξη, είναι το καθήκον ClassLoader
να κάνει τη φόρτωση.
Ωστόσο, σε αυτό το παράδειγμα κλάσης Java, το έθιμο ClassLoader
με όνομα DynamicClassLoader
χρησιμοποιείται για τη φόρτωση της δεύτερης έκδοσης του User
τάξη. Εάν αντί για DynamicClassLoader
, χρησιμοποιήσαμε ξανά τον προεπιλεγμένο φορτωτή κλάσης (με την εντολή StaticInt.class.getClassLoader()
) τότε το ίδιο User
θα χρησιμοποιηθεί η κλάση, καθώς όλες οι φορτωμένες κλάσεις αποθηκεύονται στην κρυφή μνήμη.
DynamicClassLoader
Μπορεί να υπάρχουν πολλοί φορτωτές τάξης σε ένα κανονικό πρόγραμμα Java. Αυτό που φορτώνει την κύρια τάξη σας, ClassLoader
, είναι το προεπιλεγμένο και από τον κωδικό σας, μπορείτε να δημιουργήσετε και να χρησιμοποιήσετε όσους φορτωτές τάξης θέλετε. Αυτό, λοιπόν, είναι το κλειδί για την επαναφόρτωση τάξης στην Java. Το DynamicClassLoader
είναι πιθανώς το πιο σημαντικό μέρος αυτού του σεμιναρίου, οπότε πρέπει να καταλάβουμε πώς λειτουργεί η δυναμική φόρτωση τάξης για να μπορέσουμε να επιτύχουμε τον στόχο μας.
Σε αντίθεση με την προεπιλεγμένη συμπεριφορά του ClassLoader
, το DynamicClassLoader
κληρονομεί μια πιο επιθετική στρατηγική. Ένας κανονικός φορτωτής τάξης θα έδινε στον γονέα του ClassLoader
την κατηγορία προτεραιότητας και φόρτωσης μόνο που δεν μπορεί να φορτώσει ο γονέας της. Αυτό είναι κατάλληλο για κανονικές συνθήκες, αλλά όχι στην περίπτωσή μας. Αντ 'αυτού, το DynamicClassLoader
θα προσπαθήσει να κοιτάξει μέσα από όλες τις διαδρομές της τάξης και να επιλύσει την τάξη-στόχο πριν παραιτηθεί από το δικαίωμα στον γονέα της.
Στο παραπάνω παράδειγμά μας, το DynamicClassLoader
δημιουργείται με μία μόνο διαδρομή κλάσης: 'target/classes'
(στον τρέχοντα κατάλογό μας), οπότε είναι σε θέση να φορτώνει όλες τις τάξεις που βρίσκονται σε αυτήν την τοποθεσία. Για όλες τις τάξεις που δεν υπάρχουν, θα πρέπει να αναφέρεται στον γονικό πρόγραμμα φόρτωσης. Για παράδειγμα, πρέπει να φορτώσουμε το String
τάξη στο StaticInt
class και ο φορτωτής τάξης μας δεν έχει πρόσβαση στο rt.jar
στο φάκελο JRE, οπότε το String
θα χρησιμοποιηθεί η κλάση του γονικού φορτωτή κλάσης.
Ο ακόλουθος κωδικός είναι από AggressiveClassLoader
, η γονική τάξη DynamicClassLoader
και δείχνει πού ορίζεται αυτή η συμπεριφορά.
byte[] newClassData = loadNewClass(name); if (newClassData != null) { loadedClasses.add(name); return loadClass(newClassData, name); } else { unavaiClasses.add(name); return parent.loadClass(name); }
Σημειώστε τις ακόλουθες ιδιότητες του DynamicClassLoader
:
DynamicClassLoader
μπορεί να συλλεχθεί σκουπίδια μαζί με όλες τις φορτωμένες τάξεις και αντικείμενα.Με τη δυνατότητα φόρτωσης και χρήσης δύο εκδόσεων της ίδιας κλάσης, τώρα σκεφτόμαστε να πετάξουμε την παλιά έκδοση και να φορτώσουμε τη νέα για να την αντικαταστήσουμε. Στο επόμενο παράδειγμα, θα το κάνουμε αυτό… συνεχώς.
Αυτό το επόμενο παράδειγμα Java θα σας δείξει ότι το JRE μπορεί να φορτώσει και να φορτώσει εκ νέου τις τάξεις για πάντα, με παλιές τάξεις που απορρίπτονται και συλλέγονται σκουπίδια και ολοκαίνουργια τάξη φορτωμένη από τον σκληρό δίσκο και να χρησιμοποιείται. Εδώ είναι ο πηγαίος κώδικας.
Εδώ είναι ο κύριος βρόχος:
public static void main(String[] args) { for (;;) { Class userClass = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example2.ReloadingContinuously$User'); ReflectUtil.invokeStatic('hobby', userClass); ThreadUtil.sleep(2000); } }
Κάθε δύο δευτερόλεπτα, το παλιό User
η τάξη θα απορριφθεί, θα φορτωθεί μια νέα και η μέθοδος της hobby
επικαλέστηκε.
Εδώ είναι το User
ορισμός τάξης:
@SuppressWarnings('UnusedDeclaration') public static class User { public static void hobby() { playFootball(); // will comment during runtime // playBasketball(); // will uncomment during runtime } // will comment during runtime public static void playFootball() { System.out.println('Play Football'); } // will uncomment during runtime // public static void playBasketball() { // System.out.println('Play Basketball'); // } }
Κατά την εκτέλεση αυτής της εφαρμογής, θα πρέπει να προσπαθήσετε να σχολιάσετε και να αποσυνδέσετε τον κωδικό που υποδεικνύεται κωδικός στο User
τάξη. Θα δείτε ότι ο νεότερος ορισμός θα χρησιμοποιείται πάντα.
Ακολουθεί ένα παράδειγμα εξόδου:
... Play Football Play Football Play Football Play Basketball Play Basketball Play Basketball
Κάθε φορά μια νέα παρουσία του DynamicClassLoader
έχει δημιουργηθεί, θα φορτώσει το User
τάξη από το target/classes
φάκελο, όπου έχουμε ρυθμίσει το Eclipse ή το IntelliJ να εξάγει το πιο πρόσφατο αρχείο κλάσης. Όλοι οι παλιοί DynamicClassLoader
s και παλιοί User
τα μαθήματα θα αποσυνδεθούν και θα υποβληθούν στον συλλέκτη απορριμμάτων.
Εάν είστε εξοικειωμένοι με το JVM HotSpot, τότε αξίζει να σημειωθεί ότι η δομή της τάξης μπορεί επίσης να αλλάξει και να φορτωθεί ξανά: η playFootball
η μέθοδος πρέπει να αφαιρεθεί και το playBasketball
προστέθηκε μέθοδος. Αυτό είναι διαφορετικό από το HotSpot, το οποίο επιτρέπει μόνο το περιεχόμενο της μεθόδου να αλλάζει ή η κλάση δεν μπορεί να φορτωθεί ξανά.
Τώρα που είμαστε σε θέση να φορτώσουμε ξανά μια τάξη, είναι καιρός να δοκιμάσουμε να φορτώσουμε ξανά πολλές τάξεις ταυτόχρονα. Ας το δοκιμάσουμε στο επόμενο παράδειγμα.
Η έξοδος αυτού του παραδείγματος θα είναι η ίδια με το Παράδειγμα 2, αλλά θα δείξει πώς να εφαρμόσετε αυτήν τη συμπεριφορά σε μια δομή που μοιάζει περισσότερο με εφαρμογή με αντικείμενα περιβάλλοντος, υπηρεσίας και μοντέλου. Ο πηγαίος κώδικας αυτού του παραδείγματος είναι αρκετά μεγάλος, επομένως έχω δείξει μόνο τμήματα αυτού εδώ. Ο πλήρης πηγαίος κώδικας είναι εδώ .
Εδώ είναι το main
μέθοδος:
public static void main(String[] args) { for (;;) { Object context = createContext(); invokeHobbyService(context); ThreadUtil.sleep(2000); } }
Και η μέθοδος createContext
:
private static Object createContext() { Class contextClass = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example3.ContextReloading$Context'); Object context = newInstance(contextClass); invoke('init', context); return context; }
Η μέθοδος invokeHobbyService
:
private static void invokeHobbyService(Object context) { Object hobbyService = getFieldValue('hobbyService', context); invoke('hobby', hobbyService); }
Και εδώ είναι το Context
τάξη:
public static class Context { public HobbyService hobbyService = new HobbyService(); public void init() { // Init your services here hobbyService.user = new User(); } }
Και το HobbyService
τάξη:
public static class HobbyService { public User user; public void hobby() { user.hobby(); } }
Το Context
τάξη σε αυτό το παράδειγμα είναι πολύ πιο περίπλοκη από το User
class στα προηγούμενα παραδείγματα: έχει συνδέσμους προς άλλες τάξεις και έχει το init
μέθοδος για να ονομάζεται κάθε φορά που έχει δημιουργηθεί. Βασικά, είναι πολύ παρόμοιο με τις τάξεις περιβάλλοντος της πραγματικής εφαρμογής (που παρακολουθεί τις ενότητες της εφαρμογής και κάνει την εξάρτηση εξάρτησης). Έτσι μπορείτε να το φορτώσετε ξανά Context
τάξη μαζί με όλες τις συνδεδεμένες τάξεις είναι ένα εξαιρετικό βήμα προς την εφαρμογή αυτής της τεχνικής στην πραγματική ζωή.
Καθώς αυξάνεται ο αριθμός των τάξεων και των αντικειμένων, το βήμα μας 'πτώσης παλαιών εκδόσεων' θα γίνει επίσης πιο περίπλοκο. Αυτός είναι επίσης ο μεγαλύτερος λόγος για τον οποίο η επαναφόρτωση τάξης είναι τόσο δύσκολη. Για πιθανή κατάργηση παλαιών εκδόσεων θα πρέπει να διασφαλίσουμε ότι, μόλις δημιουργηθεί το νέο περιβάλλον, όλα οι αναφορές στα παλιά μαθήματα και αντικείμενα απορρίπτονται. Πώς το αντιμετωπίζουμε κομψά;
Το main
Μέθοδος εδώ θα έχει ένα κράτημα του αντικειμένου περιβάλλοντος, και αυτός είναι ο μόνος σύνδεσμος σε όλα τα πράγματα που πρέπει να απορριφθούν. Εάν σπάσουμε αυτόν τον σύνδεσμο, το αντικείμενο περιβάλλοντος και η κλάση περιβάλλοντος και το αντικείμενο υπηρεσίας… θα υποβληθούν όλα στον συλλέκτη απορριμμάτων.
Μια μικρή εξήγηση για το γιατί συνήθως τα μαθήματα είναι τόσο επίμονα και δεν συλλέγονται σκουπίδια:
Με αυτό το παράδειγμα, βλέπουμε ότι η επαναφόρτωση όλων των τάξεων της εφαρμογής είναι μάλλον εύκολη. Ο στόχος είναι απλώς να διατηρηθεί μια λεπτή, αποσπώμενη σύνδεση από το ζωντανό νήμα με το δυναμικό φορτωτή κλάσης που χρησιμοποιείται. Αλλά τι γίνεται αν θέλουμε κάποια αντικείμενα (και τις τάξεις τους) δεν επαναφόρτωση και επαναχρησιμοποίηση μεταξύ κύκλων επαναφόρτωσης; Ας δούμε το επόμενο παράδειγμα.
Αυτός είναι ο πηγαίος κώδικας. .
Το main
μέθοδος:
public static void main(String[] args) { ConnectionPool pool = new ConnectionPool(); for (;;) { Object context = createContext(pool); invokeService(context); ThreadUtil.sleep(2000); } }
Έτσι μπορείτε να δείτε ότι το κόλπο εδώ είναι η φόρτωση του ConnectionPool
ταξινομήστε το και να το δημιουργήσετε έξω από τον κύκλο επαναφόρτωσης, διατηρώντας τον στον παραμένοντα χώρο και μεταβιβάστε την αναφορά στο Context
αντικείμενα
Το createContext
Η μέθοδος είναι επίσης λίγο διαφορετική:
private static Object createContext(ConnectionPool pool) { ExceptingClassLoader classLoader = new ExceptingClassLoader( (className) -> className.contains('.crossing.'), 'target/classes'); Class contextClass = classLoader.load('qj.blog.classreloading.example4.reloadable.Context'); Object context = newInstance(contextClass); setFieldValue(pool, 'pool', context); invoke('init', context); return context; }
Από τώρα και στο εξής, θα ονομάσουμε τα αντικείμενα και τις κλάσεις που επαναφορτώνουν με κάθε κύκλο τον 'επαναφορτιζόμενο χώρο' και άλλα - τα αντικείμενα και τις κλάσεις που δεν ανακυκλώνονται και δεν ανανεώνονται κατά τη διάρκεια των κύκλων επαναφόρτωσης - ο 'διαρκής χώρος'. Θα πρέπει να είμαστε πολύ σαφείς για το ποια αντικείμενα ή τάξεις παραμένουν σε ποιον χώρο, σχεδιάζοντας έτσι μια γραμμή διαχωρισμού μεταξύ αυτών των δύο χώρων.
Όπως φαίνεται από την εικόνα, δεν είναι μόνο τα Context
αντικείμενο και το UserService
αντικείμενο που αναφέρεται στο ConnectionPool
αντικείμενο, αλλά το Context
και UserService
Τα μαθήματα αναφέρονται επίσης στο ConnectionPool
τάξη. Αυτή είναι μια πολύ επικίνδυνη κατάσταση που συχνά οδηγεί σε σύγχυση και αποτυχία. Το ConnectionPool
η τάξη δεν πρέπει να φορτωθεί από την DynamicClassLoader
μας, πρέπει να υπάρχει μόνο μία ConnectionPool
τάξη στη μνήμη, η οποία είναι αυτή που φορτώνεται από την προεπιλογή ClassLoader
. Αυτό είναι ένα παράδειγμα του γιατί είναι τόσο σημαντικό να είστε προσεκτικοί όταν σχεδιάζετε μια αρχιτεκτονική επαναφόρτωσης τάξης στην Java.
Τι γίνεται αν το DynamicClassLoader
μας φορτώνει κατά λάθος το ConnectionPool
τάξη? Τότε το ConnectionPool
Δεν είναι δυνατή η μεταφορά αντικειμένου από τον διαρκή χώρο στο Context
αντικείμενο, επειδή το Context
αντικείμενο περιμένει ένα αντικείμενο διαφορετικής κλάσης, το οποίο ονομάζεται επίσης ConnectionPool
, αλλά στην πραγματικότητα είναι διαφορετική τάξη!
Πώς λοιπόν αποτρέπουμε το DynamicClassLoader
από τη φόρτωση του ConnectionPool
τάξη? Αντί να χρησιμοποιεί το DynamicClassLoader
, αυτό το παράδειγμα χρησιμοποιεί μια υποκατηγορία που ονομάζεται: ExceptingClassLoader
, η οποία θα μεταβιβάσει τη φόρτωση σε super classloader με βάση μια συνθήκη συνθήκης:
(className) -> className.contains('$Connection')
Εάν δεν χρησιμοποιούμε ExceptingClassLoader
εδώ, τότε το DynamicClassLoader
θα φορτώσει το ConnectionPool
class επειδή αυτή η κατηγορία βρίσκεται στο 'target/classes
' ντοσιέ. Ένας άλλος τρόπος για την αποτροπή του ConnectionPool
το μάθημα παραλαμβάνεται από το DynamicClassLoader
είναι να μεταγλωττιστεί το ConnectionPool
κλάση σε διαφορετικό φάκελο, ίσως σε διαφορετική ενότητα, και θα μεταγλωττιστεί ξεχωριστά.
Τώρα, η φόρτωση της κλάσης Java γίνεται πολύ συγκεχυμένη. Πώς καθορίζουμε ποιες κλάσεις θα πρέπει να βρίσκονται στον διαρκή χώρο και ποιες κατηγορίες στον επαναφορτώσιμο χώρο; Εδώ είναι οι κανόνες:
Context
η τάξη αναφέρει το διατηρούμενο ConnectionPool
τάξη, αλλά ConnectionPool
δεν έχει καμία αναφορά στο Context
StringUtils
μπορεί να φορτωθεί μία φορά στον παραμένον χώρο και να φορτωθεί χωριστά στον επαναφορτιζόμενο χώρο.Έτσι μπορείτε να δείτε ότι οι κανόνες δεν είναι πολύ περιοριστικοί. Εκτός από τις κλάσεις διασταύρωσης που έχουν αντικείμενα που αναφέρονται στους δύο χώρους, όλες οι άλλες κατηγορίες μπορούν να χρησιμοποιηθούν ελεύθερα είτε στον παραμένον χώρο είτε στον επαναφορτώσιμο χώρο ή και στα δύο. Φυσικά, μόνο τα μαθήματα στον επαναφορτώσιμο χώρο θα απολαύσουν την επαναφόρτωση με κύκλους επαναφόρτωσης.
Έτσι αντιμετωπίζεται το πιο δύσκολο πρόβλημα με την επαναφόρτωση τάξης. Στο επόμενο παράδειγμα, θα προσπαθήσουμε να εφαρμόσουμε αυτήν την τεχνική σε μια απλή εφαρμογή ιστού και θα απολαύσουμε την επαναφόρτωση μαθημάτων Java όπως και οποιαδήποτε γλώσσα σεναρίου.
Αυτός είναι ο πηγαίος κώδικας. .
Αυτό το παράδειγμα θα είναι πολύ παρόμοιο με αυτό που θα έπρεπε να έχει μια κανονική εφαρμογή ιστού. Είναι μια εφαρμογή μίας σελίδας με AngularJS, SQLite, Maven και Ενσωματωμένος διακομιστής Web Jetty .
Εδώ είναι ο επαναφορτώσιμος χώρος στη δομή του διακομιστή ιστού:
Ο διακομιστής ιστού δεν θα περιέχει αναφορές στους πραγματικούς servlets, οι οποίοι πρέπει να παραμείνουν στον επαναφορτώσιμο χώρο, ώστε να φορτωθούν ξανά. Αυτό που κατέχει είναι stub servlets, τα οποία, με κάθε κλήση στη μέθοδο υπηρεσίας του, θα επιλύσουν το πραγματικό servlet στο πραγματικό περιβάλλον που θα εκτελεστεί.
Αυτό το παράδειγμα εισάγει επίσης ένα νέο αντικείμενο ReloadingWebContext
, το οποίο παρέχει στον διακομιστή ιστού όλες τις τιμές όπως ένα κανονικό περιβάλλον, αλλά εσωτερικά διατηρεί αναφορές σε ένα πραγματικό αντικείμενο περιβάλλοντος που μπορεί να φορτωθεί εκ νέου από το DynamicClassLoader
. Είναι αυτό ReloadingWebContext
που παρέχουν stub servlets στον διακομιστή ιστού.
Το ReloadingWebContext
θα είναι το περιτύλιγμα του πραγματικού περιβάλλοντος και:
Επειδή είναι πολύ σημαντικό να κατανοήσουμε πώς απομονώνουμε τον παραμένον χώρο και τον επαναφορτώσιμο χώρο, εδώ είναι οι δύο τάξεις που διασχίζουν μεταξύ των δύο χώρων:
Κατηγορία qj.util.funct.F0
για αντικείμενο public F0 connF
σε Context
DynamicClassLoader
. Κατηγορία java.sql.Connection
για αντικείμενο public F0 connF
σε Context
DynamicClassLoader
, οπότε δεν θα παραληφθεί.Σε αυτό το σεμινάριο τάξεων Java, έχουμε δει πώς να φορτώσετε εκ νέου μια τάξη, να φορτώσετε ξανά μια τάξη συνεχώς, να φορτώσετε ξανά ολόκληρο χώρο πολλαπλών τάξεων και να φορτώσετε ξανά πολλές κατηγορίες ξεχωριστά από τις κλάσεις που πρέπει να διατηρηθούν. Με αυτά τα εργαλεία, ο βασικός παράγοντας για την επίτευξη αξιόπιστης επαναφόρτωσης κατηγορίας είναι να έχουμε έναν εξαιρετικά καθαρό σχεδιασμό. Τότε μπορείτε ελεύθερα να χειριστείτε τα μαθήματά σας και ολόκληρο το JVM.
Η εφαρμογή επαναφόρτωσης κλάσης Java δεν είναι το πιο εύκολο πράγμα στον κόσμο. Αλλά αν το κάνετε, και κάποια στιγμή βρείτε τα μαθήματά σας φορτωμένα, τότε είστε σχεδόν ήδη εκεί. Θα απομένουν πολύ λίγα πράγματα για να μπορέσετε να επιτύχετε εντελώς υπέροχο καθαρό σχεδιασμό για το σύστημά σας.
Καλή τύχη φίλοι μου και απολαύστε τη νέα σας υπερδύναμη!