1z0-808 Oracle Java SE 8 Programmer – Java Conventions and Primitives Part 2
- Primitive Literals
Working with literals seems pretty easy. We just declare the literal type and assign a value. But there are a few pitfalls to be aware of. We’re going to go over the topic of default literals in this lecture and then the next lecture we’ll talk about casting. So we’ve already seen some literal values already. What we mean by saying literal value is that we can look at the value of a primitive and just by looking at how it’s been typed, we can tell what data type it is. So we have, for example, any single character that has single quotes that’s going to be interpreted as a char, as a character. If the word true or the word false is written in all lower case and has no quotation marks, then that literal type is going to be a boolean. We have some literal types for our numbers as well.
So let’s start with any number that doesn’t have a decimal is an int literal by default. If I want to have a long and I would need to add a letter L right after the number. Any floating point or decimal number is by default a double, unless we add the letter F to the end, which makes it a float. We could add the letter D if we wanted to, to make a decimal a double, but it’s kind of redundant. It’s a double anyway. So let’s take these rules and look at some code. So if I have a value two, then by default it’s going to be an int. And in just a second I’m going to show you a couple of use cases where that’s not true. So the two in this case it’s a whole number. It’s going to be assumed to be an int. If you have the letter L, it’s going to be a long notice that the second version is also an L, but it’s a lowercase L and that kind of looks like a one. You might mistake that for 21.
So bottom line, don’t use a lowercase L even though it’s legal, it can be hard to read. 2. 2. There’s a decimal, so that’s going to be interpreted as a double. And then we have another decimal number but with an F. So that’s going to be interpreted to be a float. There’s the D added to another decimal number, but as we’ve already said, it’s redundant, it’s not necessary. Any decimal number is going to be assumed to be a double. We can work with other number types as well. We can work with octal and hexadecil numbers. Any integer that starts with a zero is considered an octal and that can throw people off, especially if you’re trying to pad numbers. So for example, if I have the number 70, you might think that’s 70, but it’s 70 in Octal, which is a base eight numbering system.
And so if we were to convert that to the numbering system we typically use, which is base ten that would be equivalent to the number 56. We can do Hexadecimal with zero X. If you start off any of the numbers with zero X, it’s assumed to be base 16. And so seven E here is Hexadecimal for 126. In our normal base ten numbering system in Java Seven, we had a new literal that was created. So it’s the binary literal. You can use a capital B or a lowercase b that follows a zero. So zero b 101 is binary, which is the same as saying the number five in base ten numbering. Another cool thing that was added in Java Seven was the underscore.
And the underscore doesn’t change the numeric value, but it gives us a separator so that we can better read what the number is. If I was to ask you to look at this number and tell me what it is quickly, some of you would say 12. 3 million and others would say 1. 2 million. It just depends on how often you read numbers without commas. But so many of us are used to having a separator in the numbers. If you’re from the United States, that separator is going to be a comma. If you’re from other countries, that might be a space or a decimal. So they use something that was neutral, not a comma, not a space, not a decimal, they use the underscore.
And so now we can add the underscore to the value and it just makes it easier to read. And there are a couple of rules about the underscore I’ll mention in just a minute. But here’s just another example. We’ve got the 1. 234,567 number and we could use the underscore to make that a little bit more readable as well. So the feature is really just to separate natural groups of digits. In numeric literals, the underscore character is ignored wherever they are placed. But there are a few rules. First of all, it can’t be used as the first or final character. It can’t be used immediately in front of or after a decimal. So there’s an example, something that’s legal. This second Y int is illegal because we have the underscore at the very end here. We have underscore in a decimal, totally legal. And now we’ve got the underscore right in front of the decimal and that is illegal.
- Primitive Conversion
There are going to be times when you’re working with primitives that you’re going to need to convert them from one type to another type. And so this is called conversion or casting. And there’s really two directions that this can go. There’s a widening conversion and there’s a narrowing conversion. So let me show you a few examples here. Let’s start with what a widening conversion would be. So imagine that I have int x equals five. Now I have a little chart above the INTX in case you’re ever trying to remember how many bits is a specific type. Well, int is 32 bits. And so that we’re saying that we’ve got a variable called x that can store a 32-bit number. Now, five is a pretty small number, but remember that the literal type is going to be an integer. So can I take a 32-bit number and store it in a 32 bit variable? The answer is yes, of course I can. That works great. How about if I have a long, just call it Y and I have the number five again, does that work? And the answer is sure it does. Now, how many bits is this number? Five. Well, it’s still 32 bits. The literal data type is an int and an int could we store a 32 bit number into a 64 bit variable called Y? Sure.
The variable is huge. It only needs 32 bits. So that is what’s known as a widening conversion. And so if we look at our smallest well, we can’t do bullions. That’s really our smallest primitive, but we can’t cast bullions. That was something we mentioned in the last lecture, but we can cast bytes. So if I’ve got a byte and let’s just make that I’ll make a byte, b equals four. Now, you might be thinking, wait a second, I thought you said if it’s a whole number, it’s an int and it’s 32 bits. Well, I also mentioned that there was a slightly strange rule that breaks the int literal. And what I mean by breaking the int literal is that if I take a number, which would normally be interpreted as a 32 bit number and I immediately, in the same statement, assign it to a smaller bite or short, well, it’s just going to work. It’s going to convert this 32 bit number into an eight bit number and then it will work.
It’ll do the same thing for short. Short S equals five and also char C equals four. And that’s legal. Now, you might be thinking, wait a second, there’s a lot of wait a seconds here. Right? I thought four is a number and char is a character. Well, when you assign a number to a character, it’s the Unicode code point. And so four will be looked up in that table and we could interpret it as a character. Not going to do that a whole bunch. But it is legal. The only thing that’s really important here is just to note that when we have a single statement, even though a whole number would normally be interpreted as a 32 bit, we can legally assign it to be a byte or a short if needed. Now getting back to this idea of widening conversion. If I’ve got this byte B equals four, could I store the value of B in my int? And let’s just get rid of some code here just to clean this up.
Well, I mean you can tell it works, right? We don’t have any x’s, no red underlines, so it’s valid. What it’s doing is it’s reading the value of B, which is four excuse me, an eight bit number and then it’s taking that eight bit number and storing it into a 32 bit variable. That’s widening. It just works out of the box. We don’t have to worry about it. The only thing that you really have to worry about is when you do something like if I’ve got an int and I’ve got some kind of a number here for an int into x two equals 123-45-6789, just make it some big number and then I try to store that in a float. Float F equals x two. It is possible because a float, even though these are both 32 bit numbers, so they would fit within each other.
The problem is that some of the bits mean different things to the float. We have to have some way of representing what bit represents, the decimal. And so you can lose precision. That’s the only thing that you have to worry about. You might lose a little bit of precision between a whole number and converting it into say, a float here, but otherwise widening conversion just works. Now let’s go the opposite direction. Let’s say that I’ve got my int, x equals let’s do this. X two equals ten. And now I’m going to say byte B two equals x two. No, that’s not going to work. What’s happening? We have a 32 bit number that was assigned to a 32 bit variable. We’re taking the value of that 32 bit variable and assigning it to an eight bit variable called byte B two. So eight bits on this side, 32 bits on this side, and the answer is no, that’s not going to work.
Now you could actually look at this number and say, yeah, but I know a little bit about bytes. And ten is a reasonable number. It fits in between negative 128 and 127. So it’s a legal byte number. So if you know it’s legal and you want to store it in the bite, well then what you do is what’s called casting. And so we simply write in parentheses immediately to the left of what it is we’re trying to convert and we put the type. So what we’re doing is we’re saying, hey compiler, just trust us that x two is legally a byte. So convert it into eight bits. And what it’ll do is it’ll cut it down into eight bits. If the number was really too big, it would do either an overflow or an underflow. So what do I mean by that? Well, let’s say I’ve got, for example, let’s make a big number 200. I’m going to put this in A. Let’s just do this. Put it in main public static void, main string ARGs. Let’s put all this stuff in there, that way we can run it. So let’s see what happens when I put a number that’s actually a little too big for this. B two. Let’s print it out. So system out, print line b two.
All right, let’s run it. Any guesses while we’re getting this configured? All right, there’s the number. Negative 56. That’s not probably what we expected. Oh, look, I two semicolons and it still worked. Sometimes java can be forgiving. But the problem that I was talking about was this number, 200. It’s a legal int. 200 fits within 32 bits, but the number 200 does not fit within eight bits. So when we converted it to an eight bit number, what it did is it had to get rid of some bits, and it does either an overflow or underflow, which means that it recalculates the number. If any of you remember the video game Defender, it’s kind of like going off the other side of the screen and then appearing on the opposite side. That kind of an idea. So the top number for an into would be 127. Let’s run that there. You can see down below that it says 127. That’s great. But watch.
When I put 128 now, we’re out of the range of a byte. Our value is negative 128. What if I go to 129 now it’s negative 127, I put 130, negative 126, and so on. So what happened is, when we have an overflow or underflow, it goes to the very top or the very bottom of the range, and then it starts over on the other side. That’s what’s happening. And that can be confusing. That can give you numbers that you wouldn’t expect. So, long story short, we can legally and easily do widening conversion. It’s not a problem. But we can’t do narrowing conversion unless we explicitly cast it. And we cast it by having parentheses in the data type. We put it to the left of the value that we’re trying to cast, and we just got to be careful. We just got to make sure that it is in fact legal, that it’s going to fit within the range that we would, that we’re casting it to.
- Big Numbers
One of the obvious limitations of the primitive is its size. There is a finite amount of data that we can store in a primitive and there will be times where that doesn’t work for us. We may need a larger or a more precise number than what is provided for us with our primitives. So for example, we’ve got a long. And a long can hold a really big number from negative two to the power of 63 to two to the power of 63 minus one which is roughly like 9. 2 quintillion. And we also have a Java double which is 64 bits. And that can allow us to store a very precise negative number. But what if we got a large number like 9. 3 quintillion which is outside the range, outside the top range of a long?
So if you’re doing any science or any kind of serious engineering, physics, astronomy, cryptography you’re going to run into algorithms and processes where you’re going to need larger numbers or more precise numbers. And so when you hit that limitation of primitives java gives us an API which we can work with that will allow us to have larger numbers and more precise numbers. So two of the classes they give us are big integer and big decimal and both are in the Java math package. Now with big integer you can work with arbitrarily large whole numbers and with big decimal. Now we can have arbitrary precision at our fingertips. And so when we say the word arbitrary here what we’re meaning is that at that point we’re really just limited to our computer’s memory capacity.
It’s not the size of the variable, it’s the memory in our computer. Now these are just regular Java classes so you’re going to have to instantiate them in the same way you would other objects. There are different constructors that are available for each of these different types. Some allow you to pass in a string or a byte array. Here’s a couple of examples. Here we’ve got big integer and big decimal. And in both cases we are passing in a string as the constructor. That’s a common mistake when you first start working with big integer or big decimal that you forget you’re passing in a string. The string will be converted into the big or precise number that you’re trying to create. So now we’ve got for example a my big int which is the 9. 3 quintillion.
In the next chapter we’re going to learn about some of the different operators like the plus, the minus, the multiplication division and so on. We can’t use that with big integer and big decimal because these are objects and these kind of operators only work with primitives. So what we do is we use methods that are available in big integer, in big decimal and they’ll perform the calculations. So remember that we had our big int previously. We’re creating another big integer. This one is called two. We’re passing in the number two. And now if I want to multiply, I can take my big int, which was the big integer we created in the previous slide, and we’re calling a method multiply on that passing in the big integer, which is called two. And that will do the math, and then it will return to us a new big integer object, which is even bigger, can do the same thing with big decimal as well. So each of these classes have lots of methods where we can perform mathematical computations on them.
- Primitives Lab
In lab number five, you’re going to be working with primitives. So you will be declaring primitives. You’ll be exploring up casting and down casting. You’ll be working with comments to comment out code. And if you decide to do the bonus lab, you’ll be working with big integer and big decimal. So the instructions for this lab are available in the resources section for this lecture. Look for labs five primitives PDF, download that and have added.