Commit 11ba4688 authored by michael lundquist's avatar michael lundquist
Browse files

Finished with chapter 2. It was good to formally define what's going on in java.

parent 3511c9dd
......@@ -147,29 +147,45 @@
- __multiple inheritance__: the ability to inherit more than one type.
- Java onnly allows multiple inheritance for interfaces. If java allowed multiple inheritance for classes, the instance variables and methods could become ambiguous.
- some languages allow multiple inheritance from concrete classes. Inheriting from multiple classes called __mixins__.
- These mixin classes aren't meant to be instantiated, only inherited from.
- some languages allow multiple inheritance from concrete classes. Inheriting from multiple classes called __mixins__.
- These mixin classes aren't meant to be instantiated, only inherited from.
- java approximates this with interfaces
- interfaces __extend__ other interfaces, but classes __implement__ interfaces.
- According to the book, "Java interfaces that approximate teh mixin include:"
- According to the book, "Java interfaces that approximate the mixin include:"
- `java.lang.Cloneable`
- `java.lang.Comparable`
- `java.util.Observer` which adds an update feature to a class that whishes to be notified when certain "ovservable" objects change state.
- `java.util.Observer` which adds an update feature to a class that whishes to be notified when certain "observable" objects change state.
- The book doesn't offer much information on how these methods are implemented, just that mixins implement them.
### 2.3.3 Abstract Classes
- abstract classes include teh __abstract__ modifier in their class definition.
- Abstract classes can't be instantiated themselves. They must be overridden to be instanteiated.
- Abstract classes provide a mix between the completely unspecified functionality of an interface, and the completely specified functionality of a concrete class.
- Abstract classes can have abstract methods that also have the __abstract__ modifier, don't have a method body, and must me instantiated by their subclasses.
- Although abstract methods have no body, they can be called from other methods within the abstract class because their subclasses *must* implement them. This is a design pattern known as __template method pattern__
- Abstract classes can:
- not be instantiated
- inherit and be inherited like a normal concrete class (with extra rules about abstract methods).
- Like a normal class, abstract classes only support single inheritance.
- define __abstract methods__ that don't have a body, like methods of an interface.
- classes that extend abstract classes must implement the abstract methods or be abstract themselves.
- define concrete members (methods and variables) of the class, like a normal class.
- Use abstract methods from concrete methods.
- This is only possible because an abstract class can't be instantiated and the abstract method must be implemented in the class that inherits it; therefore you know that when an object of your subclass uses the abstract method from your abstract class's concrete method, it will be defined.
- This is a design pattern known as __template method pattern__.
- Abstract classes encourage code reuse, robustness and abstraction by:
- Preventing improper instantiation which makes code robust.
- Using abstract methods from a concrete context in your abstract class essentially gives your base class knowledge of its sub-class, which encourages code reuse.
- Giving programmers the ability to define relationships they wouldn't otherwise be able to define. This encourages code reuse.
#### Mechanics of Abstract Classes in Java
- abstract classes and methods include the __abstract__ modifier in their class definition.
public abstract class ExampleAbstract{
public void exampleAbstractMethodCall(){
......@@ -182,4 +198,307 @@ public abstract class ExampleAbstract{
## 2.4 Exceptions
- In java, exceptions are *thrown* when an unexpected, possibly dangerous situation occurs.
- When thrown, exceptions will crash your program with an error message unless they are *caught* and handled by the programmer.
- There are different types of exceptions.
### 2.4.1 Catching Exceptions
- uncaught exceptions print the stack trace of the program. The stack trace shows all the nested method calls up to the error.
- at each nesting, a thrown exception can be caught or left uncaught. If uncaught, the exception bubbles up to the next most outer nesting.
- Java uses the *try-catch* catches exceptions thrown in the guarded body of the _try block_, and handles them in the _catch block_. If no exceptions are thrown, the catch blocks are ignored.
//smiple example
//guarded body
}catch(SpecificExceptionType exceptionVariable){
//handling block for exceptions of SpecificExceptionType
}catch(GeneralExceptionType exceptionVariable){
//a more general catch block
//executed after try/catch with or without errors
- In the above example:
- `SpecificExceptionType` can be any type, and `exceptionVariable` is the exception object that was thrown. These exception objects all ultimately override [Throwable](
- When an exception is caught, the code inside the first catch block who's Throwable parameter is the same type as the exception, or is a subclass of the exception's type, is used to handle the exception.
- In this example, the second catch is of a made-up type, GeneralExceptionType. which is a very general type (high in the exception hierarchy) so it will catch many exception types including SpecificExceptionType. If this block were before the block for SpecificExceptionType, it would never be called.
- Ultimately, Throwable is the most general exception type
- If an exception is thrown in the try that doesn't match any of the catch blocks, the finally block will execute then the exception keeps bubbling up as though the try blocks weren't there.
- The finally block is executed after the try/catch even if no errors occurred.
- The finally block will execute in ]almost all situations](
- After the try-catch-finally is done executing, assuming all errors were caught, execution returns to the statement after the try-catch-finally block.
- Code in catch blocks often:
- prints the error message.
- terminates the program safely
- sometimes do nothing.
- throw a different exception (maybe a custom exception)
- The also book mentions some advanced Java SE 7 features:
- [try with resource]( is a way to automatically close resources after using them.
- You can handle [multiple exception types with the same block]( with the syntax: `catch(FirstExceptionType | SecondExceptionType e)` where the catch block will catch exceptions of type `FirstExceptionType` or `SecondExceptionType` as the variable e, then handle them the same way.
### 2.4.2 Throwing Exceptions
- To generating the exceptions caught in try/catch statements, use the __throw__ keyword followed by an instance of an exception to be thrown. The syntax for this is:
__throw__ __new__ *exceptionType*(*parameters*);
- most exception types offer a constructor that takes an error message String as a parameter which can give more detail when the error is thrown.
#### The Throws Clause
- When methods have a __throws__ clause in their signature that specifies a comma sepparated list of exceptions this method could throw. Methods that call the original method must either:
- catch the error
- have throws in their signature
- Note, having a throws clause doesn't excuse you from having an `@throws` javaDoc tag.
parses a string to an int
@throws NumberFormatException when s can't be parsed
public static int parseInt(String s) throws NumberFormatException{
//*******Syntax with 2 possible exceptions****
Parses a comma separated string to an int
@throws NumberFormatException
@throws ArrayIndexOutofBoundsException
public static parseIntArr(String s) throws NumberFormatException, ArrayIndexOutOfBoundsException {
- As we'll see later, a throws clause is necessary for checked exceptions.
### 2.4.4 Java's Exception Hierarchy
- All java exceptions inherit from Throwable.
- __Error__ and __Exception__ directly inherit from throwable.
- __Error__ is used for serious errors encountered by the JVM that aren't recoverable, like a corrupt .class file or the computer runs out of memory.
- __exceptions__ may be recoverable.
![exception hierarchy](./pictures/java_e3.jpg)
#### Checked and Unchecked Exceptions
- In java, *runtime exceptions* are __unchecked exceptions__ which occur at runtime and are due to mistakes in programming logic (like an out of bounds index)
- These are called unchecked because by the time you get to production, they should no longer exist.
- everything that's not a runtime exception is a __checked exception__. These checked exceptions (like IOException or FileNotFoundException) can't be detected until the program is running so they must be *checked*.
- Methods that could throw a checked exception must have a [throws clause](#The-Throws-Clause) for that exception to compile.
#### Defining New Exception Types
- You can subclass exceptions just like any other class. When you do, be careful about what class you inherit from because it will affect how you [catch the exception](#2.4.1-Catching-Exceptions), and if you inherit an unchecked exception, the rules for unchecked exceptions apply.
## 2.5 Casting and Generics
- This section covers:
- *casting* between reference variables
- defining methods that work with different data types without explicit casting using *generics*
### 2.5.1 Casting
#### Widening Conversions
- Widening conversions allow variables in java to be polymorphic.
- Widening conversions obey the [Liskov substitution principal](#2.2.2-Polymorphism-and-Dynamic-Dispatch).
- widening conversions can be done implicitly and reliably by the compiler.
- common uses of widening conversions:
- Converting an object instance to its superclass type
- converting an object instance to one of its interface types.
- converting an object instance to of a superinterface of one of its interface types.
#### Narrowing Conversions
- Narrowing conversions require an explicit cast by the programmer and is unreliable (can't be verified by the compiler).
- common uses of narrowing conversions:
- Converting to a subclass
- converting to a subinterface
- converting to an interface
- syntax: (*type*) *object*
Object tempStr = new String("hello");//widened objects
String narrowerObj = (String) tempStr;//can be re-narrowed explicitly
#### Casting Exceptions
- Invalid narrowing casts will throw a `ClassCastException`.
- To prevent the `ClassCastException`, use the `instanceof` test
- instance of syntax: *objectReference* __instanceof__ *referenceType*
- *objectReference* is an expression that evaluates to an object reference (like a variable)
- *referenceType* is the name of a class, interface, or enum.
- `instanceof` returns true if *objectReference* is an instance of *referenceType*, else it returns false.
#### Casting with Interfaces
- Methods in an interface often have general types, then in the interface implementation when you pass an argument to them, they check if that variable with `instanceof` before doing any processing. This makes the interface more reusable.
- Although Object is a class, `Object.equals(Object)` is a good example of a method that takes a general object and can be overridden with a method that performs `instanceof`
### 2.5.2 Generics
- Java supports __generic__ (as of Java SE 5) classes and methods that take a set of __formal type parameters__ upon object initialization and use them in class as variable, parameter, and return types in the object.
- The __composition design pattern__ deals the instance variables of a class.
- Before generics, java programmers relied heavily on the Object superclass to make their code reusable. Unfortunately, this also makes code less robust as it introduces a need for narrowing casts back from the Object type.
#### Using Java's Generics Framework
- formal type parameters are capital letters by convention.
- When declaring a variable that uses generics in its parameterized type, we must explicitly specify __actual type parameters__ that will take the place of the generic formal parameters.
- Note, generics only works with Object types, not base types. However, this is rarely a problem because boxing and unboxing.
- __type inference__: an object's actual type parameters can be inferred from the actual type parameters of the variable they're being assigned to using empty angle brackets known as the "diamond" `<>`.
- These type parameters can also be explicitly stated inside the anble brackets.
- However, if you don't have any angle brackets, all the type parameters are set to Object.
public class SimpleVar<A>{
A var;
public SimpleVar(A storedVar){
this.var = storedVar;
public A getVar(){
return this.var;
SimpleVar <String> myVarStore = new <> SimpleVar("hello");//diamond style (using type inference)
SimpleVar <String> exMyVarStore = new <String> SimpleVar("hello ex");//explicit style
SimpleVar <String> badMyVarStore = new SimpleVar("hello obj");//incorrect, set to object
System.out.println(myVarStore.getVar());//prints: hello
System.out.println(exMyVarStore.getVar());//prints: ex hello
System.out.println(badMyVarStore.getVar());//prints: the object code of the string?
#### Generics and Arrays
- Java allows the declaration, but technically not instantiation of arrays using parameterized types.
- 2 examples where this issue occurs (see below for solutions):
1. Storing an array of objects that have generic parameterized types. (obviously outside the class)
2. Initializing an array that holds instances of one of the type parameter's types inside a class with generic type parameters.
##### ex1: outside the class
- In this case, the array can be instantiated with an *unparameterized* type, then cast back to the parameterized type.
SimpleVar<String> [] paragraph;
paragraph = new SimpleVar<String> [10];// compiler error. Can't initialize arrays using parameterized types.
paragraph = new SimpleVar[25];//This is legal because you're not initializing with a parameterized type.
// This will give a compiler warning about an unchecked narrowing cast because the array is cast from <Object> to <String>
paragraph[0] = new SimpleVar<>("hello");//valid assignment after cast
##### ex2: inside the class
- In this case, instantiate to `Object`, then cast to the type of the corresponding parameter.
public class SimpleVarArr<A>{
A[] varArr;
public SimpleVarArr(int capacity){
this.var = new A[capacity];// compiler error. Can't initialize arrays using parameterized types.
this.var = (A[]) new Object[capacity];//legal, but compiler warning about unchecked narrowing cast.
public void addVar(A value, int index){
varArr[index] = value;
public A getVar(int index){
return this.var[index];
#### Generic Methods
- Methods can use generic types. When they do, these types are inferred from their parameter list.
- Methods that take generic type parameters, like classes that take generic types, can use those types as a type (variable type etc.) in the method itself.
- more on these methods [here](
import javax.swing.JOptionPane;
public class Main{
public static void main(String[] args){
printOnScrn("hi");//shows type inference
public static <T> void printOnScrn(T var){
T copyRef = var;//shows you can use the type T in the method
JOptionPane.showMessageDialog(null, var);
#### Bounded Generic Types
- Use the __extends__ keyword to restrict formal parameter type possibilities to only types that extend (or implement) the specified class (or interface).
- Limiting formal parameters to subclasses (or subinterfaces) ensures the formal prameters have certain methods.
import javax.swing.JOptionPane;
import java.util.Arrays;
public class Main{
public static void main(String[] args){
String[] strArr = {"hi", "mom", "you're", "great"};
printLoop(Arrays.asList(strArr));//passing a list, which is an iterable.
public static <T> void printOnScrn(T var){
JOptionPane.showMessageDialog(null, var);
* generic types restricted to iterables
public static <T extends Iterable> void printLoop (T varArr){
for(Object var: varArr){
JOptionPane.showMessageDialog(null, "" + var);
## 2.6 Nested Classes
- __nested classes__ are normal classes defined inside an __outer class__.
- nested classes are used to:
- declutter the namespace in highly related classes by increasing encapsulation.
- You can have a class somewhere outside the outer class with the same name as the inner class.
- represent a small portion of a larger data structure
- be an auxiliary class that helps navigate a primary data structure.
- The nested class is a member of the outer class. Its full name (for initialization...) is OuterName.NestedName.
- The nested class's modifiers are independent from the outer class's
- *private* nested classes are only visible in the context of the outer class.
- Nested classes can be __static__, meaning the inner class has no association with the outer class.
- Non-static nested classes can access variables from the outer class using the syntax *OuterName*.__this__
- Inner classes can access the outer class's private members.
- Inner classes rely on the outer class's formal type parameters if the outer class is generic.
import javax.swing.JOptionPane;
import java.util.Arrays;
public class Main{
public static void main(String[] args){
String[] strArr = {"hi", "mom", "you're", "great"};
public static <T> void printOnScrn(T var){
JOptionPane.showMessageDialog(null, var);
public static <T extends Iterable> void printLoop (T varArr){
for(Object var: varArr){
JOptionPane.showMessageDialog(null, "" + var);
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment