Chapter:Objects - Creating - Exercises

From Juneday education
Jump to: navigation, search

Exercises on creating objects

Creating String objects

Exercises for creating objects. Let's start by creating some objects which are described in classes that come with the Java installation. Such classes are also said to be part of "the Java API" (API: application programming interface), or "the Java Class Library". What's important to understand, is that when you install Java, you get a huge library of classes which you can use. This is why we have been able to use, for instance, the class String in some of the example Java programs we have looked at in previous chapters.

Classes are organized in packages. Packages are just directories in a hierarchical structure. Orginizing classes in directories makes for a logical grouping of related classes. Packages are normally many directories deep. Most of the standard Java classes from the API, exist in packages starting with java., such as java.lang.String. The actual class is called String, but it is located in the java.lang package. The full name of the class is hence java.lang.String. That the actual class is called String is obvious for two reasons; 1. The name String comes last in the full name. 2. The only thing that contains (and in particular starts with) a capital letter is the class name String.

Strings are very commonplace in Java programs which is why the String class is located in the java.lang package. Classes in that package are always available to the programmer. By "available", we mean that you can always use the class names directly, without including the package name. Classes in other packages cannot be used without one of the following two techniques:

  1. The programmer (you) always writes out the full name, including the package
  2. The programmer puts an "import statement" indicating what classes (and what packages) will be used in the code

Let's start with a simple use of the String class. Create a directory called objects_in_java and navigate to that directory. This is the directory you will use for the exercises in this chapter.

Next, inside the newly created directory, create the following directories:

org/progund/strings

They can be created all at once, using the following command in your shell:

$ mkdir -p org/progund/strings

Or you may create them incrementally, using three consecutive mkdir commands:

$ mkdir org
$ mkdir org/progund
$ mkdir org/progund/strings

Use the technique you feel most comfortable with. (The -p flag for the mkdir command, tells mkdir to create a whole hierarchy at once).

Now, start your editor so that it creates a new text file called UsingStrings.java in the org/progund/strings directory, and enter the following Java code:

package org.progund.strings;

public class UsingStrings {
  public static void main(String[] args) {
    String aName = new String("Adam");
    System.out.println("The String aName has the value: " + aName);
  }
}

Make a note of the indentation of the small program. There are two levels of indentation. One for everything in the class block, and one for everything in the main method block. Note where the curly braces are put. The opening curly for the class is at the same line as the class declaration "public class UsingStrings". The opening curly for the main method is at the same line as the main method declaration "public static void main...".

Note that all closing curly braces are alone on a single line each, and how they are aligned (how they are indented). Make a note of this. No, really, make a note of it!

Compile and run this small program. You can see in the solutions section an explanation of what the different parts of your program represented. To compile issue the following command (you should still be in the same directory as before!):

$ javac org/progund/strings/UsingStrings.java

You are telling the compiler javac to compile the Java source code file org/progund/strings/UsingStrings.java. It will create the byte code file org/progund/strings/UsingStrings.class (next to your Java program).

To run the program, issue the following command (you should still be in the same directory as before!):

$ java org.progund.strings.UsingStrings

What was printed to the screen? Now, add a few lines of code to your program, which will create and print a second String. This time we will show you a common way to create String objects, without using the new operator to invoke the constructor in the String class. You should note that this alternative technique, is only available for creating objects of type String! It was added to the Java programming language to make things more convenient for programmers, since strings are so commonplace in programs. This is what the program should look like with the two extra lines of code using the second technique for creating a String object:

package org.progund.strings;

public class UsingStrings {
  public static void main(String[] args) {
    String aName = new String("Adam");
    System.out.println("The String aName has the value: " + aName);
    String aFlower = "Tulip";
    System.out.println("The String aFlower has the value: " + aFlower);
  }
}

If you run your program again, what would happen? Feel free to try, but you probably know already that you must compile the program again, in order for the changes to the source code to be incorporated in the byte code file that the java command runs. So compile again, and then run the program again. Guess what will be printed this time! Compile/run commands:

$ javac org/progund/strings/UsingStrings.java
$ java org.progund.strings.UsingStrings

What was printed to the screen now? Did you guess correctly? See the solutions for an explanation of every line of code again, if you do not understand what is going on.

Discussion and solutions to the UsingStrings program

Expand using link to the right to see an explanation, discussion and suggested solutions to the UsingStrings program.

 1 package org.progund.strings;
 2 
 3 public class UsingStrings {
 4   public static void main(String[] args) {
 5     String aName = new String("Adam");
 6     System.out.println("The String aName has the value: " + aName);
 7     String aFlower = "Tulip";
 8     System.out.println("The String aFlower has the value: " + aFlower);
 9   }
10 }

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

Line 1

1 package org.progund.strings;

This is a package statement. Package statements are always the first line of code in a compilation unit (like a Java file with a class declaration). Usually every Java class belongs to a named package like this. Examples without the package statement leave it out to make things shorter - but in the real world, classes belong to packages, and packages always symbolizes the path to the file where the class is saved.

You created the directory structure org/progund/strings and put your Java file in that directory. Now, org/progund/strings/UsingStrings.java is a relative path to the Java file you wrote (relative from the directory where you first created the org... directories -- this is also the directory where you were when you compiled and ran your program). The package statement starts with the keyword package and then lists the directories leading to the class you are defining in the source code file. But instead of the usual / (forward slash) used as directory separators, in the Java programming language, packages us a dot . as a separator. The package statement ends with a semicolon ;. The semicolon is called a "separator" in the Java language. It is used to separate statements from each other.

You can think of the package statement on line 1 as meaning: "The class defined in this file belongs to the package org.progund.strings". This is now part of the full name of the class you are defining on lines to come.

Line 2

Empty line for readability - a Java source code file may have any amount of empty lines - they will be ignored by the compiler.

Line 3

3 public class UsingStrings {

Here begins the definition of a class called UsingStrings. Public classes like this may only be declared on a one-per-file basis (this rule applies to top-level classes - the first class declared in a source code file. Classes may declare classes inside them, but that is not material for this introductory course, and you may ignore that fact for now!).

The keyword public means that this class is available for any other classes that might be part of the same program (but we only have one class in this simple program). The left-curly-brace { at the end of the line is the start of the block of the class declaration.

Line 4

4   public static void main(String[] args){

On this line, the mandatory main method (the starting point for all Java applications) is being declared. This is where the execution will begin. The syntax of the main method is something you will learn by heart after writing many Java programs. We have discussed it in previous chapters if you want to review it. The static keyword will be discussed later in the book, but for now you can think of it as meaning "has nothing to do with objects, only the class". It has nothing to do with objects of the class you were defining, because the Java Virtual Machine (the java command) can run the main method to start the execution, without having to create any objects of your class. Much more on the static keyword later!

Line 5

5     String aName = new String("Adam");

On this line, you are doing a few things at the same time! You are declaring a reference variable that you called aName and the type of objects the variable can refer to is objects of class String.

Line 6

6     System.out.println("The String aName has the value: " + aName);

This is a revisit to the System.out.println() method call. As you probably remember, a call to System.out.println() makes your program to output text to the console (the terminal window where your program was run). The interesting part of this statement, is the use of the + operator! It is not used as an arithmetic operator here. It is used to join to String expressions together. On might say that the + operator is "overloaded" - which is to say that it has more than one use. It can be used as an addition operator (as we've seen in previous chapters) but also (as we see here) as an operator to joint two String expressions together. Joining two Strings is sometimes called "concatenating" two Strings. The result is actually a completely new String object with the value of the two operands joined together.

Note the first expression in the parentheses of the call to println. It is surrounded by double quotes. This is a way to create a new String with the value of the text within the double quotes. Note in particular that the text inside the double quotes end in a space. This is because we want to join it with the value of the String object which anName refers to ("Adam"). We want the resulting text to be exactly:

The String aName has the value: Adam

Since the value of the String object which aName refers to is simply "Adam" (without a preceding space), we make sure that the first String ends in a space, so that the result of joining the expressions looks readable.

It is quite common that when we want to print the value of some variable, we join some explanatory text, ending in a space, with the variable itself. The space at the end of the explanatory text makes sure the resulting text is more readable. Try and remove the space at the end of the first part so that it reads simply The String aName has the value: (without the trailing space), save the source code, recompile and run the program again. The result would look like:

The String aName has the value:Adam

That's much less readable (and less pretty), isn't it?

Line 7

7     String aFlower = "Tulip";

This line declares another String reference variable, this time named aFlower, and assigns it a reference to a newly created String object with the value "Tulip". The perhaps surprising thing about this statement, is that there is no call to the constructor of the String class via the new operator, yet a String object is created. This is an exception to how explicit object creation is done in Java. Strings are so common, that they decided to allow a simplified form of a constructor call, using string literals inside double quotes. Wherever you see a quoted string in Java code, the compiler will translate that to some form of constructor call, so each quoted String will be replaced with a new String object with the quoted value as the string value represented by the object.

Is that all it is to it? Unfortunately not. We will later see that quoted Strings are re-used, while Strings created the normal way, using new String("Some text"), are not re-used. There, we've said it, but you don't have to care about that just yet!

Line 8

8     System.out.println("The String aFlower has the value: " + aFlower);

This line uses the println method of the out object in the class System as usual, to print something to the terminal (the standard output stream, which is normally connected to the terminal where the Java program was run). As before, two Strings are concatenated into a new, longer, String which is printed out. And, as before, the first String in the expression (the argument to println) ends in a blank space so that the next String comes after the blank. The resulting String which is printed is The String aFlower has the value: Tulip. So, once again, we had concatenated some descriptive text and the value of the object represented by a variable (in this case the variable aFlower). And, you probably have seen by now that all statements so far end in the semicolon separator.

Line 9

9   }

This right-curly-brace ends the block of the main method. Execution ends before this line!

Line 10

10 }

This right-curly-brace ends the block of the UsingStrings class.

Not that the program uses indentation (did you use that too?). Indentation was when we aligned all statements inside a block to the same number of spaces. If you look at the code again, you'll see that stuff belonging to the outermost level (like the package statement, the class declaration and end-curly-brace, are aligned to the left margin. But all statements inside the class block (in this case, only the declaration and end-curly-brace of the main method) start at two spaces further to the right. And the statements inside the main method all start at an additional two spaces to the right. This style is only for us humans, so that we easier can see that statements at the same indentation level belong together (they are inside the same block). The compiler doesn't care at all about indentation. But we (the authors) do, and probably will your future colleagues also do, so, you should care too!

Sending information to the main method

Note: this section is important if you want to do and understand the assignments/hand-ins of this course material!

So far, we have seen programs in which we have programatically (using code we write ourselves) given objects values. But it is a little strange to write a program which gives for instance a String a value (the same value every time the program runs) and then prints it to the terminal (using System.out.println). It is strange, because you might think "Couldn't I just as well had written the string in the terminal myself?". Normally in a program, variables get their values from external sources like the user inputting values, the system environment variables, reading from a source on the internet, or, from a file.

In the next exercise, we want you to write a new program which gets different values for some variables each time the program is running. The values for the program to use are provided on the command line when the java command is called.

Before we start, we'd like to refresh some old things we talked about early on in the book. You might remember the concept of giving arguments to programs? You have been doing it many times already, actually. When you compile a Java source code file, you use the command javac and you give the command an argument - the file you want to compile. When you later run your Java program, you invoke the JVM via the command java and you give the command an argument - the name of the class (the class that has the main method). But can you pass arguments also to the main method? Of course you can! This is how you do that:

$ java some.packages.ClassWithMain argument1 argument2 ...

The ... at the end symbolizes that you could go on and add arguments on the command line. What would happen is that all the arguments, in the order you wrote them, will be passed on to the main method (the JVM is kind enough to help us with that!). But where do the arguments end up, inside the main?

Remember the signature (more accurately the definition) of the main? public static void main(String[] args)? The arguments (if any are given) end up in the args variable. The args variable is a parameter to the main method. A parameter (we'll talk more about parameters soon) is a way for a method like main to accept arguments. When we give arguments to println() for instance, they end up in some parameter of the code defining the println method (in the class PrintStream, but that is not important to know right now).

So what is special about the args parameter variable? You have probably noticed the square brackets between String and args. Square brackets (they always come in pairs when used like this) are separators that indicate a list of values. Such lists are called arrays. We'll talk more about arrays at a later point, but for now you need to know that String[] means "an array of String references".

An array has a length (the number of elements in the list) and each element has an index (starting at 0 for the first element, and then it increments by one up until the last element which has the number equal to "length minus one").

The args parameter is a variable referencing an array of String references. If the JVM passes on any arguments from the command line to the main method, it inserts them into this array with the first argument at index 0, the next argument at index 1 and so on.

There a few more things we need to know about arrays right now, and that is how to dereference a value at a certain index. If we have args, and args refers to an array of Strings (actually String references), and we'd like to do something with the String at index 0 (the first argument), we would use the square separators again, but this time to indicate an index:

    String firstArgument = args[0];

So, args[0] here means "the value in the args array at index 0". But, what happens if there were no arguments? Then the array would have no elements (a length of 0). Having no elements means that args[0] would be illegal and cause an exception (an error-like situation in the execution) and the program would actually crash. So before we dereference the String at index 0, we'd better make sure that the length is not 0! We'll use a simple if-statement for that, using a comparison of the length against 0.

But how do we ask an array object for its length? We use a dot separator to access the array's length member: args.length. The if-statement for the conditional access of element 0 then becomes:

    if( args.length != 0 ) {
      String firstArgument = args[0];
    }

If you need to review the material on blocks and if-statements, now is a good time to do so. We don't want firstArgument to be a local variable only visible inside the if-statement block (if we discover that we need it later in the main method!), so we declare it outside the if-statement and we initialize it to an empty String:

    String firstArgument = "";
    if( args.length !=0 ) {
      firstArgument = args[0];
    }

Now it is time for you write some code! The complete program for you to write is the following:

package	org.progund.strings;

public class PassingValues {
  public static	void main(String[] args) {
    String firstArgument = "";
    System.out.println("You gave " + args.length + " arguments.");
    if( args.length != 0 ) {
      firstArgument = args[0];
      System.out.println("The first argument was: " + firstArgument);
    }
  }
}

The class PassingValues belongs to the org.progund.strings package. Do you remember what the file must be named, and where the file must be saved?

The file must be named PassingValues.java and must be saved in the directory org/progund/strings/. The relative path from the current directory (you haven't left the objects_in_java directory, have you!?) to the file must be: org/progund/strings/PassingValues.java.

To compile and run you can use either:

$ javac org/progund/strings/PassingValues.java
$ java org.progund.strings.PassingValues my-first-argument

or:

$ javac org/progund/strings/PassingValues.java && java org.progund.strings.PassingValues my-first-argument

The argument to the program in the example above was my-first-argument. What did the program print?

Try running the program a few times but giving different arguments. Try without any arguments and try with many arguments. Try giving an argument surrounded by double-quotes and a whole sentence like "This is my first argument". What can we learn about quotes and arguments containing spaces?

Discussion and solutions to the PassingValues program

Expand using link to the right to see the an explanation and suggested solutions to the PassingValues program.

 1 package org.progund.strings;
 2 
 3 public class PassingValues{
 4   public static void main(String[] args) {
 5     String firstArgument = "";
 6     System.out.println("You gave " + args.length + " arguments.");
 7     if( args.length != 0 ) {
 8       firstArgument = args[0];
 9       System.out.println("The first argument was: " + firstArgument);
10     }
11   }
12 }

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

We'll focus on the statements in the main method (see above for the details of the package, class and main declarations).

 4   public static void main(String[] args) {
 5     String firstArgument = "";
 6     System.out.println("You gave " + args.length + " arguments.");
 7     if( args.length != 0 ) {
 8       firstArgument = args[0];
 9       System.out.println("The first argument was: " + firstArgument);
10     }
11   }

First we declare a String reference variable called firstArgument and initialize it to refer to a String object representing the empty string. An empty string is just two double quotes with nothing between them.

Second, we call System.out.println and we give it an expression using the concatenation of three operands as the argument. When this is run, the Java runtime will evaluate the expression to a new String using the string concatenation operator +. What is interesting about this, is that only the first and last operands actually have the type String! The middle operand, args.length is actually of type int. How is this possible? The rule is that when using +, if one of the arguments is a String, the other argument will be promoted to a String if it isn't already a String! But how does the JVM promote an int to a String? It actually creates an intermediate object of class Integer, using the int as the argument to the constructor. An Integer object is the object equivalent of the primitive type int. Next, it calls a method on the Integer object called toString() which turns the integer value into a string with the textual version of the value.

We haven't talked so much about methods yet, so don't worry if you didn't fully understand that. What is important to understand at this point is that if you use + to concatenate a String and an int, the result will be a string where the last part is the int as you would type it in a string. An int of value 1 will be converted to a String of value "1" (the character "1" as text).

The int in question here is the result of the sub-expression args.length. You can think of an array as an object which has a variable called length. To access a particular array's length variable, you write the reference variable followed by a dot and then the length variable. So the expression args.length could be understood as meaning: "the value of the length variable of the array referred to by args".

The purpose of this statement is to output to the terminal a text describing how many arguments were given to the main method. If no arguments were given the resulting text would be You gave 0 arguments. and if you gave five arguments, the text would be You gave 5 arguments. instead.

Third, we use an if-statement that uses the claim that args.length is not 0. This means that the body (the code in the block) of the if-statement will only be executed if args.lengh was something other than 0, i.e. if there were some arguments given to the main method.

Fourth, we - inside the block of the if-statement - we assign the firstArgument variable a reference to the String at index 0 of the args array (the first argument, since the arguments are put in the array in order starting with index 0).

Fifth, we output text describing the value (the text) of the first argument (now stored in firstArgument).

Note, that since we declared firstArgument outside (before) the if-statement block, we could continue to use it after the if-statement if we wanted to. If we'd declared the variable inside the if-statement, we'd be forbidden to use it outside (after) the if-statement since it would have been a local variable visible only in the block of the if-statement! Revisit the section about blocks and if-statements if you fail to remember or understand this!

Finally, some words on using quotes around arguments. This is useful when you want to give a string as an argument but the string contains spaces. If we'd written my first argument as the arguments to the program, the following would happen:

$ java org.progund.strings.PassingValues my first argument
You gave 3 arguments.
The first argument was: my

That is because each word would be interpreted as a separate argument. In order to send the sentence as one argument, we'd have to use quotes:

$ java org.progund.strings.PassingValues "my first argument"
You gave 1 arguments.
The first argument was: my first argument

Arguments challenge exercise

A challenge for the ambitious student: Change the program so that it writes "You gave one argument" (without the "s") if there was only one argument, and to keep the plural "s" if there were more than one arguments.

Suggested solution - arguments challenge exercise

Se the solutions below for an explanation of the program (in less detail this time, since you have already learned the basics!).

Suggested solution to the challenge exercise (using "argument" for one argument and "arguments" for more than one):

  public static void main(String[] args){
    String firstArgument = "";
    String argumentString = " argument";
    if( args.length > 1 ){
      argumentString += "s";
    }
    System.out.println("You gave " + args.length + argumentString + ".");
    if( args.length!=0 ){
      firstArgument = args[0];
      System.out.println("The first argument was: " + firstArgument);
    }
  }

Looping through the arguments

A sad truth about the previous program, is that we only write the first argument if there are one or more arguments. Since we don't know how many arguments the user passed to the main method, we don't know when to stop so we only wrote the first. We could have gone on and written something like:

    if(args.length > 1 ) {
      System.out.println("second argument was: " + args[1]);
    }
    if(args.length > 2 ) {
      System.out.println("third argument was: " + args[2]);
    }
    if(args.length > 3 ) {
      System.out.println("fourth argument was: " + args[3]);
    }
    // etc - but when to stop???

But that would have been even sadder! What if the user provided 500 arguments (just to prove us stupid)? There must be a way to handle every case (every possible number of arguments) generically! And there is! You might have guessed already, but since we know that we can ask args about the length (the number of arguments), we could use a loop for this!

To loop through every item of an array is so common that there is an idiom for this (as we'll see, there are actually two idioms). We know that we should start at 0, and access every index until we reach the last one (index (length - 1)). So we could use a for loop for this (a while loop would work equally well, but the idiom is to use a for loop):

  for (int i = 0; i < args.length; i++) {
    // do something with each element, like print it:
    System.out.println("Argument number " + i + " was: " + args[i]);
  }

This is a standard way of iterating over the elements of an array. There is an alternative way, called the "for-each loop". We haven't seen it yet because we didn't know arrays (and other lists) existed until just recently! Here is the same loop as above using the for-each loop:

  int i = 0;
  for (String arg : args) {
    // do something with each element, like print it:
    System.out.println("Argument number " + i + " was: " + arg);
    i++;
  }

The drawback of using the for-each loop here is that we have to create the index variable i outside (before) the loop, and we have to remember to increment it inside the loop after we've used it. If we need to use the index value, the for-each loop makes less sense, since we'd have to manage the index counter variable ourselves. But if we don't care about the index number, and just want to print every element of an array, the for-each loop makes things a lot smoother. Compare the following two snippets:

  for (int i = 0; i < args.length; i++) {
    System.out.println(args[i]);
  }

and, using the for-each:

  for (String arg : args) {
    System.out.println(arg);
  }

Your task is to write a program in the class org.progund.strings.WriteAll (in the file org/progund/strings/WriteAll.java ) which writes out every argument (if any) given to the program. You may choose to use the standard for-loop or the for-each loop, whichever feels more natural to you. Try both printing the index number and skipping the number and only write the argument strings.

Food for thought: Do we need an if-statement as well? Or does the for loop cover also the case of no arguments? Think about the condition of the for loop: i < args.length. What does that evaluate to when i is 0 and args.length also is 0?

Note! The for loop is typically used when we know how many times we want to iterate. In this case we know that we want to iterate the same number of times as the length of the args array. Other times, we know that we want to iterate the number of times decided by some other factor, like some user input. In the examples above, we use the for loops to iterate over arrays, but that's not the only time we want to use a for loop. The thing that often for loops have in common, is that we express the start value and then the number of times as a comparison to some known or set number (like args.length).

While loops often have some other means to express the condition for looping, like some boolean test that doesn't necessarily have to do with numbers. If you don't remember the examples from the control flow chapter, please revisit that chapter now! One example we used for a while loop, was to loop while some temperature was too low, and increase the temperature in the loop body. That's looping on a condition of some external input (like temperature) and not a fixed number of times.

Proposed solution to WriteAll

Se the solutions below for an explanation of the WriteAll program (in less detail this time, since you have already learned the basics!).

Our proposed solution uses all four combinations of the loops. First the standard for loop, which also writes the index number, next the for-each loop that also writes the index number (but we have to provide an index variable that we increment ourselves), next the standard for loop without writing the index, and finally the for-each loop without any index.

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

package org.progund.strings;

public class WriteAll {
  public static void main(String[] args){
    /* Classic for loop with printing of index */
    System.out.println("Using classic for loop");
    for (int i = 0; i < args.length; i++) {
      System.out.println("Argument #" + i + ": " + args[i]);
    }

    /* For-each loop with printing of custom index */
    System.out.println("Using for-each loop");
    int index = 0;
    for (String arg : args) {
      System.out.println("Argument #" + index + ": " + arg);
      index++;
    }

    /* Classic for loop without printing of the index */
    System.out.println("Using classic for loop without index");
    for (int i = 0; i < args.length; i++) {
      System.out.println(args[i]);
    }

    /* For-each loop without printing of index */
    System.out.println("Using for-each loop without index");
    for (String arg : args) {
      System.out.println(arg);
    }
  }
}

You might notice that in the for-each loop with a custom index, we named the index variable index. Can you figure out why we couldn't use the name i in this case? Hint: It is declared outside (and before) the last classic for loop and what does that for loop call its loop variable?

To compile and run the program, you can use the following:

$ javac org/progund/strings/WriteAll.java && java org.progund.strings.WriteAll a b c d e

Live coding videos for WriteAll

We have made two videos where we code a solution to this exercise here and we elaborate on the "food for thought" here.

A small command line MP3 player, written in Java

Meta: The purpose of this exercise (which is of the type "do as we say, copy our code and commands"), is to show you that you often create objects from classes you haven't written yourself. Not only classes from Java's API (such as String, ArrayList etc) but actually also classes from a third party library. Such libraries are downloaded in JAR files, which are a type of ZIP archive for Java classes. We thought it would be fun and interesting for you to see how such libraries (JAR files) can be downloaded and used, so we came up with this exercise. Here you will download a JAR file with classes for playing MP3 files, import and use such a class to create a "Player" object, which will play an MP3 file which you also have downloaded.

Let's do something more useful, like writing a small Java application which can play MP3 files.

In order to do this, you need to download a so called JAR file with some useful classes for playing MP3 in Java.

Start by creating a working director for this exercise and cd down to the directory:

$ mkdir mp3player
$ cd mp3player

Download jlayer-1.0.1-1.jar into your working directory:

$ wget http://central.maven.org/maven2/com/googlecode/soundlibs/jlayer/1.0.1-1/jlayer-1.0.1-1.jar

Next, we need something to play, so download some Chopin from WikiMedia:

$ wget https://upload.wikimedia.org/wikipedia/commons/2/22/Chopin_-_Waltz_in_E_minor%2C_B_56.mp3 -O ChopinWaltzEMinor.mp3

Next, write a small program which makes use of one class in the JAR file, the javazoom.jl.player.Player class. That's why we downloaded the JAR file. The program we are writing will create an object of type javazoom.jl.player.Player and invoke the play() method on that object.

We'll put our own class with the program in a "package" called se.juneday.mp3, so you need to create the corresponding directory structure:

$ mkdir -p se/juneday/mp3/

Next, use your editor to create the file se/juneday/mp3/Mp3Player.java with the following Java code:

package se.juneday.mp3;

import javazoom.jl.player.Player;

import java.nio.file.*;
import static java.nio.file.StandardOpenOption.READ;

public class Mp3Player {

  public static void main(String[] args) throws Exception {
    Path file = Paths.get("ChopinWaltzEMinor.mp3");
    new Player(Files.newInputStream(file, READ)).play();
  }

}

Verify that you have followed our instructions:

  • In your working directory, you should have the following:
    • ChopinWaltzEMinor.mp3
    • jlayer-1.0.1-1.jar
    • se (directory)
  • In the se/ directory you should have the following:
    • se/juneday/mp3/Mp3Player.java

You shouldn't leave the working directory. For everything to work, you need to stay in the directory where the JAR file, MP3 file and the se/ directory is.

Now it's time to compile the program. Since we are using javazoom.jl.player.Player which is a class in the JAR file, we need to tell the compiler where the JAR file is:

# If you are on Windows, do:
$ javac -cp ".;jlayer-1.0.1-1.jar" se/juneday/mp3/Mp3Player.java
#
# If you are on MacOS/Linux, do instead:
$ javac -cp .:jlayer-1.0.1-1.jar se/juneday/mp3/Mp3Player.java

To run the program, you need, for the same reason, to tell the java command where the JAR file is:

# If you are on Windows, do:
$ java -cp ".;jlayer-1.0.1-1.jar" se.juneday.mp3.Mp3Player
#
# If you are on MacOS/Linux, do instead:
$ java -cp .:jlayer-1.0.1-1.jar se.juneday.mp3.Mp3Player

As a challenge, change the program so that it takes one or more arguments (of paths to MP3 files on your computer) and plays those files, rather than the hard coded Chopin Waltz. Let's give some examples of how to run your program (after the updates) on Linux/MacOS (Windows user need to fiddle a bit with the command line as done above):

$ java -cp .:jlayer-1.0.1-1.jar se.juneday.mp3.Mp3Player dogs.mp3

Which should play the file dogs.mp3.

$ java -cp .:jlayer-1.0.1-1.jar se.juneday.mp3.Mp3Player dogs.mp3 pigs.mp3 sheep.mp3

Which should play the files dogs.mp3, pigs.mp3 and sheep.mp3.


Learn more about JAR files, on our page Java:Tools:Jar.

Here are three reasons why you shouldn't leave your working directory:

  • The program needs the JAR file and you said it was in the working directory
  • The program is in a class called se.juneday.mp3.Mp3Player and in order to run that class, Java needs to find the se/ directory
  • The program reads and plays ChopinWaltzEMinor.mp3 and looks for it in the same directory as where you ran the java command

Here's what the directory tree should look like:

.
├── ChopinWaltzEMinor.mp3
├── jlayer-1.0.1-1.jar
└── se
    └── juneday
        └── mp3
            └── Mp3Player.java

The . at the top is your working directory. From that place, you can reach everything you need (compile, run, jar file, mp3 file, edit the se/juneday/mp3/Mp3Player.java file...).

Links

Further reading

  • TODO: add more links for further reading

Where to go next

Next chapter is: Objects_-_Using

« PreviousBook TOCNext »