9 λάθη που πρέπει να αποφύγετε κατά τον προγραμματισμό σε Java
Rafal Sawicki
Προγραμματιστής Java
Ποια λάθη πρέπει να αποφεύγονται κατά τον προγραμματισμό σε Java; Στο παρακάτω κομμάτι απαντάμε σε αυτό το ερώτημα.
Java είναι μια δημοφιλής γλώσσα με εδραιωμένη θέση στον κόσμο των ανάπτυξη λογισμικού. Είναι μια ισχυρή και ευέλικτη γλώσσα προγραμματισμού. Περίπου 3 δισεκατομμύρια συσκευές παγκοσμίως λειτουργούν με Java και, ως εκ τούτου, τουλάχιστον 3 δισεκατομμύρια λάθη έγιναν κατά τη χρήση του. Σε αυτό το άρθρο, ας επικεντρωθούμε στο πώς να μην κάνουμε άλλα.
1. Εξαίρεση ταυτόχρονης τροποποίησης
Αυτό είναι μακράν το πιο συνηθισμένο λάθος που συνάντησα. Στις αρχές της καριέρας μου, το έκανα κι εγώ πολλές φορές. Αυτό το λάθος συμβαίνει όταν προσπαθείτε να τροποποιήσετε τη συλλογή ενώ την επαναλαμβάνετε. Το ConcurrentModificationException μπορεί επίσης να προκύψει όταν εργάζεστε με πολλαπλά νήματα, αλλά προς το παρόν, ας επικεντρωθούμε σε ένα βασικό σενάριο.
Ας υποθέσουμε ότι έχετε ένα Συλλογή χρηστών, όπου κάποιοι από αυτούς είναι ενήλικες και κάποιοι όχι. Το καθήκον σας είναι να φιλτράρετε τα παιδιά.
for (User : users) {
if (!user.isAdult()) {
users.remove(user),
}
}
Εκτελώντας το προαναφερθέν κωδικός καταλήγει στο να πάρει ConcurrentModificationException. Πού κάναμε λάθος; Πριν ολοκληρώσουμε την επανάληψή μας, προσπαθήσαμε να αφαιρέσουμε ορισμένα στοιχεία. Αυτό είναι που προκαλεί την εξαίρεση.
Πώς μπορώ να το αποφύγω;
Υπάρχουν δύο προσεγγίσεις που μπορούν να βοηθήσουν σε αυτή την περίπτωση. Πρώτα απ' όλα, επωφεληθείτε από Java Η καλοσύνη του 8 - Ρεύμα.
List adults = users.stream()
.filter(User::isAdult)
.toList(),
Χρησιμοποιώντας ένα Predicate φίλτρου, κάναμε το αντίστροφο της προηγούμενης συνθήκης - τώρα καθορίζουμε τα στοιχεία που πρέπει να συμπεριληφθούν. Το πλεονέκτημα μιας τέτοιας προσέγγισης είναι ότι είναι εύκολο να αλυσιδωθούν άλλες συναρτήσεις μετά την αφαίρεση, π.χ. χάρτης. Αλλά για όνομα του Θεού, μην προσπαθήσετε να κάνετε κάτι σαν το παρακάτω:
Θα μπορούσε επίσης να καταλήξει στο ConcurrentModificationException επειδή τροποποιείτε την πηγή ροής. Μπορεί επίσης να σας δώσει μερικές ακόμη Εξαιρέσεις που δεν θα είναι εύκολο να εντοπιστούν.
Για να λύσετε ConcurrentModificationException σε ένα σενάριο ενός νήματος. θα μπορούσατε επίσης να μεταβείτε στη χρήση απευθείας Επαναλήπτης και το remove() ή μπορείτε απλά να μην αφαιρείτε στοιχεία κατά τη διάρκεια της επανάληψης. Ωστόσο, η σύστασή μου είναι να χρησιμοποιήσετε Ρεύματα - είναι το 2022.
2. Αποθήκευση κωδικών πρόσβασης ως συμβολοσειρές
Καθώς ασχολούμαι όλο και περισσότερο με την κυβερνοασφάλεια, δεν θα ήμουν πιστός στον εαυτό μου αν δεν ανέφερα τουλάχιστον ένα Λάθος της Java που μπορεί να οδηγήσει σε ζήτημα ασφάλειας. Η αποθήκευση των κωδικών πρόσβασης που λαμβάνονται από τους χρήστες σε ένα Συμβολοσειρά αντικείμενο είναι ακριβώς κάτι που πρέπει να φοβάστε.
Το ζήτημα (ή ίσως το πλεονέκτημα) της Συμβολοσειρά είναι ότι είναι αμετάβλητη. Στον κόσμο του κυβερνοχώρου, αυτό δημιουργεί μια πιθανή απειλή, αφού δεν μπορείτε να διαγράψετε την τιμή μιας κάποτε δημιουργηθείσας Συμβολοσειρά αντικείμενο. Ο επιτιθέμενος που αποκτά πρόσβαση στη μνήμη του υπολογιστή σας μπορεί να βρει εκεί κωδικούς πρόσβασης απλού κειμένου.
Δεύτερον, οι χορδές σε Java εσωτερικεύονται από το JVM και αποθηκεύονται στο χώρο PermGen ή στο χώρο σωρού. Όταν δημιουργείτε ένα Συμβολοσειρά αντικείμενο, αποθηκεύεται στην προσωρινή μνήμη και αφαιρείται μόνο όταν ο Συλλέκτης σκουπιδιών αρχίσει να κάνει τη δουλειά του. Δεν μπορείτε να είστε σίγουροι πότε ο κωδικός σας διαγράφεται από τη δεξαμενή String, καθώς ο Συλλέκτης Σκουπιδιών λειτουργεί με μη ντετερμινιστικό τρόπο.
Πώς να το αποφύγετε;
Η συνιστώμενη προσέγγιση είναι η χρήση char[] ή, ακόμα καλύτερα, η βιβλιοθήκη που υποστηρίζει την αποθήκευση κωδικών πρόσβασης ως char[], π.χ.Password4j. Το char[] είναι μεταβλητός και μπορεί να τροποποιηθεί μετά την αρχικοποίησή του. Μετά την επεξεργασία ενός κωδικού πρόσβασης, μπορείτε απλά να διαγράψετε την char[] πίνακα κωδικών πρόσβασης γράφοντας τυχαία γράμματα σε αυτόν. Σε περίπτωση που οι επιτιθέμενοι αποκτήσουν πρόσβαση στη μνήμη του υπολογιστή σας, θα δουν μόνο κάποιες τυχαίες τιμές που δεν έχουν καμία σχέση με τους κωδικούς πρόσβασης των χρηστών.
3. (Μη) χειρισμός εξαιρέσεων
Οι αρχάριοι αλλά και οι πιο προχωρημένοι προγραμματιστές δεν γνωρίζουν πώς να χειρίζονται σωστά τις εξαιρέσεις. Το κύριο αμάρτημά τους σε αυτό το θέμα είναι να τις αγνοούν. ΑΥΤΌ ΔΕΝ ΕΊΝΑΙ ΠΟΤΈ ΜΙΑ ΚΑΛΉ ΠΡΟΣΈΓΓΙΣΗ.
Δυστυχώς, δεν μπορούμε να σας δώσουμε μια ασημένια λύση που θα ταιριάζει σε κάθε Εξαίρεσησενάριο που συναντάτε. Πρέπει να σκεφτείτε κάθε περίπτωση ξεχωριστά. Ωστόσο, μπορούμε να σας δώσουμε κάποιες συμβουλές για το πώς να ξεκινήσετε με αυτό το θέμα.
Πώς μπορώ να το αποφύγω;
Αγνοώντας το Εξαίρεσηs δεν είναι ποτέ καλή πρακτική. Εξαίρεσηεισάγονται για κάποιο λόγο, οπότε δεν πρέπει να τα αγνοήσετε.
try {...} catch(Exception e) { log(e); } είναι σπάνια η σωστή προσέγγιση για Εξαίρεση χειρισμός.
Ρίψη Εξαίρεση, εμφανίζει ένα παράθυρο διαλόγου σφάλματος στο χρήστη ή τουλάχιστον προσθέτει ένα περιεκτικό μήνυμα στο αρχείο καταγραφής.
Αν αφήσατε τις εξαιρέσεις σας χωρίς χειρισμό (πράγμα που δεν θα έπρεπε), τουλάχιστον εξηγήστε το στο σχόλιο.
4. Χρήση null
Δυστυχώς, είναι αρκετά συνηθισμένο να βρίσκουμε μια συνάρτηση Java που σε ορισμένες περιπτώσεις επιστρέφει ένα null. Το πρόβλημα είναι ότι μια τέτοια συνάρτηση επιβάλλει στον πελάτη της να εκτελεί έλεγχο null στο αποτέλεσμα. Χωρίς αυτό, η NullPointerException απορρίπτεται.
Το άλλο πράγμα είναι το πέρασμα ενός null αξία. Γιατί το σκέφτηκες αυτό; Σε μια τέτοια περίπτωση, η συνάρτηση πρέπει να εκτελέσει έναν έλεγχο null. Όταν χρησιμοποιείτε βιβλιοθήκες τρίτων κατασκευαστών, δεν μπορείτε να αλλάξετε το εσωτερικό των συναρτήσεων. Τι γίνεται τότε;
Το πιο σημαντικό είναι ότι άλλοι προγραμματιστές που διαβάζουν τον κώδικά σας και βλέπουν ότι περνάτε null θα αποπροσανατολιστούν πιθανώς ως προς το γιατί επιλέξατε έναν τόσο παράξενο τρόπο για να υλοποιήσετε τη λειτουργία σας.
Πώς μπορώ να το αποφύγω;
Μην επιστρέφετε ένα null αξία! Ποτέ! Σε περίπτωση που η συνάρτησή σας επιστρέφει κάποιο είδος Συλλογή, μπορείτε απλά να επιστρέψετε ένα κενό Συλλογή. Αν ασχολείστε με μεμονωμένα αντικείμενα, μπορείτε να χρησιμοποιήσετε το πρότυπο σχεδίασης null object. Από τη στιγμή που το Java 8, υλοποιείται ως εξής Προαιρετικό. Εκτός από αυτό, η λιγότερο συνιστώμενη προσέγγιση περιλαμβάνει την έγερση ενός Εξαίρεση.
5. Βαριά συνένωση αλφαριθμητικών
Ελπίζω να μην είναι ένα λάθος που θα κάνετε, δεδομένου ότι είναι η πιο δημοφιλής (ή ίσως η δεύτερη πιο δημοφιλής μετά το FizzBuzz) ερώτηση συνέντευξης. Όπως θα πρέπει να γνωρίζετε μέχρι τώρα, μια Συμβολοσειρά αντικείμενο είναι αμετάβλητο στο Java - αφού δημιουργηθεί, δεν μπορεί να τροποποιηθεί. Έτσι, η συνένωση των Συμβολοσειρά σημαίνει πολλή περιττή κατανομή μνήμης. Συνδυασμός Συμβολοσειρά αντικείμενα κάθε φορά απαιτεί τη δημιουργία ενός προσωρινού StringBuilder αντικείμενο και την αλλαγή του πίσω σε συμβολοσειρά. Επομένως, αυτή η λύση δεν είναι απολύτως κατάλληλη αν θέλουμε να συνδυάσουμε έναν μεγάλο αριθμό χαρακτήρων.
Πώς μπορώ να το αποφύγω;
Για να λύσετε αυτό το πρόβλημα, χρησιμοποιήστε StringBuilder. Δημιουργεί ένα μεταβλητό αντικείμενο που μπορεί εύκολα να χειραγωγηθεί. Φυσικά, μπορείτε πάντα να χρησιμοποιήσετε StringBuffer αν το έργο χρησιμοποιείται σε ταυτόχρονο πλαίσιο.
6. Μη χρήση υφιστάμενων λύσεων
Όταν αναπτύσσετε λογισμικό, η γνώση των βασικών στοιχείων της γλώσσας στην οποία γράφετε είναι απαραίτητη, αλλά δεν αρκεί. Πολλά αλγοριθμικά προβλήματα που συναντάτε κατά την υλοποίηση μιας νέας λειτουργίας έχουν ήδη λυθεί από κάποιον άλλον. Πάρα πολλές φορές έχω δει κάποιον να υλοποιεί έναν αλγόριθμο ασφαλείας από το μηδέν. Μια τέτοια προσέγγιση είναι επιρρεπής σε λάθη. Ένα άτομο δεν μπορεί να ελέγξει διεξοδικά μια τόσο πολύπλοκη λύση. Η συλλογική γνώση του ομάδα που αποτελείται από μέτρια προχωρημένους προγραμματιστές είναι σχεδόν πάντα καλύτερη από το μεγαλείο ενός θαύματος Προγραμματιστής Java. Δεν χρειάζεται να ανακαλύψετε εκ νέου τον τροχό - αρκεί να προσαρμόσετε την υπάρχουσα λύση στις ανάγκες σας.
Πώς μπορώ να το αποφύγω;
Προσπαθήστε να αναζητήσετε βιβλιοθήκες που αντιμετωπίζουν το πρόβλημα πάνω στο οποίο εργάζεστε. Προσπαθήστε να βρείτε παρόμοιες λύσεις. Πολλές από τις βιβλιοθήκες που είναι διαθέσιμες στο διαδίκτυο είναι δωρεάν και έχουν βελτιωθεί και δοκιμαστεί από έμπειρους προγραμματιστές και ολόκληρη την κοινότητα της Java. Μη φοβηθείτε να τις χρησιμοποιήσετε.
7. Μη εύρεση αρκετού χρόνου για τη συγγραφή δοκιμών
Είναι δελεαστικό να πιστεύουμε ότι ο κώδικάς μας θα εκτελείται πάντα τέλεια. Το να μη γράφουμε δοκιμές για τον κώδικα είναι η χειρότερη αμαρτία του Java προγραμματιστές λογισμικού. Πολλοί από εμάς προτιμούν τις χειροκίνητες και διερευνητικές δοκιμές αντί για τις δοκιμές μονάδας, κάτι που είναι τρελό. Γιατί να σπαταλάτε χρόνο γράφοντας δοκιμές όταν μπορείτε να επικεντρωθείτε στην παροχή του καλύτερου κώδικα στον κόσμο για το έργο σας, που ΣΙΓΟΥΡΑ δεν έχει σφάλματα; <joke>. Αποδεικνύεται ότι η πραγματικότητα είναι βάναυση και δεν μπορούμε να παρέχουμε κώδικα υψηλής ποιότητας χωρίς να γράφουμε δοκιμές.
Πώς μπορώ να το αποφύγω;
Θα πρέπει πάντα να προετοιμάζετε δοκιμές για τον κώδικά σας. Ξέρω ότι η προσέγγιση TDD δεν είναι τόσο εύκολο να διατηρηθεί, αλλά θα πρέπει τουλάχιστον να παρέχετε δοκιμές που καλύπτουν όλες τις συνθήκες υπό τις οποίες μπορεί να εκτελεστεί ο κώδικάς σας. Αυτό περιλαμβάνει τον έλεγχο εξαιρετικών καταστάσεων. Οι δοκιμές μονάδας είναι απαραίτητες. Πρέπει να τις παρέχετε για κάθε χαρακτηριστικό του έργου σας, αν θέλετε να βεβαιωθείτε ότι ο κώδικάς σας είναι εύκολο να αναδιαμορφωθεί και να επεκταθεί σε περαιτέρω ανάπτυξη.
Και κάτι ακόμα. Διατηρήστε ένα υψηλό επίπεδο του κώδικα δοκιμών σας - θα αξίζει τον κόπο. Αυτή είναι η συμβουλή του θείου Bob και συμφωνώ απόλυτα μαζί της.
Επιπλέον, μην ξεχνάτε άλλους τύπους δοκιμών. Οι δοκιμές ενσωμάτωσης είναι κάτι που πρέπει να λαμβάνετε υπόψη σε κάθε έργο σας.
8. Ξεχνώντας τους τροποποιητές πρόσβασης
Ιδιωτικό και δημόσιο, σωστά; Πώς μπορούμε να τα ξεχάσουμε. Αποδεικνύεται ότι υπάρχουν κι άλλα. Όταν αρχίσατε να μαθαίνετε Java, σίγουρα μάθατε για τους τροποποιητές προστατευόμενης πρόσβασης. Μπορούν να είναι χρήσιμοι σε ορισμένες περιπτώσεις, οπότε αξίζει να γνωρίζετε την ύπαρξή τους.
Προγραμματιστές Java συχνά φαίνεται να ξεχνούν το πεδίο εφαρμογής του πακέτου. Είναι εύκολο να μην θυμόμαστε τη χρήση του, αφού είναι σιωπηρό και δεν απαιτεί κανένα Java λέξεις-κλειδιά. Το πεδίο εφαρμογής του πακέτου είναι σημαντικό. Σας επιτρέπει να δοκιμάσετε μια προστατευμένη μέθοδο. Τα προστατευμένα στοιχεία είναι προσβάσιμα από τη διαδρομή της κλάσης δοκιμής, εφόσον το πακέτο είναι το ίδιο.
Πώς μπορώ να το αποφύγω;
Θυμηθείτε τον τροποποιητή protected και ότι η εμβέλεια του πακέτου σας επιτρέπει να τον ελέγξετε.
9. Χρήση καθαρής JavaEE αντί της Spring
Το επόμενο βήμα μετά την εκμάθηση Java SE είναι να μάθει πώς να τρέχει Java σε διακομιστές, πώς να δημιουργήσετε μια εφαρμογή επιχειρηματικού επιπέδου.
Οι αρχάριοι πέφτουν συχνά στην παγίδα της εκμάθησης της JavaEE, δεδομένου ότι υπάρχει ένας τεράστιος αριθμός σεμιναρίων σχετικά με αυτήν. Ακόμα και το 'Thinking in Java', το Προγραμματιστές Java', αναφέρει την JavaEE και δεν λέει τίποτα για τις άλλες επιλογές.
Πώς μπορώ να το αποφύγω;
Η JavaEE είναι ένα τραγούδι του παρελθόντος. Στις μέρες μας, η Spring είναι το ζητούμενο και η Java EE είναι απλά μια καλή επιλογή. Κάθε σύγχρονη εφαρμογή επιχειρηματικού επιπέδου χρησιμοποιεί το Spring, οπότε θα πρέπει να εξετάσετε έντονα το ενδεχόμενο να μάθετε εδώ.