Lecture notes for Monday, 7/14

I've updated the references page with a bunch of sites that have info about Java.

Today marks the beginning of the second half of the semester. For those enrolled in CSCI-A 202/598, this is just the second half of the class. For those enrolled in CSCI-A 290/590, this is an entirely different course, with it's own separate grade. Since we don't have any new students, we'll keep mostly the same set-up as before, although the subject has changed.

Java

Today we start talking about the programming language Java. To begin with, we'll use the command line compiler/interpreter and only work with command line programs. Later, we'll look into graphical programs and/or web design.

Permissions

Since your homework assignments aren't going to be on the web anymore, you'll have to manage the permissions of your directories and files a little more carefully. You should have a homework directory with read and execute permissions set for everyone. So if I'm storing all my Java programs in the directory ~/homework/java, then I need to make sure that the directory java has read and execute permissions set for everyone, and its parent directories (including your home directory) need to have execute permissions set for everyone. In class, I created such a directory and demonstrated setting the correct permissions.

[ewennstr@silo ~]$ cd homework
[ewennstr@silo homework]$ cd ..
[ewennstr@silo ~]$ ls -ld homework
drwxr-xr-x 2 ewennstr faculty 4096 Jul 14 09:29 homework
[ewennstr@silo ~]$ chmod a+x .
[ewennstr@silo ~]$ cd homework
[ewennstr@silo homework]$ mkdir java
[ewennstr@silo homework]$ chmod a+rx java

You'll also have to remember to make your source code files and compiled files readable and executable (chmod a+rx) to everyone.

"Hello World" in Java

Even the simple "Hello World" program has a lot going on when it's written in Java. Eventually we'll talk about each keyword in the program, but I may gloss over a few things until a more appropriate time to talk about them arises. Here's our program, which we called hello.java:

public class hello {
  public static void main(String[] args) {
    System.out.println("sup");
  }
}

Java is an aggressively object-oriented programming language, so just about everything happens as part of a class. Classes can be public or private, and we'll talk about the difference later. For the moment, just know that we need to make our main class public. The line public class hello creates a static class named hello. The block of code (surrounded by {curly braces}) following this command actually defines the class. Because it is a "static" class, when this hello class is created, it will create an actual object named hello. A static class is a class that has one and only one object (which has the same name as the class). We'll talk about "non-static" classes later, which don't automatically create objects, but instead serve as "templates" for future objects.

If we want our class hello to do anything when it's run, it has to have a function/method that is named main() which is public and static, which takes an array of Strings as its arguments, and which returns nothing (that's what void means).

The input array args might contain some initializing information, but often it's just an empty array.

We want to print to the command line, which is what System.out points to. (Technically, System.out is an object of type OutputStream.) The function println prints out a string, followed by a line break. If you don't want to print a line break, use the function print instead.

Just like with PHP, a line of Java code that doesn't end with a {} block of code must end with a semicolon. I said in class that the Java compiler ignores line breaks, but there is an important exception. You can't have a line break in the middle of a string. You can use the escape code \n to insert a line break, but if the compiler finds an actual line break in the middle of a string, it will give an error and fail to compile. So if you're using pico, don't forget the -w option to suppress line breaks.

So we saved the file and then compiled it using the Java compiler program that's already installed on Silo:

[ewennstr@silo java]$ javac hello.java
Picked up _JAVA_OPTIONS: -Xms512m -Xmx512m
[ewennstr@silo java]$ ls
hello.class  hello.java

You can ignore the Picked up... message.

Of course when it comes to Java, "compiling" doesn't quite mean the same thing as it does with other programming languages. Up until now, we've been using Python and PHP as scripting languages (and not as compiled programming languages), which means that there's an interpreter that has to be run that takes the code and interprets it as a program. Typically, with compiled programming languages, the code first must be "compiled" by a "compiler", which turns the code into a relatively low-level language, such as assembly, which can then be run quickly and easily without the need of an interpreter. Now every computer is different, and what works as assembly code for a Windows box won't work for a Macintosh or a Unix machine or an Android smartphone.

One of the key motivating factors for creating Java was to solve the problems created by having to compile programs for each potential operating system separately. The idea was to compile each program only once, into "Java bytecode", which can be run by a virtual machine (the Java Virtual Machine, or JVM), which can be run on any operating system. You don't need to know anything about the details of Java bytecode, but you do need to know that it exists. When we compile a program (as we did using the javac command above), we're compiling to bytecode, and the resulting file (in this case, named hello.class) can be run on any machine that has the JVM installed.

The command to run a compiled Java class using the Java Virtual Machine is java:

[ewennstr@silo java]$ java hello
Picked up _JAVA_OPTIONS: -Xms512m -Xmx512m
sup

Getting Input

Just as System.out refers to the command line output stream, System.in refers to the input stream that comes from the keyboard. There are a number of different ways of getting at that input, but we'll just use what's called a "text scanner". A text scanner provides a number of useful tools for parsing text data, whether that comes as an input stream (like System.in), a text file, or even another string. Today, we're only going to use the nextLine() method, but you may find the methods next(), nextInt(), and nextFloat() useful as well. See here for more details. The class Scanner is defined in the java.util module.

To use the tools provided by the Scanner class, we need to actually have an object of type Scanner. Such an object is created by a command like new java.util.Scanner( System.in ). This command creates a scanner that reads its text from the input stream System.in, one line at a time. Of course, if we want to refer to this scanner more than once, we need to create a variable (let's call it keyboardScanner), and that variable must be of type Scanner. We did both of these things with two lines of code:

java.util.Scanner keyboardScanner;
keyboardScanner = new java.util.Scanner( System.in );

Remember that this doesn't actually get any input from the keyboard, it just creates an object that has good tools for getting the input. (In class, I named this scanner userInput, but I think the name keyboardScanner makes it clearer what's going on here.) To get a line of text from the keyboard, we'll use the scanner method nextLine(), which waits until the user hits Enter and then returns a string that contains all the text the user typed in before hitting Enter.

If we want to do anything interesting with this text, we'll want a variable of type String to store that text. Now we could use two lines of code" one to declare the variable to be of type String, and one to assign the next line of user input to that variable. But we can actually combine both of these steps into one line of code:

String name = keyboardScanner.nextLine();

Note the use of the string concatenation operator +, just as in Python.

So we put it all together to get something like:

public class hello {
  public static void main(String[] args) {
    System.out.println("sup");
    java.util.Scanner keyboardScanner;
    keyboardScanner = new java.util.Scanner( System.in );
    System.out.println("What's your name?");
    String name = keyboardScanner.nextLine();
    System.out.println("Your name is " + name + ".");
  }
}

It was pointed out during class by a few of you who had used Java before that we could save ourselves the effort of typing out java.util. every time we want to use the Scanner class by importing the module (or part of it) at the very beginning of the file. So our program could be simplified a little as follows:

import java.util.Scanner;

public class hello {
  public static void main(String[] args) {
    System.out.println("sup");
    Scanner keyboardScanner;
    keyboardScanner = new Scanner( System.in );
    System.out.println("What's your name?");
    String name = keyboardScanner.nextLine();
    System.out.println("Your name is " + name + ".");
  }
}

In fact, you could import all of the java.util classes with a line like import java.util.*;.

Primitive Data Types vs. Classes

Note that we didn't talk about primitive data types in class, but you should know about them.

In a way, there are two kinds of data "types" in Java. So far, we've seen Java classes like Scanner, InputStream, and String that are essentially a kind of data type. We can declare a variable to have one of these types, or to use the language of "classes", we might say that the variable stores an object which "instantiates" that class. These classes are powerful because in addition to storing data, they come with various methods for accessing or altering that data.

But Java also has a few "primitive" data types (such as int, boolean, and char), which are not wrapped inside a class. You can see all eight of them described here. In many cases, there is a "wrapper" class that stores the same information as a primitive type, but which also adds some useful methods. For example, the class Integer stores the same information as the primitive type int, but it comes with methods that can do things such as converting Integers to Strings and vice versa.

In general, you can tell the difference between a class and a primitive data type because the names of primitive types are all lowercase, while the names of classes typically start with a capital letter.

The Integer Class

To have the user input an integer, we used the technique of reading in the input as a String using nextLine(), and then converting it to an Integer. We could also have taken advantage of one of the many scanner methods like nextInt(), but I wanted you to get some practice converting data types. One way to convert from a String to an Integer is to use the Integer.parseInt() function.

import java.util.Scanner;

public class hello {
  public static void main(String[] args) {
    System.out.println("sup");
    Scanner keyboardScanner;
    keyboardScanner = new Scanner( System.in );
    System.out.println("What's your name?");
    String name = keyboardScanner.nextLine();
    System.out.println("Your name is " + name + ".");
    System.out.println("A=?");
    String inputLine = keyboardScanner.nextLine();
    Integer A = Integer.parseInt( inputLine );
    System.out.println("A * 5 = " + (A*5));
  }
}

Note the way we used the concatenation operator + in the last line of the program. In general, Java is very picky about data types, but this is one of those exceptions. When a string is followed by +, Java will convert the next object (at least for most of the normal classes) into a string and then concatenate.

We made one last change to the program. Instead of multiplying by 5, what would it be like if we divided by 5?

import java.util.Scanner;

public class hello {
  public static void main(String[] args) {
    System.out.println("sup");
    Scanner keyboardScanner;
    keyboardScanner = new Scanner( System.in );
    System.out.println("What's your name?");
    String name = keyboardScanner.nextLine();
    System.out.println("Your name is " + name + ".");
    System.out.println("A=?");
    String inputLine = keyboardScanner.nextLine();
    Integer A = Integer.parseInt( inputLine );
    System.out.println("A / 5 = " + (A/5));
  }
}

We compiled and ran the program:

[ewennstr@silo java]$ javac hello.java
Picked up _JAVA_OPTIONS: -Xms512m -Xmx512m
[ewennstr@silo java]$ java hello
Picked up _JAVA_OPTIONS: -Xms512m -Xmx512m
sup
What's your name?
Chris
Your name is Chris.
A=?
4
A / 5 = 0

Why 0 and not 0.8? Because A is defined as an Integer, so when you divide it by the integer 5, Java assumes you are doing "integer division", which must return an integer. So instead of giving you the exact value as a floating point decimal or as a fraction, it gives you a quotient, throwing away the remainder. (Or equivalently, you could think of it as dividing and rounding down to an integer.) If you don't mean to do integer division, you have to ensure that your values are of type Float, Double, or BigDecimal (or of one of the primitive types float or double).