Data Types, Input and Operators in Java

Hello people…! This is a new post in Java Tutorials – Data Types, Input and Operators in Java. In my previous post, Java Tutorials – An Introduction, I introduced you to the Java programming language. We didn’t do anything more than printing text on the terminal. Now, we will learn to write short programs which will involve a little computation.

 Data Types in Java

Data Types in Java

Data Types in Java

These are the data types in Java. The primitive data types are much like the C++ data types. But, unlike C++, String is not a primitive data type. Strings in Java are objects. The Java library has the String class and we create their objects when we deal with strings. When it comes to numeric data types, Java does not have any notion of unsigned integers. So, the numeric data types in Java can always store positive and negative values. We will talk about the non-primitive data types later. For now, focus on getting comfy with the primitive data types.

The sizes of the primitive data types are –

  • byte – 1 Byte
  • short – 2 Bytes
  • int – 4 Bytes
  • long – 8 Bytes
  • float – 4 Bytes – up to 7 digits of precision
  • double – 8 Bytes – up to 15 digits of precision
  • char – 2 Bytes – But holds only up to 1 character
  • boolean – JVM Dependant

Taking Input on Terminal

Before we start writing programs, we need to learn how to take input from the user, so that, we can keep testing our code for various inputs. There are several methods to take input from the user. We will look at the simplest one, by using the Scanner Class. Scanner is a class in the java.util package, or, library, a more familiar term. It has all the functions related to taking input from the user. So, to use those methods, we must first create an object of class Scanner.

Scanner scan = new Scanner(System.in);

This is how we create a an object of the class Scanner. An object is an instance (an occurrence) of a class. We use the new keyword to create an object. We will have a detailed discussion about classes and objects later. The memory for the object is dynamically allocated by the JVM. And it looks like we are calling, some sort of a function, it is a Constructor…! It is used to instantiate an object. We will have a detailed discussion about Constructors later, but for now, I must ask you not to panic looking at that statement… 😛 …. We will learn everything..!

On the right hand side, we have “Scanner scan”. The “Scanner” is to specify the data type of the variable “scan”. This variable, “scan”, is called an Object Reference. It stores the address of the newly allocated memory (by using new) for the object. We access this object by the Object Reference. But remember, this is not a pointer..! With pointers, we can add or subtract values, called Pointer Arithmetic, but we can do no such thing with Object References.

Having created an object, we can access the methods that are provided by the Scanner Class, by using the “.” operator. Let’s look at an example program to make things clear. This program is an interest calculator, which takes the necessary information from the user –

import java.util.Scanner;

public class InterestCalculator {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);

        System.out.println("Hello..! Enter your name -");

        String name = scan.nextLine();

        System.out.println("Hello " + name + "..!"
                + " Please enter the principal amount -");

        int amount = scan.nextInt();

        System.out.println("Enter the rate percentage -");

        float rate = scan.nextFloat();

        System.out.println("Enter the time period -");

        int time = scan.nextInt();
        float interest = amount * time * rate;

        System.out.println("Your interest amount is - "
                + interest);
    }

}

This is the program. It demonstrates how we scan for the most basic data types, integer, string and float. The methods that scan the specific data types return that value, which was used to initialise the variables in the above program. The one new thing there is while printing, we can use the “+” operator to concatenate the strings. The other is the import statement. It is used to include library files. You can think of it as #include of C for now. It was used to include the Scanner class which is in java.util.

Now, let’s look at the various operators in Java and later, we will have some really cool discussion about the data types. The operators are very much like the operators in C. So, if you know C well, you enjoy the dividends of your efforts here, otherwise, you pay for your sins…! 😛

Arithmetic Operators in Java

Operator Meaning Example
+ Addition
int a = 10;
int b = a + 10; // 20
Subtraction
int a = 10;
int b = a - 5; // 5
* Multiplication
int a = 10;
int b = a * 7; // 70
/ Division
int a = 50;
int b = a / 5; // 10
% Remainder / Modulo
int a = 11;
int b = a % 3; // 2
++ Increment
int a = 10;
++a; // 11
a++; // 12
Decrement
int a = 10;
a--; // 9
--a; // 8

Relational Operators in Java

Operator Meaning Example
< less than
int a = 10, b = 20;

if (a < b) {
    // true
}
> greater than
int a = 10, b = 20;

if (a > b) {
    // false
}
<= less than or equal to
int a = 10;

if (a <= 10) {
    // true
}
>= greater than or equal to
int b = 20;

if (b >= 20) {
    // true
}
== is it equal to
int a = 10, b = 20;

if (a == b) {
    // false
}
!= is not equal to
int a = 10, b = 20;

if (a != b) {
    // true
}

Logical Operators in Java

Operator Meaning Example
&& AND
boolean a = true, b = false;

if (a && b) {
    // false
}
|| OR
boolean a = true, b = false;

if (a || b) {
    // true
}
! NOT
boolean a = true;

if (!a) {
    // false
}

Bitwise Operators in Java

Operator Meaning Example
& bitwise AND
int a = 2;

a = a & 1; // 0
| bitwise OR
int a = 2;

a = a | 1; // 3
^ bitwise XOR
int a = 3;

a = a ^ 1; // 2
~ bitwise NOT / complement
int  a = 8;

a = ~a; // -9
// 2's complement
<< left shift
int a = 8;

a = a << 1; // 16
>> right shift
int a = 8;

a = a >> 1; // 4
>>> right shift with zero fill
int a = 8;

a = a >>> 1; // 4

Shorthand Operators

Operator Meaning Example
x += 1; x = x + 1;
int a = 2;

a += 1; // 3
x -= 1; x = x – 1;
int a = 8;

a -= 1; // 7
x *= 2; x = x * 2;
int a = 8;

a *= 2; // 16
x /= 2; x = x / 2;
int a = 8;

a /= 2; // 4
x %= 2; x = x % 2;
int a = 8;

a %= 2; // 0

Operator Precedence in Java

Operator Meaning
. Member Selection
function() Function Call
arr[] Array’s Random Access
-var Unary Minus
var++ Postfix Increment
var– Postfix Decrement
++var Prefix Increment
–var Prefix Decrement
! Logical Negation
~ Complement
(data_type) Type Casting
* Multiplication
/ Division
% Modulo Division
+ Addition
Subtraction
<< Left Shift
>> Right Shift
>>> Right Shift with Zero Fill
< Less than
<= Less than or equal to
> Greater than
>= Greater than or equal to
instanceof Object-Class comparison
== Is it equal to
!= Is not equal to
& Bitwise AND
^ Bitwise XOR
| Bitwise OR
&& Logical AND
|| Logical OR
(condition) ? true : false Ternary Operator

These are the precedence levels. We still haven’t discussed a few operators, such as instanceof. We will discuss them soon when we talk more about classes. You need not mug up the whole hierarchy, but if you have an idea of the hierarchy between the arithmetic operators, relational operators and logical operators, it’s enough, because those are the most frequently used ones. But if you ever need, you can always come here to lookup the precedence levels of various operators.

Automatic Type Conversions

Java employs “widening type conversion” between the primitive data types. Which means that if a binary operator has operands of two different data types, the data type of the result will be the data type of the bigger operator (which occupies more memory). And an operation between a floating point operand  and an integer type operand always yield a floating point result. This is because Java considers integer-to-floating-point to be a widening conversion. The widening conversion is safe and integer-to-floating-point conversion preserves accuracy. All through, we talked about the data type of the resulting expression. The value which is stored may ultimately depend on the LHS variable.

This is the automatic type conversion in Java. The widening type conversion is implicit, that is, it is done by Java automatically. But, sometimes we may want to do a narrowing type conversion. We can do this, but we must ask Java explicitly for it, by using the type casting syntax.

LHS  = (data_type) (expression)

It is a good practice to enclose the expression in parenthesis too, so that we can avoid logical errors, because type casting has a very high precedence.

Here are some examples –

public class CircleArea {

    public static void main(String[] args) {
        float radius = 49f / 13;   // 3.7692308

        System.out.println(radius * Math.PI);
        // 11.841387924765852

        int area = (int) (2 * radius * Math.PI);

        System.out.println(area);   // 23
    }

}

The points to be noted in the program are –

  • Math.PI is of type double which has the value of Π. It is multiplied with a float number, giving a double, demonstrating the widening type conversion.
  • If you don’t enclose the expression within parenthesis, it’s a compilation error, saying, “incompatible types: possible lossy conversion from double to int”. This is because of the precedence of the type casting.

Default Numeric Data Types

We must take a small note on how Java treats numeric data types. There are only two things to keep in mind –

Firstly, whenever Java sees a constant floating point number in an expression, it treats its data type to be double, not float. So, the declaration –

float f = 3.14;

Gives a compilation error, saying, “incompatible types: possible lossy conversion from double to float”. So, what Java is doing here is –

  • It treats the floating point constant, 3.14, as a double value.
  • It tries to convert the value to what is expected, which is float.
  • If it feels that the conversion may cause a loss of information, it gives a compilation error, saying “incompatible types: possible lossy conversion from ____ to _____”.
  • If it is so, we must explicitly type cast the value to the required data-type.

Remember, there can possibly be a loss of conversion only, if we are assigning a bigger data type (here double), to a smaller data type (float). So, the remedy for initialising float variables is –

float num = (float) 3.14;
float var = 3.14f;

The first is type casting, and the second is giving a suffix ‘f‘, to tell Java to treat it as a float number. Be careful and keep these concepts in mind because we will use them again very shortly.
Secondly, whenever Java sees an integer constant, it treats its data type to be int. So, this declaration will cause an error –

byte b = 12345;

Therefore, we must use type casting –

byte b = (byte) 12345;

In that case, what value is stored in the variable b..? We know that 12345 won’t fit in a byte. In such a situation, the excess bits are truncated..! So, the variable b would take only the first 8 bytes of 12345 (0011000000111001), that is 00111001… From the right..! 😉 … So, the variable b has the value 57. And don’t forget that the last bit is used for the sign..!

The same goes when we want to initialise long variables. The following initialisation won’t work –

long l = -123456789123456;

But we all know that the given constant is well within the range of long. The compiler gives an error saying, “integer number too large: -123456789123456”. And we know why..! Java is treating the integer constant’s data type to be int..! And what’s worse, type casting won’t work here..! Why would it work..? We can’t even store the value in the first place…! So, how on earth could we initialize double variables…? We can do, by giving a suffix, ‘l’ or ‘L’ to tell Java to treat it as a long value –

long l = -123456789123456l;

This is a little advanced discussion about the data types in Java. I can perfectly understand if you don’t follow me on this topic… 🙂 … But I wan’t you to remember how to initialize float, byte and long variables.

What Compliers know and don’t…!

This too, is a little advanced discussion, feel free to skip this…! 😉 …. I hope you got an idea about the problem we faced with –

byte b = 12345;

Now, what if we do something like –

byte b = 57;

Does this too cause a compilation error..? No..! This is because, Java knows that 57 is well within the range of byte. So there is no “lossy conversion” here. So, Java does not complaint…! Feeling good..? Check this out..!

int num = 57;
byte b = num;

This gives a compilation error..! Regarding assigning the value of num to b, the compiler repeats its dull old story, “incompatible types: possible lossy conversion from int to byte”. Now what is this…? We know that logically, we are trying to assign 57 to b, which seems to be perfectly all right..! So, what’s the compiler whining about…?
The thing is that – “A compiler knows about the data types only. It knows nothing about the values“. This is because values are a runtime stuff. So, when the compiler comes to Line 2, it sees that we are assigning a variable of type int to byte. It knows that the range of int is more than that of byte, so there is a possibility of information loss during the conversion. This is why the compiler gives an error.
But then, the 57 being assigned to num on Line 1.. isn’t that a value..? Well, yes, we are assigning a value, but what we are assigning is a constant. A constant is not a runtime stuff, it does not change, but a variable could change. I could to hell a lot of computation between Line 1 and 2. So, Java doesn’t want to take a chance, so it gives a compilation error, which can be resolved by explicit type casting.

So, golden rule here is that the compiler doesn’t know about the values. This is a very very important one..! This can be used in many places and is really helpful in solving problems, which would otherwise baffle other programmers..! So, trust me, you’ve just added something really powerful in your arsenal..! 😉 … And, you are a real genius if you already know this..! 😛

Summary

  • The primitive data types in Java are divided to numeric and non-numeric. The numeric data types are –
    • byte – 1 Byte
    • short – 2 Bytes
    • int – 4 Bytes
    • long – 8 Bytes
    • float – 4 Bytes – up to 7 digits of precision
    • double – 8 Bytes – up to 15 digits of precision

    The non-numeric data types are –

    • char – 2 Bytes – But holds only up to 1 character
    • boolean – 1 Bit
  • We take input from the user using the functions in Scanner Class. Some of the useful functions are (if “scan” is the Object Reference) –
    • scan.nextLine() – to scan a String
    • scan.nextInt() – to scan a int.
    • scan.nextLong() – to scan a long.
    • scan.nextFloat() – to scan a float.
    • scan.nextDouble() – to scan a double.
  • The operators in Java are much like the operators in C.
  • Java employs widening automatic type conversion.
  • We initialize the respective data types as follows –
    float var1 = 5.1236f;
    float var2 = (float) 5.1236;
    long var3 = 12346876314l;
    byte var4 = (byte) 12345;
    
  • A compiler knows about the data types only. It knows nothing about the values.

Practice

  • There is really nothing challenging except for the discussions we had on the data types. I don’t want to give problem which asks you to lookup into the table of precedence levels. It is monotonous. So, it only makes sense to work a little on the data types. So, try to make a table between data types and fill out the table with either “implicit” or “explicit” or “not possible” according to the type of conversion required between those two data types. Try to include all types of primitive data types.

This was to get you started with the data types in Java. It is very much like C. So, I hope you were comfortable with this post. Feel free to comment your doubts..! We will cover more topics in another post… Till then… Keep practising…! Happy Coding…! 😀