Course 3 Lesson 1. Exceptions 1.1 Catching exceptions The term "exception" means "exceptional condition" and is an occurrence that alters the normal program flow. A bunch of things can lead to exceptions, including hardware failures, resource exhaustion, and good old bugs. When an exceptional event occurs in Java, an exception is said to be "thrown." The code that's responsible for doing something about the exception is called an "exception handler," and it "catches" the thrown exception. Exception handling works by transferring the execution of a program to an appropriate exception handler when an exception occurs. For example, if you call a method that opens a file but the file cannot be opened, execution of that method will stop, and code that you wrote to deal with this situation will be run. Therefore, we need a way to tell the JVM what code to execute when a certain exception happens. To do this, we use the try and catch keywords. The try is used to define a block of code in which exceptions may occur. This block of code is called a guarded region (which really means "risky code goes here"). One or more catch clauses match a specific exception (or group of exceptions—more on that later) to a block of code that handles it. Here's how it looks in pseudocode: 1.2 Using finally A finally block encloses code that is always executed at some point after the try block, whether an exception was thrown or not. Even if there is a return statement in the try block, the finally block executes right after the return statement is encountered, and before the return executes! This is the right place to close your files, release your network sockets, and perform any other cleanup your code requires. If the try block executes with no exceptions, the finally block is executed immediately after the try block completes. If there was an exception thrown, the finally block executes immediately after the proper catch block completes. Pseudocode example: Note: It is illegal to use a try clause without either a catch clause or a finally clause. A try clause by itself will result in a compiler error. Any catch clauses must immediately follow the try block. Any finally clause must immediately follow the last catch clause (or it must immediately follow the try block if there is no catch). It is legal to omit either the catch clause or the finally clause, but not both. 2. Defining Exceptions Every exception is an instance of a class that has class Exception in its inheritance hierarchy. In other words, exceptions are always some subclass of java.lang.Exception When an exception is thrown, an object of a particular Exception subtype is instantiated and handed to the exception handler as an argument to the catch clause. An actual catch clause looks like this: In this example, e is an instance of the ArrayIndexOutOfBoundsException class. As with any other object, you can call its methods („e.printStackTrace()”); All exception classes are subtypes of class Exception. This class derives from the class Throwable (which derives from the class Object): There are two subclasses that derive from Throwable: Exception and Error. Classes that derive from Error represent unusual situations that are not caused by program errors, and indicate things that would not normally happen during program execution, such as the JVM running out of memory. Generally, your application won't be able to recover from an Error, so you're not required to handle them. Java provides many exception classes, most of which have quite descriptive names. There are two ways to get information about an exception. The first is from the type of the exception itself. The next is from information that you can get from the exception object. Class Throwable (at the top of the inheritance tree for exceptions) provides its descendants with some methods that are useful in exception handlers. One of these is printStackTrace(). As expected, if you call an exception object's printStackTrace() method, as in the earlier example, a stack trace from where the exception occurred will be printed. 3. Exceptions Matching If you have an exception hierarchy composed of a superclass exception and a number of subtypes, and you're interested in handling one of the subtypes in a special way but want to handle all the rest together, you need write only two catch clauses. When an exception is thrown, Java will try to find (by looking at the available catch clauses from the top down) a catch clause for the exception type. If it doesn't find one, it will search for a handler for a supertype of the exception. If it does not find a catch clause that matches a supertype for the exception, then the exception is propagated down the call stack. This process is called exception matching. Example: 4. Custom Exception When creating a custom exception, you should create a subclass of Exception class: Lesson 2. Threads In Java, “thread” means two things: An instance of class java.lang.Thread A thread of execution An instance of Thread class is just like an object, it has variables and methods, and lives and dies in the heap. A thread of execution is an individual process which has its own call stack. In Java there is one thread per call stack. The main () method which start the execution of the program, runs in one thread called main thread. A thread in Java begins as an instance of java.lang.Thread. You'll find methods in the Thread class for managing threads including creating, starting, and pausing them. Main methods of Thread class are: start() yield() sleep() run() The action happens in the run() method. Think of the code you want to execute in a separate thread as the job to do. In other words, you have some work that needs to be done, say, downloading stock prices in the background while other things are happening in the program, so what you really want is that job to be executed in its own thread. So if the work you want done is the job, the one doing the work (actually executing the job code) is the thread. And the job always starts from a run() method as follows: You always write the code that needs to be run in a separate thread in a run() method. The run() method will call other methods, of course, but the thread of execution—the new call stack—always begins by invoking run(). 2.1 Define Threads To define a thread, you need a place to put your run() method. This can be aquired in two ways: extending Thread class or implementing Runnable interface. 1. Extending java.lang.Thread 2. Implementing java.lang.Runnable Instantiating threads: or 2.2 Thread states A thread can be in 5 states: 1. New this is the state the thread is in after the Thread instance has been created. 2. Runnable this is the state a thread is in when it's eligible to run. A thread first enters the runnable state when the start() method is invoked, but a thread can also return to the runnable state after either running or coming back from a blocked, waiting, or sleeping state. When the thread is in the runnable state, it is considered alive. 3. Running this is the state a thread is in when the thread scheduler selects it (from the runnable pool) to be the currently executing process. 4. Waiting/blocked/sleeping this is the state a thread is in when it's not eligible to run 5. Dead a thread is considered dead when its run() method completes Lesson 3. Enums Enumerations are a special data type in Java that allows for a variable to be set to predefined constants.The variable must equal one of the values that have been predefined for it. Enums have been added in Java 1.5. An enumeration is useful when there is a limited set of options that a variable can equal and it is restricted to these known values. It can be used for days of the week (SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY and SATURDAY) , directions (NORTH, SOUTH, EAST and WEST) etc. Using enums can help reduce the bugs in your code. For instance, in your coffee shop application you might want to restrict your size selections to BIG, HUGE, and OVERWHELMING. If you let an order for a LARGE or a GRANDE slip in, it might cause an error. Enums to the rescue. With the following simple declaration, you can guarantee that the compiler will stop you from assigning anything to a CoffeeSize except BIG, HUGE, or OVERWHELMING: From then on, the only way to get a CoffeeSize will be with a statement something like this: The Enum can be defined within or outside the class, it is similar to a class: Or !! The key point to remember is that an enum that isn't enclosed in a class can be declared with only the public or default modifier. Declaring Constructors, Methods, and Variables in an enum Because an enum really is a special kind of class, you can do more than just list the enumerated constant values. You can add constructors, instance variables, methods, and something really strange known as a constant specific class body. To understand why you might need more in your enum, think about this scenario: imagine you want to know the actual size, in ounces, that map to each of the three CoffeeSize constants. For example, you want to know that BIG is 8 ounces, HUGE is 10 ounces, and OVERWHELMING is a whopping 16 ounces. You could make some kind of a lookup table, using some other data structure, but that would be a poor design and hard to maintain. The simplest way is to treat your enum values (BIG, HUGE, and OVERWHELMING), as objects that can each have their own instance variables. Then you can assign those values at the time the enums are initialized, by passing a value to the enum constructor. Example: Output: ? !!Note: Every enum has a static method, values(), that returns an array of the enum's values in the order they're declared. Lesson 4. Inner classes OOP implies that classes should be kept specialzed. a class should have code only for the things an object of that particular type needs to do; any other behavior should be part of another class better suited for that job. Sometimes, though, you find yourself designing a class where you discover you need behavior that belongs in a separate , specialized class, but also needs to be intimately tied to the class you're designing. Types: Inner classes • Method-local inner classes • Anonymous inner classes • Static nested classes Coding a “regular” inner class After compiling : The inner class is still, in the end, a separate class, so a separate class file is generated for it. But the inner class file isn't accessible to you in the usual way. The only way you can access the inner class is through a live instance of the outer class! In other words, only at runtime when there's already an instance of the outer class to tie the inner class instance to. To create an instance of an inner class, you must have an instance of the outer class to tie to the inner class. There are no exceptions to this rule: an inner class instance can never stand alone without a direct relationship to an instance of the outer class. Instantiating an Inner Class from Within the Outer Class Most often, it is the outer class that creates instances of the inner class, since it is usually the outer class wanting to use the inner instance as a helper for its own personal use. We'll modify the MyOuter class to create an instance of MyInner: Creating an Inner Class Object from Outside the Outer Class Instance Code If we want to create an instance of the inner class, we must have an instance of the outer class. Or Using „this” keyword this represents a reference of the object to itself The keyword this can be used only from within instance code. In other words, not within static code. The this reference is the way an object can pass a reference to itself to some other code, as a method argument. Output: ? Method-local inner classes A regular inner class is scoped inside another class's curly braces, but outside any method code (in other words, at the same level that an instance variable is declared). But you can also define an inner class within a method: The preceding code declares a class, MyOuter2, with one method, doStuff(). But inside doStuff(), another class, MyInner, is declared, and it has a method of its own, seeOuter(). The code above is completely useless, however, because it never instantiates the inner class! Just because you declared the class doesn't mean you created an instance of it. So to use the inner class you must make an instance of it somewhere within the method but below the inner class definition (or the compiler won't be able to find the inner class). The following legal code shows how to instantiate and use a method-local inner class: