Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Getting started 1) GETTING STARTED THE JAVA TECHNOLOGY PHENOMENON THE JAVA PROGRAMMING LANGUAGE The Java programming language is a high-level language that can be characterized by all of the following buzzwords: Architecture neutral Portable High performance Robust Secure Simple Object oriented Distributed Multithreaded Dynamic In the Java programming language, all source code is first written in plain text files ending with the .java extension. Those source files are then compiled into .class files by the javac compiler. A .class file does not contain code that is native to your processor; it instead contains bytecodes — the machine language of the Java Virtual Machine1 (Java VM). The java launcher tool then runs your application with an instance of the Java Virtual Machine. ABOUT THE JAVA TECHNOLOGY The Java platform differs from most other platforms in that it's a software-only platform that runs on top of other hardware-based platforms. The Java platform has two components: o The Java Virtual Machine o The Java Application Programming Interface (API) 1 Getting started The API is a large collection of ready-made software components that provide many useful capabilities. It is grouped into libraries of related classes and interfaces; these libraries are known as packages. As a platform-independent environment, the Java platform can be a bit slower than native code. However, advances in compiler and virtual machine technologies are bringing performance close to that of native code without threatening portability. Interview questions: THE "HELLO WORLD!" APPLICATION A CLOSER LOOK AT THE "HELLO WORLD!" APPLICATION The "Hello World!" application consists of three primary components: source code comments, the HelloWorldApp class definition, and the main method. The following explanation will provide you with a basic understanding of the code, but the deeper implications will only become apparent after you've finished reading the rest of the tutorial. SOURCE CODE COMMENTS Comments are ignored by the compiler but are useful to other programmers. The Java programming language supports three kinds of comments: 1. /* text */ : The compiler ignores everything from /* to */. 2. /** documentation */ This indicates a documentation comment (doc comment, for short). The compiler ignores this kind of comment, just like it ignores comments that use /* and */. Thejavadoc tool uses doc comments when preparing automatically generated documentation. For more information on javadoc, see the Javadoc™ tool documentation . 3. // text : The compiler ignores everything from // to the end of the line. CLASS DEFINITION The most basic form of a class definition is: 2 Getting started class name { ... } The keyword class begins the class definition for a class named name, and the code for each class appears between the opening and closing curly braces marked in bold above. MAIN METHOD public static void main(String[] args) The main method accepts a single argument: an array of elements of type String. This is how the runtime system passes information to an application. Each string in the array is called a command-line argument. Using these arguments we can affect the operation of the application without recompiling it. For example, you can specify your current location for a weather program to determine the weather in your area. OBJECTS Bundling code into individual software objects provides a number of benefits, including: Modularity: The source code for an object can be written and maintained independently of the source code for other objects. Once created, an object can be easily passed around inside the system. Information-hiding: By interacting only with an object's methods, the details of its internal implementation remain hidden from the outside world. Code re-use: If an object already exists (perhaps written by another software developer), you can use that object in your program. This allows specialists to implement/test/debug complex, task-specific objects, which you can then trust to run in your own code. Pluggability and debugging ease: If a particular object turns out to be problematic, you can simply remove it from your application and plug in a different object as its replacement. This is analogous to fixing mechanical problems in the real world. If a bolt breaks, you replace it, not the entire machine. INTERFACES Implementing an interface allows a class to become more formal about the behaviour it promises to provide. Interfaces form a contract between the class and the outside world, and this contract is enforced at build time by the compiler. If your class claims to implement 3 Learning the Java Language- (25 APR -01 MAY - CODE) an interface, all methods defined by that interface must appear in its source code before the class will successfully compile. 2) LEARNING THE JAVA LANGUAGE- (25 APR -01 MAY - CODE) OBJECT-ORIENTED PROGRAMMING CONCEPTS OBJECT An object is a software bundle of related state and behavior. Software objects are often used to model the real-world objects that you find in everyday life. Objects have state and behavior. An object stores its state in fields (variables in some programming languages) and exposes its behaviour through methods (functions in some programming languages). Methods are operations on the internal state of an object. They serve as the primary mechanism for object-to-object communication. A fundamental principle of object-oriented programming is data encapsulation. We hide the internal state and require all interaction to be performed through an object's methods. Using objects provides a number of benefits, including: o Modularity: We can write the source code of each object independent of each other. Once created, an object can be easily used inside the system. o Information-hiding: An object can interact with other objects and the system around it through its objects. This hides the implementation details from those that don’t need to see or know about it. o Code re-use: A major advantage of using objects is the ability to re-use functionality that already exists rather than re-inventing the wheel. This allows developers to work in their area of specialisation. It also means developers can focus on functionality that really matter. For example: The java API provides functionality that is tried and tested for handling files on the computer system. Developers can use this logic rather than write their own. o Pluggability and debugging ease: If a particular object turns out to be problematic, you can simply remove it from your application and plug in a different object as its replacement. This is analogous to fixing mechanical problems in the real world. If a bolt breaks, you replace it, not the entire machine. CLASS A class is the blueprint from which individual objects are created. 4 Learning the Java Language- (25 APR -01 MAY - CODE) INTERFACE A user or business can define a set of functionality they require. This can be represented programmatically as an interface. The interface forms a contract between what the class does and the outside world that expects certain behaviour/functionality. This is enforced at build time by the compiler. Programmers can write classes that actually deliver that functionality. These classes are said to implement the interface. If a class claims to implement an interface, then it must have an implementation of all the methods the interface defines. If the class doesn’t implement these methods then java will prevent your application from compiling successfully. Implementing an interface allows a class to become more formal about the behaviour it promises to provide. In its most common form, an interface is a group of related methods with empty bodies. Example: syntax interface Bicycle { // wheel revolutions per minute void changeCadence(int newValue); void changeGear(int newValue); } class ACMEBicycle implements Bicycle { int cadence = 0; int speed = 0; int gear = 1; /* The compiler will now require that methods changeCadence, changeGear, all be implemented. Compilation will fail if those methods are missing from this class.*/ void changeCadence(int newValue) { cadence = newValue; } void changeGear(int newValue) { 5 Learning the Java Language- (25 APR -01 MAY - CODE) gear = newValue; } PACKAGE This is a namespace that holds related classes and interfaces similar to a folder on the computer. LANGUAGE BASICS VARIABLES Instance Variables (Non-Static Fields) Non-static fields are also known as instance variables because their values are unique to each instance/object of a class. For example: the current Speed of one bicycle is independent from current Speed of another. Class Variables (Static Fields) A class variable is any field declared with the static modifier; this tells the compiler that there is exactly one copy of this variable in existence, regardless of how many objects of the class there are. For example the number of gears for a particular kind of bicycle could be marked as static since conceptually the same number of gears will apply to all instances. Additionally, the keyword final could be added to indicate that the number of gears will never change. Local Variables Similar to how an object stores its state in fields, a method will often store its temporary state in local variables. Local variables are only visible to the methods in which they are declared; they are not accessible from the rest of the class. Parameters: The values that are passed to a method are called actual arguments in the Java specification. However, it is very common for them to be called just arguments, actual parameters, or just plain parameters. These terms are so used interchangeably so often that even the Java specification isn't entirely consistent. These actual arguments are inside parentheses following the method name. Use commas to separate arguments if there is more than one. Before a method is called, the arguments are evaluated left-to-right. Syntax public static void main(String[] args) { } 6 Learning the Java Language- (25 APR -01 MAY - CODE) If we are talking about "fields in general" (excluding local variables and parameters), we may simply say "fields". If the discussion applies to "all of the above", we may simply say "variables". You may also occasionally see the term "member" used as well. A type's fields, methods, and nested types are collectively called its members. Naming: o Variable names are case-sensitive. o A variable's name can be any legal identifier — an unlimited-length sequence of Unicode letters and digits, beginning with a letter, the dollar sign "$", or the underscore character "_". The convention, however, is to always begin your variable names with a letter, not "$" or "_". o Subsequent characters may be letters, digits, dollar signs, or underscore characters. Conventions (and common sense) apply to this rule as well. When choosing a name for your variables, use full words instead of cryptic abbreviations. Doing so will make your code easier to read and understand. o Keep in mind that the name you choose must not be a keyword or reserved word. o If the name you choose consists of only one word, spell that word in all lowercase letters. If it consists of more than one word, capitalize the first letter of each subsequent word. o If your variable stores a constant value, such as static final int NUM_GEARS = 6, the convention changes slightly, capitalizing every letter and separating subsequent words with the underscore character. PRIMITIVE DATA TYPES The Java programming language is statically-typed, which means that all variables must first be declared before they can be used. This involves stating the variable's type and name, as you've already seen: int gear = 1; Doing so tells your program that a field named "gear" exists, holds numerical data, and has an initial value of "1". A primitive type is predefined by the language and is named by a reserved keyword. The eight primitive data types supported by the Java programming language are: o byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive). The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. o short: The short data type is a 16-bit signed two's complement integer. It has a minimum value of -32,768 and a maximum value of 32,767 (inclusive). As with byte, the same guidelines apply: you can use a short to save memory in large arrays, in situations where the memory savings actually matters. 7 Learning the Java Language- (25 APR -01 MAY - CODE) o int: By default, the int data type is a 32-bit signed two's complement integer, which has a minimum value of -231 and a maximum value of 231-1. In Java SE 8 and later, you can use theint data type to represent an unsigned 32-bit integer, which has a minimum value of 0 and a maximum value of 232-1. Use the Integer class to use int data type as an unsigned integer. o long: The long data type is a 64-bit two's complement integer. The signed long has a minimum value of -263 and a maximum value of 263-1. In Java SE 8 and later, you can use the long data type to represent an unsigned 64-bit long, which has a minimum value of 0 and a maximum value of 264-1. Use this data type when you need a range of values wider than those provided by int. o float: The float data type is a single-precision 32-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. As with the recommendations for byte andshort, use a float (instead of double) if you need to save memory in large arrays of floating point numbers. o double: The double data type is a double-precision 64-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. For decimal values, this data type is generally the default choice. o boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its "size" isn't something that's precisely defined. o char: The char data type is a single 16-bit Unicode character. It has a minimum value of'\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive). The Java programming language also provides special support for character strings via the java.lang.String class. Enclosing your character string within double quotes will automatically create a new String object; for example, String s = "this is a string";. String objects are immutable, which means that once created, their values cannot be changed. The String class is not technically a primitive data type AUTOBOXING AND UNBOXING Java compiler automatically converts between the primitive types and their corresponding object wrapper classes. This is autoboxing. For example, int to an Integer, double to a Double. Example: Character ch = 'a'; Unboxing is conversion between the object wrapper class to the primitive type. The Java compiler applies unboxing when an object of a wrapper class is: Passed as a parameter to a method that expects a value of the corresponding primitive type. 8 Learning the Java Language- (25 APR -01 MAY - CODE) Assigned to a variable of the corresponding primitive type. The following table lists the primitive types and their corresponding wrapper classes, which are used by the Java compiler for autoboxing and unboxing: Primitive type Wrapper class boolean Boolean byte Byte char Character float Float int Integer long Long short Short double Double DEFAULT VALUES Fields that are declared but not initialized will be set to a reasonable default by the compiler. Relying on such default values, however, is generally considered bad programming style. Data Type Default Value (for fields) byte 0 short 0 int 0 long 0L float 0.0f double 0.0d 9 Learning the Java Language- (25 APR -01 MAY - CODE) char '\u0000' String (or any object) null boolean false Literals A literal is the source code representation of a fixed value; literals are represented directly in your code without requiring computation. As shown below, it's possible to assign a literal to a variable of a primitive type: boolean result = true; char capitalC = 'C'; byte b = 100; short s = 10000; int i = 100000; TO READ: INTEGER LITERALS TO READ: FLOATING-POINT LITERALS TO READ: CHARACTER AND STRING LITERALS TO READ: USING UNDERSCORE CHARACTERS IN NUMERIC LITERALS ARRAYS An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created. After creation, its length is fixed. 10 Learning the Java Language- (25 APR -01 MAY - CODE) Each item in an array is called an element, and each element is accessed by its numerical index. As shown in the preceding illustration, numbering begins with 0. The 9th element, for example, would therefore be accessed at index 8. Like declarations for variables of other types, an array declaration has two components: the array's type and the array's name. An array's type is written as type[], where type is the data type of the contained elements; the brackets are special symbols indicating that this variable holds an array. As with variables of other types, the declaration does not actually create an array; it simply tells the compiler that this variable will hold an array of the specified type. float[] anArrayOfFloats; You can also place the brackets after the array's name: // this form is discouraged float anArrayOfFloats[]; CREATING, INITIALIZING, AND ACCESSING AN ARRAY // create an array of integers anArray = new int[10]; If this statement is missing, then the compiler prints an error like the following, and compilation fails: Variable anArray may not have been initialized. assign values to each element of the array: anArray[0] = 100; // initialize first element Each array element is accessed by its numerical index: System.out.println("Element 1 at index 0: " + anArray[0]); Alternatively, you can use the shortcut syntax to create and initialize an array: int[] anArray = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; 11 Learning the Java Language- (25 APR -01 MAY - CODE) Here the length of the array is determined by the number of values provided between braces and separated by commas. You can also declare an array of arrays (also known as a multidimensional array) by using two or more sets of brackets, such as String[][] names. Each element, therefore, must be accessed by a corresponding number of index values. In the Java programming language, a multidimensional array is an array whose components are themselves arrays. Example in reference: Multi-Dimensional Arrays. Finally, you can use the built-in length property to determine the size of any array. The following code prints the array's size to standard output: System.out.println(anArray.length); COPYING ARRAYS The System class has an arraycopy method that you can use to efficiently copy data from one array into another: public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) The two Object arguments specify the array to copy from and the array to copy to. The three intarguments specify the starting position in the source array, the starting position in the destination array, and the number of array elements to copy. The following program, ArrayCopyDemo, declares an array of char elements, spelling the word "decaffeinated." It uses the System.arraycopy method to copy a subsequence of array components into a second array: class ArrayCopyDemo { public static void main(String[] args) { char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e', 'i', 'n', 'a', 't', 'e', 'd' }; char[] copyTo = new char[7]; System.arraycopy(copyFrom, 2, copyTo, 0, 7); System.out.println(new String(copyTo)); } 12 Learning the Java Language- (25 APR -01 MAY - CODE) } The output from this program is: caffein ARRAY MANIPULATIONS For your convenience, Java SE provides several methods for performing array manipulations (common tasks, such as copying, sorting and searching arrays) in the java.util.Arrays class. COPYOFRANGE lass ArrayCopyOfDemo { public static void main(String[] args) { char[] copyFrom = {'d', 'e', 'c', 'a', 'f', 'f', 'e', 'i', 'n', 'a', 't', 'e', 'd'}; char[] copyTo = java.util.Arrays.copyOfRange(copyFrom, 2, 9); System.out.println(new String(copyTo)); } } OTHER USEFUL OPERATIONS Searching an array for a specific value to get the index at which it is placed (the binarySearchmethod). Comparing two arrays to determine if they are equal or not (the equals method). Filling an array to place a specific value at each index (the fill method). Sorting an array into ascending order. This can be done either sequentially, using the sortmethod, or concurrently, using the parallelSort method introduced in Java SE 8. Parallel sorting of large arrays on multiprocessor systems is faster than sequential array sorting. 13 Learning the Java Language- (25 APR -01 MAY - CODE) OPERATORS Operator Precedence Operators Precedence postfix expr++ expr-- unary ++expr --expr +expr -expr ~ ! multiplicative */% additive +- shift << >> >>> relational < > <= >= instanceof equality == != bitwise AND & bitwise exclusive OR ^ bitwise inclusive OR | logical AND && logical OR || 14 Learning the Java Language- (25 APR -01 MAY - CODE) ternary ?: assignment = += -= *= /= %= &= ^= |= <<= >>= >>>= THE FOUR FUNDAMENTAL OOP CONCEPTS The four fundamental concepts of object-oriented programming are: encapsulation, inheritance, abstraction and polymorphism. ENCAPSULATION This is also called information hiding. By this we hide the implementation details of a class (methods and data) from its users (other applications and classes). The class acts as a container that encapsulates a set of methods, attribute and properties to provide its functionalities to other classes. Encapsulation hides how a class implements functionality while allowing other classes to make requests of it. Encapsulation allows a class to change its internal implementation without impacting the overall functioning of the system. In java, we make the variables of the class private then expose only the required or implemented methods through mutators (setter methods) and accessor (getter) methods. These methods change and provide the state of an object or variable respectively. ABSTRACTION Abstraction enables us to expose or deliver functionality of an application to users without the details of how we achieved it. In java, we do this by using the abstract class and an interface. We define a contract that our application needs to fulfil and expect it to deliver as expected. The application may use one of various implementation ways and even change its implementation without the user knowing it. In java we do this by either extending an abstract class or implementing an interface. ABSTRACT CLASS 15 Learning the Java Language- (25 APR -01 MAY - CODE) An abstract class is declared with the abstract keyword and cannot be instantiated. It can only be used as a super-class for other classes that extend the abstract class. The implementation gets completed when the subclass extend it. We use abstract keyword to create an abstract method which doesn’t have body. A class with abstract methods will only compile if it is made abstract. On the other hand an abstract class doesn’t need to have an abstract methods. The subclass of abstract class must implement all the abstract methods, unless the subclass is also an abstract class. Abstract classes can implement interfaces without even providing the implementation of interface methods. We can run abstract class like any other class if it has main() method. INTERFACE An interface is a contract between an application and the outside world. It is a promise of what functionality an application will deliver. It helps abstract the details while revealing what users of an application can expect. An interface is a reference type in java and a collection of abstract methods. An interface may also contain constants, default methods, static methods, and nested types. Method bodies exist only for default methods and static methods. A concrete class will implement an interface and thus inherit the abstract methods of the interface. WHEN TO USE AN INTERFACE/ OR AN ABSTRACT CLASS To determine whether to use an interface or an abstract class or both, look at the relationship as a IS-A hierarchy or CAN-DO-THIS hierarchy. An abstract class is suitable where the relationship to be defines is an IS-A hierarchy. An Interface is suitable where the relationship is a CAN-DO-THIS relationship. DIFFERENCE BETWEEN ABSTRACT CLASS AND INTERFACE Interface is of type interface while Abstract class is of type class Interface methods do not implement the functionality they define (except for the default methods), instead it is implemented. Abstract class methods can have their own default implementations which can be run independently and they may also be extended. POLYMORPHISM Polymorphism is the ability of an object to take on many forms. For example we can use a parent class reference in reference to a child class object. We can refer to a 16 Collections (01-08 MAY - CODE) person to illustrate a person's behaviour, even when we know that that person is a teacher. An object is considered polymorphic if it passes the IS-A test - That is, it is a type of another object. As such, every object in java is polymorphic because it is of the object type. We cannot change the reference variable of an object once we have declared it, although we can reassign it to another object if it is not declared final. There are two types of polymorphism in Java: Compile time polymorphism (static binding) e.g. method overloading. Runtime polymorphism (dynamic binding) e.g. method overriding. INHERITANCE Object-oriented programming allows classes to inherit commonly used state and behavior from other classes. o Example: A Bicycle is the superclass of MountainBike, RoadBike, andTandemBike. In Java each class is allowed to have one direct superclass, and each superclass has the potential for an unlimited number of subclasses. At the beginning of your class declaration, use the extends keyword, followed by the name of the class to inherit from: class MountainBike extends Bicycle { // new fields and methods defining // a mountain bike would go here } MountainBike has the same fields and methods as Bicycle, yet allows its code to focus exclusively on the features that make it unique. 3) COLLECTIONS (01-08 MAY - CODE) INTRODUCTION WHAT IS THE COLLECTIONS FRAMEWORK 17 Collections (01-08 MAY - CODE) A collection — sometimes called a container — is simply an object that groups multiple elements into a single unit. Collections are used to store, retrieve, manipulate, and communicate aggregate data. Typically, they represent data items that form a natural group, such as a poker hand (a collection of cards), a mail folder (a collection of letters), or a telephone directory (a mapping of names to phone numbers). The collections framework is a unified architecture for representing and manipulating collections. Contains the following: Interfaces: These are abstract data types that represent collections. Interfaces allow collections to be manipulated independently of the details of their representation. In object-oriented languages, interfaces generally form a hierarchy. Implementations: These are the concrete implementations of the collection interfaces. In essence, they are reusable data structures. Algorithms: These are the methods that perform useful computations, such as searching and sorting, on objects that implement collection interfaces. o The algorithms are polymorphic: the same method can be used on many different implementations of the appropriate collection interface. So the functionality is reusable. BENEFITS OF THE JAVA COLLECTIONS FRAMEWORK Reduces programming effort Increases program speed and quality Allows interoperability among unrelated APIs Reduces effort to learn and to use new APIs Reduces effort to design new APIs Fosters software reuse LESSON: INTERFACES Core collection interfaces are the foundation of the Java Collections Framework. 18 Collections (01-08 MAY - CODE) The hierarchy consists of two distinct trees — a Map is not a true Collection. The core collection interfaces are generic When you declare a Collection instance you can and should specify the type of object contained in the collection. Specifying the type allows the compiler to verify (at compile-time) that the type of object you put into the collection is correct, thus reducing errors at runtime. The modification operations in each interface are designatedoptional — a given implementation may elect not to support all operations. If an unsupported operation is invoked, a collection throws an UnsupportedOperationException. Implementations are responsible for documenting which of the optional operations they support. All of the Java platform's general-purpose implementations support all of the optional operations. COLLECTION The Collection interface is the least common denominator that all collections implement and is used to pass collections around and to manipulate them when maximum generality is desired. Some types of collections allow duplicate elements, and others do not. Some are ordered and others are unordered. The Java platform doesn't provide any direct implementations of this interface but provides implementations of more specific subinterfaces, such as Set and List. SET A collection that cannot contain duplicate elements. Models the mathematical set storing a set such as the courses making up a student's schedule, or the processes running on a machine. The interface- java.util.Set is a subtype of the java.util.Collection interface. So all methods in the Collection interface are also available in the Set interface. LIST Ordered collection (sometimes called a sequence). Lists can contain duplicate elements. The user of a List generally has precise control over where in the list each element is inserted and can access elements by their integer index (position). QUEUE A collection used to hold multiple elements prior to processing. Besides basic collection operations, a Queue provides additional insertion, extraction, and inspection operations. 19 Collections (01-08 MAY - CODE) Queues typically, but do not necessarily, order elements in a FIFO (first-in, first-out) manner. Whatever the ordering used, the head of the queue is the element that would be removed by a call to remove or poll. In a FIFO queue, all new elements are inserted at the tail of the queue. Other kinds of queues may use different placement rules. Every Queue implementation must specify its ordering properties. DEQUE A collection used to hold multiple elements prior to processing. Besides basic Collection operations, a Deque provides additional insertion, extraction, and inspection operations. In a deque all new elements can be inserted, retrieved and removed at both ends MAP An object that maps keys to values. A Map cannot contain duplicate keys Each key can map to at most one value. The following core collection interfaces are merely sorted versions of Set and Map: SORTEDSET A set that maintains its elements in ascending order. Several additional operations are provided to take advantage of the ordering. Sorted sets are used for naturally ordered sets, such as word lists and membership rolls. SORTEDMAP A Map that maintains its mappings in ascending key order. Used for naturally ordered collections of key/value pairs, such as dictionaries and telephone directories. THE COLLECTION INTER FACE Contains methods that perform basic operations, such as int size(), boolean isEmpty(), boolean contains(Object element), boolean add(E element), boolean remove(Object element), and Iterator<E> iterator(). 20 Collections (01-08 MAY - CODE) Contains methods that operate on entire collections, such as boolean containsAll(Collection<?> c), boolean addAll(Collection<? extends E> c), boolean removeAll(Collection<?> c), boolean retainAll(Collection<?> c), and void clear(). Contains methods for array operations (such as Object[] toArray() and <T> T[] toArray(T[] a) exist as well. In JDK 8 and later, the Collection interface also exposes methods Stream<E> stream() and Stream<E> parallelStream(), for obtaining sequential or parallel streams from the underlying collection. The add method is defined generally enough so that it makes sense for collections that allow duplicates as well as those that don't. It guarantees that the Collection will contain the specified element after the call completes, and returns true if the Collection changes as a result of the call. Similarly, the remove method is designed to remove a single instance of the specified element from the Collection, assuming that it contains the element to start with, and to return true if the Collection was modified as a result. TRAVERSING COLLECTIONS There are three ways to traverse collections: (1) using aggregate operations (2) with the for-each construct and (3) by using Iterators. AGGREGATE OPERATIONS for (Person p : roster) { if (p.getGender() == Person.Sex.MALE) { System.out.println(p.getName()); } } FOREACH for (Number number : numbers){ log(number); } ITERATOR Iterator itr = al.iterator(); while(itr.hasNext()) { Object element = itr.next(); 21 Collections (01-08 MAY - CODE) 22 System.out.print(element + " "); } LESSON: IMPLEMENTATIONS General-purpose - are the most commonly used implementations, designed for everyday use. They are summarized in the table titled General-purposeimplementations. Special-purpose - designed for use in special situations and display nonstandard performance characteristics, usage restrictions, or behavior. Concurrent - designed to support high concurrency, typically at the expense of single-threaded performance. These implementations are part of the java.util.concurrent package. Wrapper - used in combination with other types of implementations, often the general-purpose ones, to provide added or restricted functionality. Convenience are mini-implementations, typically made available via static factory methods, that provide convenient, efficient alternatives to general-purpose implementations for special collections (for example, singleton sets). Abstract are skeletal implementations that facilitate the construction of custom implementations — described later in the Custom Collection Implementations section. An advanced topic, it's not particularly difficult, but relatively few people will need to do it. GENERAL-PURPOSE IMPLEMENTATIONS Java Collections Framework provides several general-purpose implementations. You may only need HashSet, ArrayList, and HashMap for most applications. The General Purpose implementation of the SortedSet and the SortedMap interfaces are the TreeSet and TreeMap, hence the interfaces are not listed in the table. There are two general purpose queue implementations — LinkedList (also a List implementation) and PriorityQueue (not in the table). LinkedList provides FIFO functionality while the PriorityQueue order elements according to their value. Interfaces Hash table Resizable array Implementations Implementations Tree Implementations Set HashSet TreeSet List ArrayList Linked list Implementations Hash table + Linked list Implementations LinkedHashSet LinkedList Collections (01-08 MAY - CODE) 23 Queue Deque ArrayDeque Map HashMap LinkedList TreeMap LinkedHashMap Similarities Each of the general-purpose implementations provides all optional operations contained in its interface. All permit null elements, keys, and values. All have Fail-fast iterators All are Serializable and clonable. None are synchronized o A break with the past - legacy collections Vector and Hashtable are synchronized. o Because collections are frequently used when the synchronization is of no benefit e.g. single-threaded use, read-only use, and as part of a larger data object that does its own synchronization. Users don’t have to pay for a feature they don't use. Also, unnecessary synchronization can result in deadlock under some circumstances. Collections (01-08 MAY - CODE) o If you need thread-safe collections, the synchronization wrappers allow any collection to be transformed into a synchronized collection. Moreover, the java.util.concurrent package provides concurrent implementations of the BlockingQueue interface, which extends Queue, and of the ConcurrentMapinterface, which extends Map. These implementations offer much higher concurrency than mere synchronized implementations. SET IMPLEMENTATION There are three general-purpose Set implementations — HashSet, TreeSet, and LinkedHashSet. HashSet is much faster than TreeSet (constant-time versus log-time for most operations) but offers no ordering guarantees. Hashset is the most commonly used implementation. If you need to use the operations in the SortedSet interface, or if value-ordered iteration is required, use TreeSet. HASHSET HashSet doesn’t maintain any order, the elements would be returned in any random order. HashSet doesn’t allow duplicates. If you try to add a duplicate element in HashSet, the old value would be overwritten. HashSet allows null values however if you insert more than one nulls it would still return only one null value. HashSet is non-synchronized. The Collection Framework provides methods to enable us to synchronise it if it will be used by multiple threads. The iterator returned by this class is fail-fast which means iterator would throw ConcurrentModificationException if HashSet has been modified after creation of iterator, by any means except iterator’s own remove method. Iteration is linear in the sum of the number of entries and the number of buckets (the capacity). Choosing an initial capacity that's too high can waste both space and time. An initial capacity that's too low wastes time by copying the data structure each time it's forced to increase its capacity. If you don't specify an initial capacity, the default is 16. In the past, there was some advantage to choosing a prime number as the initial capacity. This is no longer true. Internally, the capacity is always rounded up to a power of two. The initial capacity is specified by using the int constructor. Set<String> s = new HashSet<String>(64); If you care a lot about the space consumption of your HashSet, read the HashSet documentation for more information on the tuning parameter called 24 Collections (01-08 MAY - CODE) the load factor. Otherwise, just accept the default. If you accept the default load factor but want to specify an initial capacity, pick a number that's about twice the size to which you expect the set to grow. LINKEDHASHSET Intermediate between HashSet and TreeSet. Implemented as a hash table with a linked list running through it, it provides insertionordered iteration (least recently inserted to most recently) and runs nearly as fast as HashSet. The LinkedHashSet implementation spares its clients from the unspecified, generally chaotic ordering provided by HashSet without incurring the increased cost associated with TreeSet. LinkedHashSet has the same tuning parameters as HashSet, but iteration time is not affected by capacity. TreeSet has no tuning parameters. TREESET TreeSet is similar to the HashSet except that it sorts the elements in the ascending order unlike the HashSet. TreeSet allows null element. Like most of the other collection classes this class is also not synchronized ENUMSET A high-performance Set implementation for enum types. All of the members of an enum set must be of the same enum type. Internally, it is represented by a bit-vector, typically a single long. COPYONWRITEARRAYSET Set implementation backed up by a copy-on-write array. All mutative operations, such as add, set, and remove, are implemented by making a new copy of the array; no locking is ever required. Even iteration may safely proceed concurrently with element insertion and deletion. Unlike most Set implementations, the add, remove, and contains methods require time proportional to the size of the set. This implementation is only appropriate for sets that are rarely modified but frequently iterated. It is well suited to maintaining event-handler lists that must prevent duplicates. LIST IMPLEMENTATION 25 Collections (01-08 MAY - CODE) You can choose between the following List implementations in the Java Collections API: o java.util.ArrayList o java.util.LinkedList o java.util.Vector o java.util.Stack The order in which the elements are added to the List is stored, so you can access the elements in the same order. You can do so using either the get(int index) method, or via the Iterator returned by theiterator() method ARRAYLIST Introduced with JDK 1.2. Most used implementation after the Vector. The elements added are stored internally as an array. It permits duplicates and null values as elements. ArrayList is not synchronised. Elements can be retrieved with the conventional styles of foreach loop, iterators and indexes. Implements the List interface. It is widely used as a replacement/alternative to the traditional java Array because of the functionality and flexibility. The issue with arrays: o They are of fixed length so if it is full we cannot add any more elements to it. o If elements are removed from it, the memory consumption would be the same as it doesn’t shrink. The ArrayList can dynamically grows and shrinks. ArrayList has one tuning parameter — the initial capacity, which refers to the number of elements theArrayList can hold before it has to grow. The performance of an ArrayList decreases as it grows. When an ArrayList grows, a new array with increased capacity is created, the contents of the old array are copied into the new array and old array location is garbage collected. Repeating this slows execution. CONSTRUCTORS ArayList(): Creates an ArrayList object with an initial capacity to store 10 elements. ArayList(int capacity): Creates an ArrayList object with an initial capacity provided. 26 Collections (01-08 MAY - CODE) ArayList(Collection collection): Creates an ArrayList object with the elements of collection. KEY METHODS ensureCapacity(): Consider a scenario when there is a need to add huge number of elements to an already full ArrayList. The ArrayList has to be resized several times which would result in a poor performance. For such scenarios ensureCapacity() increases the size of the ArrayList by a specified capacity. set(int index, Object o): Used for updating an element. It replaces the element present at the specified index with the object o. LINKEDLIST A linked list is a linear data structure where each element is a separate object. Each element has two items - the data and a reference to the next node. The last node has a reference to null. The entry point into a linked list is called the head of the list. This is the first node. If the list is empty then the head is a null reference. A linked list is a dynamic data structure- can grow and shrink on demand. This makes it useful when the number of objects to be stored is unknown. The LinkedList does not allow direct access to the individual elements. If you want to access a particular item then you have to start at the head and follow the references until you get to that item. It also uses more memory in comparison to arrays – An extra 4 bytes (on 32-bit CPU) to store a reference to the next node. TYPES OF LINKED LISTS 27 Collections (01-08 MAY - CODE) LINKED LIST OPERATIONS http://www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/linked%20lists.html MAP IMPLEMENTATION GENERAL-PURPOSE MAP IMPLEMENTATIONS: HashMap, TreeMap, LinkedHadMap. The most commonly used map is the HashMap. HashMap - If you need a map with fast access and don’t care about the iteration order. TreeMap - For sorted map operations or key-ordered iteration. LinkedHashMap - If you need near HashMap performance with insertion-order iteration. o When you create a LinkedHashMap, you can order it based on key access rather than insertion. o In other words, merely looking up the value associated with a key brings that key to the end of the map. o LinkedHashMap also provides the removeEldestEntry method that can be overridden to remove stale records. This makes it easy to implement a custom cache. 28 Concurrency (23-29 MAY-CODE) SPECIAL PURPOSE MAP IMPLEMENTATIONS ENUMMAP EnumMap is a high-performance Map implementation for use with enum keys. It is internally implemented as an array Used to map an enum to a value. WEAKHASHMAP WeakHashMap is an implementation that stores only weak references to its keys. This allows a key-value pair to be garbage-collected when its key is no longer referenced outside of the WeakHashMap. It provides the easiest way to harness the power of weak references useful for implementing "registry-like" data structures, where the utility of an entry vanishes when its key is no longer reachable by any thread. IDENTITYHASHMAP IdentityHashMap is an identity-based Map implementation based on a hash table. IdentityHashMap is useful for topology-preserving object graph transformations, such as serialization or deep-copying. To perform such transformations, you need to maintain an identity-based "node table" that keeps track of which objects have already been seen. Identity-based maps are also used to maintain object-to-meta-information mappings in dynamic debuggers and similar systems. Finally, identity-based maps are useful in thwarting "spoof attacks" that are a result of intentionally perverse equals methods because IdentityHashMap never invokes the equals method on its keys. An added benefit of this implementation is that it is fast. CONCURRENT MAP IMPLEMENTATIONS CONCURRENTHASHMAP ConcurrentHashMap is a highly concurrent, high-performance implementation backed up by a hash table. ConcurrentHashMap never blocks when performing retrievals and allows the client to select the concurrency level for updates. 4) CONCURRENCY (23-29 MAY-CODE) 29 Concurrency (23-29 MAY-CODE) The Java platform is designed to support concurrent programming, with basic concurrency support in the Java programming language and the Java class libraries. Since version 5.0, the Java platform has also included high-level concurrency APIs. This tutorial will cover basic concurrency support and some of the high-level APIs in thejava.util.concurrent packages INTRODUCTION PROCESSES A process has a self-contained execution environment with a complete, private set of basic run-time resources; in particular, each process has its own memory space. To facilitate communication between processes, most operating systems support Inter Process Communication (IPC) resources, such as pipes and sockets. IPC is used for communication between processes on the same system and on different systems. THREADS Threads are sometimes called lightweight processes. Both processes and threads provide an execution environment, but creating a new thread requires fewer resources than creating a new process. Threads share the process's resources, including memory and open files. This can be a problem and that is why java has the concurrency APIs to help manage these resources efficiently. From the application programmer's point of view, you start with just one thread, called the main thread. This thread has the ability to create additional threads. Each thread is associated with an instance of the class Thread. There are two basic strategies for using Thread objects to create a concurrent application. To directly control thread creation and management, simply instantiate Thread each time the application needs to initiate an asynchronous task. To abstract thread management from the rest of your application, pass the application's tasks to an executor. DEFINING AND STARTING A THREAD An application that creates an instance of Thread must provide the code that will run in that thread. There are two ways to do this: Provide a Runnable object. The Runnable interface defines a single method, run, meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor. This is more general, because the Runnable object can subclass 30 Concurrency (23-29 MAY-CODE) a class other than Thread. Not only is this approach more flexible, but it is applicable to the high-level thread management APIs. Subclass Thread. The Thread class itself implements Runnable, though its run method does nothing. An application can subclass Thread, providing its own implementation of run. This is easier to use in simple applications, but is limited by the fact that your task class must be a descendant of Thread. Both invoke Thread.start in order to start the new thread. The Thread class defines a number of methods useful for thread management: o Static methods, which provide information about, or affect the status of, the thread invoking the method. o Methods invoked from other threads involved in managing the thread and Thread object. PAUSING EXECUTION WITH SLEEP Thread.sleep causes the current thread to suspend execution for a specified period. This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system. Two overloaded versions of sleep are provided: o one that specifies the sleep time to the millisecond o one that specifies the sleep time to the nanosecond. Sleep times are not guaranteed to be precise: o Limited by the facilities provided by the underlying OS. o Sleep period can be terminated by interrupts Sleep throws InterruptedException when another thread interrupts the current thread while sleep is active. INTERRUPTS If any thread is in sleeping or waiting state (i.e. sleep() or wait() is invoked), calling the interrupt() method on the thread, breaks out the sleeping or waiting state throwing InterruptedException. 31 Concurrency (23-29 MAY-CODE) If the thread is not in the sleeping or waiting state, calling the interrupt() method performs normal behaviour and doesn't interrupt the thread but sets the interrupt flag to true. An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate. A thread sends an interrupt by invoking interrupt on the Thread object for the thread to be interrupted. For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption. How?: o Many methods that throw InterruptedException, such as sleep, are designed to cancel their current operation and return immediately when an interrupt is received. o If a thread goes a long time without invoking a method that throws InterruptedException, then it must periodically invokeThread.interrupted, which returns true if an interrupt has been received. For example: for (int i = 0; i < inputs.length; i++) { heavyCrunch(inputs[i]); if (Thread.interrupted()) { // We've been interrupted: no more crunching. return; } } o In more complex applications, it might make more sense to throw an InterruptedException. This allows interrupt handling code to be centralized in a catch clause. if (Thread.interrupted()) { throw new InterruptedException(); } 32 Concurrency (23-29 MAY-CODE) THE INTERRUPT STATUS FLAG o The interrupt mechanism is implemented using an internal flag known as the interrupt status. o Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. o The non-static isInterrupted method, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag. JOINS The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing, t.join(); causes the current thread to pause execution until t's thread terminates. As with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify. Like sleep, join responds to an interrupt by exiting with an InterruptedException. WHEN TO USE JOIN To make one Thread to wait for another and start execution once that Thread has completed execution or died. Join is also a blocking method, which blocks until thread on which join has called die or specified waiting time is over. SYNCHRONIZATION INTRODUCTION Threads communicate primarily by sharing access to fields and the objects reference fields refer to. Synchronization is needed to prevent two kinds of errors possible: thread interference and memory consistency errors: When two or more threads try to access the same resource simultaneously this may result in thread contention causing the Java runtime to execute one or more threads more slowly, or even suspend execution. THREAD INTERFERENCE 33 Concurrency (23-29 MAY-CODE) Interference happens when two operations, running in different threads, but acting on the same data, interleave. This means that the two operations consist of multiple steps, and the sequences of steps overlap. Because they are unpredictable, thread interference bugs can be difficult to detect and fix. MEMORY CONSISTENCY ERRORS Happens because of a number of cases in which accesses to program variables may appear to execute in a different order than was specified by the program. As such different threads have inconsistent views of what should be the same data. Understanding the happens-before relationship helps us to avoid these erorrs. The compiler, for example, is free to reorder instructions to optimize performance. But also Processors may execute instructions out of order under certain circumstances. They can move data between registers, memory caches, and main memory in different order than specified by the program. EXAMPLE ACTIONS THAT LEAD TO HAPPENS-BEFORE RELATIONSHIPS. Synchronization When a statement invokes Thread.start, the effects of the code that led up to the creation of the new thread are visible to the new thread. Every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. When a thread terminates and causes a Thread.join in another thread to return, the effects of the code in the thread are now visible to the thread that performed the join. All the statements executed by the terminated thread have a happens-before relationship with all the statements following the successful join. Full list of java concurrency happens-before actions Java provides two forms of synchronization: Synchronized methods and synchronized statements. SYNCHRONIZED METHODS To make a method synchronized, simply add the synchronized keyword to its declaration. This has two effects: It is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block until the first thread is done with the object. 34 Concurrency (23-29 MAY-CODE) When a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads. Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors but can present problems with liveness. INTRINSIC LOCKS Every object has an intrinsic lock aka monitor lock associated with it. The API specification often refers to this entity simply as a "monitor." The Intrinsic lock plays a role in both aspects of synchronization: enforcing exclusive access to an object's state and establishing happens-before relationships that are essential to visibility. A thread that needs exclusive and consistent access to an object's fields acquires the intrinsic lock before accessing them, and then releases it when it's done. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. Threads that attempt to acquire the lock will block as long as the thread owns the lock. When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquistion of the same lock. A lock is a thread synchronization mechanism like synchronized blocks except locks can be more sophisticated than Java's synchronized blocks. Locks (and other more advanced synchronization mechanisms) are created using synchronized blocks. From Java 5 the package java.util.concurrent.locks contains several lock implementations, so you may not have to implement your own locks. But you will still need to know how they work and how to use them. ATOMIC ACCESS An atomic action is one that effectively happens all at once. It either happens completely, or it doesn't happen at all. It cannot stop in the middle. No side effects of an atomic action are visible until the action is complete. Even very simple expressions can define complex actions that can decompose into other actions. Examples of atomic actions: o Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double). o Reads and writes are atomic for all variables declared volatile (including long and double variables). Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, memory consistency errors are still possible. 35 Concurrency (23-29 MAY-CODE) Using volatile variables reduces the risk of memory consistency errors: Any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads. It also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change. Using simple atomic variable access is more efficient than accessing these variables through synchronized code, but requires more care by the programmer to avoid memory consistency errors. Whether the extra effort is worthwhile depends on the size and complexity of the application. Some of the classes in the java.util.concurrent package provide atomic methods that do not rely on synchronization. LOCKS IN SYNCHRONIZED METHODS When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns even if the return was caused by an uncaught exception. For static methods, the thread acquires the intrinsic lock for the Class object associated with the class. Access to class's static fields is controlled by a lock that's distinct from the class instance lock. SYNCHRONIZED STATEMENTS Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock: public void addName(String name) { synchronized(this) { lastName = name; nameCount++; } nameList.add(name); } In this example, the addName method needs to synchronize changes to lastName and nameCount, but also needs to avoid synchronizing invocations of other 36 Concurrency (23-29 MAY-CODE) objects' methods. (Invoking other objects' methods from synchronized code can create problems that are described in the section on Liveness.) Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks. public class MsLunch { private long c1 = 0; private long c2 = 0; private Object lock1 = new Object(); private Object lock2 = new Object(); public void inc1() { synchronized(lock1) { c1++; } } public void inc2() { synchronized(lock2) { c2++; } } } Use this idiom with extreme care. You must be absolutely sure that it really is safe to interleave access of the affected fields. A SIMPLE LOCK 37 Concurrency (23-29 MAY-CODE) USING SYNCHRONIZED: ESSENTIAL JAVA CLASSES - BASIC I/O USING BYTE STREAMS import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("xanadu.txt"); out = new FileOutputStream("outagain.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } 38 Concurrency (23-29 MAY-CODE) } } USING CHARACTER STREAMS import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader("xanadu.txt"); outputStream = new FileWriter("characteroutput.txt"); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } 39 Concurrency (23-29 MAY-CODE) } } LINE-ORIENTED I/O import java.io.FileReader; import java.io.FileWriter; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader("xanadu.txt")); outputStream = new PrintWriter(new FileWriter("characteroutput.txt")); String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); 40 Concurrency (23-29 MAY-CODE) } } } } SCANNING import java.io.*; import java.util.Scanner; public class ScanXan { public static void main(String[] args) throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader("xanadu.txt"))); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } } } SCANNING JAVA PRIMIT IVE TYPE TOKENS import java.io.FileReader; 41 Concurrency (23-29 MAY-CODE) import java.io.BufferedReader; import java.io.IOException; import java.util.Scanner; import java.util.Locale; public class ScanSum { public static void main(String[] args) throws IOException { Scanner s = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader("usnumbers.txt"))); s.useLocale(Locale.US); while (s.hasNext()) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } } finally { s.close(); } System.out.println(sum); } } 42 Concurrency (23-29 MAY-CODE) And here's the sample input file, usnumbers.txt 8.5 32,767 3.14159 1,000,000.1 CONCURRENCY Synchronized statement code Using a simple lock: Simple lock use code The lock() method locks the Lock instance so that all threads calling lock() are blocked until unlock() is executed. Simple Lock implementation: Simple lock implementation code o Notice the while(isLocked) loop, which is also called a "spin lock". Spin locks and the methods wait() and notify() are covered in more detail in Thread Signaling. o While isLocked is true, the thread calling lock() is parked waiting in the wait() call. o In case the thread should return unexpectedly from the wait() call without having received a notify() call (AKA a Spurious Wakeup) the thread re-checks the isLocked condition to see if it is safe to proceed or not, rather than just assume that being awakened means it is safe to proceed. o If isLocked is false, the thread exits the while(isLocked) loop, and sets isLocked back to true, to lock the Lock instance for other threads calling lock(). o When the thread is done with the code in the critical section (the code between lock() and unlock()), the thread calls unlock(). Executing unlock() sets isLocked back to false, and notifies (awakens) one of the threads waiting in the wait() call in the lock() method, if any. REENTRANT SYNCHRONIZATION This happens when synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock - the monitor object the block is synchronized on. A thread can acquire a lock that it already owns. Allowing a thread to acquire the same lock more than once enables reentrant synchronization. Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block. 43 Concurrency (23-29 MAY-CODE) Making locks re-entrant: Reentrant lock Implementation LOCK FAIRNESS Java's synchronized blocks makes no guarantees about the sequence in which threads trying to enter them are granted access. One or more of the threads may never be granted access and access may always be granted to other threads. This is called starvation. CALLING UNLOCK() FROM A FINALLY-CLAUSE Doing so makes sure that the Lock is unlocked in case an exception is thrown from the code in the critical section. If unlock() was not called from inside a finally-clause, and an exception was thrown from the critical section, the Lock would remain locked forever, causing all threads calling lock() on that Lock instance to halt indefinitely. DEADLOCKS THREAD DEADLOCK A deadlock is when two or more threads are blocked waiting to obtain locks that some of the other threads in the deadlock are holding. Deadlock can occur when multiple threads need the same locks, at the same time, but obtain them in different order. See Deadlock example Deadlock can also include more than two threads. This makes it harder to detect. DATABASE DEADLOCKS A more complicated situation in which deadlocks can occur, is a database transaction. A database transaction may consist of many SQL update requests. When a record is updated during a transaction, that record is locked for updates from other transactions, until the first transaction completes. Each update request within the same transaction may therefore lock some records in the database. If multiple transactions are running at the same time that need to update the same records, there is a risk of them ending up in a deadlock. DEADLOCK PREVENTION LOCK ORDERING 44 Concurrency (23-29 MAY-CODE) Deadlock occurs when multiple threads need the same locks but obtain them in different order. If you make sure that all locks are always taken in the same order by any thread, deadlocks cannot occur. Example: o Thread 1: lock A, lock B o Thread 2:wait for A, lock C (when A locked) o Thread 3: wait for A, wait for B, wait for C o If a thread, like Thread 3, needs several locks, it must take them in the decided order. It cannot take a lock later in the sequence until it has obtained the earlier locks. o For instance, neither Thread 2 nor Thread 3 can lock C until they have locked A first. Since Thread 1 holds lock A, Thread 2 and 3 must first wait until lock A is unlocked. Then they must succeed in locking A, before they can attempt to lock B or C. Lock ordering is a simple yet effective deadlock prevention mechanism. However, it can only be used if you know about all locks needed ahead of taking any of the locks. This is not always the case. LOCK TIMEOUT Put a timeout on lock attempts meaning a thread trying to obtain a lock will only try for so long before giving up. If a thread does not succeed in taking all necessary locks within the given timeout, it will backup, free all locks taken, wait for a random amount of time and then retry. The random amount of time waited serves to give other threads trying to take the same locks a chance to take all locks, and thus let the application continue running without locking. Example: o Thread 1 locks A, Thread 2 locks B o Thread 1 attempts to lock B but is blocked, Thread 2 attempts to lock A but is blocked. Thread 1's lock attempt on B times out, Thread 1 backs up and releases A as well. Thread 1 waits randomly (e.g. 257 millis) before retrying, o Thread 2's lock attempt on A times out, Thread 2 backs up and releases B as well, Thread 2 waits randomly (e.g. 43 millis) before retrying. o In this example Thread 2 will retry taking the locks about 200 millis before Thread 1 and will therefore likely succeed at taking both locks. Thread 1 will then wait already trying to take lock A. When Thread 2 finishes, Thread 1 will be able to take both locks too (unless Thread 2 or another thread takes the locks in between). Bear in mind that the timeouts could be because the thread holding the lock (causing the other thread to time out) takes a long time to complete its task not necessarily a deadlock. 45 Concurrency (23-29 MAY-CODE) Additionally, if enough threads compete for the same resources they still risk trying to take the threads at the same time again and again, even if timing out and backing up. This may most likely occur with more threads. A problem with the lock timeout mechanism is that it is not possible to set a timeout for entering a synchronized block in Java. You will have to create a custom lock class or use one of the Java 5 concurrency constructs in the java.util.concurrency package. DEADLOCK DETECTION Deadlock detection is a heavier deadlock prevention mechanism aimed at cases in which lock ordering isn't possible, and lock timeout isn't feasible. Every time a thread takes a lock it is noted in a data structure (map, graph etc.) of threads and locks. Additionally, whenever a thread requests a lock this is also noted in this data structure. When a thread requests a lock but the request is denied, the thread can traverse the lock graph to check for deadlocks. For instance, if a Thread A requests lock 7, but lock 7 is held by Thread B, then Thread A can check if Thread B has requested any of the locks Thread A holds (if any). If Thread B has requested so, a deadlock has occurred (Thread A having taken lock 1, requesting lock 7, Thread B having taken lock 7, requesting lock 1). Of course a deadlock scenario may be a lot more complicated than two threads holding each others locks. One possible action is to release all locks, backup, wait a random amount of time and then retry. This is similar to the simpler lock timeout mechanism except threads only backup when a deadlock has actually occurred. Not just because their lock requests timed out. 46 Concurrency (23-29 MAY-CODE) However, if a lot of threads are competing for the same locks they may repeatedly end up in a deadlock even if they back up and wait. A better option is to determine or assign a priority of the threads so that only one (or a few) thread backs up. The rest of the threads continue taking the locks they need as if no deadlock had occurred. If the priority assigned to the threads is fixed, the same threads will always be given higher priority. To avoid this you may assign the priority randomly whenever a deadlock is detected. LIVENESS A concurrent application's ability to execute in a timely manner is known as its liveness. DEADLOCK When Deadlock runs, it's extremely likely that both threads will block and the block will not come to an end, because each thread is waiting for the other to exit bow. STARVATION Starvation describes a situation where a thread is unable to gain regular access to shared resources as they are made unavailable for long periods by "greedy" threads. For example, suppose an object provides a synchronized method that often takes a long time to return. If one thread invokes this method frequently, other threads that also need frequent synchronized access to the same object will often be blocked. CAUSES 1. Threads with high priority swallow all CPU time from threads with lower priority The higher the priority the more CPU time the thread is granted. You can set the priority of threads between 1 and 10. Exactly how this is interpreted depends on the operating system your application is running on. For most applications you are better off leaving the priority unchanged. 2. Threads are blocked indefinitely waiting to enter a synchronized block Java's synchronized code block makes no guarantee about the sequence in which threads waiting to enter the synchronized block are allowed to enter. This means that there is a theoretical risk that a thread remains blocked forever trying to enter the block, because other threads are constantly granted access before it. 3. Threads waiting on an object (called wait() on it) remain waiting indefinitely 47 Concurrency (23-29 MAY-CODE) The notify() method makes no guarantee about what thread is awakened if multiple thread have called wait() on the object notify() is called on. It could be any of the threads waiting. Therefore there is a risk that a thread waiting on a certain object is never awakened because other waiting threads are always awakened instead of it. SOLUTION USING LOCKS INSTEAD OF SYNCHRONIZED BLOCKS Remember that a thread calling wait() releases the synchronization lock on the Lock instance, so threads waiting to enter lock() can now do so. The result is that multiple threads can end up having called wait() inside lock(). Synchronized blocks makes no guarantees about what thread is being granted access if more than one thread is waiting to enter. Nor does wait() make any guarantees about what thread is awakened when notify() is called. If instead each thread calls wait() on a separate object, so that only one thread has called wait() on each object, the Lock class can decide which of these objects to call notify() on, thereby effectively selecting exactly what thread to awaken. FAIR LOCK every thread calling lock() is now queued, and only the first thread in the queue is allowed to lock the FairLock instance, if it is unlocked. All other threads are parked waiting until they reach the top of the queue. LIVELOCK A thread often acts in response to the action of another thread. If the other thread's action is also a response to the action of another thread, then livelock may result. However, the threads are not blocked — they are simply too busy responding to each other to resume work. This is comparable to two people attempting to pass each other in a corridor: Alphonse moves to his left to let Gaston pass, while Gaston moves to his right to let Alphonse pass. Seeing that they are still blocking each other, Alphone moves to his right, while Gaston moves to his left. They're still blocking each other, so... GUARDED BLOCKS 48 Concurrency (23-29 MAY-CODE) Such a block begins by polling a condition that must be true before the block can proceed. There are a number of steps to follow in order to do this correctly. Suppose, for example guardedJoy is a method that must not proceed until a shared variable joy has been set by another thread. Such a method could, in theory, simply loop until the condition is satisfied, but that loop is wasteful, since it executes continuously while waiting. public void guardedJoy() { // Simple loop guard. Wastes // processor time. Don't do this! while(!joy) {} System.out.println("Joy has been achieved!"); } A more efficient guard invokes Object.wait to suspend the current thread. The invocation of wait does not return until another thread has issued a notification that some special event may have occurred — though not necessarily the event this thread is waiting for: public synchronized void guardedJoy() { // This guard only loops once for each special event, which may not // be the event we're waiting for. while(!joy) { try { wait(); } catch (InterruptedException e) {} } System.out.println("Joy and efficiency have been achieved!"); } Always invoke wait inside a loop that tests for the condition being waited for. 49 Concurrency (23-29 MAY-CODE) Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true. When a thread invokes d.wait, it must own the intrinsic lock for d — otherwise an error is thrown. Invoking wait inside a synchronized method is a simple way to acquire the intrinsic lock. When wait is invoked, the thread releases the lock and suspends execution. At some future time, another thread will acquire the same lock and invoke Object.notifyAll, informing all threads waiting on that lock that something important has happened: Some time after the second thread has released the lock, the first thread reacquires the lock and resumes by returning from the invocation of wait. There is a second notification method, notify, which wakes up a single thread. Because notify doesn't allow you to specify the thread that is woken up, it is useful only in massively parallel applications — that is, programs with a large number of threads, all doing similar chores. In such an application, you don't care which thread gets woken up. IMMUTABLE OBJECTS An object is considered immutable if its state cannot change after it is constructed. Using immutable objects is widely accepted as a sound strategy for creating simple, reliable code. Immutable objects are particularly useful in concurrent applications. Since they cannot change state, they cannot be corrupted by thread interference or observed in an inconsistent state. The reluctance to using immutable objects is usually attributed to the cost of creating a new object as opposed to updating an object in place. This can be offset by some of the efficiencies associated with immutable objects including: decreased overhead due to garbage collection and the elimination of code needed to protect mutable objects from corruption. A STRATEGY FOR DEFINING IMMUTABLE OBJECTS The following rules define a simple strategy for creating immutable objects. However, such strategies require sophisticated analysis and are not for beginners. 1. Don't provide "setter" methods — methods that modify fields or objects referred to by fields. 2. Make all fields final and private. 3. Don't allow subclasses to override methods. o The simplest way to do this is to declare the class as final. 50 Concurrency (23-29 MAY-CODE) o A more sophisticated approach is to make the constructor private and construct instances in factory methods. 4. If the instance fields include references to mutable objects, don't allow those objects to be changed: o Don't provide methods that modify the mutable objects. o Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods. HIGH LEVEL CONCURRENCY OBJECTS These are higher-level building blocks needed for more advanced tasks for massively concurrent applications that fully exploit today's multiprocessor and multi-core systems. Introduced with version 5.0 of the Java platform. Most of these features are implemented in the new java.util.concurrent packages. There are also new concurrent data structures in the Java Collections Framework. LOCK OBJECTS Synchronized code relies on a simple kind of reentrant lock. This kind of lock is easy to use, but has many limitations. More sophisticated locking idioms are supported by the java.util.concurrent.locks package. Lock objects work very much like the implicit locks used by synchronized code. As with implicit locks, only one thread can own a Lock object at a time. Lock objects also support a wait/notify mechanism, through their associated Condition objects. The biggest advantage of Lock objects over implicit locks is their ability to back out of an attempt to acquire a lock. The tryLock method backs out if the lock is not available immediately or before a timeout expires (if specified). The lockInterruptibly method backs out if another thread sends an interrupt before the lock is acquired. EXECUTORS In all of the previous examples, there's a close connection between the task being done by a new thread, as defined by its Runnable object, and the thread itself, as defined by a 51 Concurrency (23-29 MAY-CODE) Thread object. This works well for small applications, but in large-scale applications, it makes sense to separate thread management and creation from the rest of the application. Objects that encapsulate these functions are known as executors. EXECUTOR INTERFACES The java.util.concurrent package defines three executor interfaces: Executor, a simple interface that supports launching new tasks. ExecutorService, a subinterface of Executor, which adds features that help manage the lifecycle, both of the individual tasks and of the executor itself. ScheduledExecutorService, a subinterface of ExecutorService, supports future and/or periodic execution of tasks. THE EXECUTOR INTERFACE The Executor interface provides a single method, execute, designed to be a drop-in replacement for a common thread-creation idiom. If r is a Runnable object, and e is an Executor object you can replace (new Thread(r)).start(); with e.execute(r); However, the definition of execute is less specific. The low-level idiom creates a new thread and launches it immediately. THE EXECUTORSERVICE INTERFACE The ExecutorService interface supplements execute with a similar, but more versatile submit method. Like execute, submit accepts Runnable objects, but also accepts Callable objects, which allow the task to return a value. The submit method returns a Future object, which is used to retrieve the Callable return value and to manage the status of both Callable and Runnable tasks. ExecutorService also provides methods for submitting large collections of Callable objects. Finally, ExecutorService provides a number of methods for managing the shutdown of the executor. To support immediate shutdown, tasks should handle interrupts correctly. THE SCHEDULEDEXECUTORSERVICE INTERFACE 52 Concurrency (23-29 MAY-CODE) The ScheduledExecutorService interface supplements the methods of its parent ExecutorService with schedule, which executes a Runnable or Callable task after a specified delay. In addition, the interface defines scheduleAtFixedRate and scheduleWithFixedDelay, which executes specified tasks repeatedly, at defined intervals. THREAD POOLS Most of the executor implementations in java.util.concurrent use thread pools, which consist of worker threads. This kind of thread exists separately from the Runnable and Callable tasks it executes and is often used to execute multiple tasks. Using worker threads minimizes the overhead due to thread creation. Thread objects use a significant amount of memory, and in a large-scale application, allocating and deallocating many thread objects creates a significant memory management overhead. FIXED THREAD POOL One common type of thread pool is the fixed thread pool. This type of pool always has a specified number of threads running; if a thread is somehow terminated while it is still in use, it is automatically replaced with a new thread. Tasks are submitted to the pool via an internal queue, which holds extra tasks whenever there are more active tasks than threads. An important advantage of the fixed thread pool is that applications using it degrade gracefully. To understand this, consider a web server application where each HTTP request is handled by a separate thread: o If the application simply creates a new thread for every new HTTP request, and the system receives more requests than it can handle immediately, the application will suddenly stop responding toall requests when the overhead of all those threads exceed the capacity of the system. o With a limit on the number of the threads that can be created, the application will not be servicing HTTP requests as quickly as they come in, but it will be servicing them as quickly as the system can sustain. A simple way to create an executor that uses a fixed thread pool is to invoke the newFixedThreadPoolfactory method in java.util.concurrent.Executors This class also provides the following factory methods: The newCachedThreadPool method creates an executor with an expandable thread pool. This executor is suitable for applications that launch many short-lived tasks. The newSingleThreadExecutor method creates an executor that executes a single task at a time. 53 Concurrency (23-29 MAY-CODE) Several factory methods are ScheduledExecutorService versions of the above executors. FORK/JOIN The fork/join framework is an implementation of the ExecutorService interface that helps you take advantage of multiple processors. It is designed for work that can be broken into smaller pieces recursively. The goal is to use all the available processing power to enhance the performance of your application. As with any ExecutorService implementation, the fork/join framework distributes tasks to worker threads in a thread pool. The fork/join framework is distinct because it uses a work-stealing algorithm. Worker threads that run out of things to do can steal tasks from other threads that are still busy. The center of the fork/join framework is the ForkJoinPool class, an extension of the AbstractExecutorService class. ForkJoinPool implements the core work-stealing algorithm and can execute ForkJoinTask processes. Standard Implementations Besides using the fork/join framework to implement custom algorithms for tasks to be performed concurrently on a multiprocessor system (such as the ForkBlur.java example in the previous section), there are some generally useful features in Java SE which are already implemented using the fork/join framework. One such implementation, introduced in Java SE 8, is used by thejava.util.Arrays class for its parallelSort() methods. These methods are similar to sort(), but leverage concurrency via the fork/join framework. Parallel sorting of large arrays is faster than sequential sorting when run on multiprocessor systems. CONCURRENT COLLECTIONS The java.util.concurrent package includes a number of additions to the Java Collections Framework. They help avoid Memory Consistency Errors by defining a happens-before relationship between an operation that adds an object to the collection with subsequent operations that access or remove that object. They are most easily categorized by the collection interfaces provided: BlockingQueue defines a first-in-first-out data structure that blocks or times out when you attempt to add to a full queue, or retrieve from an empty queue. ConcurrentMap is a subinterface of java.util.Map that defines useful atomic operations. These operations remove or replace a key-value pair only if the key is 54 Abstract Classes and Interfaces (09-15 MAY - CODE & STUDY) present, or add a key-value pair only if the key is absent. Making these operations atomic helps avoid synchronization. The standard general-purpose implementation of ConcurrentMap is ConcurrentHashMap, which is a concurrent analog of HashMap. ConcurrentNavigableMap is a subinterface of ConcurrentMap that supports approximate matches. The standard general-purpose implementation of ConcurrentNavigableMap isConcurrentSkipListMap, which is a concurrent analog of TreeMap. ATOMIC VARIABLES The java.util.concurrent.atomic package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable. The atomiccompareAndSet method also has these memory consistency features, as do the simple atomic arithmetic methods that apply to integer atomic variables. Replacing an int field with an AtomicInteger allows us to prevent thread interference without resorting to synchronization and we would avoid the liveness impact of unnecessary synchronization CONCURRENT RANDOM NUMBERS In JDK 7, java.util.concurrent includes a convenience class, ThreadLocalRandom, for applications that expect to use random numbers from multiple threads or ForkJoinTasks. For concurrent access, using ThreadLocalRandom instead of Math.random() results in less contention and, ultimately, better performance. All you need to do is call ThreadLocalRandom.current(), then call one of its methods to retrieve a random number. Here is one example: int r = ThreadLocalRandom.current() .nextInt(4, 77) 5) ABSTRACT CLASSES AND INTERFACES (09-15 MAY - CODE & STUDY) TBC 6) CONSTRUCTORS (09-15 MAY - CODE & STUDY) DEFINING A CONSTRUCTOR IN JAVA Java constructors are special methods that are called to instantiate an object. 55 Constructors (09-15 MAY - CODE & STUDY) Example: MyClass myObject=new MyClass(); An object is created using a constructor with no arguments: The no-arg constructor. public class MyClass { public MyClass() { } } The 1st part of the constructor declaration (public) is an access modifier. See access modifiers section for the different types of access modifiers. The 2nd part of the constructor declaration (MyClass) is the name of the class the constructor belongs to. This tells the Java compiler that we are looking to construct an object of type: MyClass. The 3rd part is a list of parameters in parenthesis. These are used by the constructor to initialise fields required by the object or to create a custom object with different states. Lastly, there's the body of the constructor in curly brackets { } Constructor Overloading - Multiple Constructors for a Java Class Constructor overloading occurs when a class has multiple constructors. A class can have multiple constructors, as long as they have different parameters – the signature. public class ExampleClass { private String name; /* no-arg constructor */ public ExampleClass() {} public ExampleClass(String _objectName) {this.name= _ objectName;} } Default, no-arg Constructor If you don't define any constructor, the Java compiler will insert a default, no-argument constructor for you. If you do define a constructor for your class, then the Java compiler will not insert the default no-argument constructor into your class. A Java constructor parameter can have the same name as a field. If a constructor parameter has the same name as a field, the Java compiler has problems knowing which you refer to. By default, if a parameter (or local variable) has the same name as a field in the same class, the parameter (or local variable) "shadows" for the field. Look at this constructor example: public class Employee { 56 Constructors (09-15 MAY - CODE & STUDY) private String firstName = null; private String lastName = null; private int birthYear = 0; public Employee(String firstName, String lastName, int birthYear ) { firstName = firstName; lastName = lastName; birthYear = birthYear; } } Inside the constructor of the Employee class the firstName, lastName and birthYear identifiers now refer to the constructor parameters, not to the Employee fields with the same names. Thus, the constructor now just sets the parameters equal to themselves. The Employee fields are never initialized. To signal to the Java compiler that you mean the fields of the Employee class and not the parameters, put the this keyword and a dot in front of the field name. Here is how the Java constructor declaration from before looks with that change: public class Employee { private String firstName = null; private String lastName = null; private int birthYear = 0; 57 Constructors (09-15 MAY - CODE & STUDY) public Employee(String firstName, String lastName, int birthYear ) { this.firstName = firstName; this.lastName = lastName; this.birthYear = birthYear; } } Now the Employee fields are correctly initialized inside the constructor. Calling a Constructor You call a constructor when you create a new instance of the class containing the constructor. Here is a Java constructor call example: MyClass myClassVar = new MyClass(); This example invokes (calls) the no-argument constructor for MyClass as defined earlier in this text. In case you want to pass parameters to the constructor, you include the parameters between the parentheses after the class name, like this: MyClass myClassVar = new MyClass(1975); This example passes one parameter to the MyClass constructor that takes an int as parameter. Calling a Constructor From a Constructor 58 Constructors (09-15 MAY - CODE & STUDY) In Java it is possible to call a constructor from inside another constructor. When you call a constructor from inside another constructor, you use the this keyword to refer to the constructor. Here is an example of calling one constructor from within another constructor in Java: public class Employee { private String firstName = null; private String lastName = null; private int birthYear = 0; public Employee(String first, String last, int year ) { firstName = first; lastName = last; birthYear = year; } public Employee(String first, String last){ this(first, last, -1); } } Notice the second constructor definition. Inside the body of the constructor you find this Java statement: this(first, last, -1); The this keyword followed by parentheses and parameters means that another constructor in the same Java class is being called. Which other constructor that is being called depends on what parameters you pass to the 59 Constructors (09-15 MAY - CODE & STUDY) constructor call (inside the parentheses after the this keyword). In this example it is the first constructor in the class that is being called. Calling Constructors in Superclasses In Java a class can extend another class. When a class extends another class it is also said to "inherit" from the class it extends. The class that extends is called the subclass, and the class being extended is called the superclass. Inheritance is covered in more detail in my tutorial about inheritance in Java. A class that extends another class does not inherit its constructors. However, the subclass must call a constructor in the superclass inside one of the subclass constructors! Look at the following two Java classes. The class Car extends (inherits from) the class Vehicle. public class Vehicle { private String regNo = null; public Vehicle(String no) { this.regNo = no; } } public class Car extends Vehicle { private String brand = null; public Car(String br, String no) { super(no); this.brand = br; } } Notice the constructor in the Car class. It calls the constructor in the superclass using this Java statement: 60 File IO and Serialization (30 MAY – 05 JUN) super(no); Using the keyword super refers to the superclass of the class using the super keyword. When super keyword is followed by parentheses like it is here, it refers to a constructor in the superclass. In this case it refers to the constructor in the Vehicle class. Because Car extends Vehicle, the Car constructors must all call a constructor in the Vehicle. Java Constructor Access Modifiers The access modifier of a constructor determines what classes in your application that are allowed to call that constructor. The access modifiers are explained in more detail in the text on Java access modifiers. For instance, if a constructor is declared protected then only classes in the same package, or subclasses of that class can call that constructor. A class can have multiple constructors, and each constructor can have its own access modifier. Thus, some constructors may be available to all classes in your application, while other constructors are only available to classes in the same package, subclasses, or even only to the class itself (private constructors). 7) FILE IO AND SERIALIZATION (30 MAY – 05 JUN) I/O STREAMS An I/O Stream represents an input source or an output destination. A stream can represent many different kinds of sources and destinations, including disk files, devices, other programs, and memory arrays. Streams support many different kinds of data, including simple bytes, primitive data types, localized characters, and objects. Some streams simply pass on data; others manipulate and transform the data in useful ways. No matter how they work internally, all streams present the same simple model to programs that use them: A stream is a sequence of data. A program uses an input stream to read data from a source, one item at a time: Reading information into a program: 61 File IO and Serialization (30 MAY – 05 JUN) Writing information from a program: BYTE STREAMS Programs use byte streams to perform input and output of 8-bit bytes. All byte stream classes are descended from InputStream and OutputStream. Closing a stream when it's no longer needed is very important. This practice helps avoid serious resource leaks. Byte streams should only be used for the most primitive I/O. We discuss it here because all other stream types are built on byte streams. Code example: Using byte streams CHARACTER STREAMS The Java platform stores character values using Unicode conventions. Character stream I/O automatically translates this internal format to and from the local character set. In Western locales, the local character set is usually an 8-bit superset of ASCII. Input and output done with stream classes automatically translates to and from the local character set. A program that uses character streams in place of byte streams automatically adapts to the local character set and is ready for internationalization. If internationalization becomes a priority, your program can be adapted without extensive recoding. See the Internationalization trail for more information. 62 File IO and Serialization (30 MAY – 05 JUN) All character stream classes are descended from Reader and Writer. As with byte streams, there are character stream classes that specialize in file I/O: FileReader and FileWriter. The character value is held in the last 16 bits compared to the last 8 bits in bytestream. Code example: Using character streams Character streams are often "wrappers" for byte streams. The character stream uses the byte stream to perform the physical I/O, while the character stream handles translation between characters and bytes. FileReader, for example, uses FileInputStream, while FileWriter uses FileOutputStream There are two general-purpose byte-to-character "bridge" streams: InputStreamReader and OutputStreamWriter. Use them to create character streams when there are no prepackaged character stream classes that meet your needs. Line-Oriented I/O Character I/O usually occurs in bigger units than single characters. One common unit is the line: a string of characters with a line terminator at the end. A line terminator can be a carriage-return/line-feed sequence ("\r\n"), a single carriage-return ("\r"), or a single line-feed ("\n"). Supporting all possible line terminators allows programs to read text files created on any of the widely used operating systems. To do this, we have to use two classes we haven't seen before, BufferedReader and PrintWriter. Code example: Line-Oriented I/O BUFFERED STREAMS Most of the examples we've seen so far use unbuffered I/O. This means each read or write request is handled directly by the underlying OS. This can make a program much less efficient, since each such request often triggers disk access, network activity, or some other operation that is relatively expensive. To reduce this kind of overhead, the Java platform implements buffered I/O streams. Buffered input streams read data from a memory area known as a buffer; the native input API is called only when the buffer is empty. Similarly, buffered output streams write data to a buffer, and the native output API is called only when the buffer is full. A program can convert an unbuffered stream into a buffered stream using the wrapping idiom we've used several times now, where the unbuffered stream object is passed to the constructor for a buffered stream class. Example: o inputStream = new BufferedReader(new FileReader("xanadu.txt")); o outputStream = new BufferedWriter(new FileWriter("characteroutput.txt")); There are four buffered stream classes used to wrap unbuffered streams: 63 File IO and Serialization (30 MAY – 05 JUN) o BufferedInputStream and BufferedOutputStream create buffered byte streams, o BufferedReader and BufferedWriter create buffered character streams. Flushing Buffered Streams It often makes sense to write out a buffer at critical points, without waiting for it to fill. This is known as flushing the buffer. Some buffered output classes support autoflush, specified by an optional constructor argument. When autoflush is enabled, certain key events cause the buffer to be flushed. For example, an autoflush PrintWriter object flushes the buffer on every invocation of println or format. To flush a stream manually, invoke its flush method. The flush method is valid on any output stream, but has no effect unless the stream is buffered. SCANNING AND FORMATTING The scanner and formatting APIs provide capability to deal with translating to and from neatly formatted data. o The scanner API breaks input into individual tokens associated with bits of data. o The formatting API assembles data into nicely formatted, human-readable form. SCANNING The Scanner breaks down formatted input into tokens and translates individual tokens according to their data type. BREAKING INPUT INTO TOKENS The scanner uses white space to separate tokens by default. White space characters include blanks, tabs, and line terminators. For the full list, refer to the documentation for Character.isWhitespace.) To use a different token separator, invoke useDelimiter(). Specify a regular expression e.g. for a comma, followed by white space, invoke s.useDelimiter(",\\s*"); Scanning example - a program that reads the individual words inxanadu.txt and prints them out, one per line. Close the scanner after use to close underlying stream. TRANSLATING INDIVIDUAL TOKENS The scanning example treats all input tokens as simple String values. 64 File IO and Serialization (30 MAY – 05 JUN) The scanner supports tokens for all of the Java language's primitive types (except for char), as well as BigInteger and BigDecimal. Numeric values can use thousands separators. Thus, in a US locale, Scanner correctly reads the string "32,767" as representing an integer value. The ScanSum example reads a list of double values and adds them up. Thousands separators and decimal symbols are locale specific. The period will be a different character in some locales, because System.out is a PrintStream object, and that class doesn't provide a way to override the default locale. We could override the locale for the whole program — or we could just use formatting, as described next FORMATTING Formatting is implemented by instances of the PrintWriter for characters and PrintStream for bytes. In addition to the standard set of write methods, both PrintStream and PrintWriter implement methods for converting internal data into formatted output. There are two levels of formatting: o Standard formatting with print and println o Formatting for almost any number of values based on a format string, with options for precise formatting using the format method. Format strings support many features. see format string syntax in the API specification. It can also contain several additional elements that further customize the formatted output. ELEMENTS OF A FORMAT SPECIFIER. The elements must appear in the order shown. Working from the right, the optional elements are: o Precision. For floating point values, this is the mathematical precision of the formatted value. For s and other general conversions, this is the maximum width of the formatted value; the value is right-truncated if necessary. o Width. The minimum width of the formatted value; the value is padded if necessary. By default the value is left-padded with blanks. o Flags specify additional formatting options. + flag specifies that the number should always be formatted with a sign, and the 0 flag specifies that 0 is the padding character. Other flags include - (pad on the right) and, (format number 65 File IO and Serialization (30 MAY – 05 JUN) with locale-specific thousands separators). Note that some flags cannot be used with certain other flags or with certain conversions. o The Argument Index allows you to explicitly match a designated argument. You can also specify< to match the same argument as the previous specifier. I/O FROM THE COMMAND LINE Java supports command line interaction in two ways: Standard Streams and Console. STANDARD STREAM Many Operating Systems have Standard Streams and read input from the keyboard and write output to the display by default. They also support I/O on files and between programs through the command line interpreter. Java supports three Standard Streams on the command line: o Standard Input- System.in o Standard Output-System.out o Standard Error-System.err. These objects are defined automatically and do not need to be opened. Standard Output and Standard Error are both output separately to allow the user to divert regular output and deal with error messages separately. More details should be available in the command line interpreter documentation. These Standard Streams are byte streams historically. o System.out and System.err are defined as PrintStream objects. However, PrintStream has an internal character stream object to emulate many of the features of character streams. o System.in has no character stream features but you can wrap it in InputStreamReader. InputStreamReader characterInputStream = new InputStreamReader(System.in); CONSOLE Console Is a more advanced alternative to Standard Streams. It has more features besides those from the Standard Streams. It is particularly useful for secure password entry. It has true character input and output streams through its reader and writer methods. If the Console object is available, invoking System.console() will return a value, otherwise it will return NULL. This could be because the OS doesn't support it or because the program was launched in a non-interactive environment. The Console object has the readPassword method which supports secure password entry. It enables secure password entry in two ways: o Suppresses echoing- password is not visible on the user's screen. o Returns a character array (not a String) so the password can be overwritten, removing it from memory as soon as it is no longer needed. 66 File IO and Serialization (30 MAY – 05 JUN) DATA STREAMS Data streams support binary I/O of primitive data type values: boolean, char, byte, short, int,long, float, and double as well as String values. Data streams implement the DataInput interface or the DataOutput interface. The most used implementations are DataInputStream and DataOutputStream. DataStreams detects an end-of-file condition by catching EOFException, instead of testing for an invalid return value. All implementations of DataInput methods use EOFException instead of return values. Each specialized write in DataStreams is exactly matched by the corresponding specialized read e.g. out.writeDouble(doubleValue), in.readDouble(). Make sure to match output types and input types. The input stream is simple binary data and does not indicate the type of individual values, or where they begin in the stream. DataStreams use floating point numbers to represent monetary values which is considered a bad technique. This is because floating point is bad for precise values especially decimal fractions as they don’t have a binary representation. The correct type BigDecimal is an object type and won't work with data streams. OBJECT STREAMS Object streams support I/O of objects. Most standard classes implement the interface Serializable and therefore support serialization of their objects. The object stream classes are ObjectInputStream and ObjectOutputStream implementing ObjectInput and ObjectOutput interfaces. These are subinterfaces of DataInput andDataOutput. Thus ObjectInputStream and ObjectOutputStream implement all the primitive data I/O methods of Data Streams. An Object stream can thus contain both primitive and object values. If readObject() doesn't return the object type expected, attempting to cast it to the correct type may throw a ClassNotFoundException. Many objects contain references to other objects. If readObject is to reconstitute an object from a stream, it has to be able to reconstitute all of the objects the original object referred to. writeObject traverses the entire web of object references and writes all objects in that web onto the stream. Thus a single invocation of writeObject can cause a large number of objects to be written to the stream. A stream can only contain one copy of an object, though it can contain any number of references to it. Thus if you explicitly write an object to a stream twice, you're really writing only the reference twice. Each writeObject has to be matched by a readObject If a single object is written to two different streams, it is effectively duplicated — a single program reading both streams back will see two distinct objects. FILE I/O (FEATURING NIO.2) This is the file I/O mechanism introduced in the JDK 7 release 67 File IO and Serialization (30 MAY – 05 JUN) The Path class is the primary entry point for the package. The Files class contains methods that deal with file operations. THE PATH Most file systems store files in a tree structure with one or more root nodes at the top of the tree (Microsoft Windows supports multiple root nodes). Under the root node, there are files and directories (folders in Microsoft Windows). Each directory can contain almost limitless files and subdirectories. The root node maps to a volume e.g. C:\ or D:\ for Microsoft and a single root node for the Solaris OS denoted by /. o A file is identified by its path through the file system from the root node e.g. /home/user/text.txt – Solaris (uses the forward slash (/) delimiter) o C:\home\user\text.txt – Microsoft (uses the backslash (\) delimiter) These are absolute paths – All the information required to find the file is in the path. A file location can also be represented relative to another location in the file system. As such we need to combine the relative path with the absolute path of the original/primary location to find the file. Relative path is generally used to shorten the length of the path representation. A symbolic link is a special file that references/ redirects to another file (target) transparent to the application or user accessing the file. However, when a symbolic link is deleted, or renamed it’s the link itself that will be renames or changed, not the target of the link. The process of substituting the actual location in the file system for the symbolic link is called resolving the link. If not carefully used, you could encounter a circular reference. This is when the target of a link points back to the original link. This could cause a problem when a program is recursively walking a directory structure. THE PATH CLASS Java’s representation of a path in the file system which contains the file name and directory list used to resolve the location of the file. The path instance is system dependent and will be different for Windows and Solaris. Operations: Once created, you can append to it, extract pieces of it, compare it to another path. Used with the Files class, you can create and look for the file represented by the path, open it, delete it, change its permissions etc. PATH OPERATIONS CREATING A PATH These are the syntactic operations as they operate on the path. A Path instance specifies the location of a file or directory with a series of one or more names. You can easily create a Path object by using one of the get methods from the Paths(note the plural) helper class. This is a shorthand to getting the path using FileSystems.getDefault(). 68 File IO and Serialization (30 MAY – 05 JUN) Path path = FileSystems.getDefault().getPath("/users/testUser"); Using the Paths helper class: o Path path = Paths.get("/tmp "); o Path path = Paths.get(args[0]); o Path path = Paths.get(URI.create("file:///Users/testUser/TestFile.java")); o Path p5 = Paths.get(System.getProperty("user.home"),"testDir", "test.txt"); o Creates the file in the user’s home directory as /home/testUser/testDir/test.txt for solaris and C:\testUser\testDir\test.txt on windows. RETRIEVING INFORMATION ABOUT A PATH Think of the Path as storing these name elements as a sequence. The highest element in the directory structure would be located at index 0. The lowest element in the directory structure would be located at index [n-1]. n is the number of name elements in the Path. Methods are available for retrieving individual elements or a subsequence of the Path using these indexes. Example methods available for retrieving information about the path: Note: Root is / in solaris and C:\ in windows. path.toString() - Returns the string representation of the Path with minor syntactic cleanup. path.getFileName() - Returns the file name or the last element of the sequence of name elements. path.getName(0) - Returns the path element corresponding to the specified index. The 0th element is the path element closest to the root. path.getNameCount()- Returns the number of elements in the path. path.subpath(0,2)- Returns the subsequence of the Path (not including a root element) as specified by the beginning and ending indexes. path.getParent()- Returns the path of the parent directory. path.getRoot()- Returns the root of the path. This method will return null if a relative path is used rather than absolute. REMOVING REDUNDANCIES FROM A PATH normalize method removes any redundant elements, which includes any "." or "directory/.." occurrences. Many file systems use "." notation to denote the current directory and ".." to denote the parent directory. normalize doesn't check at the file system when it cleans up a path. It is a purely syntactic operation. To clean up a path while ensuring that the result locates the correct file, you can use thetoRealPath method. CONVERTING A PATH toUri -To a string that can be opened from a browser. 69 File IO and Serialization (30 MAY – 05 JUN) toAbsolutePath - To convert to an absolute path. Returns the same path object if the path given is absolute. The file does not need to exist for this method to work. toRealPath method returns the real path of an existing file, performing the following operations in one: o If true is passed to this method and the file system supports symbolic links, it resolves any symbolic links in the path. o Returns an absolute path if the path is relative. o Removes any redundant elements if the Path contains any. This method will throw a NoSuchFileException if the file does not exist or cannot be accessed. JOINING PATHS You can combine paths by using the resolve method. You pass in a partial path , which is a path that does not include a root element, and that partial path is appended to the original path. CREATING A PATH BETWEEN TWO PATHS We can construct a path from one location in the file system to another location using the relativize method. The new path is relative to the original path. Path p1 = Paths.get("home"); Path p3 = Paths.get("home/tom/email"); Path p1_to_p3 = p1.relativize(p3); // results in tom/email Path p3_to_p1 = p3.relativize(p1); // Result in ../.. This method required the paths to include a root element. As such it is system dependent. COMPARING TWO PATHS startsWith andendsWith to test if a path begins or ends with a particular string. isSameFile – To verify that two Path objects locate the same file equals –to test two paths for equality. The Path class implements the Iterable interface. The iterator method returns an object that enables you to iterate over the name elements in the path with the first element being the one closest to the root in the directory tree. The Path class also implements the Comparable interface. Use it to compare Path objects by using compareTo, useful for sorting. FILE OPERATIONS 70 File IO and Serialization (30 MAY – 05 JUN) The Files class is the other primary entrypoint of the java.nio.file package. It offers a rich set of static methods for reading, writing, and manipulating files and directories. Files methods work on instances of Path objects. Releasing System Resources Most resources for File IO e.g. streams or channels, implement or extend the java.io.Closeable interface. As such the close method must be invoked to release the resource when no longer required to avoid impacting the application's performance. The try-with-resources statement from Java 7 closes resources for you. Catching Exceptions With File IO, unexpected conditions such as a file doesn't exist, the program doesn't have access to the file system, the default file system implementation does not support a particular function are common. All methods that access the file system can throw an IOException. Catch these exceptions in a try-with-resources statement which causes the compiler to automatically generate code to close the resource(s) when no longer required. You can use the try catch closing resources in a finally block. Other specific exceptions extend FileSystemException which has some useful methods: o getFile returns the file involved o getMessage returns the detailed message string o getReason returns the reason why the file system operation failed o getOtherFile returns the "other" file involved, if any. Your code should always handle exceptions. Varargs Some Files methods accept an arbitrary number of arguments when flags are specified e.g. the CopyOption below accepts a variable number of arguments (varargs) Path Files.move(Path, Path, CopyOption...) You can pass it a comma-separated list of values or an array (CopyOption[]) of values. For varargs syntax: Arbitrary Number of Arguments. Atomic Operations Some Files methods e.g. move can perform operation that cannot be interrupted or "partially" performed (Either the entire operation is performed or the operation fails) in some file systems - atomic operations. This is important when you have multiple processes operating on the same area of the file system to guarantee that each process accesses a complete file. 71 File IO and Serialization (30 MAY – 05 JUN) Method Chaining A technique where you invoke a method that returns an object and immediately invoke a method on that object that returns yet another object, and so on. Method chaining produces compact code and avoid declaring temporary variables Many File IO methods support method chaining. The Glob Glob is a pattern using wildcards to specify filenames. For example *.java specifies all files with extension ‘java’. Two wildcard characters that are widely used are * and ?. Asterisk * stands for “any string of characters” and question mark ? stands for “any one character”. Glob originated from Unix operating system with a “global command” that abbreviated to glob. It is similar to regular expression but simpler in representation and limited in abilities. Two Files class methods accept a glob argument for pattern-matching behavior. The glob pattern is matched against other strings, such as directory or file names. Glob syntax follows these rules: o An asterisk (*) matches any number of characters (including none). o Double asterisks (**) like (*) but crosses directory boundaries - used for matching complete paths. o A question mark (?) matches exactly one character. o Braces ({}) specify a collection of sub-patterns. For example: o {temp,tmp} matches "temp", "tmp". o {temp*,tmp*} matches all strings beginning with "temp" or "tmp". o Square brackets ([]) for a set of single characters or with the hyphen (-) - a range of characters. For example: [aeiou] matches any lowercase vowel. [0-9] matches any digit. [A-Z] matches any uppercase letter. [a-z,A-Z] matches any uppercase or lowercase letter. Within the square brackets, *, ?, and \ match themselves. o All other characters match themselves. o To match *, ?, or the other special characters, you can escape them by using the backslash character, \. For example: \\ matches a single backslash, and \? matches the question mark. Examples of glob syntax: o *.html – Matches all strings that end in .html o ??? – Matches all strings with exactly three letters or digits o *[0-9]* – Matches all strings containing a numeric value o *.{htm,html,pdf} – Matches any string ending with .htm, .html or .pdf o a?*.java – Matches any string beginning with a, followed by at least one letter or digit, and ending with .java o {foo*,*[0-9]*} – Matches any string beginning with foo or any string containing a numeric value 72 Access Specifiers (09-15 MAY - CODE & STUDY) If you are typing the glob pattern at the keyboard and it contains one of the special characters, you must put the pattern in quotes ("*"), use the backslash (\*), or use whatever escape mechanism is supported at the command line. See the getPathMatcher method for more information on the glob syntax. If glob is not sufficient for your needs, you can use Regular Expressions. Link Awareness The Files class is "link aware." Every Files method either detects what to do when a symbolic link is encountered or has the option for you to configure what to do when a symbolic link is encountered. 8) ACCESS SPECIFIERS (09-15 MAY - CODE & STUDY) TBC 9) EXCEPTIONS (06-12 JUN) The term exception is shorthand for the phrase "exceptional event." An exception is an event, which occurs during the execution of a program that disrupts the normal flow of the program's instructions. When an error occurs within a method, the method creates an object and hands it off to the runtime system. The object, called an exception object, contains information about the error, including its type and the state of the program when the error occurred. Creating an exception object and handing it to the runtime system is called throwing an exception. The set of possible "somethings" to handle the exception is the ordered list of methods that had been called to get to the method where the error occurred. The list of methods is known as the call stack (see the next figure). The runtime system searches the call stack for a method that contains a block of code that can handle the exception. This block of code is called an exception handler. 73 Exceptions (06-12 JUN) An exception handler is considered appropriate if the type of the exception object thrown matches the type that can be handled by the handler. The exception handler chosen is said to catch the exception. If the runtime system exhaustively searches all the methods on the call stack without finding an appropriate exception handler, the runtime system (and, consequently, the program) terminates. Valid Java programming language code must honor the Catch or Specify Requirement o A try statement that catches the exception: a handler for the exception o A method that specifies that it can throw the exception. The method must provide a throws clause that lists the exception Code that fails to honor the Catch or Specify Requirement will not compile. Only one of 3 exception types is subject to this Requirement. The Java platform provides numerous exception classes. All the classes are descendants of the Throwable class THE THREE KINDS OF EXCEPTIONS 1. Checked exception A well-written application should anticipate and recover from All exceptions are checked exceptions, except for those indicated by Error, RuntimeException, and their subclasses. Example: java.io.FileNotFoundException 2. Error External to the application, and that the application usually cannot anticipate or recover from. Example: java.io.IOError An application might choose to catch this exception, in order to notify the user of the problem — but it also might make sense for the program to print a stack trace and exit. Errors are not subject to the Catch or Specify Requirement. Errors are those exceptions indicated by Error and its subclasses. 3. Runtime exception. These are exceptional conditions that are internal to the application, and that the application usually cannot anticipate or recover from. These usually indicate programming bugs, such as logic errors or improper use of an API. Example: NullPointerException Not subject to the Catch or Specify Requirement. Runtime exceptions are those indicated by RuntimeException and its subclasses. Errors and runtime exceptions are collectively known as unchecked exceptions. 74 Exceptions (06-12 JUN) CATCHING AND HANDLING EXCEPTIONS You can put each line of code that might throw an exception within its own try block and provide separate exception handlers for each or within a single try block and associate multiple handlers with it. You associate exception handlers with a try block by providing one or more catch blocks directly after the try block. No code can be between the end of the try block and the beginning of the first catch block. Exception handlers can do more than just print error messages or halt the program. They can do error recovery, prompt the user to make a decision, or propagate the error up to a higher-level handler using chained exceptions, as described in the Chained Exceptions section. From Java SE 7 and later, a single catch block can handle more than one type of exception. specify the types of exceptions that block can handle, and separate each exception type with a vertical bar (|): catch (IOException|SQLException ex) { logger.log(ex); throw ex; } The finally block always executes when the try block exits. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated. Finally may not execute if: The JVM exits while the try or catch code is being executed. The thread executing the try or catch code is interrupted or killed, even though the application as a whole continues. If you are using Java SE 7 or later, consider using the try-with-resources statement in these situations, which automatically releases system resources when no longer needed. THE TRY-WITH-RESOURCES STATEMENT: A resource is an object that must be closed after the program is finished with it. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource. Example: static String readFirstLineFromFile(String path) throws IOException { 75 Exceptions (06-12 JUN) try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } } A try-with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed. SUPPRESSED EXCEPTIONS If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-withresources statement are suppressed, and the exception thrown by the block is the one that is thrown from the try logic. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressedmethod from the exception thrown by the try block. The Closeable interface extends the AutoCloseable interface. The close method of the Closeable interface throws exceptions of type IOException while the close method of the AutoCloseable interface throws exceptions of type Exception. Consequently, subclasses of the AutoCloseable interface can override this behavior of the close method to throw specialized exceptions, such as IOException, or no exception at all. HOW TO THROW EXCEPTIONS The Java platform provides numerous exception classes. All the classes are descendants of the Throwable class, and all allow programs to differentiate among the various types of exceptions that can occur during the execution of a program. All methods use the throw statement to throw an exception. The throw statement requires a single argument: a throwable object. Throwable objects are instances of any subclass of the Throwable class. The objects that inherit from the Throwable class include direct descendants (objects that inherit directly from the Throwable class) and indirect descendants (objects that inherit from children or grandchildren of the Throwable class). The figure below illustrates the class hierarchy of the Throwable class and its most significant subclasses. As you can see, Throwable has two direct descendants: Error and Exception. 76 Exceptions (06-12 JUN) Error Class When a dynamic linking failure or other hard failure in the Java virtual machine occurs, the virtual machine throws an Error. Simple programs typically do not catch or throw Errors. Exception Class Indicates that a problem occurred, but it is not a serious system problem. Most programs you write will throw and catch Exceptions. One Exception subclass, RuntimeException, is reserved for exceptions that indicate incorrect use of an API. An example of a runtime exception is NullPointerException. CHAINED EXCEPTIONS An application often responds to an exception by throwing another exception. In effect, the first exception causes the second exception. It can be very helpful to know when one exception causes another. Chained Exceptions help the programmer do this. The following are the methods and constructors in Throwable that support chained exceptions. o Throwable getCause() o Throwable initCause(Throwable) o Throwable(String, Throwable) o Throwable(Throwable) The Throwable argument to initCause and the Throwable constructors is the exception that caused the current exception. getCause returns the exception that caused the current exception, and initCause sets the current exception's cause. The following example shows how to use a chained exception. try { 77 Exceptions (06-12 JUN) } catch (IOException e) { throw new SampleException("Other IOException", e); } In this example, when an IOException is caught, a new SampleException exception is created with the original cause attached and the chain of exceptions is thrown up to the next higher level exception handler. CREATING EXCEPTION CLASSES Do you need an exception type that isn't represented by those in the Java platform? Would it help users if they could differentiate your exceptions from those thrown by classes written by other vendors? Does your code throw more than one related exception? If you use someone else's exceptions, will users have access to those exceptions? A similar question is, should your package be independent and self-contained? For readable code, it's good practice to append the string Exception to the names of all classes that inherit (directly or indirectly) from the Exception class. UNCHECKED EXCEPTIONS — THE CONTROVERSY One case where it is common practice to throw a RuntimeException is when the user calls a method incorrectly. For example, a method can check if one of its arguments is incorrectly null. If an argument is null, the method might throw a NullPointerException, which is an unchecked exception. Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don't want to be bothered with specifying the exceptions your methods can throw. Here's the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception. ADVANTAGES OF EXCEPTIONS 1. Separating Error-Handling Code from "Regular" Code Exceptions enable you to write the main flow of your code and to deal with the exceptional cases elsewhere. 2. Propagating Errors Up the Call Stack 78 Generics (16-22 MAY - CODE) A method can duck any exceptions thrown within it, thereby allowing a method farther up the call stack to catch it. Hence, only the methods that care about errors have to worry about detecting errors. 3. Grouping and Differentiating Error Types You can create groups of exceptions and handle exceptions in a general fashion, or you can use the specific exception type to differentiate exceptions and handle exceptions in an exact fashion. 10) GENERICS (16-22 MAY - CODE) INTRODUCTION Help us to detect bugs at compile time rather than runtime. With generics we use types as parameters in defining types and methods. The input to type parameters are types. Compile time type checks: The java compiler throws errors at compile time for code that fails type checks. Generics enable us to implement algorithms that flexibly work on objects of different types. Generics remove the need for casting to choose the right type. Example: List list=new ArrayList(); list.add(“John”); The way the list has been instantiated makes it unclear as to what data type it will store. This is potentially unsafe as we may also try to add an Integer. To get the stored value, we would need to cast to a String. String name=(String)list.get(0); Generics enable us to make it type safe without casting. TYPE PARAMETER NAMING CONVENTIONS Java convention uses single uppercase letters for the type parameter name. This is different from the ordinary class or interface name so there’s no confusion. COMMON PARAMETERS T – Type S,U,V etc. – 2nd, 3rd, 4th types E – Element (used extensively by the Java Collections Framework) K – Key V – Value N – Number GENERIC TYPES 79 Generics (16-22 MAY - CODE) DEFINING A GENERIC CLASS The format for defining a Generic class: Public class PersonAnalyser<T>{ Public int getAge(){ Return } Public setAge(T t){ } } The type variable can be any non-primitive: class, interface, array, another type variable. INSTANTIANTING A GENERIC TYPE Person<Teacher> teachingPerson; teachingPerson= new Person<Teacher(); Type Parameter and Type Argument Terminology: PersonAnalyser<T> - Type parameter Person<Teacher> - Type argument JAVA 7 TYPE INFERENCE From Java 7 the Java compiler can infer the type from the variable the instance is assigned to: Person <Teacher>teachingPerson = new Person<>(); This is also referred to as the diamond operator because of the empty <>. TYPE INFERENCE AND GENERIC METHODS Generally, a Java compiler can infer the type parameters of a generic method call. Consequently, in most cases, you do not have to specify them. TYPE INFERENCE AND INSTANTIATION OF GENERIC CLASSES You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. To take advantage of type inference during generic class instantiation, you must use the diamond. 80 Generics (16-22 MAY - CODE) Compilers from releases prior to Java SE 7 are able to infer the actual type parameters of generic constructors, similar to generic methods. However, compilers in Java SE 7 and later can infer the actual type parameters of the generic class being instantiated if you use the diamond (<>). TARGET TYPES The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. The target type of an expression is the data type that the Java compiler expects depending on where the expression appears. MULTIPLE TYPE PARAMETERS A generic class can have multiple type parameters. Example: public interface Pair<K, V> { public K getKey(); public V getValue(); } RAW TYPES This is similar to using a generic type without the parameter type. Raw Types are used in legacy code because lots of API classes (such as the Collections classes) were not generic prior to JDK 5.0. When using raw types, you essentially get pre-generics behaviour. GENERIC METHODS These are methods with their own type parameters limited to the method where it is declared Static and non-static generic methods are allowed, as well as generic class constructors. Example: interface Collection<E> { public <T> boolean containsAll(Collection<T> c); } Static method public class MyComparator { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey()) && 81 Generics (16-22 MAY - CODE) p1.getValue().equals(p2.getValue()); } } Class constructor public class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } } Type inference allows you to invoke a generic method without specifying a type between angle brackets. Further reading: Type Inference. BOUNDED TYPE PARAMETERS Bounded Type Parameters restrict the types that can be used as type arguments in a parameterized type. A method that operates on numbers should only accept instances of Number or its subclasses. To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number. Note that, in this context,extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces). public <U extends Number> void validate (U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + u.getClass().getName()); } In addition to limiting the types you can use to instantiate a generic type, bounded type parameters allow you to invoke methods defined in the bounds: MULTIPLE BOUNDS 82 Java Keywords - Static , Final , volatile, synchronized , transient, this super etc. (06-12 JUN) A type variable with multiple bounds is a subtype of all the types listed in the bound. If one of the bounds is a class, it must be specified first. GENERICS, INHERITANCE, AND SUBTYPES Given two concrete types A and B (for example, Number and Integer), MyClass<A> has no relationship to MyClass<B>, regardless of whether or not A and B are related. The common parent of MyClass<A> and MyClass<B> is Object. Generic Classes and Subtyping You can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by theextends and implements clauses. Using the Collections classes as an example, ArrayList<E> implements List<E>, and List<E> extends Collection<E>. SoArrayList<String> is a subtype of List<String>, which is a subtype of Collection<String>. So long as you do not vary the type argument, the subtyping relationship is preserved between the types. 11) JAVA KEYWORDS - STATIC , FINAL , VOLATILE, SYNCHRONIZED , TRANSIENT, THIS SUPER ETC. (06-12 JUN) TBC Here is a list of keywords in the Java programming language. You cannot use any of the following as identifiers in your programs. The keywords const and goto are reserved, even though they are not currently used. true, false, and null might seem like keywords, but they are actually literals; you cannot use them as identifiers in your programs. abstract continue for new switch assert*** default goto* package synchronized boolean do if private this 83 Annotations (06-12 JUN) break double implements protected throw byte else import public throws case enum**** instanceof return transient catch extends int short try char final interface static void class finally long strictfp** volatile const* float native super while * not used ** added in 1.2 *** added in 1.4 **** added in 5.0 12) ANNOTATIONS (06-12 JUN) Annotation type can be one of the types that are defined in the java.lang or java.lang.annotation or you can define your own annotation. Annotations are used to provide meta data for your Java code. Being meta data, Java annotations do not directly affect the execution of your code, Annotations have a number of uses, among them: Information for the compiler —Can be used by the compiler to detect errors or suppress warnings. Compile-time and deployment-time processing — Build tools may scan your Java code for specific annotations and generate source code or other files based on these annotations. 84 Annotations (06-12 JUN) Runtime processing —Normally, Java annotations are not present in your Java code after compilation. It is possible, however, to define your own annotations that are available at runtime. Format In its simplest form, an annotation looks like @Entity. The at sign character (@) indicates to the compiler that what follows is an annotation. The annotation can include elements, which can be named or unnamed, and there are values for those elements: @Author(name = "Benjamin Franklin", date = "3/27/2003" ) or @SuppressWarnings(value = "unchecked"). If there is just one element named value, then the name can be omitted, as in: @SuppressWarnings("unchecked"). If the annotation has no elements, then the parentheses can be omitted. It is also possible to use multiple annotations on the same declaration: @Author(name = "Jane Doe") @EBook If the annotations have the same type, then this is called a repeating annotation: @Author(name = "Jane Doe") @Author(name = "John Smith"). Repeating annotations are supported as of the Java SE 8 release Where to use annotations Annotations can be applied to declarations of classes, fields, methods, and other program elements. Used on a declaration, an annotation appears on its own line by convention. Annotations can also be applied to the use of types from Java 8. This is called Type annotation. Examples: Class instance creation expression: new @Interned MyObject(); Type cast: myString = (@NonNull String) str; Implements clause: class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... } Thrown exception declaration: void monitorTemperature() throws @Critical TemperatureException { ... } 85 Annotations (06-12 JUN) Declaring an Annotation Type @interface ClassPreamble { String author(); String date(); int currentRevision() default 1; String lastModified() default "N/A"; String lastModifiedBy() default "N/A"; // Note use of array String[] reviewers(); } After the annotation type is defined, you can use annotations of that type, with the values filled in, like this: @ClassPreamble ( author = "John Doe", date = "3/17/2002", currentRevision = 6, lastModified = "4/12/2004", lastModifiedBy = "Jane Doe", // Note array notation reviewers = {"Alice", "Bob", "Cindy"} ) To make the information in @ClassPreamble appear in Javadoc-generated documentation, you must annotate the @ClassPreamble definition with the @Documentedannotation: // import this to use @Documented import java.lang.annotation.*; @Documented 86 JVM and Memory Management (06-12 JUN) @interface ClassPreamble { // Annotation element definitions } Predefined Annotation Types Annotation Types Used by the Java Language The predefined annotation types defined in java.lang are @Deprecated, @Override, and@SuppressWarnings. 13) JVM AND MEMORY MANAGEMENT (06-12 JUN) Java Virtual Machine refers to any of the following: An abstract specification, a concrete implementation of the JVM or a runtime instance of the JVM. o The abstract specification is a concept o Concrete implementations-exist on many platforms and from many vendors. They are either all software or a combination of hardware and software. o A runtime instance is used by a single running Java application. Each Java application runs inside a runtime instance of some concrete implementation of the abstract specification of the Java virtual machine. Java bytecode runs in a JRE (Java Runtime Environment). The JVM is the a part of the JRE which loads and executes Java byte code. THE LIFE OF A JVM A runtime instance of the JVM is started when a java application starts and will only run for one Java application. If you start many Java applications at the same time, on the same computer, you’ll have as many JVM instances running on the same concrete implementation of a given JVM concrete implementation. The JVM instance starts running the java application by invoking the main() method of the initial class. o This method must be public, static, return void, and accept one parameter of a String array. 87 JVM and Memory Management (06-12 JUN) o The main() method of an application's initial class serves as the starting point for that application's initial thread. The initial thread can in turn fire off other threads. A Java application continues to execute only during the life of non-daemon and terminates when all non-daemon threads terminate. At that point the virtual machine instance will exit. THE ARCHITECTURE OF THE JVM The architecture of the JVM defines a specification that any JVM implementation adopts. The architecture consists of the following: o A class loader subsystem: A mechanism for loading types (classes and interfaces) given fully qualified names. o An execution engine: A mechanism to execute the methods of loaded classes. o The JVM needs memory to store classes and all other information loaded by the class loader, parameters passed in, objects created, local variables, work in progress of computations being performed, return values. The memory that the JVM needs to execute a program is organized into a number of runtime data areas. The actual structure details vary according to the JVM implementation. The Java virtual machine has one method area and one heap area shared by all threads. o Method area: The class loader parses all the type information from the binary data into the memory area. o The heap: Objects created by the JVM are stored in the heap. Each thread gets its own Java stack and pc register also called the program counter. 88 JVM and Memory Management (06-12 JUN) The pc register: indicates the next instruction to execute when the thread is executing a Java method. The java stack: stores the state of Java method invoked for the thread. This includes the parameters, local variables, its return values and intermediate calculations. The java stack is made up of stack frames. A stack frame contains the state of an invoked Java method. The JVM pushes a new frame onto the stack when a method is invoked and pops and discards the frame when the method completes execution. The native method stack: stores the state of native method invocation in a way that varies by implementation. Information about the invocation may also be stored in registers. The execution engine: consists of the interpreter and the Just In Time(JIT) compiler. The Interpreter: converts byte code instruction into machine level instruction JIT Compiler: o When JIT is enabled, the JVM will analyze the Java application method calls and compile the bytecode (after some internal thresholds are reached) into native, more efficient, machine code. The JIT process is normally prioritized by the busiest method calls first. o Once such method call is compiled into machine code, the JVM executes it directly instead of “interpreting” it. o The above process leads to improved run time performance over time. CLASS LOADER Java loads and links the class when it refers to a class for the first time at runtime. o Hierarchy: Class loaders in Java are organized into a hierarchy with a parent-child relationship. The Bootstrap Class Loader is the parent of all class loaders. o Delegation: When a class is required, the parent class loader is checked to determine whether or not the class is in the parent class loader. If the upper class loader has the class, the class is used. If not, the class loader requests the parent to load the class. o Class visibility: A child class loader can find the class in the parent class loader and not the other way round. Each class loader has its namespace that stores the loaded classes. To load a class, the class loader checks the namespace, using the FQCN (Fully Qualified Class Name) to see if the class has already been loaded. Even if the class has an identical FQCN but a different namespace it means that the class has been loaded by another class loader and so the class loader will need to load its own. Loading a class The class loader checks if a class has been loaded in the class loader cache. If not, it checks the parent class loader. If the class is not found in the bootstrap class loader, the requested class loader searches for the class in the file system. 89 JVM and Memory Management (06-12 JUN) BOOTSTRAP CLASS LOADER o Loads Java APIs, including object classes: the runtime classes in rt.jar, internationalization classes in i18n.jar, and others. o Unlike other class loaders, it is implemented in native code instead of Java. It is created when running the JVM. EXTENSION CLASS LOADER o Loads the extension classes and various security extension functions: classes in JAR files in the lib/ext directory of the JRE, and in the system-wide, platform-specific extension directory (such as /usr/jdk/packages/lib/ext on the Solaris™ Operating System, but note that use of this directory applies only to Java™ 6 and later). SYSTEM CLASS LOADER Loads the application classes. It loads the class in the $CLASSPATH specified by the user. User-defined class loader: Written for an application by a user. RUNTIME DATA AREAS 90 JVM and Memory Management (06-12 JUN) These are the memory areas assigned when the JVM program runs on the OS. The runtime data areas are divided into 6 areas as in the diagram below. o The PC Register, JVM Stack, and Native Method Stack are created for one thread. o The Heap, Method Area, and Runtime Constant Pool are shared by all threads. PC REGISTER The PC (Program Counter) Register is created when the thread starts. The PC register has the address of the currently executing JVM instruction. JVM STACK The JVM stack is created at the same time as the thread and stores frames. A new frame is created each time a method is invoked. The frame is destroyed when the method exits normal or with an exception. The JVM pushes or pops the stack frame to the JVM stack. NATIVE METHOD STACK If supplied, native method stacks are typically allocated per thread when each thread is created. 91 JVM and Memory Management (06-12 JUN) The JVM has a Native Method Stack: colloquially called "C stacks”. It supports native methods (methods written in a language other than the Java programming language). THE METHOD AREA It is created when the JVM starts. The class loader locates the appropriate class file to load a type. It reads in the class file (a linear stream of binary data). The virtual machine extracts information about the type from the binary data and stores it in the method area. Memory for class (static) variables declared in the class is also taken from the method area. How the JVM represents type information internally is a decision of the implementation designer. All threads share the same method area, so access to the method area's data structures must be designed to be thread-safe THE HEAP Java objects reside in an area called the heap. The heap is created when the JVM starts up and may increase or decrease in size while the application runs. When the heap becomes full, garbage is collected. During the garbage collection objects that are no longer used are cleared, thus making space for new objects. The heap is sometimes divided into two areas (or generations) called the nursery (or young space) and the old space. The nursery is a part of the heap reserved for allocation of new objects. When the nursery becomes full, garbage is collected by running a special young collection, where all objects that have lived long enough in the nursery are promoted (moved) to the old space, thus freeing up the nursery for more object allocation. When the old space becomes full garbage is collected there, a process called an old collection. The reasoning behind a nursery is that most objects are temporary and short lived. A young collection is designed to be swift at finding newly allocated objects that are still alive and moving them away from the nursery. Typically, a young collection frees a given amount of memory much faster than an old collection or a garbage collection of a single-generational heap (a heap without a nursery). 92 JVM and Memory Management (06-12 JUN) RUNTIME CONSTANT POOL This area is included in the method area. However as it plays a core role in JVM operation, the JVM specification separately describes it. It contains the constant of each class and interface and all references for methods and fields. In short, when a method or field is referred to, the JVM searches the actual address of the method or field on the memory by using the runtime constant pool. The diagram below will help explain the role of the constant pool further. The frame is where the operands (operation instructions) reside and that is where the dynamic linking occurs. It is a shorthand way, so to speak, using the constant pool to keep track of the class and its members. Each frame contains a reference to the runtime constant pool. This reference points to the constant pool for the class of the method being executed for that frame. This reference helps to support dynamic linking. C/C++ code is typically compiled to an object file then multiple object files are linked together to product a usable artifact such as an executable or dll. During the linking phase symbolic references in each object file are replaced with an actual memory address relative to the final executable. In Java this linking phase is done dynamically at runtime. When a Java file is compiled, all references to variables and methods are stored in the class's constant pool as a symbolic reference. 93 Design Patterns (16-19 JUN) A symbolic reference is a logical reference not a reference that actually points to a physical memory location Source: stackflow question response by James Drinkard EXECUTION ENGINE The bytecode that the class loader puts in the runtime data area is executed by the execution engine which reads the Java Bytecode in units of instruction. Java Bytecode is written in human-readable form. The execution engine changes the bytecode to machine code in the JVM. This is done in one of two ways: o Interpreter: Reading, interpreting and executing the bytecode instructions one by one. As it interprets and executes instructions one by one, it can quickly interpret one bytecode, but slowly executes the interpreted result. o JIT (Just-In-Time) compiler: The JIT compiler compensates for the disadvantage of the slow execution of the interpreter. The execution engine runs as an interpreter first, and at the appropriate time, the JIT compiler compiles the entire bytecode to change it to native code. After that, the execution engine no longer interprets the method, but directly executes using native code. The compiled code can be executed quickly since the native code is stored in the cache. However, it takes more time for JIT compiler to compile the code than for the interpreter to interpret the code one by one. Therefore, if the code is to be executed just once, it is better to interpret it instead of compiling. The JVM implementations that use the JIT compiler internally check how frequently the method is executed and compile the method only when the frequency is higher than a certain level. Further reading and reference: https://dzone.com/articles/understanding-jvm-internals http://blog.jamesdbloom.com/JVMInternals.html https://www.artima.com/insidejvm/ed2/jvmP.html http://www.cubrid.org/blog/dev-platform/understanding-jvm-internals/ 14) DESIGN PATTERNS (16-19 JUN) TBC 94 TBD-Other topics to study TBD-OTHER TOPICS TO STUDY Classes and objects including nested classes Java Memory Management REFERENCE MULTI-DIMENSIONAL ARRAYS The arrays you have been using so far have only held one column of data. But you can set up an array to hold more than one column. These are called multi-dimensional arrays. As an example, think of a spreadsheet with rows and columns. If you have 6 rows and 5 columns then your spreadsheet can hold 30 numbers. It might look like this: A multi dimensional array is one that can hold all the values above. You set them up like this: int[ ][ ] aryNumbers = new int[6][5]; They are set up in the same way as a normal array, except you have two sets of square brackets. The first set of square brackets is for the rows and the second set of square brackets is for the columns. In the above line of code, we're telling Java to set up an array with 6 rows and 5 columns. To hold values in a multi-dimensional array you have to take care to track the rows and columns. Here's some code to fill the first rows of numbers from our spreadsheet image: aryNumbers[0][0] = 10; aryNumbers[0][1] = 12; aryNumbers[0][2] = 43; aryNumbers[0][3] = 11; aryNumbers[0][4] = 22; So the first row is row 0. The columns then go from 0 to 4, which is 5 items. To fill the second row, it would be this: aryNumbers[1][0] = 20; aryNumbers[1][1] = 45; 95 Reference aryNumbers[1][2] = 56; aryNumbers[1][3] = 1; aryNumbers[1][4] = 33; The column numbers are the same, but the row numbers are now all 1. To access all the items in a multi-dimensional array the technique is to use one loop inside of another. Here's some code to access all our number from above. It uses a double for loop: The first for loop is used for the rows; the second for loop is for the columns. The first time round the first loop, the value of the variable i will be 0. The code inside of the for loop is another loop. The whole of this second loop will be executed while the value of the variable i is 0. The second for loop use a variable called j. The i and the j variables can then be used to access the array. aryNumbers[ i ][ j ] So the two loop system is used to go through all the values in a multi-dimensional array, row by row. Source: http://www.homeandlearn.co.uk/java/multi-dimensional_arrays.html FAIL-FAST Detect illegal concurrent modification during iteration and fail quickly and cleanly by throwing ConcurrentModificationException rather than risking arbitrary, nondeterministic behaviour at an undetermined time in the future. This can happen in the following situations: Single Threaded Environment - After the creation of the iterator , structure is modified at any time by any method other than iterator's own remove method. 96 Reference Multiple Threaded Environment: If one thread is modifying the structure of the collection while other thread is iterating over it . Fail-fast behaviour of an iterator cannot be guaranteed rather on a best-effort basis. It is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. The fail-fast behavior of iterators should be used only to detect bugs. HOW IT WORKS Iterator read internal data structure (object array) directly . The internal data structure should not be modified while iterating through the collection. To ensure this it maintains an internal flag "mods" .Iterator checks the "mods" flag whenever it gets the next value (using hasNext() method and next() method). Value of mods flag changes whenever there is an structural modification. Thus the iterator would throw ConcurrentModificationException. Source: java hungry FAIL SAFE HOW IT WORKS Fail Safe Iterator makes a copy of the internal data structure (object array) and iterates over the copied data structure. Any structural modification done to the iterator affects the copied data structure. The original data structure remains structurally unchanged and no ConcurrentModificationException is thrown by the fail safe iterator. The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterator will not reflect additions, removals, or changes to the list since the iterator was created. Element-changing operations on iterators themselves (remove(), set(), and add()) are not supported. These methods throw UnsupportedOperationException. THE LIMITATIONS: The overhead of maintaining the copied data structure i.e memory. The fail safe iterator does not guarantee that the data being read is the data currently in the original data structure. 97 Code Thus fail safe iterator is ordinarily too costly, but may be useful when you cannot or don’t want to synchronize traversals, yet need to preclude interference among concurrent threads. Example Fail Safe Iterator: Iterator with ConcurrentHashMap Source: java hungry HASHTABLE Was part of the original java.util and is a concrete implementation of a Dictionary. Java 2 re-engineered Hashtable so that it also implements the Map interface. Thus, Hashtable is now integrated into the collections framework. It is similar to HashMap, but is synchronized. HOW IT WORKS Like HashMap, Hashtable stores key/value pairs in a hash table. When using a Hashtable, you specify an object that is used as a key, and the value that you want linked to that key. The key is then hashed, and the resulting hash code is used as the index at which the value is stored within the table. INNER CLASS In Java you are allowed to define a class (say, B) inside of another class (say, A). The class A is called the outer class, and the class B is called the inner class. The purpose of inner classes is purely to be used internally as helper classes. An inner class is a member of its enclosing class and has access to other members (including private) and vice versa. An inner class can be declared private, public, protected, or package private. There are two kind of inner classes: static and non-static. A static inner class cannot refer directly to instance variables or methods defined in its outer class: it can use them only through an object reference. CODE ESSENTIAL JAVA CLASSES - BASIC I/O USING BYTE STREAMS import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; 98 Code public class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("xanadu.txt"); out = new FileOutputStream("outagain.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } } USING CHARACTER STREAMS import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; 99 Code public class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader("xanadu.txt"); outputStream = new FileWriter("characteroutput.txt"); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } } LINE-ORIENTED I/O import java.io.FileReader; import java.io.FileWriter; import java.io.BufferedReader; 100 Code import java.io.PrintWriter; import java.io.IOException; public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader("xanadu.txt")); outputStream = new PrintWriter(new FileWriter("characteroutput.txt")); String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } } SCANNING import java.io.*; 101 Code import java.util.Scanner; public class ScanXan { public static void main(String[] args) throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader("xanadu.txt"))); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } } } SCANNING JAVA PRIMIT IVE TYPE TOKENS import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.util.Scanner; import java.util.Locale; public class ScanSum { public static void main(String[] args) throws IOException { 102 Code Scanner s = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader("usnumbers.txt"))); s.useLocale(Locale.US); while (s.hasNext()) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } } finally { s.close(); } System.out.println(sum); } } And here's the sample input file, usnumbers.txt 8.5 32,767 3.14159 1,000,000.1 CONCURRENCY 103 Code SYNCHRONIZED STATEMENT CODE public class Counter{ private int count = 0; public int inc(){ synchronized(this){ return ++count; } } } SIMPLE LOCK USE CODE public class Counter{ private Lock lock = new Lock(); private int count = 0; public int inc(){ lock.lock(); int newCount = ++count; lock.unlock(); return newCount; } } SIMPLE LOCK IMPLEMENTATION CODE public class Lock{ 104 Code private boolean isLocked = false; public synchronized void lock() throws InterruptedException{ while(isLocked){ wait(); } isLocked = true; } public synchronized void unlock(){ isLocked = false; notify(); } } REENTRANT LOCK IMPLEMENTATION public class Lock{ boolean isLocked = false; Thread lockedBy = null; int lockedCount = 0; public synchronized void lock() throws InterruptedException{ Thread callingThread = Thread.currentThread(); 105 Code while(isLocked && lockedBy != callingThread){ wait(); } isLocked = true; lockedCount++; lockedBy = callingThread; } public synchronized void unlock(){ if(Thread.curentThread() == this.lockedBy){ lockedCount--; if(lockedCount == 0){ isLocked = false; notify(); } } } ... } DEADLOCK EXAMPLE public class TreeNode { 106 Code TreeNode parent = null; List children = new ArrayList(); public synchronized void addChild(TreeNode child){ if(!this.children.contains(child)) { this.children.add(child); child.setParentOnly(this); } } public synchronized void addChildOnly(TreeNode child){ if(!this.children.contains(child){ this.children.add(child); } } public synchronized void setParent(TreeNode parent){ this.parent = parent; parent.addChildOnly(this); } public synchronized void setParentOnly(TreeNode parent){ this.parent = parent; } } 107 Code If a thread (1) calls the parent.addChild(child) method at the same time as another thread (2) calls the child.setParent(parent) method, on the same parent and child instances, a deadlock can occur. Here is some pseudo code that illustrates this: Thread 1: parent.addChild(child); //locks parent --> child.setParentOnly(parent); Thread 2: child.setParent(parent); //locks child --> parent.addChildOnly() First thread 1 calls parent.addChild(child). Since addChild() is synchronized thread 1 effectively locks the parent object for access from other treads. Then thread 2 calls child.setParent(parent). Since setParent() is synchronized thread 2 effectively locks the child object for acces from other threads. Now both child and parent objects are locked by two different threads. Next thread 1 tries to call child.setParentOnly() method, but the child object is locked by thread 2, so the method call just blocks. Thread 2 also tries to call parent.addChildOnly() but the parent object is locked by thread 1, causing thread 2 to block on that method call. Now both threads are blocked waiting to obtain locks the other thread holds. Note: The two threads must call parent.addChild(child) and child.setParent(parent) at the same time as described above, and on the same two parent and child instances for a deadlock to occur. The code above may execute fine for a long time until all of a sudden it deadlocks. The threads really need to take the locks *at the same time*. For instance, if thread 1 is a bit ahead of thread2, and thus locks both A and B, then thread 2 will be blocked already when trying to lock B. Then no deadlock occurs. Since thread scheduling often is unpredictable there is no way to predict *when* a deadlock occurs. Only that it *can* occur. 108