"Hej! I dagens lektion kommer vi att fortsätta vårt samtal om in- och utdataströmmar i Java ( Java I/O ). Detta är inte den första lektionen om detta ämne, och det kommer definitivt inte att vara den sista :)
Eftersom det händer ger Java-språket många sätt att arbeta med I/O. Det finns en hel del klasser som implementerar denna funktionalitet, så vi har delat upp dem i flera lektioner — så att du inte blir förvirrad från början :) Tidigare lektioner, vi berörde
Så här ser det ut att läsa data från en fil med

BufferedReader
, liksom de InputStream
abstrakta OutputStream
klasserna och flera ättlingar. Idag ska vi överväga 3 nya klasser: , FileInputStream
, FileOutputStream
och BufferedInputStream
.
Klassen FileOutputStream
Huvudsyftet medFileOutputStream
klassen är att skriva bytes till en fil. Inget komplicerat :) FileOutputStream
är en av implementeringarna av den OutputStream
abstrakta klassen. I konstruktorn tar objekt av denna klass antingen sökvägen till målfilen (där byte ska skrivas) eller ett File
objekt. Vi kommer att undersöka exempel på var och en:
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Username\\Desktop\\test.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
När vi skapade File
objektet skickade vi den önskade sökvägen till konstruktorn. Vi behöver inte skapa det i förväg: om det inte finns kommer programmet att skapa det. Du kan också klara dig utan att skapa ett extra objekt, bara skicka en sträng med sökvägen:
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt");
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Resultatet i båda fallen blir detsamma. Vi kan öppna vår fil och se följande där:
Hi! Welcome to CodeGym — The best site for would-be programmers!
Men det finns en nyans här. Prova att köra koden från exemplet ovan flera gånger i rad. Titta sedan i filen och svara på denna fråga: hur många rader har den? Bara en. Men du körde koden flera gånger. Det visar sig att data skrivs över varje gång - den gamla ersätts av den nya. Vad gör vi om det inte passar oss och vi behöver skriva sekventiellt till filen? Tänk om vi vill skriva en hälsning till en fil tre gånger i rad? Det hela är väldigt enkelt. Eftersom språket inte kan veta vilket beteende vi behöver i varje enskilt fall, FileOutputStream
kan konstruktören ta en extra parameter —boolean append
. Om dess värde är sant kommer data att skrivas till slutet av filen. Om det är falskt (och som standard är det falskt), kommer alla gamla data att raderas och ersättas med nya data. Låt oss kontrollera detta genom att köra vår modifierade kod tre gånger:
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt", true);
String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!\r\n";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Filinnehåll:
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
Nu är det annorlunda! Glöm inte den här funktionen när du använder I/O-klasser. Det fanns en tid då jag spenderade timmar på uppgifter, tjafsade i timmar, försökte förstå hur min data försvann från filer :) Och naturligtvis, precis som med andra I/O-klasser, glöm inte att använda close()
metoden att frigöra resurser.
Klassen FileInputStream
DenFileInputStream
har det motsatta syftet - att läsa bytes från en fil. Precis som FileOutputStream
ärver härstammar OutputStream
denna klass från den InputStream
abstrakta klassen. Vi kommer att skriva några rader text i vår " test.txt "-fil:
"So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters"

FileInputStream
:
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");
int i;
while((i=fileInputStream.read())!= -1){
System.out.print((char)i);
}
}
}
Vi läser en byte från filen, konverterar de lästa byten till tecken och visar dem på konsolen. Och här är konsolutgången:
So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters
Klassen BufferedInputStream
Jag tror, med tanke på kunskapen från tidigare lektioner, att du enkelt kan säga varför vi behöver klassenBufferedInputStream
och vilka fördelar den har jämfört med FileInputStream
:) Vi har redan stött på buffrade strömmar, så försök gissa (eller kom ihåg) innan du fortsätter läsa :) Buffertade strömmar behövs främst för att optimera I/O. Att komma åt en datakälla, som att läsa från en fil, är en dyr operation i termer av prestanda och att komma åt en fil för att läsa varje byte är slöseri. Det är därför som BufferedInputStream
läser data inte en byte åt gången, utan i block, och lagrar dem tillfälligt i en speciell buffert. Detta låter oss optimera programmet genom att minska antalet gånger vi får åtkomst till filen. Låt oss se hur det här ser ut:
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 200);
int i;
while((i = bufferedInputStream.read())!= -1){
System.out.print((char)i);
}
}
}
Här skapade vi ett BufferedInputStream
objekt. Dess konstruktor tar en instans av InputStream
klassen eller någon av dess avkomlingar, så FileInputStream
kommer att göra det. Som ett ytterligare argument tar den buffertstorleken i byte. Tack vare detta argument kommer data nu att läsas från filen inte en byte åt gången, utan 200 byte åt gången! Föreställ dig hur mycket vi har minskat antalet filåtkomster. För att jämföra prestanda kan du ta en stor textfil (flera megabyte text) och jämföra hur lång tid det tar i millisekunder att läsa och mata ut till konsolen med FileInputStream
och BufferedInputStream
. Här är kod som visar båda alternativen:
public class Main {
public static void main(String[] args) throws IOException {
Date date = new Date();
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\textBook.rtf");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int i;
while((i = bufferedInputStream.read())!= -1){
System.out.print((char)i);
}
Date date1 = new Date();
System.out.println((date1.getTime() - date.getTime()));
}
}
public class Main {
public static void main(String[] args) throws IOException {
Date date = new Date();
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\26951280.rtf");
int i;
while((i = fileInputStream.read())!= -1){
System.out.print((char)i);
}
Date date1 = new Date();
System.out.println((date1.getTime() - date.getTime()));
}
}
När jag läste en 1,5 MB fil på min dator, FileInputStream
slutförde arbetet på ~3500 millisekunder, men BufferedInputStream
klarade det på ~1700 millisekunder. Som du kan se optimerade den buffrade strömmen arbetet och halverade det! :) Vi kommer att fortsätta att studera I/O-klasser — vi ses snart!
GO TO FULL VERSION