Being a good Android developer means being good with Java, though not necessarily a good Java developer. That might sound weird, but it’s true. The architecture of Java apps is very different from Android apps, but the language is the same. Think of it this way: being a good writer of technical documentation means being good with a language like English, but it doesn’t mean you need to be a good writer of novels or other literature. You want to master the language itself and apply it to each type of project. That’s why we looked at the basics of Java for Android development in Part 1 of this series and are continuing here with more information about the Java programming language. In Part 1 we covered data types, variables, and methods, and in this post we’re going to talk about classes, objects, conditionals, and loops.
Contents
Comments
In some of the code samples below I’m going to make notes in the code using comments. Comments in Java code are lines of code that don’t have any effect on how the program runs. They are purely informational, meant to help us understand how the code works or is organized.
Single Line Comments
Single line comments begin with two forward slashes: //
. Everything on the line after the forward slashes makes up the comment and is completely ignored by the program.
// This is a single-line comment String code = "4-8-15-16-23-42"; // Only this part is a comment
Multi Line Comments
Sometimes we want longer comments that span more than one line. In that case we use an opening marker to mark the start of the comment: /*
, and a closing marker to mark the end of the comment: */
.
/* This is a multi-line comment. Though only the opening * and closing markers are required, we often add an * asterisk at the beginning of each line to make it * more readable, and end the comment with the ending * marker on a new line. */
Classes and Objects
In general, files of code in Java are organized as classes. A file (with the file extension .java) usually contains one class definition, and it gets compiled into a machine-readable class file (with the file extension .class).
Remember from Part 1 that Java is an object-oriented programming language, meaning that it is built around the concept of objects. Objects can be representations of physical objects, like a mug, or more abstract objects, like a connection to a website. A class is meant to define an object and how it works. In this way, a class is like a blueprint of an object. Classes define things about objects as properties, or member variables of the class. And abilities of the object are defined as methods.
Let’s take a look at a class that could be used to represent a simple connection to a website:
public class UrlConnection { // Member variables (properties about the object) private String mUrl; // Methods (abilities of the object) public void setUrl(String url) { mUrl = url; } public void connect() { // Code to make an HTTP connection goes here } }
In this example we have one member variable mUrl
which represents the URL that this object is intended to connect to. The lower-case “m” prefix is a convention to indicate member variable. This helps with scoping issues as we can see in the setUrl() method where a different variable named “url” is clearly different from the member variable for URL. We also have two methods, or things this object can do. The first is called setUrl()
and is used to set the private member variable (more on “private” in a minute), and the second is called connect()
and would be where code to establish a connection to the URL would go.
ACCESS MODIFIERS
For classes, member variables, and methods, we usually want to specify who can access them. In some cases we want things to be publicly available, meaning that anyone can see and do things with them. Other times, we may want to keep things private to protect data from the outside world or accidental changes. The keywords public
, private
, and protected
are access modifiers that control who can access the class, variable, or method.
Access Modifiers | Same Class | Same Package | Subclass | Other Packages |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
private | Y | N | N | N |
no access modifier | Y | Y | N | N |
Packages
Classes are often grouped together along some kind of criteria into packages. A package is simply a collection of related classes and are usually named in a format that follows reverse domain notation, meaning you take the URL for your website and reverse it, i.e. teamtreehouse.com becomes com.teamtreehouse.
A Note on Casting
If you’ve worked through Build a Simple Android App or Build a Blog Reader App, or if you’ve done any Android development at all, then you’ll be familiar with the concept of “casting”. We often see it when getting Views from a layout in code:
Button saveButton = (Button) findViewById(R.id.saveButton);
The findViewById()
method returns a generic View object, which is useful because it can return anything from the layout. All widgets like Buttons and TextViews are subclassed from the View base class. But since each widget behaves differently and has different properties, we want to declare it with the most detailed data type we know it to be. In most cases we will know the type of widget being returned from the findViewById()
method, so we specify its exact type with a cast.
View myView = findViewById(R.id.saveButton); // valid Button saveButton = findViewById(R.id.saveButton); // invalid
Casting in Java, like the cast to Button in the example above, is done by declaring the type we are casting to inside parenthesis in front of the method that’s returning an object. For more details about casting in Java, please visit Oracle’s documentation on classes and inheritance.
Conditionals
One of the basic building blocks of all programming languages is the power to check a condition and then do something if the condition is met. In Java this achieved using the if
conditional, which checks if a condition evaluates to boolean values true
or false
.
if (name.equals("Bruce Wayne")) { isBatman = true; }
We start with the keyword if
, and the condition to test is surrounded by parenthesis. If the condition is met, then the code inside the curly braces is executed. Java, like many other languages, uses curly braces to start and finish blocks of code like this. If we want to take some action if the condition is not met, then we can use the else
keyword to specify what code should run, as in the following example:
if (name.equals("Bruce Wayne")) { isBatman = true; } else { isBatman = false; }
Further, we can chain as many conditions together as we want using else if
, like this:
if (name.equals("Bruce Wayne")) { isBatman = true; } else if (name.equals("Clark Kent")) { isSuperman = true; } else { isRegularHuman = true; }
Extra Credit
Depending on your experience with programming, you may be wondering why the examples above use the equals() method instead of double-equals, like if (name == “Bruce Wayne”). The reason is because in Java, applying the double-equals operator to String objects does not compare the actual values of the objects, just whether or not the references are equal, which usually isn’t what we’re looking to compare.
Arrays
Arrays are structures used to organize multiple pieces of data or other variables. Basically they are structured lists where each piece of data is referenced by its position in the array.
Take this example of an App Drawer from an Android phone. We can think of the app icons in the drawer as items in an array. The first item in the array, Gmail, is at index 0. The second, Go SMS Pro, is at index 1, and so on to the last, Camera, at index 4. The length of this array (or the number of items in it) is 5.
When we declare an array variable, we need to specify the type of the items of data we’ll be holding in the array. For example, if we’re going to use an array to store a list of names, then we will need to declare it as a String array (since String is the data type for text).
Declaring arrays is similar to declaring variables; we simply need to add square brackets after the data type, like this:
String[] appNames;
We can initialize an array in one step or multiple steps. In one step, we simply define all the elements of the array inside curly braces, like these two examples:
String[] colors = { "Red", "White, "Black" }; int[] ages = { 18, 24, 33 };
If we want to load data in the array at a later time, the we still need to specify how big the array will be. (We can’t change the number of items in an array–if we want that we need to use a different data structure like an ArrayList.) Specific items in the array are accessed and/or assigned using the index of the array. The index is specified inside square brackets following the name of the array. Arrays in Java start with an index of zero, so the first item of an array is at position zero and is referenced like this:
String[] appNames = new String[5]; appNames[0] = "Gmail"; appNames[1] = "Go SMS Pro"; appNames[2] = "Chrome"; appNames[3] = "Falcon Pro"; appNames[4] = "Camera"; String firstApp = appNames[0]; // firstApp will be "Gmail"
The amount of items an array can hold is known as its length. The Array object has a property called length
that returns this number:
int numberOfApps = appNames.length; // will be 5
Loops
Looping through collections or sets of things is another basic building block of programming. For example, let’s say we want to loop through that list of app names from the Arrays section above and output them to a log. If we did it one by one, the code would look like this:
Log.d(TAG, appNames[0]); Log.d(TAG, appNames[1]); Log.d(TAG, appNames[2]); Log.d(TAG, appNames[3]); Log.d(TAG, appNames[4]);
What if we have hundreds of apps? Or what if we’re dealing with millions of records from a database? What if we run 20 lines of code for each record in the database? That’s a lot of code to write, and most of it is repeating ourselves, which violates the “Don’t Repeat Yourself (DRY)” principle.
The solution is to loop through an array (or whatever collection you’re dealing with) to run code for each item in the collection. Let’s rewrite the code above as a few different loops.
The “for” Loop
for (int i = 0; i < appNames.length; i++) { Log.d(TAG, appNames[i]); }
Let’s walk through this syntax, because there’s a lot here if you’ve never seen it before. The basic idea is this: “for (some conditions) do the code inside these curly braces”:
for (initialize statement; terminate statement; step statement) { // code to be executed each step of the loop }
This type of for loop often utilizes a counter variable (named “i” for “index” in our example above) that keeps track of our position in the collection.
- initialize statement – The first statement inside the parenthesis sets up the counter variable or whatever mechanism will be used to keep track of our position in the loop. In our example the variable “i” is set to 0, the first index of the appNames array.
- terminate statement – The next statement inside the parenthesis (separated by a semicolon) tells us when this loop should stop processing and exit to the next line of code after the closing curly brace. In our example we want to keep going as long as “i” is less than the number of items in the array. Since there are 5 items in the array, but the last item is at index 4 (because it starts at 0), we want to stop once i surpasses 4. Otherwise we will get an
ArrayIndexOutOfBoundsException
. - step statement – This last statement (again separated by a semicolon) defines how our counter will change after each step is executed. In our example we are using a special “increment” shortcut that adds one to an integer value.
++
can be used to increment a variable by one, and--
can be used to decrement a variable by one.
We must be careful in defining these conditions. What if we decrement our counter instead of incrementing it, and our terminate condition is never met? In such cases our program or app will get caught in what is known as an “infinite loop”. The loop will never exit and our program will use up resources indefinitely, which will lead to a crash, freeze, or “Application Not Responding (ANR)” in Android.
The “while” Loop
An alternative to the for
loop is a while
loop, which executes code while a condition is met. The biggest difference is that we need to define how the loop steps through the collection in the code between the curly braces instead of being a statement inside the parenthesis where the loop is started.
01 int i = 0; 02 while (i < appNames.length) { 03 Log.d(TAG, appNames[i]); 04 i++; 05 }
Note that the three statements inside the for loop are here as well: line 1 is the initialize statement, line 2 contains the terminate statement, and line 4 is the step statement. The general syntax is as follows:
while (condition) { // code to execute }
Each type of loop can often be rewritten as another, so basically we can choose whichever makes the most sense for how we want to loop. Sometimes it’s easier to write one or the other based on the data we’re working with.
The do-while Loop
Similar to the while
loop, the do-while
loop is different in that it checks the condition at the bottom of the loop instead of the top. This guarantees that the code inside the loop will get executed at least one time. If the condition is not met in the first pass of a loop, the while
loop will skip the code inside the curly braces, but the do-while
loop will process the code and then move on.
int i = 999; do { Log.d(TAG, "Just looking at app names"); } while (i < appNames.length);
The for-each Loop
Sometimes those iterators and conditions can get plain ugly. Fortunately, Java contains yet another way of writing a loop when you want to iterate over a collection of items. The idea behind a for-each
loop is that you want to run some code for each item in the collection.
for (String name : appNames) { Log.d(TAG, name); }
The for-each
loop replaces the three conditions of the for loop with a simplified declaration. The right side of the colon is the array or collection to be looped through, and the left side is a temporary variable used to hold the item in the current iteration of the loop. So in this example we get a String from each item in the appNames array and the String is stored in a temporary variable named “name”. We can read this as “for each name in appNames, do the code in these curly braces”.
Error Handling: try-ing Code and catch-ing Exceptions
Remember that ArrayIndexOutOfBoundsException
I mentioned in passing above? Java provides us with a framework for gently handling all sorts of errors in our programs. The basic idea is that if something bad happens in our program or app, the Java runtime system will kind of raise a little flag that says something is wrong. If we are on the lookout for these flags we can handle them appropriately and save our users from a crash or bad experience.
The act of raising that little flag is called throwing an exception, and we must catch them in code in our app. Let’s use an example to illustrate:
01 String url = "https://blog.teamtreehouse.com"; 02 try { 03 URL blogUrl = new URL(url); 04 } 05 catch (MalformedURLException e) { 06 Log.e(TAG, "Error creating a URL object with " + url); 07 }
Okay, so what’s going on with these blocks? Up til now, we’ve seen code that executes normally in the main thread of the program. If any exceptions are thrown in that type of code, then they are very serious and the app will crash. But oftentimes there are methods we want to use that throw less serious exceptions, and we are not allowed to ignore them. In such cases, we need to put that code inside a try
block, like lines 2-4 above. In this example, the URL constructor throws a MalformedURLException
if a bad URL is given as the input parameter. If we don’t catch that exception (or some superclass of it, like Exception
) then our code will not compile.
The catch
block requires the type of exception to be caught to be declared as a variable inside the parenthesis after the catch
keyword. Then, as usual, the code to be executed in case of that exception is written between the curly braces.
If we need to check for multiple exceptions, like if we tried to open an HTTP connection using blogUrl, which throws an IOException
, then we can chain catch
statements on after another, like this:
try { // some code } catch (MalformedURLException e) { Log.e(TAG, "There was an error creating a URL object."; } catch (IOException e) { Toast.makeText(this, "Error connecting to site.", Toast.LENGTH_LONG).show(); }
*Note that no other code can go between the ending curly brace of one catch
statement and the catch
keyword of the next.
Finally, there is one more block of code that can be chained to this sequence: the finally
block. This is where we put code that we want to execute even if an exception is thrown. In the example below, we open a connection to a file for writing and want to make sure we close it no matter what happens. Otherwise that connection might be open indefinitely:
Writer writer; try { writer = new OutputStreamWriter(new FileOutputStream("out.txt")); writer.write("Ben was here."); } catch (IOException e) { Log.e(TAG, "Error writing to file."); } finally { if (writer != null) { writer.close(); } }
*Note that the writer
variable will be null if an exception was thrown trying to initialize it. Hence we need to check if it’s null or not, because we only want to close it if it was initialized.
Wrapping Up
Mastering the basics covered here and in Part 1 will go a long way in helping you be a good programmer in general, and especially for Android development. If you want to go further with Java learning, Oracle houses some great tutorials on all sorts of concepts related to Java programming. Once you master the basics and work through the Android courses at Treehouse, you can hone your skills with fun side projects or code katas where you can exercise your programming brain to become a Java master!