- [Instructor] In the last video,
we used java dot NIO to write data to a data dot dat file.
Now we're gonna read that data back into the application.
Now we're going to use java dot NIO,
but we'll start off using java dot IO
to accomplish this reading of the data
back into the application.
Now a file that's just a file,
it doesn't matter how it was written
so we use java dot IO to write a file
and java dot NIO to read it and vice versa.
That really doesn't matter.
So let's look at the code we used
to write the data to the file.
First, we started off by using the write method
to write a series of bytes out
for our hello world byte array.
And, if you recall when you use methods such as write UTF,
the length of the string is written before the string itself
so that when the read UTF method's called,
the method knows how many bytes to read.
Now you didn't write the string length
because this is just a simple demo app.
After the strength, we followed on
by writing the integer 245,
which we defined on line 21 and wrote on line 23,
and then finally we wrote the integer
we defined on line 27, negative 98765,
and then wrote it with a code on line 29.
So now that we've done that, let's actually use
the java dot IO dot random access file class
to read in this data.
Now we're gonna use the same project, as you can see
that I've got open this last time,
and we're just going to add more code into the main method.
And we're going to add it into
the trial three sources block.
I'm going to clean out all of this code
that was commented out from a previous video.
Alright, so we're going to start here
with defining our random access file.
So random access file RA is equal to
new random access file, the file name being data dot DAT.
And the mode, we're going to do RWD,
as we've discussed previously.
Next, we're going to define a new byte array
that's gonna contain the data we're reading.
So, byte array B, equals new byte,
and we're going to define the number of bytes
based on the byte array link that we defined on line 16.
So we can just use new byte and then square brackets
output bytes dot length.
So we're getting the right length
for the hello world text there to our byte array.
And then we're going to the us the RA dot read method
and pass the parameter of B,
so that what we read is going into the byte array.
Then let's just do a proof of concept
by printing out a new string, B, which converts
the byte array to a string, and we should
be able to see that on our console.
So if you try that first, we should get a printout,
showing it, showing us rather, that we've run it okay,
and you can see we got hello world, showing there
on the screen, after the number of bytes that were written,
so clearly that's worked okay, so, so far so good.
So next, what we need to do is, we need to
read in the two integers.
To do that, we're gonna to a long, int one,
equals RA dot read int.
Then long, int two, equals RA dot read int,
and let's print them both out, so int one, int two.
Let's try that, make sure it works.
Alright, so you can see, we got the numeric results,
245 and negative 98765.
So really nothing remarkable here,
we've used the random access folder,
read int methods on lines 38 and 39,
to retrieve our integers.
Now it's okay to write the data every
time you run the main method.
Okay, you've demonstrated that data you wrote
is indeed what ended up in the file.
We demonstrated that by being able to retrieve it
and seeing the results on screen.
And that also confirms that the package
you use to write a file doesn't matter.
In other words, there's no association formed
between the file and the package.
You can use a different RA package to read the file,
or a different application, written in something
other than java for that matter, and that will
work just fine when we come back to reading it.
Alright, so again, as I mentioned, we're using
the random dot access file class, which is part
of the IO package.
What we're gonna do now is read the file using java dot NIA.
So for now, let's comment out that code
that we just used to retrieve,
retrieve it via the random access file class,
leave it commented there saying you've
got access to that code, and let's do the equivalent thing,
this time using java dot NIO.
So we actually start with a random access file again,
but I've left that code commented out so it's,
we've got an entire block of code there that's accurate.
So random access file, RA equals, new random access file.
We'll need that starting code again.
And set the mode to RWD again,
then now let's actually define a channel.
File channel, and file channels of NIO as you can see,
are from the NIO package.
Channel equals RA dot get channel.
And we're gonna use existing the buffers to
read the data back into the application.
Firstly the string that we're going to create
a new variable here, long num bytes read,
equals channel, dot read, buffer.
And we can see, we're writing back into our byte buffer
buffer here, that we defined on line 17.
So after this call the string, we'll be in
the variable buffer, which we used to write out
the string earlier in the main method
as I just pointed out.
Again that's on line 17.
But at least the definition for the buffer
is on line 17, we actually wrote it out, on line 18.
Now to print this string, we need access the byte array
back in the buffer.
Now in this case, there's actually two ways to get at it.
Firstly, if you recall that when we called
the wrap method, the byte array would pass as a parameter,
is used to back the buffer, so after the read,
the string should be in the output bytes, byte array,
and this is the byte array we defined on line 16.
And I'm talk about this wrap function here
on line 17 that was called.
So let's see if that's true, that does happen,
but first, are we forgetting something?
What do we have to do before switching from
writing to reading?
Well we should call the flip method.
But let's actually see what happens if
we don't call it, we're going to, at this state
we're now to print the output buffer, parable.
So I'm gonna do that after the call to channel dot read,
on line 35.
So we're doing output, and we'll call it,
we'll say output, bytes, equals,
plus new string, output bytes.
So if we try to run this now, let's see what happens.
Alright, so we see the expected string,
hello world's showing, in the console,
but this is actually deceiving, and that's because
the output bytes byte array, already contained
hello world, from when wrote the file originally,
and because we didn't call flip, the read method
that we called, here on line 35, didn't actually do anything
and that's because the buffer's position
was actually at the end of the buffer.
So to demonstrate this, we're gonna change the contents
of the output bytes array, before we do the read.
So let's just go ahead and just put a couple
of changes there, so I'll put, output bytes
then zero in square brackets equals A,
then output bytes one equals B,
and actually that should've been before
our call to read, so I'm just going to,
cut it out of there and put it in before.
So that's after the RA dot read channel,
but before the channel dot read,
so I'm gonna put it in there.
Alright, and now we're going to run this again,
and you can see we've now got A, B, LLO world now showing,
which is representative of the fact that
we changed the output bytes variable on line 35
and lines 36, they list the first two elements
of the byte array.
So what should've happened is, when
we read this buffer, we've actually saved this
as hello world, and that's what's been saved to the file.
So we've actually changed the buffer, in memory,
to AB as the first two characters there,
as the first two charters you can see there,
so the read to the read buffer,
should've actually replaced that with hello world again,
so clearly that confirms that the read method
isn't being called correctly.
Or rather, it is being called, but not actually
returning what we think it should be returning.
So now to demonstrate this further,
let's add a call to flip, and we'll do that
after those two definitions, on line 35 and 36.
So we'll do buffer dot flip, and we'll run that again.
And this time you can see we've now got
the expected results.
We're seeing hello world printed to the console,
even though we changed the byte array,
on line 35 and 36, but then of course we should've
got hello world showing because we wrote that out
originally on line 18, and now that we've done this,
introduced the call to flip, the read method
is successfully able to read that byte array
back in, and we're getting hello world printed.
The bottom line, always remember to call the flip method.
Now it unfortunately so easy to skip this step,
and this is one reason why many developers
aren't really all that keen on using java dot NIO,
to do IO for their files.
Now there is another way to get the string from the buffer.
We could call the byte buffer dot array method,
which returns the byte array, backing the buffer.
Now, to do that, it's actually good practise
to call the byte buffer dot has array method
before doing that to ensure that you can
access the byte buffer backing the array.
So let's add the code to get the byte array
using this second way of doing it.
So after the read, what we're going to do,
we'll just comment that line out for now,
and we'll put, if buffer, dot, has array,
then we're going to do, printout of,
output to be byte buffer, is equal to,
plus new string, then we'll do buffer, dot array.
We'll just demonstrate then see that that still works.
We've still got hello world printing out,
so that's working okay.
Okay, so what I'll do is, now read the two integers,
using the existing buffer instance.
So to do that we're going to type int, buffer dot flip.
Then we're gonna do num bytes read, is equal to
channel dot read, int buffer.
And then int buffer, dot flip again,
and we're gonna do a print out, int buffer,
dot get int, then we need to do int buffer
dot flip again, and num bytes read is equal to,
channel dot read, int buffer, int buffer dot flip,
and we need to printout, int buffer dot int.
And the other thing we should really be doing,
is closing the channel and file after reading the data,
and that's because we didn't use
the try with resources statement to open them.
So we need to do channel, dot close,
and also, random access file object dot close as well.
And we'll just run this to make sure that it does work.
And we get our two integers, the number 245
and negative 98765.
But we had to flip between each access of the buffer,
and just to confirm the recall, actually writes the buffer,
and then the get int method, reads the buffer
if that makes sense.
Now to avoid calling flip, you can use another
version of the get int method.
When you don't pass any parameters as we're doing here,
our call to get int on line 45,
and also on lines 49, or line 49,
if you do that without any parameters,
then the read begins at the buffer's current position,
but instead you can pass an index to get int,
and when you do that, the read will begin
at the index that you passed.
So the first type of reads based on the buffer's position
is called a relative read, the second type of read
is called an absolute read.
So we're doing obviously a relative read here
because we're not passing any parameters,
but let's now change the code so we can pass zero
to the get int calls, and get rid of one of
the flip calls for each method.
So what I'm going to do here, just to make it clear,
I'm going to comment this code out
so you've got a copy of it.
And we might actually put on comment in there
to that effect, actually I'll include
this first line as well.
Tap that over a little bit.
And we'll say this is the relative read,
and now we're going to put absolute read.
So, it's a little bit different the code,
and I'll close this front window down
so we can see the code for both.
So we start off still doing the initial flips,
so int buffer dot flip, but then we can do
num bytes read, equals channel dot read, int buffer.
So far that's the same.
But we go to print it out now,
we can do int buffer dot, get int,
but pass the number zero as I mentioned,
this is basically going to begin the read
at the index position we specified, zero in this case.
And, then we need to do one flip,
int buffer dot flip, and basically we can
do the same thing again there.
We'll just copy that code.
So I can see that we've only called flip twice
here in total, but in this case here
for the relative read, we actually called
it a total of four times.
But, importantly, let's just check that it actually works.
Still got the same results as you can see.
And just to be clear, I'm not actually using
the num bytes read variable here,
we could print that out if you wanted
to know how many bytes we've read,
I'm just sort of showing you the code for that.
So basically by using a relative,
sorry, by using an absolute read,
which was defined from line 43 down to line 49,
we don't have to flip the buffer after reading
from the file channel, because we used
the absolute version of the get int methods
on lines 46 and 49 where we passed the index number of zero.
Now you can do relative and absolute gets,
for all the data types, and you can do the same
when doing puts to the buffer, so in other words
you can flip the buffer, or you can provide an index.
You have to do one or the other.
Now and depending on what you're trying to do,
you may not have a choice, but when you do,
good idea as always is just to be consistent.
Alright, so let's finish the video here,
in the next video, I wanna go through and just talk
about the buffer position, and how it's not updated
when you're using absolute indexes.
But more on that, in the next video.