This notes extends Midterm Review. The final exams includes everything tested in the midterm
1Frameworks¶
1.1Framework¶
A framework is a set of classes and interface types that structures the essential mechanisms of a particular domain. In other words, a framework is a set of tools that change the way you program something. e.g. Swing, jQuery, django
An application framework is a framework for creating applications of a particular type: it supplies a set of classes that an application programmer extends to build an app.
The programmer has little or no influence on the order in which the extended classes are called - the programmer becomes the framework's bitch. This phenomenon is often called inversion of control. The flow of execution is controlled by the framework classes, not the application specific classes.
1.2Example: Graph Editor Framework¶
Uses the prototype pattern
2Objects and Types¶
2.1Types and Subtypes¶
- Type
- A set of values together with a set of operations that can be applied to the values
Strongly typed languages: casts are almost never implicitly done, example: Java, python
Weakly typed languages: When the compiler finds the wrong type, it tries to use the operations anyway, causing weird behavior. Example: Javascript, C
Java is statically typed, that means type validation is done during compile time, so if you call an undefined method on a variable, it will not compile.
Some languages like javascript or python are dynamically typed, which means types aren't known during compile time, and an undefined method will result in a runtime exception.
Types in Java are:
- Primitive type(
int
,short
,long
,byte
,char
,float
,double
,boolean
) - A class type
- An interface type
- An array type
- The null type
Values in java are:
- A primitive value
- A reference to an object
- A reference to an array
null
You can substitute a value of a subtype whenever a supertype value is required.
S
is a subtype of T
if:
S
andT
are the same typeS
andT
are both class types andS
is a direct or indirect subclass ofT
S
andT
are both interface types andS
is a direct or indirect subinterface ofT
S
is a class type, andT
is an interface type, andS
or one of its superclasses implementsT
or a subinterface ofT
S
andT
are both array types and the component type ofS
is a subtype ofT
's component typeT
of typejava.lang.Object
,S
is not a primitiveS
is an array type andT
is the typeCloneable
orSerializable
S
isnull
andT
is not a primitive type
2.1.1Arrays¶
Arrays are annoying. The following will generate a runtime error:
Rectangle [] r = new Rectangle[10]; Shape [] s = r; // valid because Rectangle is a subtype of Shape s[0] = new Circle(); // valid because Circle is a subtype of Shape, BUT s IS NOT A SHAPE ARRAY! IT IS A RECTANGLE ARRAY!
2.1.2Wrapper classes¶
Primitives types have their corresponding wrapper classes. They are all immutable classes. You can also assign primitivive values to objects of the wrapper classes because of autoboxing (Since java 1.5)
2.1.3enums¶
An enumerated type is a type with a finite set of values, to declare an enum the syntax is:
public enum Day {MONDAY, TUESDAY, WEDNESDAY}; // FUCK THE REST OF THE WEEK
Each member in an enum is a singleton of the class, thus Day.MONDAY == Day.MONDAY
will always be true. null
is also a possible value of the enum.
You can also add methods and fields to an enum class:
public enum Day{ MONDAY(1), TUESDAY(2), WEDNESDAY(3); public String foo(){ return "bar"; } }
Enums are Comparable.
2.2Type Inquiry¶
The instanceof
operator can be used to test the type, with the syntax if (var instanceof Type)
.
Typically you use it before casting to ensure safety. If var is null, it will return false.
You can also find the type of an object by using the Class
object.
Class c = var.getClass();
The object c
contains information about the class of var
, including its name, superclass.
You can get a class object from a name by using the Class.forName("java.awt.Rectangle")
method.
Primitive types also have an associated Class
object.
Class
objects are singletons, thus you can use ==
to test type equality like so: if (var.getClass() == Type.class)
.
You can use isArray()
to test whether a type is an array, then use getComponentType()
to get the class of the component type.
2.3The Object Class¶
All java classes are subclasses of the Object
class. It contains the following methods:
String toString()
boolean equals(Object other)
int hashCode()
Object clone()
The equals
method must be reflexive, symmetric, and transitive. The default equals method is testing equality using ==
The default hashCode()
method returns the memory address.
3Persistence¶
3.1Serialization¶
Serialization denotes the process of storing an object and its dependent objects in a stream.
To serialize something, you use an ObjectOutputStream
on an object that implements Serializable
:
Cat kitty = new Cat(); ObjectOutputStream out = new ObjectOutputStream(new FileOutputSTream("kitty.dat")); out.writeObject(kitty); out.close();
To read it back, you use an ObjectInputStream
:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("kitty.dat")); Cat kitty = (Cat)in.readObject(); in.close();
The class Cat
must implement Serializable
.
To have fields that you don't want to be serialized, you use the transient
keyword.
private transient boolean badCat;
4Generics¶
4.1Type Variables¶
A generic type has one or more type variables, like List<E>
.
Use them in the following format:
public class Heap<E> { public E pop(){} public void set(int i, E value){} }
Methods can also be generic:
public class Foo{ public <E> void bar(Heap<E> a, E value){ a.set(0,value); } }
We can also specify conditions on generic types
public class Foo{ public <E, F extends E> void compareEF(ArrayList<E> a, ArrayList<F> b){} public <E> void compareExtends(ArrayList<E> a, ArrayList<? extends E> b){} public <E> void compareSuper(ArrayList<? super E> a, ArrayList<E> b){} }
4.2Type Erasure¶
Generic types are erased during compile time
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
5Code quality¶
If you hate yourself when you read your code 2 weeks later, you're doing it wrong. If you don't, then you're clueless.
6Thread¶
6.1Threads and the Runnable Interface Type¶
In java you can implement blocks of code as separate threads of execution. They are executed independently of other parts of the program.
To use threads, you use the following syntax:
Thread t = new Thread(new Runnable(){ void run(){ for(int i = 0;i<100;i++){ System.out.println("Hello"); } } }); t.start();
You can use the sleep
method to put the current thread to sleep for a given number of milliseconds. A thread terminates when the run()
method terminates.
6.2Scheduling Threads¶
A thread scheduler allows each thread to execute for a short amount of time, called a time slice. Each thread has a thread state and a priority. The thread state can be:
- new (before
start()
is called) - runnable
- blocked
- dead (after
run()
terminates)
A thread enters the blocked state by either:
- Sleeping
- Waiting on I/O
- Waiting to acquire a lock
- Waiting for a condition
Once a thread is blocked, it stays blocked until it gets the reason it gets blocked for is resolved (no fucking shit).
The scheduler will activate a new thread when one of three events occurs:
- A thread has completed its time slice
- A thread has blocked itself
- A thread with higher priority has become runnable
6.3Stopping threads¶
To stop a thread do not use stop()
because if your thread is holding any lock on resources, shit might happen. To stop a thread, use the interrupt()
method.
The call doesn't terminate the thread, it just politely interrupts the thread's dinner. It's up to the thread whether or not he wants to leave the restaurant.
7Synchronization¶
7.1Thread Synchronization¶
When threads share access to a common object, they can conflict with each other. If you have two threads adding to an ArrayList, it's possible that it might not behave as expected.
A race condition occurs if the effect of multiple threads on shared data depends on the order in which the threads are scheduled.
7.2Locks¶
A thread can acquire a lock, and when another thread tries to acquire the same lock, it is blocked. When the first thread releases the lock, the other threads are unblocked. To lock a block of code, we use ReentrantLock()
l = new ReentrantLock(); l.lock(); try{ // only one thread can be in here at once } finally{ l.unlock(); }
Putting synchronized
in front of a method like:
public synchronized void add(E e){}
makes sure that only one thread can be inside this function at once.
7.3Deadlocks¶
A deadlock occurs if no thread can proceed because each thread is waiting for another to do some work first. Most of the time it happens because a thread is blocked inside a locked block of code, and other threads can't unblock it because it has the lock, like so:
public void add(E new){ l.lock(); try{ while(!condition()){ //condition never becomes true } } finally{ l.unlock(); } }
To avoid deadlocks, you can use a Condition
object to briefly release a lock, like so:
private Condition c = l.newCondition(); public void add(E new){ l.lock(); try{ while(!condition()){ c.await(); } } finally{ l.unlock(); } } public void condition(){ c.signalAll() }
When signalAll
is called, the waiting threads are unblocked.
If synchronized
was used, each Thread has one condition, and to block it you use wait()
, and notifyAll()
unblocks it.
8The Visitor Design Pattern¶
Context
- An object structure contains element classes of multiple types, and you want to carry out operations that depend on the object types.
- The set of operations should be extensible over time.
- The set of element classes is fixed.
Solution
- Define a
visitor
interface type that has methods for visiting elements of each of the given types. - Each element class defines an
accept
method that invokes the matching element visitation method on the visitor parameter. - To implement an operation, define a class that implements the visitor interface type and supplies the operation's action for each element type.