Hvilke feil bør man unngå når man programmerer i Java? I det følgende besvarer vi dette spørsmålet.
Java er et populært språk med en etablert posisjon i verden av programvareutvikling. Det er et sterkt og allsidig programmeringsspråk. Rundt 3 milliarder enheter på verdensbasis kjører på Java og derfor ble det gjort minst 3 milliarder feil når du brukte den. I denne artikkelen skal vi fokusere på hvordan vi kan unngå å gjøre flere.
1. Få unntak for samtidig modifisering
Dette er den desidert vanligste feilen jeg har støtt på. I begynnelsen av min karriere gjorde jeg den også mange ganger. Denne feilen oppstår når du prøver å endre samlingen mens du itererer gjennom den. Den ConcurrentModificationException kan også oppstå når du jobber med flere tråder, men la oss foreløpig fokusere på et basisscenario.
Anta at du har en Samling av brukere, der noen av dem er voksne og andre ikke. Din oppgave er å filtrere ut barna.
for (Bruker : brukere) {
if (!user.isAdult()) {
users.remove(bruker);
}
}
Kjører den nevnte kode ender med å få ConcurrentModificationException. Hvor gikk vi galt? Før vi fullførte iterasjonen, prøvde vi å fjerne noen elementer. Det er det som utløser unntaket.
Hvordan kan jeg unngå det?
Det er et par tilnærminger som kan hjelpe i så fall. Først og fremst kan du dra nytte av Java 8's godhet - Strøm.
List adults = users.stream()
.filter(User::isAdult)
.toList();
Ved hjelp av en Predikat filter har vi gjort det omvendte av den forrige betingelsen - nå bestemmer vi hvilke elementer som skal inkluderes. Fordelen med en slik tilnærming er at det er enkelt å kjede andre funksjoner etter fjerningen, f.eks. kart. Men for guds skyld, ikke prøv å gjøre noe som nedenfor:
Det kan også ende opp i ConcurrentModificationException fordi du endrer strømkilden. Det kan også gi deg noen flere unntak som ikke er enkle å feilsøke.
Å løse ConcurrentModificationException i et enkelttrådsscenario. du kan også bytte til å bruke direkte Iterator og dens remove() metoden, eller du kan rett og slett la være å fjerne elementer under iterasjonen. Min anbefaling er imidlertid å bruke Strømmer - Det er 2022.
2. Lagring av passord som strenger
Etter hvert som jeg blir mer og mer involvert i cybersikkerhet, ville jeg ikke være tro mot meg selv hvis jeg ikke nevnte minst én Java-feil som kan føre til et sikkerhetsproblem. Lagring av passord mottatt fra brukere i en Streng er nettopp noe du bør være redd for.
Problemet (eller kanskje fordelen) med Streng er at den er uforanderlig. I cyberverdenen skaper det en potensiell trussel, siden du ikke kan slette verdien av en en gang opprettet Streng objekt. Angriperen som får tilgang til datamaskinens minne, kan finne passord i klartekst der.
For det andre, strenger i Java internaliseres av JVM-en og lagres i PermGen-området eller i heap-området. Når du oppretter en Streng objektet, blir det bufret, og det blir bare fjernet når Garbage Collector begynner å gjøre jobben sin. Du kan ikke være sikker på når passordet ditt blir slettet fra String-poolen, siden Garbage Collector fungerer på en ikke-deterministisk måte.
Hvordan unngå det?
Den anbefalte fremgangsmåten er å bruke char[] eller, enda bedre, biblioteket som støtter lagring av passord som char[]f.eks.Passord4j. De char[] er foranderlig og kan endres etter at den har blitt initialisert. Etter at du har behandlet et passord, kan du bare slette char[] passordmatrisen ved å skrive tilfeldige tegn i den. Hvis angriperne får tilgang til datamaskinens minne, vil de bare se noen tilfeldige verdier som ikke har noe med brukernes passord å gjøre.
3. (U)håndtering av unntak
Nybegynnere og også mer avanserte programmerere vet ikke hvordan de skal håndtere unntak på riktig måte. Deres største synd i så måte er å ignorere dem. DET ER ALDRI EN GOD TILNÆRMING.
Dessverre kan vi ikke gi deg en universalløsning som passer inn i alle Unntaks' scenario du kommer over. Du må tenke på hvert enkelt tilfelle for seg. Vi kan imidlertid gi deg noen råd om hvordan du kan komme i gang med dette temaet.
Hvordan kan jeg unngå det?
Ignorerer Unntaker aldri en god praksis. Unntaks er lagt inn av en eller annen grunn, så du bør ikke ignorere dem.
try {...} catch(Unntak e) { log(e); } er sjelden den riktige tilnærmingen til Unntak håndtering.
Kast Unntak, vise en feildialog til brukeren eller i det minste legge til en omfattende melding i loggen.
Hvis du ikke har håndtert unntakene dine (noe du ikke bør), kan du i det minste forklare deg i kommentaren.
4. Bruk av null
Dessverre er det ganske vanlig å finne en Java-funksjon som i noen tilfeller returnerer en null. Problemet er at en slik funksjon tvinger klienten til å utføre en nullsjekk på resultatet. Uten dette vil NullPointerUunntak blir kastet.
Den andre tingen er å sende en null verdi. Hvorfor tenkte du i det hele tatt på det? I et slikt tilfelle må funksjonen utføre en null-sjekk. Når du bruker tredjepartsbiblioteker, kan du ikke endre innsiden av funksjonene. Hva gjør man da?
Enda viktigere er det at andre utviklere som leser koden din og ser at du passerer null vil sannsynligvis bli forvirret over hvorfor du velger en så bisarr måte å implementere funksjonen din på.
Hvordan kan jeg unngå det?
Ikke returner en null verdi! Aldri! Hvis funksjonen din returnerer en eller annen type Samlingkan du bare returnere en tom Samling. Hvis du arbeider med enkeltobjekter, kan du bruke designmønsteret nullobjekt. Siden Java 8, er den implementert som Valgfritt. Ellers er den minst anbefalte tilnærmingen å reise en Unntak.
5. Sammenkjeding av tunge strenger
Forhåpentligvis er det ikke en feil du gjør, siden det er det mest populære (eller kanskje det nest mest populære etter FizzBuzz) intervjuspørsmålet. Som du burde vite nå, er en Streng objektet er uforanderlig i Java - Når den først er opprettet, kan den ikke endres. Så sammenkjeding av Streng literaler betyr mye unødvendig minneallokering. Sammenkjeding av Streng objekter hver gang krever at det opprettes en midlertidig StringBuilder objektet og endre det tilbake til en streng. Derfor er denne løsningen absolutt ikke egnet hvis vi ønsker å kombinere et stort antall tegn.
Hvordan kan jeg unngå det?
For å løse dette problemet kan du bruke StringBuilder. Det skaper et foranderlig objekt som enkelt kan manipuleres. Du kan selvfølgelig alltid bruke StringBuffer hvis din prosjekt brukes i en samtidig kontekst.
6. Ikke bruk av eksisterende løsninger
Når du utvikler programvare, er det et must å kjenne til det grunnleggende i språket du skriver i, men det er ikke nok. Mange algoritmiske problemer du støter på når du implementerer en ny funksjon, har allerede blitt løst av noen andre. Altfor mange ganger har jeg sett noen implementere en sikkerhetsalgoritme fra bunnen av. En slik tilnærming er feilutsatt. Én person kan ikke teste en så kompleks løsning grundig. Den kollektive kunnskapen til team som består av middels avanserte programmerere, er nesten alltid bedre enn storheten til ett vidunderbarn Java-utvikler. Du trenger ikke å finne opp hjulet på nytt - du trenger bare å tilpasse den eksisterende løsningen til dine behov.
Hvordan kan jeg unngå det?
Prøv å finne biblioteker som tar for seg problemet du jobber med. Prøv å finne lignende løsninger. Mange av bibliotekene som er tilgjengelige på nettet, er gratis og har blitt finpusset og testet av erfarne utviklere og hele Java-miljøet. Ikke vær redd for å bruke dem.
7. Ikke nok tid til å skrive tester
Det er fristende å tro at koden vår alltid vil kjøre perfekt. Å ikke skrive tester for koden er den verste synden i Java programvareutviklere. Mange av oss foretrekker manuelle og utforskende tester i stedet for enhetstester, noe som er helt sprøtt. Hvorfor kaste bort tid på å skrive tester når du kan fokusere på å levere verdens beste kode til prosjektet ditt, som DEFINITIVT ikke har noen bugs? Det viser seg at virkeligheten er brutal, og vi kan ikke levere kode av høy kvalitet uten å skrive tester.
Hvordan kan jeg unngå det?
Du bør alltid lage tester for koden din. Jeg vet at TDD-tilnærmingen ikke er så lett å vedlikeholde, men du bør i det minste lage tester som dekker alle forhold der koden din kan kjøres. Dette inkluderer testing av eksepsjonelle situasjoner. Enhetstestene er nødvendige. Du må lage dem for hver eneste funksjon i prosjektet ditt hvis du vil være sikker på at koden din er enkel å refaktorere og utvide i videreutviklingen.
En ting til. Hold en høy standard på testkoden din - det vil være verdt det. Det er onkel Bobs råd, og jeg er helt enig i det.
Glem heller ikke andre typer tester. Integrasjonstester er noe du bør vurdere i alle prosjekter.
8. Glemmer tilgangsmodifikatorer
Privat og offentlig, ikke sant? Hvordan kan vi glemme dem? Det viser seg at det finnes flere. Da du først begynte å lære Javahar du helt sikkert lært om beskyttede tilgangsmodifikatorer. De kan være nyttige i noen tilfeller, så det er verdt å kjenne til deres eksistens.
Java-utviklere ser ofte ut til å glemme pakkeomfanget. Det er lett å glemme å bruke det, siden det er implisitt og ikke krever noen Java nøkkelord. Pakkeomfanget er viktig. Det lar deg teste en beskyttet metode. Beskyttede elementer er tilgjengelige fra testklassestien, så lenge pakken er den samme.
Hvordan kan jeg unngå det?
Husk den beskyttede modifikatoren og at du kan teste den i pakkeomfanget.
9. Bruk av ren JavaEE i stedet for Spring
Det neste steget etter å ha lært Java SE er å lære hvordan man kjører Java på servere, hvordan man lager en applikasjon på bedriftsnivå.
Nybegynnere går ofte i fellen når de skal lære seg JavaEE, siden det finnes et stort antall veiledninger om det. Selv "Thinking in Java", den Java-programmerere', nevner JavaEE, men sier ingenting om de andre alternativene.
Hvordan kan jeg unngå det?
JavaEE er en sang fra fortiden. I dag er Spring en selvfølge, og Java EE er bare hyggelig å ha. Alle moderne applikasjoner på bedriftsnivå bruker Spring, så du bør sterkt vurdere å lære deg det her.