INF530 Lesson 1

Intro to Java

Since none of us are familiar with Java, Professor Philippe Chassignet gave us some instructions about Java.

System.out

Basically, there are two types of prints that we usually use in java:

  • println() - Print Line
  • print() - Print

See the following example:

public static class Hello {
    public static void main(String[] args) {
        System.out.println("Hello World!")
        System.out.println("Hello World!")
        System.out.print("Hello World!")
        System.out.print("Hello World!")
    }
}

Compile it and run, we’ll get:

Hello World!
Hello World!
Hello World!Hello World!

The difference is quite obvious.

Compile

For Java program, for instance, we firsly write our own Hello.java file (remember that the file name has to be consistent with the class name). Then we can either use an IDE to compile it, or we can use command line as shown below:

$ javac Hello.java
$ ls -l Hello.*
-rw-r--r--   1 bmiller  bmiller  391 Sept 18 14:47 Hello.class
-rw-r--r--   1 bmiller  bmiller  117 Sept 18 14:46 Hello.java
$

The command javac compiles our java source code into compiled byte code and saves it in a file called Hello.class. Hello.class is a binary file so you won’t learn much if you try to examine the class file with an editor.

Now that we have compiled our java source code we can run the compiled code using the java command.

$ java Hello
Hello World!
Hello World!
Hello World!Hello World!
$

Arguments

Let’s have a look at a new program here:

public class Arguments {
    public static void main(String[] args) {
        System.out.println(args[0]);
    }
}

If we compile and run it, what will happen?

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
    at Arguments.main(Arguments.java:5)

So we are receiving an error. So the arguments here are defined as an array of string named args (ererything in Java should have a type). Different from C++, whose arguments by default has at least one element which is the command line, the arguments in Java by default has nothing in the list.

If we try to print the length of the arguments like:

public class Arguments {
    public static void main(String[] args) {
        System.out.println(args.length);
    }
}

we’ll get:

0

We can define arguments either by editting configurations or using command line. The former method is easy. We only need to find a box named arguments in the corresponding window and then we shall see the difference. The latter one is even easier.

$ javac Arguments
$ java Arguments Hello World!
2
$

From this example, we can see that it takes the input after java Arguments as our arguments. And it’s parsed automatically by white space ` , which divides Hello World!` into two pieces.

Data Type

There are several types of data in Java. Similar to Python and C++, boolean, int, double, char, String, String[], …, are supported in Java. There are several things to be noted in Java however:

Double

Do not use == to check whether two Double typed data are the same.

int

Conversion between int and String:

int i = 18;
String str1 = Integer.toString(i);
String str2 = String.valueOf(i);
String str = '18';
int i = Integer.parseInt(str);

String

String type is immutable. However, manipulating strings in Java is not quite as obvious since Strings do not support an indexing or slicing operator. That is not to say that you can’t index into a Java string, you can. You can also pull out a substring just as you can with slicing. The difference is that Java uses method calls where Python uses Operators.

Python Java Description
str[3] str.charAt(3) Return character in 3rd position
str[2:5] str.substring(2,4) Return substring from 2nd to 4th
len(str) str.length() Return the length of the string
str.find('x') str.indexOf('x') Find the first occurrence of x
str.split() str.split('\s') Split the string on whitespace into a list/array of strings
str.split(',') str.split(',') Split the string at ‘,’ into a list/array of strings
str + str str.concat(str) Concatenate two strings together
str.strip() str.trim() Remove any whitespace at the beginning or end

Loop

Like C++, Java has both definite and indefinite loops

for (start clause; stop clause; step clause) {
    statement1
    statement2
    ...
}
while (condition) {
    statement1
    statement2
    ...
}
do {
    statement1
    statement2
    ...
} while (condition);

Besides, in Python, we can iterate over a list as follows:

l = [1, 1, 2, 3, 5, 8, 13, 21]
for fib in l:
   print fib

Likewise, in Java, we can iterate over an ArrayList of integers too:

ArrayList<Integer> l = new ArrayList<Integer>();
l.add(1); l.add(1); l.add(2); l.add(3);
for (Integer i : l) {
    System.out.println(i)
}

Class and Object

You have already seen how to define classes in Java. Its unavoidable for even the simplest of programs. In this section we will look at how we define classes to create our own data types. Lets start by creating a fraction class to extend the set of numeric data types provided by our language.

The requirements for this new data type are as follows:

  • Given a numerator and a denominator create a new Fraction.
  • When a fraction is printed it should be simplified.
  • Two fractions can be added or subtracted
  • Two fractions can be multiplied or divided
  • Two fractions can be compared
  • A fraction and an integer can be added together.
  • Given a list of Fractions that list should be sortable by the default sorting function.

The instance variables (data members) we will need for our fraction class are the numerator and denominator. Of course in Python we can add instance variables to a class at any time by simply assigning a value to objectReferenc.variableName In Java all data members must be declared up front, more like in C++ (including the definition of public, private, and ‘protected’ types).

Hence, the first part of the Fraction class definition is as follows:

public class Fraction {
    private Integer numerator;
    private Integer denominator;
    ...
}

Notice that we have declared the numerator and denominator to be private. This means that the compiler will generate an error if another method tries to write code like the following:

Fraction f = new Fraction(1,2);
Integer y = f.numerator * 10;

Direct access to instance variables is not allowed. Therefore if we legitimately want to be able to access information such as the numerator or denominator for a particular fraction we must have getter functions. It is very common programming practice to provide getter and setter functions for instance variables in Java.

public Integer getNumerator() {
    return numerator;
}

public void setNumerator(Integer numerator) {
    this.numerator = numerator;
}

public Integer getDenominator() {
    return denominator;
}

public void setDenominator(Integer denominator) {
    this.denominator = denominator;
}

Once you have identified the instance variables for you class the next thing to consider is the constructor. In Java, constructors have the same name as the class and are declared public. They are declared without a return type. So any function that is named the same as the class and has no return type is a constructor. Our constructor will take two parameters the numerator and the denominator.

public Fraction(Integer num, Integer den) {
    this.num = num;
    this.den = den;
}

The whole implementation of this class looks like this:

public class Fraction {

    private Integer numerator;
    private Integer denominator;

    public Fraction(Integer num, Integer den) {
        this.numerator = num;
        this.denominator = den;
    }

    public Fraction(Integer num) {
        this.numerator = num;
        this.denominator = 1;
    }

    public Fraction add(Fraction other) {
        Integer newNum, newDen, common;

        newNum = other.getDenominator()*this.numerator + this.denominator*other.getNumerator();
        newDen = this.denominator * other.getDenominator();
        common = gcd(newNum,newDen);
        return new Fraction(newNum/common, newDen/common );
    }

    public Fraction add(Integer other) {
        return add(new Fraction(other));
    }

    private static Integer gcd(Integer m, Integer n) {
        while (m % n != 0) {
            Integer oldm = m;
            Integer oldn = n;
            m = oldn;
            n = oldm%oldn;
        }
        return n;
    }

    public static void main(String[] args) {
        Fraction f1 = new Fraction(1,2);
        Fraction f2 = new Fraction(2,3);

        System.out.println(f1.mul(f2));
        System.out.println(f1.add(1));
    }

}

Assignment - Parsing URL

Excercise 1 - Parsing URL

Write a static method:

  • parse(String url) which parses the given String and prints the corresponding elements of the URL (i.e., the protocol, hostname, port and path)

The recognized URL syntax is of the form:

<protocol>://<hostname>[:<port>]/<path>

For simplification, consider that:

  • and are non-empty strings, which do not contain '/' nor ':',
  • is any integer, and
  • is any (even empty) string.
  • Note also that an url such as "http://www.google.com" MUST NOT be recognized as a valid URL, as a trailing ‘/’ is expected.
  • In any case of unrecognized syntax in url argument the method must throw an IllegalArgumentException, carrying a descriptive message indicating what went wrong.

For instance, for the input url "http://www.google.com/", your parse method should print:

url : "http://www.google.com/"
protocol : "http"
host : "www.google.com"
port : -1
path : "/" 

Exercise 2 - Defining a Java object for an URL

You have now to write a small equivalent of the java.net.URL class. This class will be named MyURL, in the default package and it MUST implement the following constructor:

  • MyURL(String url) that parses the given String and stores the corresponding elements of the URL (at least, the protocol, hostname, port and path) as fields of the object
  • In any case of unrecognized syntax in url argument the constructor must throw an IllegalArgumentException, carrying a descriptive message indicating what went wrong.
  • Your MyURL class MUST, at least, implement the following 4 methods getProtocol(), getHost(), getPort() and getPath(), as those specified for the class java.net.URL.
  • Note that, for our implementation, the value returned by getPath() always contains a leading ‘/’, and is never the empty String.

For instance, for the input url "http://www.google.com/",

getProtocol() returns "http", 
getHost() returns "www.google.com", 
getPort() returns -1, as an integer, 
getPath() returns "/".

My Solution

public class MyURL {
    private String url;
    private String protocol;
    private String host;
    private int port = -1;
    private String path = "/";

    MyURL(String _url) {
        this.url = _url;

        String[] tmp = url.split("\\/", 4);
        if (tmp.length == 4) {

            this.protocol = tmp[0].split("\\:")[0];
            this.path += tmp[3];

            tmp = tmp[2].split("\\:", 2);

            if (tmp.length < 2) {
                this.host = tmp[0];
            } else {
                this.host = tmp[0];
                this.port = Integer.parseInt(tmp[1]);
            }

        } else {
            throw new IllegalArgumentException("Invalid url!");
        }
    }

    String getProtocol() {
        return this.protocol;
    }

    String getHost() {
        return this.host;
    }

    int getPort() {
        return this.port;
    }

    String getPath() {
        return path;
    }
}

As you can see, this solution is rather primitive with brute force. However, it offers basic functions as required from the assignments. Also, if you want the url to be defined once and for all, try to define private variable like this:

public class MyURL {
    private final String url;
    ...
}

With syntax final, the variable can only be defined once. No other value can be assigned to it anymore. If you are trying to define it with a constructor as follows:

public class MyURL {
    public final String url;
    ...

    public MyURL(String _url) {
        this.url = _url;
        ...
    }
}

Then, it works basically in the same way as private.

Pattern and Matcher

Professor Philippe Chassignet then told us to try using Pattern and Matcher to parse the url. You can find online the references for Pattern and Matcher.

Throwing Error Warning

In order to throw error to users if the url they input is invalid, it would be better to throw IllegalArgumentException and respond with different situations.

try {
    ...
    if (...) {
        IllegalArgumentException e = new IllegalArgumentException("...");
        throw e;
    }
} catch(IllegalArgumentException e) {
    System.out.println("Caught an IllegalArgumentException... " + e.getMessage());
    //Or use:
    // e.printStackTrace();
    System.exit(0);
} finally {
    ...
}

In such way, an error can be caught and then implement corresponding reactions.

My improved Solution

import java.util.regex.Pattern;
import java.util.regex.Matcher;


public class MyURL {
    private String url;
    private String protocol;
    private String host;
    private int port = -1;
    private String path = "/";
    MyURL(String _url) {
        this.url = _url;
        try {

//          replace white space pattern
            Pattern p = Pattern.compile("\\s");
            Matcher m = p.matcher(this.url);
            this.url = m.replaceAll("");

//          remove HTML tags pattern
            p = Pattern.compile("]*>");
            m = p.matcher(this.url);
            this.url = m.replaceAll("");

//          validate URL
            boolean isValid = Pattern.matches(".+://.+(:\\d+){0,1}/.*", this.url);
            if (isValid) {

                p = Pattern.compile("://");
                String[] str = p.split(this.url);
                this.protocol = str[0];

                str = str[1].split("\\/", 2);
                this.path += str[1];

                if (Pattern.matches(".+:\\d+", str[0])) {
                    str = str[0].split("\\:", 2);
                    this.host = str[0];
                    this.port = Integer.parseInt(str[1]);
                } else {
                    this.host = str[0];
                }

            } else {
                throw new IllegalArgumentException("Invalid URL!!!");
            }
        }
        catch (IllegalArgumentException exp) {
            
            System.out.println("Caught an IllegalArgumentException... ");
            exp.printStackTrace();

            boolean isProtocolValid = Pattern.matches(".+://.+", this.url);
            if (!isProtocolValid) {
                IllegalArgumentException e_protocol = new IllegalArgumentException("Expected \"://\" ...");
                e_protocol.printStackTrace();
            }

            boolean isInvalidAtBegin = Pattern.matches("^\\W+", this.url);
            if (isInvalidAtBegin) {
                IllegalArgumentException e_begin = new IllegalArgumentException("Contain non-word characters at beginning!");
                e_begin.printStackTrace();
            }

            boolean isHostValid = Pattern.matches("://.+(:\\d+){0,1}/", this.url);
            if (!isHostValid) {
                IllegalArgumentException e_host = new IllegalArgumentException("Something wrong with the host.");
                e_host.printStackTrace();
            }

            boolean isPortInvalid = Pattern.matches("://.+:\\D*/", this.url);
            if (isPortInvalid) {
                IllegalArgumentException e_port = new IllegalArgumentException("Port has to be a positive integer!");
                e_port.printStackTrace();
            }

            System.exit(0);
        }
    }

    String getProtocol() {
        return this.protocol;
    }

    String getHost() {
        return this.host;
    }

    int getPort() {
        return this.port;
    }

    String getPath() {
        return path;
    }
}