Reading

Required: These notes!
Recommended: Java in a Nutshell, Part 1, Chapter 1, Sections "Primitive data types" and "Arrays".

Java's three different kinds of types

Java has three different kinds of types: primitive types, array-objects. and class-objects. Be careful: in the context of Java, "object" is a technical term with a different meaning than it has in C++.
primitive values array objects class objects
int i = 7;
double x = 4.32;
char c = 'q';
boolean b = i*x > 28.5;
int j = (int)x;
byte b = (byte)255;
int[] A = new int[2];
A[0] = 5;
A[1] = 7;
String[] B = new String[3];
B[0] = "hi";
B[1] = "bye";
B[2] = "wow";
String s1 = "Groot";
String s2 = "I am" + s1;
Scanner s = new Scanner(System.in);
String s3 = s.next();
String s4 = s3.toLowerCase();
String iAmEmpty = new String();

Important! Now, here's the truly important thing to know: values of a primitive type are basically the same as in C++; picture a box with the value written in the box. Assignment copies the contents of the right-hand side box into the left-hand side box. Function arguments are passed by value, just as they are by default in C++. Variables referring to array-objects and class-objects work just like pointers in C++ (though they are called references in Java); picture a little box with an arrow pointing to the box that actually has values written inside.

public class Ex1
{
  public static void foo(int a, int[] b, String c)
  {
    // do something here!
  }
  public static void main(String[] args)
  {
    int i = 4, j;
    int[] A = {3,5,7}, B;
    String s = "hey", t;
    j = i;
    B = A;
    t = s;
    foo(i,A,s);
  }  
}
   
This depicts the state of affairs at the // do something here! comment. Notice that we always have pass-by-value semantics in Java, but that passing a reference by value means getting a reference that points to the same thing. This is exactly the same as the behavior we saw with pointers in C++!

Because variables of array-object or class-object type are references, they behave differently than the primimitive types when their not initialized. For example:

int k;
String s;
The variable k is an uninitialized int ... it's and int, we just can't rely on it having any specific value. The variable s, however, is a reference that doesn't refer to any object. We call this a null-reference, and it's just like a NULL-pointer. The literal for the null-reference is simply null. Thus, you can make a test like
if (s != null) ...
or set a reference deliberately to null like:
s = null;

Primitive Types

The primitive types of Java... are not very interesting.

Primitive types: boolean char byte short int long float double
What's up with the byte type? Well, Java's char type is actually 16-bits, because Java uses Unicode rather than ASCII, so that strings can contain characters from many different languages. While that's pretty transparent to us as programmers, it leaves us without the char-byte equivalence we rely upon in C/C++. Therefore, Java has a type byte that is an 8-bit signed integer — just like C/C++'s char type.
They're very similar to C++: int, double, char, and boolean (instead of bool) are the common ones. There is also: byte, long, short (which are integral), and float (which is decimal). Operators are the same as well: +,-,*,/,%. All the rules about integer division/modulus still apply.

So what happens when this is run?

  int i = 13;
  int j = 7;
  int k = 2;
  double m = 0.0;
  System.out.println("values: " + (i%j) + " " + (j/k) + " " + (j+m)/k);

  boolean a = true;
  boolean b = false;
  System.out.println("values: " + (a+b));
  System.out.println("values: " + (a||a) + " " + (a || b) + " " + (b || b) + " " 
                    + (a&&a) + " " + (a && b) + " " + (b && b));

  char x = 'y';
  char y = 'x';
  System.out.println("values: " + x + " " + y + " " + (x+y));

What would happen if we took out the () inside the printlns?

Conversions.

Just as with C++, types can be converted to one another, but with more restrictions.

Widening conversions are converting a type to a "larger" type, like int to double. There are 19 possible:

These can be done automatically:

      int i = 3;
      double d = i;
    

the int 3 is widened to a double. Widening usually doesn't lose information.

Narrowing can lose information and precision, and requires a "cast". There are 22 Narrowings

      float fmin = Float.NEGATIVE_INFINITY;
      float fmax = Float.POSITIVE_INFINITY;
      System.out.println("long: " + (long)fmin + ".." + (long)fmax);
      System.out.println("int: " + (int)fmin + ".." + (int)fmax);
      System.out.println("short: " + (short)fmin + ".." + (short)fmax);
      System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax);
      System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax);
    

Everything can be converted to a string. That's what is happening when you do this: "STRING " + 3; There are a number of other conversions that we'll talk about later on.

When do these conversions happen?

  1. During assignment, as we saw with widening.
  2. During function calls:
  3. 	  public static double foo(double argh){
    	     return (argh*argh)/(argh-1);
    	  }
    	  public static void main(String[] args){
    	     int i = 3;
    	     System.out.println("foo: " + foo(i));
    	  }
    	
  4. to Strings when using the +.
  5. When casting: int i = (int)3.3;
  6. Numeric promotion. This is what happens when you multiple an int by a double. The int is promoted to a double, and then the multiplication occurs.

Strings

As with C++, strings are not a "primitive type," but are objects of class String. As you have already seen, you can make string constants by putting text in quotes. String stringVariable = "String Constant";

One important difference is you can NOT treat a string like an array. stringVariable[1]= 'p'; will cause your compiler to yell at you. However, Java Strings DO have a function called .charAt(int), which will retrieve a copy of the character at a certain index in the string. Additionally, Strings have a large number of other useful functions, including .length(), indexOf(), .substring(), .toUpperCase() and many, many more. It's worth taking a look in your book or online to see all the things Strings can do.

As with C++, the '+' operator with Strings does concatenation - i.e. glues the strings together.

String s = "he", t = "llo";
String w = s + t; // now w is "hello"
What's crucially different, however, is that an expression of the form String + value is evaluated by converting value into a String and then doing concatenation. Thus, "r" + 3 results in "r3". The + operator is left-associative, so "r" + 3 + 2 gives "r32", but 3 + 2 + "r" gives "5r".

One last important point about strings: they are immutable, meaning they can;'t be modified. So, if we have String s = "hello world"; and we want to capitalize the first letter, we can't simply modify the first letter of the String object s refers to. Instead, we have to make a new String that looks like what we want.

s = s.substring(0,1).toUpperCase() + s.substring(1);
This makes a new string "h" from s, then makes a new string "H" from the first, then makes a new string "ello world" from s, then makes a new string by concatenation, then assigns s to point to the result. What happens to the original s, "h", "H" and "ello world" you might ask? They are orphaned! They sit out there on the heap and are no longer referred-to/pointed-to by anything. In C++ this would be a disaster, but not in Java. Java has automatic garbage collection, which means the virtual machine automatically detects and reclaims orphaned memory! That's a huge burden off your shoulders as a programmer!

Arrays

In java, there are only dynamic arrays.

      int[] anIntArray;
      boolean[] aBoolArray = new boolean[10];
      String[] aStringArray = { "array", "of", "String" };
    

As with C++, building an array is a three-step process of declaration, allocation, and assignment. The declaration (like int[] anArray) creates the variable, not the memory itself, so we need the "new" to allocate the memory in the heap. The brackets can go on either side of the variable name.

Access is done as with C++. Note, however, that we can always use .length to arrive at the length of an array. This is not a function, as with Strings, but is an attribute of the array:

      int[] ia = new int[101];
      for (int i = 0; i < ia.length; i++)
          ia[i] = i;
      int sum = 0;
      for (int i = 0; i < ia.length; i++)
          sum += ia[i];
      System.out.println(sum);
    

If we change one of the < to a <=, our C++ experience tells us to expect either a Segmentation Fault or a silent bit of weirdness, which is kind of horrible. Instead we get a message about an ArrayIndexOutOfBoundsException, and an indication of which line caused it to happen, which seems strangely wordy, but much, much more useful.

Multidimensional arrays work just the same, but are easier to allocate:

  int[][] anArr = new int[10][9]; //An array with 10 rows and 9 columns
  for (int i = 0; i < anArr.length; i++) //anArr.length is 10, just like C++
    for (int j = 0; j < anArr[i].length; j++) //anArr[i].length is 9, just like C++
      anArr[i][j]=0;

Problems