├── res ├── table1.1.JPG ├── table1.2.JPG ├── table1.3.JPG ├── table1.4.JPG ├── table1.5.JPG ├── table2.1.JPG ├── table2.2.JPG ├── table2.5.JPG ├── table2.6.jpg ├── table3.1.JPG ├── table4.1.JPG ├── table4.3.JPG ├── table4.6.JPG ├── table4.8.JPG ├── table4.9.JPG ├── table5.4.JPG ├── table5.6.JPG ├── table5.7.JPG ├── table5.9.JPG ├── table6.5.JPG ├── table6.6.JPG ├── table6.8.JPG ├── figure3.1.JPG ├── figure6.10.JPG ├── figure6.4.JPG ├── figure6.7.JPG ├── figure7.1.JPG ├── figure9_2.JPG ├── figure_10.2.JPG ├── figure_11.4.JPG ├── figure_8.4.JPG ├── moduleinfo.jpg ├── table4.10.JPG ├── table4.11.JPG ├── table4.12.jpg ├── table4.13.jpg ├── table_9.2.JPG ├── moduleexport.jpg └── moduleinfofull.jpg └── README.md /res/table1.1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table1.1.JPG -------------------------------------------------------------------------------- /res/table1.2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table1.2.JPG -------------------------------------------------------------------------------- /res/table1.3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table1.3.JPG -------------------------------------------------------------------------------- /res/table1.4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table1.4.JPG -------------------------------------------------------------------------------- /res/table1.5.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table1.5.JPG -------------------------------------------------------------------------------- /res/table2.1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table2.1.JPG -------------------------------------------------------------------------------- /res/table2.2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table2.2.JPG -------------------------------------------------------------------------------- /res/table2.5.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table2.5.JPG -------------------------------------------------------------------------------- /res/table2.6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table2.6.jpg -------------------------------------------------------------------------------- /res/table3.1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table3.1.JPG -------------------------------------------------------------------------------- /res/table4.1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.1.JPG -------------------------------------------------------------------------------- /res/table4.3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.3.JPG -------------------------------------------------------------------------------- /res/table4.6.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.6.JPG -------------------------------------------------------------------------------- /res/table4.8.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.8.JPG -------------------------------------------------------------------------------- /res/table4.9.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.9.JPG -------------------------------------------------------------------------------- /res/table5.4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table5.4.JPG -------------------------------------------------------------------------------- /res/table5.6.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table5.6.JPG -------------------------------------------------------------------------------- /res/table5.7.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table5.7.JPG -------------------------------------------------------------------------------- /res/table5.9.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table5.9.JPG -------------------------------------------------------------------------------- /res/table6.5.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table6.5.JPG -------------------------------------------------------------------------------- /res/table6.6.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table6.6.JPG -------------------------------------------------------------------------------- /res/table6.8.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table6.8.JPG -------------------------------------------------------------------------------- /res/figure3.1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure3.1.JPG -------------------------------------------------------------------------------- /res/figure6.10.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure6.10.JPG -------------------------------------------------------------------------------- /res/figure6.4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure6.4.JPG -------------------------------------------------------------------------------- /res/figure6.7.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure6.7.JPG -------------------------------------------------------------------------------- /res/figure7.1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure7.1.JPG -------------------------------------------------------------------------------- /res/figure9_2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure9_2.JPG -------------------------------------------------------------------------------- /res/figure_10.2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure_10.2.JPG -------------------------------------------------------------------------------- /res/figure_11.4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure_11.4.JPG -------------------------------------------------------------------------------- /res/figure_8.4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/figure_8.4.JPG -------------------------------------------------------------------------------- /res/moduleinfo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/moduleinfo.jpg -------------------------------------------------------------------------------- /res/table4.10.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.10.JPG -------------------------------------------------------------------------------- /res/table4.11.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.11.JPG -------------------------------------------------------------------------------- /res/table4.12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.12.jpg -------------------------------------------------------------------------------- /res/table4.13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table4.13.jpg -------------------------------------------------------------------------------- /res/table_9.2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/table_9.2.JPG -------------------------------------------------------------------------------- /res/moduleexport.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/moduleexport.jpg -------------------------------------------------------------------------------- /res/moduleinfofull.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jrandj/java-11/HEAD/res/moduleinfofull.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java 11 2 | 3 | - [Java SE 11 Programmer I](#Java-SE-11-Programmer-I) 4 | - [Java SE 11 Programmer II](#Java-SE-11-Programmer-II) 5 | - [Practise Tests](#Practise-Tests) 6 | 7 | ## Java SE 11 Programmer I 8 | 9 | - [Creating a Simple Java Program](#Creating-a-Simple-Java-Program) 10 | - [Working with Java Primitive Data Types and String APIs](#Working-with-Java-Primitive-Data-Types-and-String-APIs) 11 | - [Using Operators and Decision Constructs](#Using-Operators-and-Decision-Constructs) 12 | - [Working with Java Arrays](#Working-with-Java-Arrays) 13 | - [Describing and Using Objects and Classes](#Describing-and-Using-Objects-and-Classes) 14 | - [Creating and Using Methods](#Creating-and-Using-Methods) 15 | - [Applying Encapsulation](#Applying-Encapsulation) 16 | - [Reusing Implementations Through Inheritance](#Reusing-Implementations-Through-Inheritance) 17 | - [Programming Abstractly Through Interfaces](#Programming-Abstractly-Through-Interfaces) 18 | - [Handling Exceptions](#Handling-Exceptions) 19 | - [Understanding Modules](#Understanding-Modules) 20 | - [Understanding Java Technology and Environment](#Understanding-Java-Technology-and-Environment) 21 | 22 | ### Creating a Simple Java Program 23 | 24 | 1. A Java class is compiled using the **javac** program. Compilation results in one or more class files depending on the contents of the Java source file. An example is shown below: 25 | ```java 26 | javac TestClass.java 27 | ``` 28 | 29 | 1. A Java class is executed using the **java** program. An example is shown below: 30 | ```java 31 | java TestClass a b c 32 | ``` 33 | 34 | 1. The **Java Virtual Machine (JVM)** is an executable. When the JVM runs, it loads the given class and looks for the main method of that class to run. The executable for the JVM is named java. 35 | 36 | 1. Every Java class belongs to a package. There can be at most one package statement in the source file, and it must be the first statement in the file. If there is no package statement, then the classes defined in that file belong to an unnamed package which is known as the default package. Classes from other packages can be imported so that they can be referred to without using the **Fully Qualified Class Name (FQCN)**. 37 | 38 | 1. Java code is made up of expressions, statements, and blocks. An expression is made up of variables, operators, and method invocations. An expression evaluates to a single value. An expression is something which evaluates a value, while a statement is a line of code that does something. Some expressions can be made into a statement by terminating it with a semicolon, such as assignment expressions, usage of ++ or --, method invocation and object creation expressions. A block is a group of zero or more statements between balanced branches and can be used wherever a single statement is allowed. 39 | 40 | 1. The stack is used for storing local variables, function calls, and references to objects. The heap is used for storing objects. 41 | 42 | ### Working with Java Primitive Data Types and String APIs 43 | 44 | 1. Java is a statically typed language. This means that the data type of a variable is defined at compile time and cannot change during run time. 45 | 46 | 1. Java has primitive and reference variables. The Java compiler and JVM inherently know what primitive types mean, while reference data types are built by combining primitive data types and other reference data types. 47 | 48 | 1. Primitive data types are shown below: 49 | | Date Type | Size | Description | Sample | 50 | |-----------|---------|-------------------------------------------------------------------------|-------------------------| 51 | | byte | 1 byte | -128 to 127 | -1, 0, 1 | 52 | | short | 2 bytes | -32,768 to 32,767 | -1, 0, 1 | 53 | | int | 4 bytes | -2,147,483,648 to 2,147,483,647 | -1, 0, 1 | 54 | | long | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | -1, 0, 1 | 55 | | float | 4 bytes | Stores fractional numbers. Sufficient for storing 6 to 7 decimal digits | 1.1f, 2.0f | 56 | | double | 8 bytes | Stores fractional numbers. Sufficient for storing 15 decimal digits | 1.1d, 2.0d | 57 | | boolean | 1 bit | Stores true or false values | true, false | 58 | | char | 2 bytes | Stores a single character | '\u0000', 'a' | 59 | 60 | 1. Wrapper classes exist for Byte, Short, Integer, Long, Float and Double. 61 | 62 | 1. Reference types include: 63 | * Classes 64 | * Interfaces 65 | * Enums 66 | 67 | 1. A variable contains a raw number and assigning one variable to another simply copies that number from one variable to another. Java uses **pass by value** semantics. 68 | 69 | 1. Java initialises all static and instance variables of a class automatically if you do not initialise them explicitly. You must initialise local variables explicitly before they are used. 70 | 71 | 1. Assigning a smaller type to a larger type is known as **implicit widening conversion** as no cast is required. To assign a larger type to a smaller type, for a compile time constant (i.e., final) the compiler will automatically narrow the value. If the source variable is not a constant then a cast is required, this is known as **explicit narrowing**. Determining the value that will be assigned when doing an explicit can be complicated. 72 | 73 | 1. A String is an object of class java.lang.String. String is a final class and implements java.lang.CharSequence. String is immutable so the value cannot be changed after instantiation. 74 | 75 | 1. A String can be instantiated using: 76 | ```java 77 | String myString = new String(); 78 | String myString = new String("example"); 79 | String myString = "example"; 80 | String myString = new String(StringBuilder sb); 81 | String myString = new String(byte[] bytes); 82 | String myString = new String(char[] value); 83 | String myString = s1 + "add"; 84 | ``` 85 | 86 | 1. String concatenation can occur with the += operator if one of the operands is a String. 87 | 88 | 1. Strings created without using the new keyword are kept in the **string pool**. If a String is created without the new keyword and the same string already exists in the string pool, then a reference to the existing String will be returned. 89 | 90 | 1. Useful String methods include: 91 | ```java 92 | int length(); 93 | char charAt(int index); 94 | int indexOf(int ch); 95 | String substring(int beginIndex, int endIndex); 96 | String substring(int beginIndex); 97 | String concat(String str); 98 | String toLowerCase(); 99 | String toUpperCase(); 100 | String replace(char oldChar, char newChar); 101 | String strip(); 102 | String stripLeading(); 103 | String stripTrailing(); 104 | String trim(); 105 | ``` 106 | 107 | 1. Note that in the substring methods the ending index is exclusive, and the starting index is inclusive. 108 | 109 | 1. Useful methods for inspecting a String include: 110 | ```java 111 | boolean isBlank(); 112 | boolean isEmpty(); 113 | boolean startsWith(String prefix); 114 | boolean endsWith(String suffix); 115 | boolean contains(CharSequence s); 116 | boolean equals(Object anObject); 117 | boolean equalsIgnoreCase(String anotherString); 118 | ``` 119 | 120 | 1. java.lang.StringBuilder is the mutable twin of java.lang.String. StringBuilder is also a final class and implements java.lang.CharSequence. StringBuilder is better suited for creating temporary strings that have no use once a method ends. 121 | 122 | 1. StringBuilder provides several constructors: 123 | ```java 124 | StringBuilder(); 125 | StringBuilder(CharSequence seq); 126 | StringBuilder(int capacity); 127 | StringBuilder(String str); 128 | ``` 129 | 130 | 1. Note that the default size for the no argument constructor is 16 characters. 131 | 132 | 1. Useful StringBuilder methods include: 133 | ```java 134 | insert(int position, value); 135 | append( value); 136 | reverse(); 137 | delete(int start, int end); 138 | deleteCharAt(int index); 139 | replace(int start, int end, String replacement); 140 | int capacity(); 141 | char charAt(int index); 142 | int length(); 143 | int indexOf(String str); 144 | int indexOf(String str, int startIndex); 145 | void setLength(int len); 146 | String substring(int start); 147 | String substring(int start, int end); 148 | String toString(); 149 | ``` 150 | 1. The value to append is typically determined by *String.valueOf()*. 151 | 152 | ### Using Operators and Decision Constructs 153 | 154 | 1. Arithmetic operators are used to perform standard mathematical operations on all primitive variables (and wrapper objects for numeric types) except Boolean. 155 | 156 | 1. Operators and their precedence shown below: 157 | | Operator Type | Category | Precedence | 158 | |---------------|----------------------|-----------------------------------------| 159 | | Unary | postfix | expr++ expr-- | 160 | | Unary | prefix | ++expr --expr +expr -expr ~ ! | 161 | | Arithmetic | multiplicative | * / % | 162 | | Arithmetic | additive | + - | 163 | | Shift | shift | << >> >>> | 164 | | Relational | comparison | < > <= >= instanceof | 165 | | Relational | equality | == != | 166 | | Bitwise | bitwise AND | & | 167 | | Bitwise | bitwise exclusive OR | ^ | 168 | | Bitwise | bitwise inclusive OR | \| | 169 | | Logical | logical AND | && | 170 | | Logical | logical OR | \|\| | 171 | | Ternary | ternary | ? : | 172 | | Assignment | assignment | = += -= *= /= %= &= ^= \|= <<= >>= >>>= | 173 | 174 | 1. Java applies the rules of **numeric promotion** while working with operators that deal with numeric values. **Unary numeric promotion** occurs if an operand is smaller than int and causes the operand to be automatically promoted to int. **Binary numeric promotion** occurs if one of the promoted operands is larger than int, and causes both operands to be promoted to the larger operand. 175 | 176 | 1. In the case of a Dangling Else statement, the Else is associated to the nearest if statement. 177 | 178 | 1. In a switch statement the expression must be: 179 | * Integral types (byte, char, short, int) and their wrapper classes. Note that this excludes long. 180 | * enum type. 181 | * A String. 182 | 183 | 1. In a switch statement case labels are optional, but if provided they must be compile time constants. If an enum is used the unqualified enum constant (e.g., VALUE_A and not Class.MyEnum.VALUE_A) must be used. 184 | 185 | 1. In a switch statement the default block is optional, but if provided there can only be 1. 186 | 187 | ### Working with Java Arrays 188 | 189 | 1. Examples for array instantiation: 190 | ```java 191 | int[] ia = new int[10]; 192 | int[] ia = {0, 1, 2, 3, 4, 5}; 193 | int[][] iaa = new int[2][3]; 194 | int[][] iaa = new int[3][]; 195 | int[][] iaa = new int[][]{new int[]{1,2}}; 196 | int[][] iaa = {{1,2}}; 197 | ``` 198 | 199 | 1. Members of an array object include: 200 | * length 201 | * clone 202 | 203 | 1. Arrays have two interesting runtime properties: 204 | * They are **reified** meaning that type checking is done at runtime by the JVM and not the compiler. 205 | * They are **covariant** meaning that a subclass object can be stored in an array that is declared to be the type of its superclass (i.e., you can store an integer in a number array). 206 | 207 | 1. The compare method compares 2 int arrays and returns 0 if they are equal, a value less than 0 if the first array is lexicographically less than the second array, and a value greater than 0 if the first array is lexicographically greater than the second array. 208 | 209 | 1. The mismatch method takes two int arrays and returns the index of the first mismatch, otherwise it returns -1 if no mismatch is found. 210 | 211 | ### Describing and Using Objects and Classes 212 | 213 | 1. When an object is instantiated the JVM allocates the necessary heap space to contain the various fields defined in the class. An example of instantiation is shown below: 214 | ```java 215 | new java.lang.Object(); 216 | ``` 217 | 218 | 1. In Java if you do not specify any reference variable explicitly within any instance method, the JVM assumes that you mean to access the same object for which the method has been invoked. You can make this explicit with the **this** keyword. 219 | 220 | 1. The structure of a Java source file is: 221 | * At most one package statement. 222 | * Zero or more import statements. 223 | * One or more reference type (i.e., class, interface, or enum) definitions. 224 | 225 | 1. Members of a class definition include field declarations, methods, constructors, and initialisers. Members can be static or non-static. 226 | 227 | 1. The code for a top-level public reference type must be written inside a Java file with the same name. 228 | 229 | 1. Java has three **visibility scopes** for variables - class, method, and block. Java has five **lifespan scopes** for variables - class, instance, method, for loop, and block. 230 | 231 | 1. From Java 10 you can use a **var declaration** to infer types in a local method. An example is shown below: 232 | ```java 233 | var str = "Java 11"; 234 | ``` 235 | 236 | 1. If there are no more references to an Object, the JVM concludes that the object is not required anymore and is marked as **garbage**. The object is then removed using **garbage collection**. 237 | 238 | ### Creating and Using Methods 239 | 240 | 1. The basic structure of a method is shown below: 241 | ```java 242 | returnType methodName(parameters){ 243 | methodBody 244 | } 245 | ``` 246 | 247 | 1. The method must return a value of the type specified, with the following exceptions: 248 | * If the type is numeric then the return value can be one of any other numeric type as long as the type of the return value is smaller than the declared type. 249 | * Wrapper classes and primitives are interchangeable. 250 | * A subtype of the declared type can be returned (referred to as **covariant return**). 251 | 252 | 1. The method signature includes the method name and its ordered list of parameter types. Where multiple methods exist with the same name but different parameter types, it is said that the class has **overloaded** the method name. 253 | 254 | 1. If the type but not exact number of parameters is known, the varargs syntax can be used. This is represented by 3 dots after a data type. Restrictions on the usage of varargs are: 255 | * A method cannot have more than 1 varargs parameter. 256 | * The varargs parameter must be the last parameter in the parameter list. 257 | 258 | 1. The following rules used by the compiler to disambiguate method calls to overloaded methods: 259 | * A compilation error will occur if the compiler is not able to successfully disambiguate a particular method call. 260 | * If the compiler finds a method whose parameter list is an exact match to the argument list of the method call, that method is selected. 261 | * If more than one method is capable of accepting a method call and none of them are an exact match, the one that is most specific is chosen by the compiler. 262 | * Higher priority is given to primitive versions if the argument can be widened to the method parameter type, this occurs before autoboxing is considered. 263 | * Autoboxing is considered before varargs. 264 | 265 | 1. When a new instance of a class is created, the JVM does four things: 266 | * Checks if the class has been initialised, if not, loads and initialises the class. 267 | * Allocates the memory to hold the instance variables of the class in the heap space. 268 | * Initialises the instance variables to their default values. 269 | * Gives the instance an opportunity to set the values of the instance variables as per the instance initialisers and constructors. 270 | 271 | 1. An **instance initialiser** is shown below: 272 | ```java 273 | class TestClass{ 274 | { 275 | System.out.println("In instance initialiser"); 276 | } 277 | } 278 | ``` 279 | 280 | 1. An instance variable is not allowed to use the value of a variable if that variable is declared below the initialiser. It can assign a value to such a variable. An instance initialiser is not expected to throw any exceptions. Instance initialisers should generally be avoided, and a well-designed class should not need to use them. 281 | 282 | 1. A constructor is a method that always has the same name as the class and does not have a return type. 283 | 284 | 1. A no argument constructor is defined by default only if no constructors are provided explicitly. 285 | 286 | 1. Constructors can be overloaded through **constructor overloading**. When a constructor calls another constructor of the same class, this is done with the **this** keyword and is called **constructor chaining**. The call to another constructor must be the first line of code in a constructor. 287 | 288 | 1. Java forces the programmer to assign a value to a final variable explicitly. A static variable can be used by other classes only after the class is loaded made ready to use. You can assign a value to a final static variable at the time of declaration or in any one of the **static initialisers**. 289 | 290 | 1. Access to static members is decided by the compiler at compile time by checking the declared type of the variable. This is referred to as **static binding**. Static binding uses type information to bind a method to a method call, as opposed to **dynamic binding** which considers the actual object. 291 | 292 | 1. The first step in creating an instance of a class is to initialise the class itself. Whenever the JVM encounters the usage of a class for the first time, it allocates and initialises space for the static fields of that class. The rules for static blocks: 293 | * A class can have any number of static blocks. They are executed in the order that they appear in the class. 294 | * A static block can access all static variables and static methods of the class. However, if the declaration of a static variable appears after the static block, then only the value can be set. 295 | * If the class has a superclass, and the superclass has not been initialised already, the JVM will initialise the superclass first. 296 | * There is no way to access or refer to a static block. It can only be invoked by the JVM, and only once. 297 | 298 | 299 | ### Applying Encapsulation 300 | 301 | 1. Encapsulation, Inheritance and Polymorphism are the three pillars of Object-Orientated Programming. 302 | 303 | 1. **Encapsulation** is about restricting direct access to an objects data fields. This is achieved using of **access modifiers**. Access modifiers and their impact on accessibility are shown below: 304 | 305 | | Modifier | Class | Package | Subclass | World | 306 | |-------------|-------|---------|----------|-------| 307 | | public | Y | Y | Y | Y | 308 | | protected | Y | Y | Y | N | 309 | | no modifier | Y | Y | N | N | 310 | | private | Y | N | N | N | 311 | 312 | 1. A top-level class, interface or enum can only have a public or default access modifier. 313 | 314 | 1. Members of an interface are always public, even if not declared that way. The compiler will generate an error if you define them as private or protected. From Java 9 an interface can have private methods. 315 | 316 | 1. Enum constants are always public, even if not declared that way. The compiler will generate an error if you define them as private or protected. Enum constructors are always private. 317 | 318 | 1. Encapsulation encourages loose coupling between classes. If the functionality of a class is exposed through methods, there are two advantages: 319 | * The implementation can be modified without users being aware (i.e., the implementation details of the functionality are hidden from the users). 320 | * The value of a variable can be ensured to be consistent with the business logic of the class. For example, you could restrict setting the age of a Person class instance from being a negative number. 321 | 322 | ### Reusing Implementations Through Inheritance 323 | 324 | 1. A class defines a type and contains a state (the instance fields) and the implementation (the methods). Thus, inheritance could be of state, implementation, or type. 325 | 326 | 1. Java restricts a class from extending more than one class, so it is said that Java does not support multiple inheritance of state. A class can inherit implementation by extending a class and/or by implementing interfaces. As a class can implement multiple interfaces, Java supports multiple implementation inheritance. Java also supports multiple inheritance of type as an object can have multiple types: the type of its own class and the types of all the interfaces that the class implements. 327 | 328 | 1. To inherit features from another class, that class is extended using the **extends** keyword. Constructors, static and instance initialisers of a class are not considered members of a class so are not inherited by a subclass. Only class members that are visible to another class as per the rules of access modifiers are inherited in a subclass. 329 | 330 | 1. Note that when the JVM initialises a class, memory is allocated for all instance variables irrespective of whether they will be accessible. 331 | 332 | 1. A subclass cannot be initialised before its parent. A call to *super()* is automatically inserted by the compiler in the first line of the constructor if no other call is provided. 333 | 334 | 1. The order of initialisation for loading a class is summarised below: 335 | * If there is a super class, initialise static fields and execute static initialisers of the super class in the order of their appearance. 336 | * Initialise static fields and execute static initialisers of the class in the order of their appearance. 337 | * If there is a super class, initialise the instance fields and execute instance initialisers of the super class in the order of their appearance. 338 | * Execute the super class's constructor. 339 | * Initialise the instance fields and execute instance initialisers of the class in the order of their appearance. 340 | * Execute class constructor. 341 | 342 | 1. An **abstract class** is used to capture common features of multiple related object types while knowing that no object that exhibits only the feature captured in the abstract class can exist. 343 | 344 | 1. An **abstract method** allows you to capture declaration without providing implementation. 345 | 346 | 1. A summary of the application of access modifiers, final, abstract, and static keywords is shown below: 347 | * An abstract class doesn't have to have an abstract method but if a class has an abstract method, it must be declared abstract. In other words, a concrete class cannot have an abstract method. 348 | * An abstract class cannot be instantiated irrespective of whether it has an abstract method or not. 349 | * A final class or a final method cannot be abstract. 350 | * A final class cannot contain an abstract method but an abstract class may contain a final method. 351 | * A private method is implicitly final. 352 | * A private method can never be abstract. 353 | * A static method can be final but can never be abstract. 354 | 355 | 1. The conventional sequence of modifiers in a method declaration is shown below: 356 | ```java 357 | methodName() 358 | ``` 359 | 360 | 1. **Polymorphism** refers to the ability of an object to exhibit behaviour associated with different types. The objective of polymorphism is to enable classes to become standardised components that can be easily exchanged without any impact on other components. 361 | 362 | 1. Polymorphism works because of **dynamic binding** of method calls. When you invoke an instance method using a reference variable, it is not the compiler, but the JVM that determines which code to execute based on the class of the actual object referenced by the variable. In Java, calls to non-private and non-final instance methods are dynamically bound. Everything else is **statically bound** at compile time by the compiler. 363 | 364 | 1. Static methods, static variables, and instance variables are accessed as per the declared type of the variable through which they are accessed and not according to the actual type of the object to which the variable refers. Contrast this with methods, where the actual type of the object determines which instance method is used. 365 | 366 | 1. A class can completely replace the behaviour of an instance method that it inherited by providing its own implementation of that method. This is known as **overriding**. The rules for overriding a method are shown below: 367 | * An overriding method must not be less accessible than the overridden method. 368 | * The return type of the overriding method must be a covariant return of the overridden method. 369 | * The types and order of the parameter list must be the same. 370 | * An overriding method cannot put a wider exception in its throws clause than the ones present in the throws clause of the overridden method. 371 | 372 | 1. A class can **hide** static methods and variables and instance variables. Basically, static methods are hidden, non-static methods are overridden. 373 | 374 | 1. The purpose of casting is to provide the compiler with the type information of the actual object to which a variable will be pointing at run time. When you cast a reference to another type, you are basically saying that the program does something that is not evident from the code itself. Ideally, you should never need to use casting. 375 | 376 | 1. The instanceof operator can be used to check if an object is an instance of a particular reference type. An example is shown below: 377 | ```java 378 | if(f instanceof Mango){ 379 | } 380 | ``` 381 | 382 | ### Programming Abstractly Through Interfaces 383 | 384 | 1. An **interface** is used to describe behaviour as captured by a group of objects. An example is shown below: 385 | ```java 386 | interface Movable{ 387 | void move(int x); 388 | } 389 | ``` 390 | 391 | 1. From an OOP perspective an interface should not contain any implementation. It should only contain method declarations. However, Java allows interfaces to contain static fields as well as default, static, and private methods. 392 | 393 | 1. Key rules for interfaces are shown below: 394 | * Members of an interface are always public, even if not declared that way. The compiler will generate an error if you define them as private or protected. From Java 9 an interface can have private methods. 395 | * An interface is always abstract. 396 | * All variables are implicitly public, static, and final. Static methods are assumed to be public if not otherwise marked. Non-static methods must be explicitly marked private or default. 397 | 398 | 1. Methods that interfaces can contain are shown below: 399 | * **Abstract methods:** Contain only declarations. An example is shown below: 400 | ```java 401 | interface Movable{ 402 | void move(int x); // implicitly abstract 403 | abstract void move2(int x); // explicitly abstract 404 | } 405 | ``` 406 | 407 | * **Default methods:** The opposite of abstract, a method cannot be both default and abstract. An example is shown below: 408 | ```java 409 | interface Movable{ 410 | default move(int x){ 411 | System.out.println("Moving by "+x+" points"); 412 | } 413 | } 414 | ``` 415 | 416 | * **Static methods:** Can be public or private but not protected or default. This is because static methods are not inherited or overridden in any meaningful way, and because protected methods can be seen by classes in the same package, and which extend the class containing the method. However, a static method cannot be inherited so the package-private access modifier should be used instead of protected, as the only remaining intention is for it to be accessible within the same package. If no access modifier is specified, they are implicitly public. An example is shown below: 417 | ```java 418 | interface Movable{ 419 | static void sayHello(){ 420 | System.out.println("Hello!"); 421 | } 422 | } 423 | ``` 424 | 425 | * **Private methods:** Helpful when methods get too big and need to be refactored into smaller internal methods. An example is shown below: 426 | ```java 427 | interface Movable{ 428 | private void moveInternal(){ 429 | System.out.println("in moveInternal"); 430 | } 431 | default void move(int n){ 432 | while(n-->0) moveInternal(); 433 | } 434 | } 435 | ``` 436 | 437 | 1. An empty interface is known as a **marker interface**. The most common marker interface used in Java is java.io.Serializable. It signifies to the JVM that objects of classes implementing this interface can be serialised and deserialised. 438 | 439 | 1. A class can implement any number of interfaces. Once a class declares that it implements an interface, it must have an implementation for all the abstract methods declared in that interface. Implementation could be inherited from an ancestor class. If the class does not have implementation for even one of the methods, the class must be declared abstract. An example is shown below: 440 | ```java 441 | interface Movable{ 442 | void Move(); 443 | } 444 | 445 | interface Readable{ 446 | void read(); 447 | } 448 | 449 | class Price implements Movable, Readable{ 450 | public void move(){ 451 | System.out.println("Moving..."); 452 | } 453 | public void read(){ 454 | System.out.println("Reading..."); 455 | } 456 | } 457 | ``` 458 | 459 | 1. Unlike the static methods of a class, the static methods of an interface cannot be inherited. Multiple fields with the same name can be inherited if they are not used ambiguously. 460 | 461 | 1. An interface can extend any number of interfaces. The extending interface inherits all members except static methods of each of the extended interfaces. 462 | 463 | 1. An interface defines behaviour. An interface tells you nothing about the actual object other than how it behaves. An abstract class defines an object which in turn drives the behaviour. 464 | 465 | 1. An ArrayList is a class that implements java.util.List, which in turn extends java.util.Collection. 466 | 467 | 1. A **parametrised** class uses a type parameter instead of the data type. An example is shown below: 468 | ```java 469 | public class DataHolder{ 470 | E data; 471 | public E getData() { return data; } 472 | public void setData(E e){ data = e; } 473 | } 474 | 475 | public class SomeClass{ 476 | public static void consumeData(DataHolder stringData){ 477 | String s = stringData.getData(); // no cast required 478 | } 479 | } 480 | ``` 481 | 482 | 1. This allows a class to be written in a generic fashion, without hardcoding it to any type. This also allows that class to be typed to any type as per the requirement at the time of use. 483 | 484 | 1. A parametrised method is like a parametrised class. The only difference is that the type parameter is valid only for that method instead of the whole class. 485 | 486 | 1. All generic information is stripped at run time, this is known as **type erasure**. This means that the presence of generics can throw up complicated situations with respect to Overloading and Overriding. 487 | 488 | 1. The java.util.Collection interface is the root interface in the collection hierarchy. The java.util.List interface defines the behaviour of collections that keep objects in an order. The functionality is implemented by classes such as ArrayList and LinkedList. 489 | 490 | 1. Useful List methods are shown below: 491 | ```java 492 | E get(int index); 493 | E set(int index, E e); 494 | boolean add(E e); 495 | void add(int index, E e); 496 | boolean addAll(Collection c); 497 | void addAll(int index, Collection c); 498 | E remove(int index); 499 | boolean remove(Object obj); 500 | boolean removeAll(Collection c); 501 | boolean retainAll(Collection c); 502 | void clear(); 503 | int size(); 504 | default void forEach(Consume action); 505 | boolean isEmpty(); 506 | boolean contains(Object o); 507 | boolean containsAll(Collection c); 508 | List subList(int fromIndex, int toIndex); 509 | int indexOf(Object o); 510 | int lastIndexOf(Object o); 511 | Object[] toArray(); 512 | T[] toArray(T[] a); 513 | ``` 514 | 515 | 1. The of and copyOf methods take 0 to 10 parameters and return an unmodifiable list. 516 | 517 | 1. ArrayList constructors are shown below: 518 | ```java 519 | ArrayList(); 520 | ArrayList(Collection c); 521 | ArrayList(int initialCapacity); 522 | ``` 523 | 524 | 1. Advantages of ArrayList over an array include dynamic sizing, type safety, and readymade features. Disadvantages of ArrayList over an array include higher memory usage, no type safety, and no support for primitive values. 525 | 526 | 1. The java.util.HashMap class implements the java.util.Map interface. Map does not extend the Collection interface. Useful Map methods are shown below: 527 | ```java 528 | V get(Object key); 529 | V put(K key, V value); 530 | V remove(Object key); 531 | Set keySet(); 532 | Collection values(); 533 | void clear(); 534 | int size(); 535 | default void forEach(BiConsumer action); 536 | ``` 537 | 538 | 1. A **lambda expression** is a shortcut for the compiler that defines a class with a method and instantiates that class. The below is an example of a class that implements a car shop: 539 | ```java 540 | class Car { 541 | String company; 542 | int year; 543 | double price; 544 | String type; 545 | 546 | Car(String c, int y, double p, String t) { 547 | this.company = c; 548 | this.year = y; 549 | this.price = p; 550 | this.type = t; 551 | } 552 | 553 | public String toString() { 554 | return "(" + company + "" + year + ")"; 555 | } 556 | } 557 | 558 | class CarMall { 559 | List cars = new ArrayList<>(); 560 | 561 | CarMall() { 562 | cars.add(new Car("Honda", 2012, 9000.0, "HATCH")); 563 | cars.add(new Car("Honda", 2018, 17000.0, "SEDAN")); 564 | cars.add(new Car("Toyota", 2014, 19000.0, "SUV")); 565 | cars.add(new Car("Ford", 2014, 13000.0, "SPORTS")); 566 | cars.add(new Car("Nissan", 2017, 8000.0, "SUV")); 567 | } 568 | 569 | List showCars(CarFilter cf) { 570 | ArrayList carsToShow = new ArrayList<>(); 571 | for (Car c : cars) { 572 | if (cf.showCar(c)) 573 | carsToShow.add(c); 574 | } 575 | return carsToShow; 576 | } 577 | } 578 | 579 | interface CarFilter { 580 | boolean showCar(Car c); 581 | } 582 | ``` 583 | 1. The showCars method returns a list of cars based on any given criteria. By accepting an interface as an argument, the showCars method lets the caller decide the criteria for the search. TestClass below represents a third party class that uses CarMall: 584 | ```java 585 | public class TestClass { 586 | public static void main(String[] args) { 587 | CarMall cm = new CarMall(); 588 | CarFilter cf = new CompanyFilter("Honda"); 589 | List carsByCompany = cm.showCars(cf); 590 | System.out.println(carsByCompany); 591 | } 592 | } 593 | 594 | class CompanyFilter implements CarFilter { 595 | private String company; 596 | 597 | public CompanyFilter(String c) { 598 | this.company = c; 599 | } 600 | 601 | public boolean showCar(Car c) { 602 | return company.contentEquals(c.company); 603 | } 604 | } 605 | ``` 606 | 607 | 1. Observe that the above implementation of CarFilter can be replaced with the below: 608 | ```java 609 | public class TestClass { 610 | public static void main(String[] args) { 611 | CarMall cm = new CarMall(); 612 | List carsByCompany = cm.showCars(c -> c.company.contentEquals("Honda")); 613 | System.out.println(carsByCompany); 614 | } 615 | } 616 | ``` 617 | 618 | 1. The compiler knows that the showCars method must pass an object of a class that implements CarFilter. As a result it is easy to generate the below: 619 | ```java 620 | class XYZ implements CarFilter{ 621 | public boolean showCar(Car <>){ 622 | return <>; 623 | } 624 | ``` 625 | 626 | 1. The parameterName and expression are contained within the lambda expression. 627 | 628 | 1. A lambda expression can be written only where the target type is an interface with exactly one abstract method. Such an interface is known as a **functional interface**. 629 | 630 | 1. The syntax for a lambda expression is the variable declarations on the left side of the arrow operator and the right side for the code that you want execution. 631 | 632 | 1. Multiple lines of code must be written within curly braces. Parameter options are shown below: 633 | ```java 634 | () -> true // no parameter 635 | a -> a*a // 1 parameter 636 | (a) -> a*a // 1 parameter 637 | (int a) -> a*a // 1 parameter 638 | (a, b, c) -> a + b + c // multiple parameters 639 | (var a) -> a*a // var is also allowed 640 | ``` 641 | 642 | 1. Filtering a list of objects is so common that Java provides a generic interface java.util.function.Predicate for this purpose. This is shown below: 643 | ```java 644 | interface Predicate{ 645 | boolean test(T t); 646 | } 647 | ``` 648 | 649 | 1. This allows us to remove the CarFilter interface in the above example: 650 | ```java 651 | List showCars(Predicate cp) { 652 | ArrayList carsToShow = new ArrayList<>(); 653 | for (Car c : cars) { 654 | if (cp.test(c)){ 655 | carsToShow.add(c); 656 | } 657 | } 658 | return carsToShow; 659 | } 660 | ``` 661 | 1. Other methods for the Predicate interface are shown below: 662 | ```java 663 | default Predicate and(Predicate other); 664 | default Predicate negate(); 665 | default Predicate or(Predicate other); 666 | static Predicate isEqual(Object targetRef); 667 | ``` 668 | 1. The Predicate interface is an example of a functional interface. They are called functional interfaces because they represent a single function and are for doing exactly one thing. 669 | 670 | ### Handling Exceptions 671 | 672 | 1. Java **exceptions** are designed to help you write code that covers all possible execution paths. This includes normal operations, exceptional situations, and unknown exceptional situations. This is shown below: 673 | ```java 674 | try{ 675 | // code for normal course of action 676 | } catch(SecurityException se) { 677 | // code for known exceptional situation 678 | System.out.println("No permission!"); 679 | } 680 | catch(Throwable t) { 681 | // code for unknown exceptional situations 682 | System.out.println("Some problem in copying: "+t.getMessage()); 683 | t.printStackTrace(); 684 | } 685 | ``` 686 | 687 | 1. When developing code there is always the provider and the client. Exceptions are a means for the provider to let the client know about any exceptional events and allow the client to determine how they want to deal with them. The mechanism to let the client know is to **throw** an exception. An example is shown below: 688 | ```java 689 | public void copyFile(String inputPath, String outputPath) throws IOException { 690 | // code to copy file 691 | } 692 | ``` 693 | 694 | 1. If the client can resolve the situation a **catch** statement can be used. If the client cannot handle the exceptional situation either, the exception can be propagated to the client's client. An exception is considered handled when it is caught in a catch block. 695 | 696 | 1. The throw statement is used to explicitly throw an exception. Throwing an exception implies that the code has encountered an unexpected situation with which it does not want to deal. Java requires that you list the exceptions that a method might throw in the throws clause of that method. This is shown below: 697 | ```java 698 | public double computeSimpleInterest (double p, double r, double t) throws Exception{ 699 | if(t<0) { 700 | throw new IllegalArgumentException("time is less than 0"); 701 | } 702 | // other code 703 | } 704 | ``` 705 | 706 | 1. A **try statement** provides an opportunity to recover from an exceptional situation that arises while executing a block of code. 707 | ```java 708 | try { 709 | // code that might throw exceptions 710 | } catch( e1){ 711 | // code to execute if code in try throws exception 1 712 | } catch( e2){ 713 | // code to execute if code in try throws exception 2 714 | } catch( en){ 715 | // code to execute if code in try throws exception N 716 | } finally{ 717 | // code to execute after try and catch blocks finish execution 718 | } 719 | ``` 720 | 721 | 1. The java.lang.Throwable class is the root of all exceptions. A Throwable object includes the chain of the method calls that led to the exception (known as the "stack trace") and any informational message specified by the programmer. 722 | 723 | 1. The Throwable hierarchy is shown below: 724 |

725 | 726 |

727 | 728 | 1. Generally **checked exceptions** are those that extend java.lang.Throwable but do not extend java.lang.RuntimeException or java.lang.Error. The remaining exceptions are **checked exceptions**. Checked exceptions must be declared in the throws clause of the method or caught in a catch block within the method. 729 | 730 | ### Understanding Modules 731 | 732 | 1. JAR files are used to package multiple classes into a file and deliver the file as an application to users. A JAR file is a zip file with a special directory named META-INF. This directory contains one or more files with MAINIFEST.MF always being one of them. The manifest contains extra information about the JAR file such as the version of Java used to build the file, and the class with the *main()* method. Each line in the manifest is a key/value pair separated by a colon. 733 | 734 | 1. Packaging classes into a JAR file without a well thought out plan can give rise to unwieldy applications that are difficult to update and/or reuse. JAR files also make it difficult to use part of an application without having the whole set of JAR files. Various Java community projects such as Ant, Maven, and Graven have attempted to provide a way of managing these issues. 735 | 736 | 1. In Java 9 the Java Module System was introduced to assist with these problems. The idea of Modules is that classes are mixed and matched at run time to form an application. 737 | 738 | 1. A module is declared using the module-info.java file. By convention, this file is placed in a folder with the same name as the module name. An example of the contents of this file is shown below: 739 | ```java 740 | module simpleinterest{ 741 | } 742 | ``` 743 | 744 | 1. An example of the file structure for a module shown below: 745 |

746 | 747 |

748 | 749 | 1. The module is compiled using the below: 750 | ```java 751 | javac -d out --module-source-path src --module simpleinterest 752 | ``` 753 | 754 | 1. The above command used 3 switches. The -d switch directed the output to a particular directory, the --module-source-path switch provided the location of the source module definition, and the --module switch specified the name of the module to compile. Although not used here, the --module-path (or -p for short) switch would be used to specify the location of modules required by the module that is being compiled. The -m switch specifies a name of the nodule. 755 | 756 | 1. A valid module name consists of Java identifiers separated by ".". A Java identifier cannot start with a number or contain a dash (-). 757 | 758 | 1. The file structure after compilation is shown below: 759 |

760 | 761 |

762 | 763 | 1. The module can be run using the below: 764 | ```java 765 | java --module-path out --module simpleinterest/simpleinterest.SimpleInterestCalculator 766 | ``` 767 | 768 | 1. Note that the format of the --module switch argument is \/\
. 769 | 770 | 1. The module is compiled into a JAR using the below: 771 | ```java 772 | jar --create --file simpleinterest.jar --main-class simpleinterest.SimpleInterestCalculator -C out\simpleinterest 773 | ``` 774 | 775 | 1. The above command used 4 switches. The --create switch tells the JAR tool to create, the --file switch specifies the name of the file, the --main-class switch adds a Main-Class entry in the JAR file manifest, and the -C switch makes the JAR tool change working directories so that the structure inside the JAR file is the same as the structure inside out\simpleinterest. 776 | 777 | 1. The module can now be run using the below: 778 | ```java 779 | java --module-path . --module simpleinterest 780 | ``` 781 | 782 | 1. It is a good design practise to define functionality in the form of an interface and let the actual implementation implement that interface. Separating the interface and the implementation into separate modules allows us to build an application by mixing and matching modules without the need to bundle classes that are not required for the application. An interface is added as shown below: 783 | ```java 784 | package calculators; 785 | public interface InterestCalculator{ 786 | public double calculate(double principle, double rate, double time); 787 | } 788 | ``` 789 | 790 | 1. The exports clause allows the public types within a package be eligible to be accessible by other modules. A module can only export packages, and not individual types. The contents of module-info.java for the calculators module is shown below: 791 | ```java 792 | module calculators{ 793 | exports calculators; 794 | } 795 | ``` 796 | 797 | 1. The requires clause is the counterpart of the exports clause. The purpose of having a requires class is to make the dependencies of a module explicitly clear to the users. The contents of module-info.java for the simpleinterest module is shown below: 798 | ```java 799 | module simpleinterest{ 800 | requires calculators; 801 | } 802 | ``` 803 | 804 | 1. A simplified SimpleInterestCalculator.java is shown below: 805 | ```java 806 | package simpleinterest; 807 | import calculators.InterestCalculator; 808 | public class SimpleInterestCalculator implements InterestCalculator{ 809 | public double calculate(double principle, double rate, double time){ 810 | return principle*rate*time; 811 | } 812 | public static void main(String[] args){ 813 | InterestCalculator ic = new SimpleInterestCalculator(); 814 | System.out.println(ic.calculate(100, .05, 2)); 815 | } 816 | } 817 | ``` 818 | 819 | 1. The directory structure after compilation is shown below: 820 |

821 | 822 |

823 | 824 | 1. The exports clause allows any other module to require it. The Java module systems allows you to fine tune access to a module only to specific modules using a variation of the exports clause. This is shown below: 825 | ```java 826 | module { 827 | exports to ; 828 | } 829 | ``` 830 | 831 | 1. If a module A reads another module B, and module B reads another module C, module A does not read module C. That is to say that dependencies are not transitive. An example is shown below: 832 | ```java 833 | module ui{ 834 | requires hr; 835 | } 836 | 837 | module hr{ 838 | requires valueobjects; 839 | exports hrservice; 840 | } 841 | 842 | // code appearing in a class in the ui module 843 | HRService hrService = new HRService(); // HRService is defined in hr module 844 | Employee e = hrService.getEmployee(employeeId); // Employee is defined in valueObjects module 845 | ``` 846 | 847 | 1. In this case the ui module does not have a requires valueobjects, so the ui module cannot access the Employee class from the valueobjects module. This code will fail to compile. A requires valueobjects could be added to the ui module, but there could be many requires clauses in the hr module. Only multiple compilation failures can make this information known to the ui module. 848 | 849 | 1. A **requires transitive** clause allows you to specify that if a module depends on another module, any module that depends on it should also depend on the other module. An example is shown below 850 | ```java 851 | module hr{ 852 | requires transitive valueobjects; 853 | exports hrservice; 854 | } 855 | ``` 856 | 857 | 1. This has the added advantage of not requiring a requires valueobjects clause in the ui module. The requires hr clause in the ui module automatically makes all the modules transitively required by the hr module, readable to the ui module. This is called **implied readability**. 858 | 859 | 1. A modular JAR file can be ran using the -classpath or -jar options, however the JVM will not enforce the access rules specified in the module descriptor. 860 | 861 | 1. A common use case is wanting to develop a module that depends on a third-party non-modular JAR. If you put a non-modular JAR on the module-path, Java will consider the non-modular JAR to be a module. Such a module is known as an **automatic module** or a **named module** and the name of the module is created automatically using the name of the JAR file. 862 | 863 | 1. As there is no module-info.class in a non-modular JAR, an automatic module exports all its packages and can read all exported packages of modules on the module-path and classes available on the classpath. 864 | 865 | 1. If a module depends on a non-modular third-party JAR, you need to add a requires clause in module-info and put the third-party JAR in the --module-path. If additionally, the automatic module requires a class from another non-modular JAR, that JAR needs to be included on the classpath. 866 | 867 | 1. The specification of Standard modules is governed by the Java Community Process (JCP). Standard modules have names starting with "java". All other modules are part of the JDK and have names starting with "jdk". A standard module may contain both standard and non-standard API packages, however if the standard module exports a non-standard package then the export must be qualified. A standard module must not grant implied readability to any non-standard module. A non-standard module must not export any standard API packages. 868 | 869 | ### Understanding Java Technology and Environment 870 | 871 | 1. Java code is compiled into **Java bytecode**, which is interpreted by the JVM. A class file produced on one platform will run on any platform that has a JVM. 872 | 873 | 1. Java is a separate application installed on top of an Operating System. 874 | 875 | 1. The **Java Runtime Environment (JRE)** includes the class libraries and executables that are required to run a Java program while the **Java Development Kit (JDK)** includes tools such as the Java compiler and the Java debugger. 876 | 877 | ## Java SE 11 Programmer II 878 | 879 | - [Java Fundamentals](#Java-Fundamentals) 880 | - [Annotations](#Annotations) 881 | - [Generics and Collections](#Generics-and-Collections) 882 | - [Functional Programming](#Functional-Programming) 883 | - [Exceptions, Assertions, and Localization](#Exceptions-Assertions-and-Localization) 884 | - [Modular Applications](#Modular-Applications) 885 | - [Concurrency](#Concurrency) 886 | - [I/O](#IO) 887 | - [NIO.2](#NIO2) 888 | - [JDBC](#JDBC) 889 | - [Security](#Security) 890 | 891 | ### Java Fundamentals 892 | 893 | 1. A final variable does not need to be assigned when it is declared, only before it is used. A variable reference being marked as final does not mean the associated object cannot be modified. If an instance variable is final, then it must be assigned a value when it is declared or when the object is instantiated. Similarly, static variables must be assigned a value when declared or in a static initialiser. 894 | 895 | 1. Methods marked final cannot be overridden by a subclass. This essentially prevents any polymorphic behaviour on the method call and ensures that a specific version of the method is always called. The opposite of a final method is an abstract method as an abstract method must be implemented. 896 | 897 | 1. A final class cannot be extended. A class cannot be both abstract and final. 898 | 899 | 1. An enum can be used to specify a fixed set of constants. Using an enum is better than using constants because it provides type-safe checking. Another advantage of an enum is the enum value can be used in a switch statement. An enum can contain methods but the first line must be the list of values. Note that if an enum has a body (e.g. default value) then the semicolon becomes mandatory. 900 | ```java 901 | public enum Season{ 902 | WINTER, SPRING, SUMMER, FALL 903 | } 904 | ``` 905 | 906 | 1. A nested class is one that is defined within another class. There are 4 types: 907 | * **Inner class:** A non-static type defined at the member level. 908 | * **Static nested class:** A static typed defined at the member level. 909 | * **Local class:** A class defined within a method body. 910 | * **Anonymous class:** A special case of a local class that does not have a name. 911 | 912 | 1. An inner class cannot declare static fields or methods, except for static final fields. It can also access members of the outer class including private methods. An inner classes will result in an Outer$Inner.class file being created by the compiler. As inner class can have the same variable names as outer classes, a call to **this** is prefixed with the class name. 913 | 914 | 1. A static nested class can be instantiated without an instance of the enclosing class. However, it cannot access the instance variables or methods in the outer class directly. It requires an explicit reference to the outer class variable. The nesting creates a namespace because the enclosing class name must be used to refer to it. 915 | 916 | 1. A local class is declared in a method, constructor, or initialiser. A local class does not have any access modifiers and cannot be declared static or declare static fields unless they are static final. When defined in an instance method, they have access to all fields and methods of the enclosing class. They can access local variables only if the variables are final or effectively final. An effectively final variable is one whose value does not change after it is set. 917 | 918 | 1. An anonymous class is a special form of a local class that does not have a name. It is declared and instantiated in one statement using the new keyword, a type name with parentheses, and a set of braces. Anonymous classes are required to extend an existing class or implement an existing interface. 919 | 920 | 1. The rules for modifiers in nested classes are summarised below: 921 |

922 | 923 |

924 | 925 | 1. The rules for members in nested classes are summarised below: 926 |

927 | 928 |

929 | 930 | 1. The rules for access in nested classes are summarised below: 931 |

932 | 933 |

934 | 935 | 1. When Java was first released, there were only two types of members an interface declaration could include: abstract methods and static final variables. Since Java 8 and 9, new method types have been added. The interface member types are summarised below: 936 |

937 | 938 |

939 | 940 | 1. A default method may be declared within an interface to provide a default implementation. The default method is assumed to be public and cannot be marked abstract, final, or static. It may also be overridden by a class that implements the interface. If a class inherits two or more default methods with the same method signature, then the class must override the method. 941 | 942 | 1. To call a default method from a class which overrides the implementation: 943 | ```java 944 | interfaceName.super.methodName(); 945 | ``` 946 | 947 | 1. A static interface method must include a method body and is assumed to be public. It cannot be marked abstract or final and cannot be referenced without using the interface name. Static interface methods are not inherited by a class implementing the interface. 948 | 949 | 1. A private interface method is used to avoid code duplication in instance methods. It must be marked private and include a method body. It may only be called by default and private (non-static) methods within the interface definition. 950 | 951 | 1. A private static method is used to avoid code duplication in static methods. It must be marked private and static and may only be called by other methods within the interface definition. 952 | 953 | 1. The rules for interface member access are summarised below: 954 |

955 | 956 |

957 | 958 | 1. A functional interface is an interface that contains a single abstract method. A lambda expression is like an anonymous class that defines one method. Any functional interface can be implemented as a lambda expression. 959 | 960 | 1. Note that if a functional method includes an abstract method with the same signature as a public method found in Object, then those methods do not count towards the single abstract method test. This includes: 961 | ```java 962 | String toString() 963 | boolean equals(Object) 964 | int hashCode() 965 | ``` 966 | 967 | 1. A lambda expression contains a parameter name, arrow, and body. The parameters list the variables, which must be compatible with the type and number of input parameters of the functional interface's single abstract method. The body must also be compatible with the return type of the functional interface's abstract method. Example lambda expressions are shown below: 968 | ```java 969 | a -> a.canHop() 970 | (Animal a) -> {return a.canHop();} 971 | ``` 972 | 973 | 1. A var parameter can be used in the parameter list, but then all parameters must use var. If the type is specified for one parameter, then it must be specified for all parameters. A semicolon is mandatory in the body if there is only a single expression. An expression-based lambda body is not terminated with a semicolon as it is an expression not a statement. However, a statement-based lambda with multiple lines requires that each statement be terminated with a semicolon. 974 | 975 | ### Annotations 976 | 977 | 1. Annotations are all about metadata. They let you assign metadata attributes to classes, methods, variables, and other Java types. An example is shown below: 978 | ```java 979 | public class Mammal{} 980 | public class Bird{} 981 | 982 | @ZooAnimal public class Lion extends Mammal{} 983 | @ZooAnimal public class Peacock extends Bird{} 984 | ``` 985 | 986 | 1. The above could have been achieved by extending a ZooAnimal class but that would require the class hierarchy to be changed. Annotations are like interfaces. While interface can be applied to classes, annotations can be applied to classes, methods, expressions, and even other annotations. Annotations also allow a set of values to be passed. An example is shown below: 987 | ```java 988 | public class Veterinarian{ 989 | @ZooAnimal(habitat="Infirmary") private Lion sickLion; 990 | @ZooAnimal(habitat="Safari") private Lion healthyLion; 991 | @ZooAnimal(habitat="Special Enclosure") private Lion blindLion; 992 | } 993 | ``` 994 | 995 | 1. The values are part of the type declaration and not of the variable. Without annotations, a new Lion type for each habitat value would need to be defined. This would become difficult in large applications. 996 | 997 | 1. To declare an annotation the @interface annotation is used. An example is shown below: 998 | ```java 999 | public @interface Exercise{} 1000 | } 1001 | ``` 1002 | 1003 | 1. To apply the annotation to other code we simply use the @Exercise annotation. A parenthesis is required if there are elements specified, and optional otherwise. Examples are shown below: 1004 | ```java 1005 | @Exercise() public class Cheetah{} 1006 | @Exercise public class Sloth{} 1007 | @Exercise 1008 | public class ZooEmployee{} 1009 | ``` 1010 | 1011 | 1. To declare an annotation with elements the elements need to be available in the annotation declaration. An example is shown below: 1012 | ```java 1013 | public @interface Exercise{} 1014 | int hoursPerDay(); 1015 | } 1016 | ``` 1017 | 1018 | 1. This changes how the annotation is used. An example is shown below: 1019 | ```java 1020 | @Exercise(hoursPerDay=3) public class Cheetah{} 1021 | ``` 1022 | 1023 | 1. When declaring an annotation, any element without a default value is considered required. A default value must be a non-null constant expression. An example including a default value is shown below: 1024 | ```java 1025 | public @interface Exercise{} 1026 | int hoursPerDay(); 1027 | int startHour() default 6; 1028 | } 1029 | ``` 1030 | 1031 | 1. The element type must be a primitive type, a String, a Class, an enum, another annotation, or an array of any of these types. Note that this excludes wrapper classes and arrays of arrays. 1032 | 1033 | 1. Like abstract interface methods, annotation elements are implicitly abstract and public. Declaring elements protected, private or final will result in a compilation failure. 1034 | 1035 | 1. Like interface variables, annotation variables are implicitly public, static, and final. A constant variable can be declared in an annotation but are not considered elements. 1036 | 1037 | 1. A shorthand format exists for using annotations. This can occur if the annotation declaration contains an element named *value()*, the usage of the annotation provides no values for other elements, and the declaration does not contain any elements that are required. An example is shown below: 1038 | ```java 1039 | public @interface Injured{ 1040 | String veterinarian() default "unassigned"; 1041 | String value() default "foot"; 1042 | int age() default 1; 1043 | } 1044 | 1045 | @Injured("Legs") public void fallDown() {} 1046 | ``` 1047 | 1048 | 1. A shorthand format also exists for providing an array that contains a single element. An example is shown below: 1049 | ```java 1050 | public @interface Music{ 1051 | String[] genres(); 1052 | } 1053 | 1054 | public class Giraffe{ 1055 | @Music(genres={"Rock and roll"}) String mostDisliked; 1056 | @Music(genres="Classical") String favorite; 1057 | } 1058 | ``` 1059 | 1060 | 1. An annotation can be applied to an annotation to specify what types the annotation can be applied to. This is done by specifying the ElementType using @Target. An example is shown below: 1061 | ```java 1062 | @Target({ElementType.METHOD,ElementType.CONSTRUCTOR}) 1063 | public @interface ZooAttraction{} 1064 | ``` 1065 | 1066 | 1. The options for ElementType are shown below: 1067 |

1068 | 1069 |

1070 | 1071 | 1. The TYPE_USE value covers nearly all other values. One exception is that it can only be used on a method that returns a value, a void method would still need METHOD defined in the annotation. TYPE_USE is typically used for cast operations, object creation with new and inside type declarations. 1072 | 1073 | 1. The compiler discards certain types of information when converting source code into a .class file. Annotations may be discarded by the compiler at runtime. The @Retention annotation can be used to specify. The options for @Retention are shown below: 1074 |

1075 | 1076 |

1077 | 1078 | 1. Javadoc is a built-in standard within Java that generates documentation for a class or API. If the @Documented annotation is present, then the generated Javadoc will include annotation information defined on Java types. An example is shown below: 1079 | ```java 1080 | // Hunter.java 1081 | import java.lang.annotation.Documented; 1082 | @Documented public @interface Hunter{} 1083 | 1084 | // Lion.java 1085 | @Hunter public class Lion{} 1086 | ``` 1087 | 1088 | 1. In the above example @Hunter would be published with the Lion Javadoc information because it is marked with @Documented. 1089 | 1090 | 1. The @Inherited annotation is used to allow subclasses to inherit the annotation information found in the parent class. 1091 | ```java 1092 | // Vertebrate.java 1093 | import java.lang.annotation.Inherited; 1094 | @Inherited public @interface Vertebrate{} 1095 | 1096 | // Mammal.java 1097 | @Vertebrate public class Mammal{} 1098 | 1099 | // Dolphin.java 1100 | public class Dolphin extends Mammal{} 1101 | ``` 1102 | 1103 | 1. In the above example the @Vertebrate annotation will be applied to both Mammal and Dolphin. 1104 | 1105 | 1. The @Repeatable annotation can be used to apply an annotation more than once. To declare a @Repeatable annotation, a containing annotation with type value must be defined. 1106 | ```java 1107 | // Containing annotation type 1108 | public @interface Risks{ 1109 | Risk[] value(); 1110 | } 1111 | 1112 | // Containing annotation class 1113 | @Repeatable(Risks.class) 1114 | public @interface Risk{ 1115 | String danger(); 1116 | int level() default 1; 1117 | } 1118 | 1119 | public class Zoo{ 1120 | public static class Monkey{} 1121 | @Risk(danger="Silly") 1122 | @Risk(danger="Aggressive",level=5) 1123 | @Risk(danger="Violent",level=10) 1124 | private Monkey monkey; 1125 | } 1126 | ``` 1127 | 1128 | 1. Commonly used built-in annotations are shown below: 1129 |

1130 | 1131 |

1132 | 1133 | 1. Example applications of commonly used annotations are shown below: 1134 |

1135 | 1136 |

1137 | 1138 | ### Generics and Collections 1139 | 1140 | 1. Method references can make code easier to read. An example is shown below: 1141 | ```java 1142 | @FunctionalInterface 1143 | public interface LearnToSpeak{ 1144 | void speak(String sound); 1145 | } 1146 | 1147 | public class DuckHelper{ 1148 | public static void teacher(String name, LearnToSpeak trainer){ 1149 | trainer.speak(name); 1150 | } 1151 | } 1152 | 1153 | // long version 1154 | public class Duckling{ 1155 | public static void makeSound(String sound){ 1156 | LearnToSpeak learner = s -> System.out.println(s); 1157 | DuckHelper.teacher(sound, learner); 1158 | } 1159 | } 1160 | 1161 | // short version 1162 | public class Ducking{ 1163 | public static void makeSound(String sound){ 1164 | LearnToSpeak learner = System.out::println; 1165 | DuckHelper.teacher(sound, learner); 1166 | } 1167 | } 1168 | ``` 1169 | 1170 | 1. There are four formats for method references. Examples are shown below: 1171 | ```java 1172 | // Static Methods 1173 | Consumer> methodRef = Collections::sort; 1174 | Consumer> lambda = x -> Collections.sort(x); 1175 | 1176 | // Instance Methods on a Particular Object 1177 | var str = "abc"; 1178 | Predicate methodRef = str::startsWith; 1179 | Predicate lambda = s -> str.startsWith(s); 1180 | 1181 | var random = new Random(); 1182 | Supplier methodRef = random::nextInt; 1183 | Supplier lambda = () -> random.nextInt(); 1184 | 1185 | // Instance Methods on a Parameter 1186 | Predicate methodRef = String::isEmpty; 1187 | Predicate lambda = s -> s.isEmpty(); 1188 | 1189 | // Constructors 1190 | Supplier> methodRef = ArrayList::new; 1191 | Supplier> lambda = () -> new ArrayList(); 1192 | ``` 1193 | 1194 | 1. Each Java primitive has a corresponding wrapper class. A null value can be assigned to a wrapper class as a null value can be assigned to any reference variable. Attempting to unbox a wrapper class with a null value will cause a NullPointerException. 1195 | 1196 | 1. The Diamond Operator is a shorthand notation that allows you to omit the generic type from the right side of a statement when the type can be inferred. An example is shown below: 1197 | ```java 1198 | List list = new ArrayList(); 1199 | List list = new ArrayList<>(); 1200 | ``` 1201 | 1202 | 1. A collection is a group of objects contained in a single object. The Java Collection Framework is a set of classes in java.util for storing collections. The common 1203 | * **List:** Ordered collection of elements that can contain duplicates. Accessed by an int index. 1204 | * **Set:** A collection that does not allow duplicate entries. 1205 | * **Queue:** A collection that orders its elements in a specific order. A typical queue is FIFO. 1206 | * **Map:** A collection that maps keys to values, with no duplicate keys allowed. The elements are key/value pairs. 1207 | 1208 | 1. The Collection interface and its sub interfaces as well as some implementing classes are shown below. Interfaces are shown in rectangles, with classes in rounded boxes: 1209 |

1210 | 1211 |

1212 | 1213 | 1. The Collection interface contains useful convenience methods. These are shown below: 1214 | ```java 1215 | boolean add(E element); 1216 | boolean remove(Object object); 1217 | boolean isEmpty(); 1218 | int size(); 1219 | void clear(); 1220 | boolean contains(Object object); 1221 | boolean removeIf(Predicate filter); 1222 | void forEach(Consumer action); 1223 | ``` 1224 | 1225 | 1. The *Collections.sort()* method is commonly used when working with collections. To sort objects that you create yourself, Java provides an interface called Comparable. This is shown below: 1226 | ```java 1227 | public interface Comparable{ 1228 | int compareTo(T o); 1229 | } 1230 | ``` 1231 | 1232 | 1. The *compareTo()* method returns: 1233 | * The number 0 when the current object is equivalent to the argument to *compareTo()*. 1234 | * A negative number when the current object is smaller than the argument to *compareTo()*. 1235 | * A positive number when the current object is larger than the argument to *compareTo()*. 1236 | 1237 | 1. An example is shown below: 1238 | ```java 1239 | public class MissingDuck implements Comparable{ 1240 | private String name; 1241 | public int compareTo(MissingDuck quack){ 1242 | if(quack == null) 1243 | throw new IllegalArgumentException("Poorly formed duck!"); 1244 | if(this.name == null && quack.name == null) 1245 | return 0; 1246 | else if(this.name == null) return -1; 1247 | else if(quack.name == null) return 1; 1248 | else return name.compareTo(quack.name); 1249 | } 1250 | } 1251 | ``` 1252 | 1253 | 1. Note that only one *compareTo()* method can be implemented for a class. If we want to sort by something else, a Comparator can be used. Comparator is a functional interface. An example is shown below: 1254 | ```java 1255 | public static void main(String[] args){ 1256 | Comparator byWeight = new Comparator(){ 1257 | public int compare(Duck d1, Duck d2){ 1258 | return d1.getWeight()-d2.getWeight(); 1259 | } 1260 | } 1261 | }; 1262 | 1263 | // alternate implementation with lambda 1264 | Comparator byWeight = (d1,d2) -> d1.getWeight()-d2.getWeight(); 1265 | 1266 | // alternate implementation with method reference 1267 | Comparator byWeight = Comparator.comparing(Duck::getWeight); 1268 | 1269 | Collection.sort(ducks, byWeight); 1270 | ``` 1271 | 1272 | 1. A summary of the differences between Comparable and Comparator are shown below: 1273 |

1274 | 1275 |

1276 | 1277 | 1. When building a comparator there are several helper methods that can be used. These are shown below: 1278 | ```java 1279 | reversed(); 1280 | thenComparing(function); 1281 | thenComparingDouble(function); 1282 | thenComparingInt(function); 1283 | thenComparing(function); 1284 | ``` 1285 | 1286 | 1. Generics allow you to write and use parametrised types. This allows the compiler to detect issues rather than a ClassCastException exception being thrown. 1287 | 1288 | 1. Generics can be introduced into classes using angle brackets. An example is shown below: 1289 | ```java 1290 | public class Crate{ 1291 | private T contents; 1292 | public T emptyCrate(){ 1293 | return contents; 1294 | } 1295 | public void packCrate(T contents){ 1296 | this.contents = contents; 1297 | } 1298 | } 1299 | ``` 1300 | 1301 | 1. A type parameter can have any name. By convention, the below letters are used: 1302 | * E for an element 1303 | * K for a map key 1304 | * V for a map value 1305 | * N for a number 1306 | * T for a generic data type 1307 | * S, U, V etc. for multiple generic types 1308 | 1309 | 1. Generics can also be introduced into methods using angle brackets. An example is shown below: 1310 | ```java 1311 | public class Handler{ 1312 | public static Crate ship(T t){ 1313 | System.out.println("Shipping " + t); 1314 | return new Crate(); 1315 | } 1316 | } 1317 | ``` 1318 | 1319 | 1. A bounded parameter type is a generic type that specifies a bound for the generic. A wildcard generic type is an unknown generic type represented with a question mark. 1320 | 1321 | 1. An unbounded wildcard is used when any type is okay. An example is shown below: 1322 | ```java 1323 | public static void printList(List list){ 1324 | for (Object x:list) 1325 | System.out.println(x); 1326 | } 1327 | ``` 1328 | 1329 | 1. Note that a generic type cannot use a subclass. An example that will not compile is shown below: 1330 | ```java 1331 | ArrayList list = new ArrayList(); 1332 | ``` 1333 | 1334 | 1. An upper-bounded wildcard can be used to say that any class that extends a class or that class itself can be the parameter type. An example is shown below: 1335 | ```java 1336 | public static long total(List list){ 1337 | long count = 0; 1338 | for(Number number:list) 1339 | count += number.longValue(); 1340 | return count; 1341 | } 1342 | ``` 1343 | 1344 | 1. Note that due to type erasure the above code is converted to something like: 1345 | ```java 1346 | public static long total(List list){ 1347 | long count = 0; 1348 | for(Object obj:list) 1349 | Number number = (Number) obj; 1350 | count += number.longValue(); 1351 | } 1352 | return count; 1353 | } 1354 | ``` 1355 | 1356 | 1. When upper bounds or unbounded wildcards are used in such a way the list becomes immutable and cannot be modified. 1357 | 1358 | 1. A lower-bounded wildcard can be used to say that any instance of a class or an instance of a superclass can be the parameter type. An example is shown below: 1359 | ```java 1360 | public static void addSound(List list){ 1361 | list.add("quack"); 1362 | } 1363 | 1364 | List strings = new ArrayList(); 1365 | strings.add("tweet"); 1366 | 1367 | List objects = new ArrayList(strings); 1368 | addSound(strings); 1369 | addSound(objects); 1370 | ``` 1371 | 1372 | 1. A useful mnemonic is PECS: Producer Extends, Consumer Super. If you need a List to produce T values (you want to read Ts from the list), you need to declare it using extends. If you need a list to consume T values (you want to write Ts into the list), you need to declare it using super. If you need to both read and write to a list, you need to declare it exactly with no wildcards. 1373 | 1374 | 1. In the below example, if you want to write elements into the list, you cannot add a Number, Integer or a Double because each one is not compatible with all types. A Number can be read because any of the lists will contain a Number or a subclass of Number. When using extends like this you can only read, and not write. 1375 | ```java 1376 | List foo = new ArrayList<>(); 1377 | 1378 | // The foo list could be one of these 1379 | List foo = new ArrayList(); 1380 | List foo = new ArrayList(); 1381 | List foo = new ArrayList foo = new ArrayList<>(); 1387 | 1388 | // The foo list could be one of these 1389 | List foo = new ArrayList(); 1390 | List foo = new ArrayList(); 1391 | List foo = new ArrayList(); 1392 | ``` 1393 | 1394 | ### Functional Programming 1395 | 1396 | 1. The functional interfaces shown below are provided as built-in functional interfaces in the java.util.function package: 1397 |

1398 | 1399 |

1400 | 1401 | 1. A Supplier is used when you want to generate or supply values without taking any input. A supplier is often used to construct new objects. The definition is shown below: 1402 | ```java 1403 | @FunctionalInterface 1404 | public interface Supplier{ 1405 | T get(); 1406 | } 1407 | ``` 1408 | 1409 | 1. An example for Supplier is shown below: 1410 | ```java 1411 | Supplier s1 = LocalDate::now; 1412 | Supplier s2 = () -> LocalDate.now(); 1413 | 1414 | LocalDate d1 = s1.get(); 1415 | LocalDate d2 = s2.get(); 1416 | ``` 1417 | 1418 | 1. A Consumer is used when you want to do something with a parameter but not return anything. A BiConsumer is the same but takes two parameters. The definitions are shown below: 1419 | ```java 1420 | @FunctionalInterface 1421 | public interface Consumer{ 1422 | void accept(T t); 1423 | // default method omitted 1424 | } 1425 | 1426 | @FunctionalInterface 1427 | public interface BiConsumer{ 1428 | void accept(T t, U u); 1429 | // default method omitted 1430 | } 1431 | ``` 1432 | 1433 | 1. An example for Consumer is shown below: 1434 | ```java 1435 | Consumer c1 = System.out::println; 1436 | Consumer c2 = x-> System.out.println(x); 1437 | 1438 | c1.accept("Hi"); 1439 | c2.accept("Hi"); 1440 | ``` 1441 | 1442 | 1. An example for BiConsumer is shown below: 1443 | ```java 1444 | var map = new HashMap(); 1445 | BiConsumer b1 = map::put; 1446 | BiConsumer b2 = (k, v) -> map.put(k, v); 1447 | 1448 | b1.accept("chicken", 7); 1449 | b2.accept("chick", 1); 1450 | ``` 1451 | 1452 | 1. A Predicate is often used when filtering or matching. A BiPredicate is the same but takes two parameters. The definitions are shown below: 1453 | ```java 1454 | @FunctionalInterface 1455 | public interface Predicate{ 1456 | boolean test(T t); 1457 | // default and static methods omitted 1458 | } 1459 | 1460 | @FunctionalInterface 1461 | public interface BiPredicate{ 1462 | boolean test(T t, U t); 1463 | // default methods omitted 1464 | } 1465 | ``` 1466 | 1467 | 1. An example for Predicate is shown below: 1468 | ```java 1469 | Predicate p1 = String::isEmpty; 1470 | Predicate p2 = x -> x.isEmpty(); 1471 | System.out.println(p1,test(""); // true 1472 | System.out.println(p2,test(""); // true 1473 | ``` 1474 | 1475 | 1. An example for BiPredicate is shown below: 1476 | ```java 1477 | BiPredicate b1 = String::startsWith; 1478 | BiPredicate b2 = (string, suffix) -> string.startsWith(prefix); 1479 | 1480 | System.out.println(b1.test("chicken", "chick")); // true 1481 | System.out.println(b2.test("chicken", "chick")); // true 1482 | ``` 1483 | 1484 | 1. A Function turns one parameter into a value of a potentially different type and returns it. A BiFunction turns two parameters into a value and returns it. The definitions are shown below: 1485 | ```java 1486 | @FunctionalInterface 1487 | public interface Function{ 1488 | R apply(T t); 1489 | // default and static methods omitted 1490 | } 1491 | 1492 | @FunctionalInterface 1493 | public interface BiFunction{ 1494 | R apply(T t, U u); 1495 | // default method omitted 1496 | } 1497 | ``` 1498 | 1499 | 1. An example for Function is shown below: 1500 | ```java 1501 | Function f1 = String::length; 1502 | Function f2 = x -> x.length(); 1503 | 1504 | System.out.println(f1.apply("cat")); // 3 1505 | System.out.println(f2.apply("cat")); // 3 1506 | ``` 1507 | 1508 | 1. An example for BiFunction is shown below: 1509 | ```java 1510 | BiFunction b1 = String::concat; 1511 | BiFunction b2 = (string, toAdd) -> string.concat(toAdd); 1512 | 1513 | System.out.println(b1.apply("cat ", "dog")); // cat dog 1514 | System.out.println(b2.apply("cat ", "dog")); // cat dog 1515 | ``` 1516 | 1517 | 1. A UnaryOperator is a special case of a Function where all the type parameters are the same type. A BinaryOperator merges two values into one of the same type. The definitions are shown below: 1518 | ```java 1519 | @FunctionalInterface 1520 | public interface UnaryOperator extends Function{} 1521 | 1522 | @FunctionalInterface 1523 | public interface BinaryOperator extends BiFunction{ 1524 | // omitted static methods 1525 | } 1526 | ``` 1527 | 1528 | 1. An example for UnaryOperator is shown below: 1529 | ```java 1530 | UnaryOperator u1 = String::toUpperCase; 1531 | UnaryOperator u2 = x -> x.toUpperCase(); 1532 | 1533 | System.out.println(u1.apply("hi")); // HI 1534 | System.out.println(u2.apply("hi")); // HI 1535 | ``` 1536 | 1537 | 1. An example for BinaryOperator is shown below: 1538 | ```java 1539 | BinaryOperator b1 = String::concat; 1540 | BinaryOperator b2 = (string, toAdd) -> string.concat(toAdd); 1541 | 1542 | System.out.println(u1.apply("hi ", "there")); // hi there 1543 | System.out.println(u2.apply("hi ", "there")); // hi there 1544 | ``` 1545 | 1546 | 1. The built-in functional interfaces contain various helpful default methods. An example for the Predicate helper methods is shown below with the two statements being equivalent: 1547 | ```java 1548 | Predicate combination = s -> s.contains("cat") && ! s.contains("brown"); 1549 | Predicate combination = cat.and(brown.negate()); 1550 | ``` 1551 | 1552 | 1. An example for the Consumer helper methods is shown below: 1553 | ```java 1554 | Consumer c1 = x -> System.out.print("1:" + x); 1555 | Consumer c2 = x -> System.out.print(",2:" + x); 1556 | 1557 | Consumer combined = c1.andThen(c2); 1558 | combined.accept("hi"); // 1:hi,2:hi 1559 | ``` 1560 | 1561 | 1. An example for the Function helper methods is shown below: 1562 | ```java 1563 | Function before = x -> x + 1; 1564 | Function after = x -> x * 2; 1565 | 1566 | Function combined = after.compose(before); 1567 | System.out.println(combined.apply(3)); // 8 1568 | ``` 1569 | 1570 | 1. An Optional type is used to express a result that could be "not applicable" without using null references. The Optional instance methods are summarised below: 1571 |

1572 | 1573 |

1574 | 1575 | 5. An example using the *isPresent()* and *get()* methods is shown below: 1576 | ```java 1577 | public static Optional average(int... scores){ 1578 | if(scores.length == 0) return Optional.empty(); 1579 | int sum = 0; 1580 | for(int score:scores) sum += score; 1581 | return Optional.of((double) sum/scores.length); 1582 | } 1583 | 1584 | Optional opt = average(90, 100); 1585 | if(opt.isPresent()) 1586 | System.out.println(opt.get()); // 95.0 1587 | ``` 1588 | 1589 | 1. The *ofNullable()* method can be used to return an empty Optional if the value is null: 1590 | ```java 1591 | Optional o (value == null) ? Optional.empty() : Optional.of(value); 1592 | Optional o = Optional.ofNullable(value); 1593 | ``` 1594 | 1595 | 1. The *ifPresent()* method can be used to specify a Consumer to be run when there is a value inside of an Optional: 1596 | ```java 1597 | Optional opt = average(90, 100); 1598 | opt.ifPresent(System.out::println); 1599 | ``` 1600 | 1601 | 1. There are multiple methods that can be used to handle an empty Optional. Note that if the value does exist then the value will just be printed. Examples are shown below: 1602 | ```java 1603 | Optional opt = average(); 1604 | System.out.println(opt.orElse(Double.NaN)); // NaN 1605 | System.out.println(opt.orElseGet(() -> Math.random())); // random number 1606 | System.out.println(opt.orElseThrow()); // NoSuchElementException 1607 | System.out.println(opt.orElseThrow( 1608 | () -> new IllegalStateException())); // Can throw something else 1609 | ``` 1610 | 1611 | 1. A **stream** in Java is a sequence of data. A stream pipeline consists of the operations that run on a stream to produce a result. A stream can be finite or infinite. 1612 | 1613 | 1. A stream pipeline consists of: 1614 | * **Source:** Where the stream comes from. 1615 | * **Intermediate operations:** Transforms the stream into another one. There can be many intermediate operations. They do not run until the terminal operation runs. 1616 | * **Terminal operations:** Produces a result. A stream can only be used once, the stream is no longer valid after a terminal operation completes. 1617 | 1618 | 1. The Stream interface is defined in the java.util.stream package. 1619 | 1620 | 1. Examples for creating a finite stream are shown below: 1621 | ```java 1622 | Stream empty = Stream.empty(); // 0 1623 | Stream singleElement = Stream.of(1); // 1 1624 | Stream fromArray = Stream.of(1,2,3); // 3 1625 | 1626 | var list = List.of("a","b","c"); 1627 | Stream fromList = list.stream(); 1628 | Stream fromListParallel = list.parallelStream(); 1629 | ``` 1630 | 1631 | 1. Examples for creating an infinite stream are shown below: 1632 | ```java 1633 | Stream randoms = Stream.generate(Math::random); 1634 | Stream oddNumbers = Stream.iterate(1, n -> n + 2); 1635 | Stream oddNumbersUnder100 = Stream.iterate{ 1636 | 1, // seed 1637 | n -> n < 100; // Predicate to specify when done 1638 | n -> n + 2; // UnaryOperator to get next value 1639 | } 1640 | ``` 1641 | 1642 | 1. A terminal operation can be performed without an intermediate operation. Reductions are a special type of terminal operation where the contents of the stream are combined into a single primitive or Object. Terminal stream operation method signatures and examples are shown below: 1643 | ```java 1644 | // long count() 1645 | Stream s = Stream.of("monkey", "gorilla", "bonobo"); 1646 | System.out.println(s.count()); // 3 1647 | 1648 | // Optional min(Comparator comparator) 1649 | // Optional max(Comparator comparator) 1650 | Stream s = Stream.of("monkey", "ape", "bonobo"); 1651 | Optional min = s.min((s1, s2) -> s1.length()-s2.length()); 1652 | min.ifPresent(System.out.println); // ape 1653 | 1654 | Optional minEmpty = Stream.empty().min((s1, s2) -> 0); 1655 | System.out.println(minEmpty.isPresent()); // false 1656 | 1657 | // Optional finndAny() 1658 | // Optional findFirst() 1659 | Stream s = Stream.of("monkey", "gorilla", "bonobo"); 1660 | Stream infinite = Stream.generate(() -> "chimp"); 1661 | s.findAny().ifPresent(System.out:prinln); // monkey (usually) 1662 | infinite.findAny().ifPresent(System.out::println); // chimp 1663 | 1664 | // boolean anyMatch(Predicate predicate) 1665 | // boolean allMatch(Predicate predicate) 1666 | // boolean noneMatch(Predicate predicate) 1667 | 1668 | var list = List.of("monkey", "2", "chimp"); 1669 | Stream infinite = Stream.generate(() -> "chimp"); 1670 | Predicate pred = x -> Character.isLetter(x.charAt(0)); 1671 | 1672 | System.out.println(list.stream().anyMatch(pred)); // true 1673 | System.out.println(list.stream().allMatch(pred)); // false 1674 | System.out.println(list.stream().noneMatch(pred)); // false 1675 | System.out.println(infinite.anyMatch(pred)); // true 1676 | 1677 | // void forEach(Consumer action) 1678 | 1679 | Stream s = Stream.of("Monkey", "Gorilla", "Bonobo"); 1680 | s.forEach(System.out::print); // MonkeyGorillaBonobo 1681 | 1682 | // T reduce(T identity, BinaryOperator accumulator) 1683 | // Optional reduce(BinaryOperator accumulator) 1684 | // reduce(U identity, 1685 | // BiFunction accumulator, 1686 | // BinaryOperator combiner) 1687 | 1688 | Stream stream = Stream.of{"w", "o", "l", "f"}; 1689 | String word = stream.reduce("", (s,c) -> s + c); 1690 | System.out.println(word); // wolf; 1691 | 1692 | // R collect(Supplier supplier, 1693 | // BiConsumer( accumulator, 1694 | // BiConsumer combiner) 1695 | // R collect(Collector collector) 1696 | 1697 | Stream stream = Stream.of("w", "o", "l", "f"); 1698 | TreeSet set = stream.collect( 1699 | Treeset::new, 1700 | Treeset::add, 1701 | Treeset::addAll); 1702 | System.out.println(set); // [f, l, o, w] 1703 | ``` 1704 | 1705 | 1. An intermediate operation produces a stream as its result. Intermediate operation method signatures and examples are shown below: 1706 | ```java 1707 | // Stream filter(Predicate predicate) 1708 | 1709 | Stream s = Stream.of("monkey", "gorilla", "bonobo"); 1710 | s.filter(x -> x.startsWith("m")) 1711 | .forEach(System.out::print); // monkey 1712 | 1713 | // Stream distinct() 1714 | 1715 | Stream s = Stream.of("duck", "duck", "duck", "goose"); 1716 | s.distinct() 1717 | .forEach(System.out::print); // duckgoose 1718 | 1719 | // Stream limit(long maxSize) 1720 | // Stream skip(long n) 1721 | 1722 | Stream s = Stream.iterate(1, n -> n + 1); 1723 | s.skip(5) 1724 | .limit(2) 1725 | .forEach(System.out::print); // 67 1726 | 1727 | // Stream map(Function, ? extends R> mapper) 1728 | Stream s = Stream.of("monkey", "gorilla", "bonobo"); 1729 | s.map(String::length) 1730 | .forEach(System.out::print); // 676 1731 | 1732 | // Stream flatMap( 1733 | Function> mapper) 1734 | 1735 | List zero = List.of(); 1736 | var one = List.of("Bonobo"); 1737 | var two = List.of("Mama Gorilla", "Baby Gorilla"); 1738 | Stream> animals = Stream.of(zero, one, two); 1739 | 1740 | animals.flatMap(m -> m.stream()) 1741 | .forEach(System.out::println); 1742 | 1743 | // Bonobo 1744 | // Mama Gorilla 1745 | // Baby Gorilla 1746 | 1747 | // Stream sorted() 1748 | // Stream sorted(Comparator comparator> 1749 | 1750 | Stream s = Stream.of("brown-", "bear-"): 1751 | s.sorted() 1752 | .forEach(System.out:print); // bear-brown- 1753 | 1754 | Stream s = Stream.of("brown bear-", "grizzly-"); 1755 | s.sorted(Comparator.reverseOrder()) 1756 | .forEach(System.out::print); // grizzly-brown bear- 1757 | 1758 | // Stream peek(Consumer action) 1759 | 1760 | var stream = Stream.of("black bear", "brown bear", "grizzly"); 1761 | long count = stream.filter(s -> s.startsWith("g")) 1762 | .peek(System.out::println).count() // grizzly 1763 | System.out.println(count); // 1 1764 | ``` 1765 | 1766 | 1. Intermediate and a terminal operation can be chained in a pipeline. An example is shown below: 1767 | ```java 1768 | var list = List.of("Toby", "Anna", "Leroy", "Alex"); 1769 | list.stream() 1770 | .filter(n -> n.length() == 4) 1771 | .sorted() 1772 | .limit(2) 1773 | .forEach(System.out::println); // AnnaAlex 1774 | ``` 1775 | 1776 | 1. Primitive Streams allow you to work with the int, double and long primitives. They include specialised methods for working with numeric data. The primitive streams are intStream, longStream and doubleStream. 1777 | 1778 | 1. Primitive streams can be created from other streams. An example is shown below: 1779 | ```java 1780 | Stream objStream = Stream.of("penguin", "fish"); 1781 | IntStream intStream = objStream.mapToInt(s -> s.length()); 1782 | ``` 1783 | 1784 | 1. To create a primitive stream from another stream the below methods are used: 1785 |

1786 | 1787 |

1788 | 1789 | 1. The function parameters used when mapping streams are shown below: 1790 |

1791 | 1792 |

1793 | 1794 | 1. Methods can return OptionalDouble, OptionalInt and OptionalLong types when dealing with streams of primitives. A summary of the Optional types for primitives is shown below: 1795 |

1796 | 1797 |

1798 | 1799 | 1. To use multiple terminal operations to produce a result from a stream summary statistic can be used. Summary statistics includes the *getMin()*, *getMax()*, *getAverage()*, *getSum()* and *getCount()* methods. An example is shown below: 1800 | ```java 1801 | private static int range(intStream ints){ 1802 | IntSummaryStatistics stats = ints.summaryStatistics(); 1803 | if(stats.getCount() == 0) throw new RuntimeException(); 1804 | return stats.getMax()-stats.getMin(); 1805 | } 1806 | ``` 1807 | 1808 | 1. There are special functional interfaces for primitives. The BooleanSupplier functional interface is shown below: 1809 | ```java 1810 | // boolean getAsBoolean() 1811 | 1812 | BooleanSupplier b1 = () -> true; 1813 | BooleanSupplier b2 = () -> Math.random() > 0.5; 1814 | System.out.println(b1.getAsBoolean()); // true 1815 | System.out.println(b2.getAsBoolean()); // true or false 1816 | ``` 1817 | 1818 | 1. Common functional interfaces for other primitives are shown below: 1819 |

1820 | 1821 |

1822 | 1823 | 1. Common functional interfaces for other primitives are shown below: 1824 |

1825 | 1826 |

1827 | 1828 | 1. Predefined collectors are available via static methods on the Collectors interface. These are shown below: 1829 |

1830 | 1831 |

1832 | 1833 | 1. Examples for using collectors are shown below: 1834 | ```java 1835 | var ohMy = Stream.of("lions", "tigers", "bears"); 1836 | String result = ohMy.collect(Collectors.joining(", ")); 1837 | System.out.println(result); // lions, tigers, bears 1838 | 1839 | var ohMy = Stream.of("lions", "tigers", "bears"); 1840 | String result = ohMy.collect(Collectors.averagingInt(String::length)); 1841 | System.out.println(result); // 5.333333333333 1842 | 1843 | var ohMy = Stream.of("lions", "tigers", "bears"); 1844 | TreeSet result = ohMy 1845 | .filter(s -> s.startsWith("t)) 1846 | .collect(Collectors.toCollection(TreeSet::new)); 1847 | System.out.println(result); // [tigers] 1848 | 1849 | var ohMy = Stream.of("lions", "tigers", "bears"); 1850 | Map map = ohMy.collect( 1851 | Collectors.toMap(s -> s, String::length)); 1852 | Systemout.println(map); // {lions=5, bears=5, tigers=6} 1853 | 1854 | var ohMy = Stream.of("lions", "tigers", "bears"); 1855 | Map map = ohMy.collect(Collectors.toMap( 1856 | String::length, 1857 | k -> k, 1858 | (s1, s2) -> s1 + "," + s2)); 1859 | System.out.println(map); // {5=lions,bears, 6=tigers} 1860 | System.out.println(map.getClass()); // class java.util.HashMap 1861 | 1862 | var ohMy = Stream.of("lions", "tigers", "bears"); 1863 | Map> map = ohMy.collect( 1864 | Collectors.groupingBy(String::length)); 1865 | System.out.println(map); // {5=[lions, bears], 6=[tigers]} 1866 | 1867 | var ohMy = Stream.of("lions", "tigers", "bears"); 1868 | Map map = ohMy.collect( 1869 | Collectors.partitioningBy(s -> s.length() <= 5)); 1870 | System.out.println(map); // {false=[tigers], true=[lions, bears]} 1871 | ``` 1872 | 1873 | ### Exceptions, Assertions, and Localization 1874 | 1875 | 1. A custom exception class can be created by extending Exception (for a checked exception), or RuntimeException (for an unchecked exception). 1876 | 1877 | 1. A try-with-resources statement ensures that any resources declared in the try block are automatically closed at the conclusion of the try block. A resource is typically a file or a database that requires a stream or connection to read or write data. 1878 | 1879 | 1. To be used in a try-with-resources statement the resource is required to implement the AutoClosable interface. Inheriting AutoClosable requires implementing a *close()* method. If multiple resources are included in a try-with-resources statement they are closed in the reverse order in which they are declared. 1880 | 1881 | 1. It is possible to use resources declared prior to a try-with-resources statement, provided they are marked final or are effectively final. The syntax is to use the resource name in place of the resource declaration, separated by a semicolon. 1882 | 1883 | 1. An assertion is a Boolean expression that you place where you expect something to be true. An assert statement contains this statement along with an optional message. An assertion allows for detecting defects in the code. You can turn on assertions for testing and debugging while leaving them off when your program is in production. Unit tests are most frequently used to verify behaviour, whereas assertions are commonly used to verify the internal state of a program. 1884 | 1885 | 1. Assertions should never alter outcomes. Assertions should be turned off in a production environment. 1886 | 1887 | 1. The syntax for an assertion is shown below: 1888 | ```java 1889 | assert test_value; 1890 | assert test_value: message; 1891 | ``` 1892 | 1893 | 1. An assertion evaluating to false will result in an AssertionError being thrown at runtime if assertions are enabled. To enable assertions a flag can be passed as per the below: 1894 | ```java 1895 | java -enableassertions Rectangle 1896 | java -ea Rectangle 1897 | ``` 1898 | 1899 | 1. Assertions can be enabled or disabled for specific classes or packages. 1900 | ```java 1901 | java -ea:com.demos... my.programs.Main // enable for classes in the com.demos package and any subpackages 1902 | java -ea:com.demos... -da:com.demos.TestColors my.programs.Main // enable for com.demos but disables in TestColors class 1903 | ``` 1904 | 1905 | 1. Java includes numerous classes for dates and times: 1906 |

1907 | 1908 |

1909 | 1910 | 1. Each of these types contains a *now()* method to get the current date or time and an *of()* method to instantiate an object. Various get methods are also provided. 1911 | 1912 | 1. The *format()* method can take a DateTimeFormatter to display standard or custom formats. Note that enclosing values in single quotes escapes the values. Examples are shown below: 1913 | ```java 1914 | LocalTime time = LocalTime.of(11, 12, 34); 1915 | LocalDate date = LocalDate.of(2020, Month.OCTOBER, 20); 1916 | System.out.println(time.format(DateTimeFormatter.ISO_LOCAL_TIME)); // standard format example 1917 | var f = DateTimeFormatter.ofPattern("MMMM dd, yyyy 'at' hh:mm"); 1918 | System.out.println(dt.format(f)); // October 20, 2020 at 11:12 1919 | ``` 1920 | 1921 | 1. Supported symbols for each date and time class are shown below: 1922 |

1923 | 1924 |

1925 | 1926 | 1. Internationalisation is the process of designing a program so that it can be adapted. Localisation means supporting multiple locales or geographic regions. Examples for working with locales are shown below: 1927 | ```java 1928 | Locale locale = Locate.getDefault(); 1929 | System.out.println(locale); // en_US 1930 | System.out.println(Locate.GERMAN); // de 1931 | System.out.println(Locale.GERMANY); // de_DE 1932 | ``` 1933 | 1934 | 1. Formatting or parsing currency and number values can change depending on the locale. Methods to get a number format based on a locale are shown below: 1935 |

1936 | 1937 |

1938 | 1939 | 1. An example of their usage is shown below: 1940 | ```java 1941 | int attendeesPerYear = 3_200_000; 1942 | int attendeesPerMonth = attendeesPerYear / 12; 1943 | var us = NumberFormat.getInstance(Locale.US); 1944 | System.out.println(us.format(attendeesPerMonth)); // 266,666 1945 | var gr = NumberFormat.getInstance(Locale.GERMANY); 1946 | System.out.println(gr.format(attendeesPerMonth)); /// 266.666 1947 | ``` 1948 | 1949 | 1. The DecimalFormat class can be used to express currency. A *#* is used to omit the position if no digit exists, and a 0 is used to place a 0 in the position if no digit exists. Examples are shown below: 1950 | ```java 1951 | double d = 1234567.467; 1952 | NumberFormat f1 = new DecimalFormat("###,###,###.0"); 1953 | System.out.println(f1.format(d)); // 1,234,567.5 1954 | ``` 1955 | 1956 | 1. Date formats can also vary by locale. Methods used to retrieve an instance of DateTimeFormatter using the default locale are shown below: 1957 |

1958 | 1959 |

1960 | 1961 | 1. A resource bundle contains locale-specific objects used by a program. It is commonly stored in a properties file. A properties file is a text file in a specific format with key/value pairs. 1962 | 1963 | 1. An example using two property files is shown below: 1964 | ```java 1965 | Zoo_en.properties // name of file 1966 | hello=Hello 1967 | open=The zoo is open 1968 | 1969 | Zoo_fr.properties // name of file 1970 | hello=Bonjour 1971 | open=Le zoo est ouvert 1972 | 1973 | public static void printWelcomeMessage(Locale locale){ 1974 | var rb = ResourceBundle.getBundle("Zoo", locale); 1975 | System.out.println(rb.getString("hello") + "," + rb.getString("open")); 1976 | } 1977 | 1978 | public static void main(String[] args){ 1979 | var us = new Locale("en", "US"); 1980 | var france = new Locale("fr", "FR"); 1981 | printWelcomeMessage(us); 1982 | printWelcomeMessage(france); 1983 | } 1984 | ``` 1985 | 1986 | 1. To find a resource bundle to use Java looks for the language/country in the filename, followed by just the language. The default resource bundle is used if no matching locale can be found. 1987 | 1988 | ### Modular Applications 1989 | 1990 | 1. There are three types of modules: 1991 | * **Named Modules:** Contains a module-info file which appears in the root of the JAR alongside one or more packages. Unless otherwise specified, a module is a named module. Named modules appear on the module path rather than the classpath. 1992 | * **Automatic Module:** Appears on the module path but does not contain a module-info file. It is a regular JAR file that is placed on the module path and gets treated as a module. The code referencing an automatic module treats it as if there is a module-info present, and automatically exports all packages. If an Automatic-Module-Name is specified in the manifest, then that name is used. Otherwise, a module name is automatically determined based on the JAR filename. To determine the name the file extension is removed, then the version number, and then special characters are replaced with a period. If a period is the first or last character it is also removed. 1993 | * **Unnamed Module:** Like an automatic module, it is a regular JAR file. An unnamed module is on the classpath rather than the module path. An unnamed module does not usually contain a module-info file, and if it does, it is ignored since it is on the classpath. Unnamed modules do not export any packages to named or automatic modules, and an unnamed module can read from any JARs on the classpath or module path. 1994 | 1995 | 1. Modules prefixed with *java* (standard modules) are shown below: 1996 |

1997 | 1998 |

1999 | 2000 | 1. Modules prefixed with *jdk* (part of the JDK) are shown below: 2001 |

2002 | 2003 |

2004 | 2005 | 1. The *jdeps* command provides information about dependencies. An example is shown below: 2006 | ```java 2007 | // Animatronic.java 2008 | 2009 | import java.time.*; 2010 | import java.util.*; 2011 | import sun.misc.Unsafe; 2012 | 2013 | public class Animatronic { 2014 | private List names; 2015 | private LocalDate visitDate; 2016 | 2017 | public Animatronic(List names, LocalDate visitDate){ 2018 | this.names = names; 2019 | this.visitDate = visitDate; 2020 | } 2021 | 2022 | public void unsafeMethod(){ 2023 | Unsafe unsafe = Unsafe.getUnsafe(); 2024 | } 2025 | } 2026 | ``` 2027 | 2028 | ```shell 2029 | javac *.java 2030 | jar -cvf zoo.dino.jar . 2031 | jdeps zoo.dino.jar 2032 | ``` 2033 | 2034 | 1. Before older applications can be migrated to use modules, the structure of the packages and libraries in the existing application need to be determined. In the below diagram style, the arrows point from the project that will require the dependency to the one that makes it available. Projects that do not have any dependencies are at the bottom. 2035 |

2036 | 2037 |

2038 | 2039 | 1. A bottom-up migration is the easiest migration approach. It works when you have the power to convert any JAR files that are not already modules. The approach is: 2040 | * Pick the lowest-level project that has not yet been migrated. 2041 | * Add a module-info.java file to that project. Be sure to add any exports to expose any package used by higher level JAR files. Also, add the requires directive for any modules it depends on. 2042 | * Move this newly migrated named module from the classpath to the module path. 2043 | * Ensure any projects that have not yet been migrated stay as unnamed modules on the classpath. 2044 | * Repeat with the next-lowest-level project until you are done. 2045 | 2046 | 1. A top-down migration is most useful when you do not have control of every JAR file used by your application. The approach is: 2047 | * Place all projects on the module path. 2048 | * Pick the highest-level project that has not yet been migrated. 2049 | * Add a module-info file to that project to convert the automatic module into a named module. Remember to add any exports or requires directives. Automatic module names can be used when writing the requires directive since most of the projects on the module path do not have names yet. 2050 | * Repeat with the next-highest-level project until you are done. 2051 | 2052 | 1. An example of the bottom-up migration approach (left) and top-down migration approach (right) is shown below: 2053 |

2054 | 2055 |

2056 | 2057 | 1. When splitting up a project into modules, a problem with **cyclic dependencies** may arise. A cyclic dependency occurs when 2 or more things have dependencies on each other. Modules that have cyclic dependencies will not compile. A common technique to resolve this issue is to introduce another module containing all the code that the modules share. Note that a cyclic dependency can still exists between packages with a module. 2058 | 2059 | 1. Although not recommended, is possible to customise what packages a module exports from the command line. 2060 | ```java 2061 | javac --add-reads moduleA=moduleB --add-exports moduleB/com.modB.package1=moduleA ... 2062 | java --add-reads moduleA=moduleB --add-exports moduleB/com.modB.package1=moduleA ... 2063 | // --add-reads moduleA=moduleB implies that moduleA wants to read all exported packages of moduleB 2064 | // add-exports moduleB/com.modB.package1=moduleA implies that moduleB exports package com.modB.package1 to moduleA 2065 | // add-open is used ot provide access to privat members of classes through reflection (not required for exam) 2066 | ``` 2067 | 2068 | 1. The previous section discussed modules in terms of dependencies, with one module exporting its public types and another module requiring them. This is a very tight coupling. A looser coupling can be if one module requires an implementation of an interface from another module, and the other module provides that implementation. 2069 | 2070 | 1. A **service** is composed of an interface, classes referenced by the interface references, and a way to look up the implementations of the interface. A sample tours application will be used to introduce this concept. The 4 modules within this application are shown below: 2071 |

2072 | 2073 |

2074 | 2075 | 1. A **service provider interface** specifies the behaviour that the service will have. The service provider interface is exported for other modules to use. For the tours application this is shown below: 2076 | ```java 2077 | // Souvenir.java 2078 | package zoo.tours.api; 2079 | 2080 | public class Souvenir{ 2081 | private String description; 2082 | 2083 | public String getDescription(){ 2084 | return description; 2085 | } 2086 | 2087 | public void setDescription(String description){ 2088 | this.description = description; 2089 | } 2090 | } 2091 | 2092 | // Tour.java 2093 | package zoo.tours.api; 2094 | 2095 | public interface Tour { 2096 | String name(); 2097 | int length(); 2098 | Souvenir getSouvenir(); 2099 | } 2100 | 2101 | // module-info.java 2102 | module zoo.tours.api{ 2103 | exports zoo.tours.api; 2104 | } 2105 | ``` 2106 | 2107 | 1. A **service locator** can find any classes that implement a service provider interface. At runtime, there may be many service providers (or none) that are found by the service locator. The service locator requires the service provider interface package, uses the Tour class to lookup classes that implement a service provider interface, and exports the package with the lookup for other modules to use. For the tours application this is shown below: 2108 | ```java 2109 | // TourFinder.java 2110 | package zoo.tours.reservations; 2111 | 2112 | import java.util.*; 2113 | import zoo.tours.api.*; 2114 | 2115 | public class TourFinder{ 2116 | 2117 | public static Tour findSingleTour(){ 2118 | ServiceLoader loader = ServiceLoader.load(Tour.class); 2119 | for(Tour tour : loader) 2120 | return tour; 2121 | return null; 2122 | } 2123 | 2124 | public static List findAllTours(){ 2125 | List tours = new ArrayList<>(); 2126 | ServiceLoader loader = ServiceLoader.load(Tour.class); 2127 | for(Tour tour : loader) 2128 | tours.add(tour); 2129 | return tours; 2130 | } 2131 | } 2132 | 2133 | // module-info.java 2134 | module zoo.tours.reservations { 2135 | exports zoo.tours.reservations; 2136 | requires zoo.tours.api; 2137 | uses zoo.tours.api.Tour; 2138 | } 2139 | ``` 2140 | 2141 | 1. A **consumer** refers to a module that obtains and uses a service. Once the consumer has acquired a service via the service locator, it is able to invoke the methods provided by the service provider interface. The consumer requires service provider interface and the service locator. For the tours application this is shown below: 2142 | ```java 2143 | // Tourist.java 2144 | package zoo.visitor; 2145 | 2146 | import java.util.*; 2147 | import zoo.tours.api.*; 2148 | import zoo.tours.reservations.*; 2149 | 2150 | public class Tourist{ 2151 | public static void main(String[] args){ 2152 | Tour tour = TourFinder.findSingleTour(); 2153 | System.out.println("Single tour: " + tour); 2154 | 2155 | List tours = TourFinder.findAllTours(); 2156 | System.out.println("# tours: " + tours.size()); 2157 | } 2158 | } 2159 | 2160 | // module-info.java 2161 | module zoo.visitor{ 2162 | requires zoo.tours.api; 2163 | requires zoo.tours.reservations; 2164 | } 2165 | ``` 2166 | 2167 | 1. A **service provider** is the implementation of a service provider interface. The service provider requires the service provider interface and provides an implementation of the behaviour specified in the service provider interface. Note that the export directive is not used as we do not want consumers referring to the service provider directly. For the tours application this is shown below: 2168 | ```java 2169 | // TourImpl.java 2170 | package zoo.tours.agency; 2171 | 2172 | import zoo.tours.api.*; 2173 | 2174 | public class TourImpl implements Tour{ 2175 | public String name(){ 2176 | return "Behind the Scenes"; 2177 | } 2178 | 2179 | public int length(){ 2180 | return 120; 2181 | } 2182 | 2183 | public Souvenir getSouvenir(){ 2184 | Souvenir gift = new Souvenir(); 2185 | gift.setDescription("stuffed animal"); 2186 | return gift; 2187 | } 2188 | } 2189 | 2190 | // module-info.java 2191 | module zoo.visitor{ 2192 | requires zoo.tours.api; 2193 | provides zoo.tours.api.Tour with zoo.tours.agency.TourImpl; 2194 | } 2195 | ``` 2196 | 2197 | 1. If a service provider declares a provider method, then the service loader invokes that method to obtain an instance of the service provider. A provider method is a public static method named "provider" with no formal parameters and a return type that is assignable to the service's interface or class. In this case, the service provider **need not** be assignable to the service's interface or class. 2198 | 2199 | 1. If a service provider does not declare a provider method, then the service provider is instantiated directly, via its constructor. There must be a service provider constructor that takes no arguments and is assignable to the service's interface or class. The provides directive in a service provider cannot specify the same service more than once. 2200 | 2201 | 1. If the used directive occurs in a class, the *ServiceLoader.load()* method returns a ServiceLoader object that can provide instances of the service type. The module system automatically discovers provider modules at start up by scanning modules in the Java runtime image and modular jars in the module path. As there can be multiple implementations of the service, multiple instances of the service type can be returned. The service type should offer enough descriptor methods for a consumer to select the best implementation. 2202 | 2203 | 1. The requires directive takes an object name, the exports directive takes a package name, the uses directive takes a type name, and the provides directive takes a service type and provider class. A consumer module will contain requires/uses, while a provider module will contain requires/provides. 2204 | 2205 | 1. A summary of the directives required for reach service artefact is shown below: 2206 |

2207 | 2208 |

2209 | 2210 | ### Concurrency 2211 | 2212 | 1. Disk and network operations are extremely slow compared to CPU operations. Multithreaded processing is used by modern operating systems to allow applications to execute multiple tasks at the same time, which allows tasks waiting for resources to give way to other processing requests. Java has traditionally supported multithreaded programming using the **Thread** class. The **Concurrency** API has grown over time to provide numerous classes for performing complex thread-based tasks. 2213 | 2214 | 1. A thread is the smallest unit of execution that can be scheduled by the operating system. A process is a group of associated threads that execute in the same, shared environment. A **task** is a single unit of work performed by a thread. 2215 | 2216 | 1. A process model is shown below: 2217 |

2218 | 2219 |

2220 | 2221 | 1. Thread types include **system threads** threads created by the JVM, and **user-defined** threads which are created by the application developer. Operating systems use a **thread scheduler** to determine which threads should be executing. A **context switch** is the process of storing a thread's current state and later restoring the state of the thread to continue executing. A thread can interrupt or supersede another thread if it has a higher **thread priority**. 2222 | 2223 | 1. The *java.lang.Runnable* interface is a functional interface that takes no arguments and returns no data. It is commonly used to define the task or work that a thread will execute, separate from the main application thread. The definition of runnable is shown below: 2224 | ```java 2225 | @FunctionalInterface 2226 | public interface Runnable { 2227 | void run(); 2228 | } 2229 | ``` 2230 | 2231 | 1. To execute a thread first you define an instance of *java.lang.Thread*, and then you start the task using the *Thread.start()* method. Examples of defining a thread are shown below: 2232 | ```java 2233 | // Providing a Runnable object to the Thread constructor 2234 | public class PrintData implements Runnable{ 2235 | @Override public void run() { 2236 | for(int i = 0; i < 3; i++) 2237 | System.out.println("Printing record: "+i); 2238 | } 2239 | 2240 | public static void main(String[] args){ 2241 | (new Thread(new PrintData())).start(); 2242 | } 2243 | 2244 | // Creating a class that extends Thread and overrides the run() method 2245 | public class ReadInventoryThread extends Thread{ 2246 | @Override public void run() { 2247 | System.out.println("Printing zoo inventory"); 2248 | } 2249 | 2250 | public static void main(String[] args){ 2251 | (new ReadInventoryThread()).start(); 2252 | } 2253 | } 2254 | ``` 2255 | 2256 | 1. While threads operate asynchronously, one thread may need to wait for the results of another thread. In such a case the *Thread.sleep()* method can be used to make a thread pause until results are ready. An example is shown below: 2257 | ```java 2258 | class CheckResults { 2259 | private static int counter = 0; 2260 | 2261 | public static void main(String[] a) throws InterruptedException { 2262 | new Thread(() -> { 2263 | for (int i = 0; i < 500; i++) { 2264 | CheckResults.counter++; 2265 | } 2266 | }).start(); 2267 | while (CheckResults.counter < 100) { 2268 | System.out.println("Not reached yet"); 2269 | Thread.sleep(1000); // 1 SECOND 2270 | } 2271 | System.out.println("Reached!"); 2272 | } 2273 | } 2274 | ``` 2275 | 2276 | 1. To improve on the above and to assist with creating and managing threads, the *ExecutorService* interface in the Concurrency API can be used. 2277 | 2278 | 1. An example of using *newSingleThreadExecutor()* is shown below: 2279 | ```java 2280 | import java.util.concurrent.*; 2281 | public class ZooInfo{ 2282 | public static void main(String[] args){ 2283 | ExecutorService service = null; 2284 | Runnable task1 = () -> 2285 | System.out.println("Printing zoo inventory"); 2286 | Runnable task2 = () -> {for(int i = 0; i < 3; i++) 2287 | System.out.println("Printing record: "+i);}; 2288 | 2289 | try{ 2290 | service = Executors.newSingleThreadExecutor(); 2291 | System.out.println("begin"); 2292 | service.execute(task1); 2293 | service.execute(task2); 2294 | service.execute(task1); 2295 | System.out.println("end"); 2296 | } finally { 2297 | if(service != null) service.shutdown(); 2298 | } 2299 | } 2300 | } 2301 | ``` 2302 | 2303 | 1. If the *shutdown()* method is not called then the application will never terminate. As part of shutdown the thread executor rejects any new tasks submitted to the thread executor while continuing to execute any previously submitted tasks. The *isShutdown()* and *isTerminated()* methods can be used to check the status of the thread. The *shutdownNow()* method attempts to stop all running tasks immediately. 2304 | 2305 | 1. Tasks can be submitted to an *ExecutorService* in multiple ways. The *execute()* method is inherited from the *Executor* interface, which the *ExecutorService* interface extends. It is considered a "fire-and-forget" method as once it is submitted the result is not directly available to the calling thread. The *submit()* method returns a *Future* instance that can be used to determine whether the task is complete. 2306 | 2307 | 1. Useful *ExecutorService* methods are shown below: 2308 | ```java 2309 | void execute(Runnable command); 2310 | Future submit(Runnable task); 2311 | Future submit(Callable task); 2312 | List> invokeAll(Collections> tasks) throws InterruptedException; 2313 | T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException; 2314 | ``` 2315 | 2316 | 1. To improve on the previous CheckResults implementation and avoid managing threads directly, the below implementation using *submit()* to return a *Future* object can be used: 2317 | ```java 2318 | public class CheckResults { 2319 | private static int counter = 0; 2320 | 2321 | public static void main(String[] unused) throws Exception { 2322 | ExecutorService service = null; 2323 | try { 2324 | service = Executors.newSingleThreadExecutor(); 2325 | Future result = service.submit(() -> { 2326 | for (int i = 0; i < 500; i++) { 2327 | CheckResults.counter++; 2328 | } 2329 | }); 2330 | result.get(10, TimeUnit.SECONDS); 2331 | System.out.println("Reached!"); 2332 | } catch (TimeoutException e) { 2333 | System.out.println("Not reached in time"); 2334 | } finally { 2335 | if (service != null) { 2336 | service.shutdown(); 2337 | } 2338 | } 2339 | } 2340 | } 2341 | ``` 2342 | 2343 | 1. The *java.util.concurrent.Callable* functional interface is similar to *Runnable* except that its *call()* method returns a value and can throw a checked exception. The definition of the *Callable* interface is shown below: 2344 | ```java 2345 | @FunctionalInterface 2346 | public interface Callable { 2347 | V call() throws Exception; 2348 | } 2349 | ``` 2350 | 2351 | 1. The *Callable* interface is often preferable over *Runnable* since it allows more details to be retrieved easily from the task after it is completed. 2352 | 2353 | 1. After submitting tasks to a thread executor, it is common to wait for the results. An example is shown below for a simple generic pattern where the result from the thread executor doesn't need to be retained: 2354 | ```java 2355 | ExecutorService service = null; 2356 | try { 2357 | service = Executors.newSingleThreadExecutor(); 2358 | // Add tasks to the thread executor 2359 | } finally { 2360 | if (service != null) { 2361 | service.shutdown(); 2362 | } 2363 | } 2364 | if (service != null) { 2365 | service.awaitTermination(1, TimeUnit.MINUTES); 2366 | // Check whether all tasks are finished 2367 | if (service.isTerminated()) { 2368 | System.out.println("Finished!"); 2369 | } else { 2370 | System.out.println("At least one task is still running"); 2371 | } 2372 | } 2373 | ``` 2374 | 2375 | 1. The *invokeAll()* and *invokeAny()* methods can be used to execute task collections synchronously. The *invokeAll()* method will wait indefinitely until all tasks are complete, while the *invokeAny()* method will wait indefinitely until at least one task completes. 2376 | 2377 | 1. The *ScheduledExecutorService* can be used to schedule a task to happen at some future time. Useful *ScheduledExecutorService* methods are shown below: 2378 | ```java 2379 | schedule(Callable callable, long delay, TimeUnit unit); 2380 | schedule(Runnable command, long delay, TimeUnit unit); 2381 | scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); 2382 | scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); 2383 | ``` 2384 | 2385 | 1. An example usage of *ScheduledExecutorService* is shown below: 2386 | ```java 2387 | ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); 2388 | Runnable task1 = () -> System.out.println("Hello zoo"); 2389 | Callable task2 = () -> "Monkey"; 2390 | ScheduledFuture r1 = service.schedule(task1, 10, TimeUnit.SECONDS); 2391 | ScheduledFuture r2 = service.schedule(task2, 8, TimeUnit.MINUTES); 2392 | ``` 2393 | 2394 | 1. Additional factory methods in the *Executors* class are available that use a pool of threads. A *thread pool* is a group of pre-instantiated reusable threads that are available to perform a set of tasks. Useful *ScheduledExecutorService* factory methods are shown below: 2395 | ```java 2396 | ExecutorService.newSingleThreadExecutor(); 2397 | ScheduledExecutorService.newSingleThreadScheduledExecutor(); 2398 | ExecutorService.newCachedThreadPool(); 2399 | ExecutorService.newFixedThreadPool(int); 2400 | ScheduledExecutorService.newScheduledThreadPool(int); 2401 | ``` 2402 | 2403 | 1. An instance of a pooled-thread executor will execute concurrently if the number of tasks is less than the number of available threads. Calling *newFixedThreadPool()* with a value of 1 is equivalent to calling *newSingleThreadExecutor()*. The number of threads in the pool is often set to equal the number of available CPUs. 2404 | 2405 | 1. Thread-safety is the property of an object that guarantees safe execution by multiple threads at the same time. As threads run in a shared environment and memory space, data must be organised so that we do not end up with unexpected results. 2406 | 2407 | 1. An example of non-thread safe code is shown below: 2408 | ```java 2409 | public class SheepManager { 2410 | private int sheepCount = 0; 2411 | 2412 | private void incrementAndReport() { 2413 | System.out.print((++sheepCount) + " "); 2414 | } 2415 | 2416 | public static void main(String[] args) { 2417 | ExecutorService service = null; 2418 | try { 2419 | service = Executors.newFixedThreadPool(20); 2420 | SheepManager manager = new SheepManager(); 2421 | for (int i = 0; i < 10; i++) { 2422 | service.submit(() -> manager.incrementAndReport()); 2423 | } 2424 | } finally { 2425 | if (service != null) { 2426 | service.shutdown(); 2427 | } 2428 | } 2429 | } 2430 | } 2431 | ``` 2432 | 2433 | 1. Multiple threads read and write the sheepCount variable, with one of the threads overwriting the results of the others. When 2 or more threads execute the right side of the expression, only the result of one of the increment operations is stored. This is known as a **race condition** and means the output will be different each time. 2434 | 2435 | 1. *Atomic* is the property of an operation to be carried out as a single unit of execution without any interference by another thread. The *java.util.concurrent.atomic* package contains the following useful Atomic classes: 2436 | ```java 2437 | AtomicBoolean 2438 | AtomicInteger 2439 | AtomicLong 2440 | ``` 2441 | 2442 | 1. Each class contains numerous methods that equivalent to many of the primitive built-in operators. Common atomic methods (e.g. for AtomicInteger) are shown below: 2443 | ```java 2444 | int get(); 2445 | void set(int); 2446 | int getAndSet(int); 2447 | int incrementAndGet(); 2448 | int getAndIncrement(); 2449 | int decrementAndGet(); 2450 | int getAndDecrement() 2451 | ``` 2452 | 2453 | 1. Replacing *++sheepCount* and the int declaration with *sheepCount.incrementAndGet()* and an AtomicInteger declaration results in all of the numbers being printed, but the order is still not guaranteed. No increment operation is lost but we do not know which thread will return first. 2454 | 2455 | 1. A monitor (or lock) is a structure that supports *mutual exclusion*, which is the property that at most one thread is executing a particular segment of code at a given time. As each thread arrives at a lock it checks if any threads are already in the block, and only a single thread can hold the lock. Adding the synchronized block as per the below will ensure the output of the above is sequential: 2456 | ```java 2457 | private void incrementAndReport() { 2458 | synchronized(this) { 2459 | System.out.print((++sheepCount) + " "); 2460 | } 2461 | } 2462 | ``` 2463 | 2464 | 1. The synchronized modifier can also be added to a method to achieve the same effect. A static synchronized block can also be used if thread access needs to be ordered across all instances, and not just a single instance. 2465 | 2466 | 1. Correctly using the synchronised keyword can be challenging and has performance implication. Other classes within the Concurrency API that are easier to use are recommended. 2467 | 2468 | 1. The Concurrency API includes the *lock* interface that is conceptually like using the synchronized keyword, but that has a lot more features. The below blocks are equivalent: 2469 | ```java 2470 | // Implementation #1 with a synchronized block 2471 | Object object = new Object(); 2472 | synchronized (object) { 2473 | // protected code 2474 | } 2475 | 2476 | // Implementation #2 with a Lock 2477 | Lock lock = new ReentrantLock(); 2478 | try { 2479 | lock.lock(); 2480 | // protected code 2481 | } finally { 2482 | lock.unlock(); 2483 | } 2484 | ``` 2485 | 2486 | 1. Useful lock interface methods include: 2487 | ```java 2488 | void lock(); 2489 | void unlock(); 2490 | boolean tryLock(); 2491 | boolean tryLock(long, TimeUnit); 2492 | ``` 2493 | 2494 | 1. The *tryLock()* method attemps to acquire a lock and immediately returns a *boolean* result. It does not wait if another thread already holds the lock, it returns immediately whether a lock is available. The *tryLock(long, TimeUnit)* method is similar but will wait for a period of time to acquire the lock. An example for *tryLock()* is shown below: 2495 | ```java 2496 | Lock lock = new ReentrantLock(); 2497 | new Thread(() -> printMessage(lock)).start(); 2498 | if (lock.tryLock()) { 2499 | try { 2500 | System.out.println("Lock obtained, entering protected code"); 2501 | } finally { 2502 | lock.unlock(); 2503 | } 2504 | } else { 2505 | System.out.println("Unable to acquire lock, doing something else"); 2506 | } 2507 | ``` 2508 | 2509 | 1. The *CyclicBarrier* class can be used to allow a set of threads to wait for each other to reach a common execution point, known as a barrier. This allows multiple threads to still run. An example is shown below: 2510 | ```java 2511 | public class LionPenManager { 2512 | private void removeLions() { 2513 | System.out.println("Removing lions"); 2514 | } 2515 | 2516 | private void cleanPen() { 2517 | System.out.println("Cleaning the pen"); 2518 | } 2519 | 2520 | private void addLions() { 2521 | System.out.println("Adding lions"); 2522 | } 2523 | 2524 | public void performTask(CyclicBarrier c1, CyclicBarrier c2) { 2525 | try { 2526 | removeLions(); 2527 | c1.await(); 2528 | cleanPen(); 2529 | c2.await(); 2530 | addLions(); 2531 | } catch (InterruptedException | BrokenBarrierException e) { 2532 | // handle 2533 | } 2534 | } 2535 | 2536 | public static void main(String[] args) { 2537 | ExecutorService service = null; 2538 | try { 2539 | service = Executors.newFixedThreadPool(4); 2540 | var manager = new LionPenManager(); 2541 | var c1 = new CyclicBarrier(4); 2542 | var c2 = new CyclicBarrier(4, () -> System.out.println("*** Pen Cleaned!")); 2543 | for (int i = 0; i < 4; i++) { 2544 | service.submit(() -> manager.performTask(c1, c2)); 2545 | } 2546 | } finally { 2547 | if (service != null) { 2548 | service.shutdown(); 2549 | } 2550 | } 2551 | } 2552 | } 2553 | ``` 2554 | 2555 | 1. The Concurrency API also includes interfaces and classes to help solve common memory consistency errors. A *memory consistency* error occurs when two threads have inconsistent views of what should be the same data. The JVM may throw a *ConcurrentModificationException* in such a scenario. An example is shown below: 2556 | ```java 2557 | // ConcurrentModificationException thrown 2558 | var foodData = new HashMap(); 2559 | foodData.put("penguin", 1); 2560 | foodData.put("flamingo", 2); 2561 | for(String key: foodData.keySet()) { 2562 | foodData.remove(key); 2563 | } 2564 | 2565 | // No exception thrown 2566 | var foodData = new ConcurrentHashMap(); 2567 | foodData.put("penguin", 1); 2568 | foodData.put("flamingo", 2); 2569 | for(String key: foodData.keySet()) { 2570 | foodData.remove(key); 2571 | } 2572 | ``` 2573 | 2574 | 1. In the above example the iterator on *keySet()* is not properly updated after the first element is removed, so an exception is thrown. Concurrent collection classes should be used any time multiple threads are going to modify a collections object outside of a *synchronized* block. Concurrent collection classes are shown below: 2575 | ```java 2576 | ConcurrentHashMap; 2577 | ConcurrentLinkedQueue; 2578 | ConcurrentSkipListMap; 2579 | ConcurrentSkipListSet; 2580 | CopyOnWriteArrayList; 2581 | CopyOnWriteArraySet; 2582 | LinkedBlockingQueue; 2583 | ``` 2584 | 2585 | 1. The *SkipList* classes, *ConcurrentSkipListSet* and *ConcurrentSkipListMap*, are concurrent versions of their sorted counterparts, *Treeset* and *Treemap*, respectively. They maintain their elements or keys in the natural ordering of their elements. 2586 | 2587 | 1. The *CopyOnWriteArrayList* and *CopyOnWriteArraySet* classes copy all their elements to a new underlying structure anytime an element is added, modified, or removed from the collection. By modified element, we mean that the reference in the collection is changed. Modifying the actual contents of objects within the collection will not cause a new structure to be allocated. Although the data is copied to a new underlying structure, our reference to the Collection object does not change. This is particularly useful in multithreaded environments that need to iterate the collection. Any iterator established prior to modification will not see the changes, but instead it will iterate over the original elements prior to the modification. 2588 | 2589 | 1. The *CopyOnWriteArraySet* class is used like a *Hashset* and has similiar properties as the *CopyOnWriteArrayList* class. The *CopyOnWrite* classes can use a lot of memory, since a new collection structure needs to be allocated anytime the collection is modified. They are commonly used in multithreaded environment situations where reads are far more common than writes. 2590 | 2591 | 1. The *LinkedBlockingQueue* class implements the *BlockingQueue* interface. The *BlockingQueue* is just like the regular *Queue*, except that it includes methods that will wait a specific amount of time to complete an operation. 2592 | 2593 | 1. The Concurrency API includes methods for obtaining synchronised versions of existing noncurrent collection objects. They operate on the inputted collection and return a reference that is the same type as the underlying collection. These are listed below: 2594 | ```java 2595 | synchronizedCollection(Collection c); 2596 | synchronizedList(List list); 2597 | synchronizedMap(Map m); 2598 | synchronizedNavigableMap(NavigableMap m); 2599 | synchronizedNavigableSet(NavigableSet s); 2600 | synchronizedSet s); 2601 | synchronizedSortedMap(SortedMap m); 2602 | synchronizedSortedSet(SortedSet s); 2603 | ``` 2604 | 2605 | 1. If you are given an existing collection that is not a concurrent class and need to access it among multiple threads, you can wrap it using the methods above. 2606 | 2607 | 1. A threading problem can occur in multithreaded applications when two or more threads interact in an unexpected an undesirable way. For example, two threads may block each other from accessing a particular segment of code. As shown above, the Concurrency API creates threads and manages complex thread interactions for you. Although it reduces the potential for threading issues, it does not eliminate them. 2608 | 2609 | 1. *Liveness* is the ability of an application to be able to execute in a timely manner. There are three types of liveness issues with which you should be familiar: deadlock, starvation, and livelock. Deadlock occurs when two or more threads are blocked forever, each waiting on each other. Livelock occurs when two or more threads are conceptually blocked forever, although they are each active and trying to complete their task. Starvation occurs when a single thread is perpetually denied access to a shared resource or lock. Livelock is a special case of resource starvation in which two or more threads actively try to acquire a set of locks, are unable to do so, and restart part of the process. In practise, livelock is often difficult to detect, as threads in this state appear active and able to respond to respond to requests. 2610 | 2611 | 1. A *race condition* is an undesirable result that occurs when two tasks, which should be completed sequentially, are completed at the same time. Race conditions lead to invalid data if they are not properly handled. They tend to appear in highly concurrent applications. 2612 | 2613 | 1. A *parallel stream* is a stream that is capable of processing results concurrently, using multiple threads. For example, you can use a parallel stream and the map() operation to operate concurrently on the elements in the stream, vastly improving performance over processing a single element at a time. 2614 | 2615 | 1. A parallel stream can be created on an existing stream (note that a terminal operation on s2 makes s1 unavailable for further use): 2616 | ```java 2617 | Stream s1 = List.of(1,2).stream(); 2618 | Stream s2 = s1.parallel(); 2619 | ``` 2620 | 2621 | 1. A parallel stream can also be created directly: 2622 | ```java 2623 | Stream s3 = List.of(1,2).parallelStream(); 2624 | ``` 2625 | 2626 | 1. A *parallel decomposition* is the process of taking a task, breaking it up into smaller pieces that can be performed concurrently, and then reassembling the results. As an example: 2627 | ```java 2628 | private static int doWork(int input) { 2629 | try { 2630 | Thread.sleep(5000); 2631 | } catch (InterruptedException e) { 2632 | } 2633 | return input; 2634 | } 2635 | 2636 | // serial outputs 1 2 3 4 5 25 seconds 2637 | long start = System.currentTimeMillis(); 2638 | List.of(1, 2, 3, 4, 5).stream().map(w -> doWork(w)) 2639 | .forEach(s -> System.out.print(s + " ")); 2640 | 2641 | // parallel outputs 3 2 1 5 4 5 seconds 2642 | long start = System.currentTimeMillis(); 2643 | List.of(1, 2, 3, 4, 5).parallelstream().map(w -> doWork(w)) 2644 | .forEach(s -> System.out.print(s + " ")); 2645 | ``` 2646 | 2647 | 1. The results are no longer ordered or predictable. In this case, our system had enough CPUs for all the tasks to be run concurrently. 2648 | 2649 | ### I/O 2650 | 2651 | 1. There are three primary *java.io.File* constructors: 2652 | ```java 2653 | public File(String pathname); 2654 | public file(File parent, String child); 2655 | public File(String parent, String child); 2656 | ``` 2657 | 2658 | 1. An instance of the *File* class only represents a path to the file. Unless operated upon, it is not connected to an actual file within the file system. Common methods of the *File* class include: 2659 | ```java 2660 | boolean delete(); 2661 | boolean exists(); 2662 | String getAbsolutePath(); 2663 | String getName(); 2664 | String getParent(); 2665 | boolean isDirectory(); 2666 | boolean isFile(); 2667 | long lastModified(); 2668 | long length(); 2669 | File[] listFiles() 2670 | boolean mkdir(); 2671 | boolean mkdirs(); 2672 | boolean renameTo(File dest); 2673 | ``` 2674 | 2675 | 1. The contents of a file may be accessed or written via an I/O stream. The *java.io* API defines two sets of stream classes: byte streams and character streams. Byte streams read and write binary data and have class names that end in *InputStream* or *OutputStream*. Character streams read and write text data and have class names that end in *Reader* or *Writer*. The byte streams are primarily used to work with binary data, such as an image or executable file, while character streams are used to work with text files. 2676 | 2677 | 1. Generally, most *InputStream* classes have a corresponding *OutputStream* class. Similiarly, *Reader* classes typically have a corresponding *Writer* class. Exceptions to this rule include *PrintWriter* (which has no accompanying *PrintReader* class), and *PrintStream* which has no corresponding *InputStream*. 2678 | 2679 | 1. The *java.io* library defines four abstract classes that are the parents of all stream classes defined within the API: *InputStream*, *OutputStream*, *Reader*, and *Writer*. The *java.io* concrete stream classes are: 2680 | ```java 2681 | FileInputStream; 2682 | FileOutputStream; 2683 | FileReader; 2684 | FileWriter; 2685 | BufferedInputStream; 2686 | BufferedOutputStream; 2687 | BufferedReader; 2688 | BufferedWriter; 2689 | ObjectInputStream; 2690 | ObjectOutputStream; 2691 | PrintStream; 2692 | PrintWriter; 2693 | ``` 2694 | 2695 | 1. An example of reading and writing from a stream (note that the *int* value returned will be -1 at the end of the stream) is shown below: 2696 | ```java 2697 | // InputStream and Reader 2698 | public int read() throws IOException; 2699 | 2700 | // OutputStream and Writer 2701 | public void write(int b) throws IOException; 2702 | ``` 2703 | 2704 | 1. Overloaded methods are also provided to read from an *offset* and based on a particular *length*: 2705 | ```java 2706 | // InputStream 2707 | public int read(byte[] b) throws IOException; 2708 | public int read(byte[] b, int offset, int length) throws IOException; 2709 | 2710 | // OutputStream 2711 | public void write(byte[] b) throws IOException; 2712 | public void write(byte[] b, int offset, int length) throws IOException; 2713 | 2714 | // Reader 2715 | public void write(char[] c) throws IOException; 2716 | public void write(char[] c, int offset, int length) throws IOException; 2717 | 2718 | // Writer 2719 | public void write(char[] c) throws IOException; 2720 | public void write(char[] c, int offset, int length) throws IOException; 2721 | ``` 2722 | 2723 | 1. All I/O streams include a *close* method to release any resources within the stream when they are no longer needed. It is imperative that all I/O streams are closed lest they lead to resource leaks. Since all I/O streams implement *Closeable*, the best way to do this is with a try-with-resources statement. Note that when working with wrapped streams, you only need to close the topmost objects: 2724 | ```java 2725 | try (var fis = new FileInputStream("zoo-data.txt")) { 2726 | System.out.print(fis.read()); 2727 | } 2728 | ``` 2729 | 2730 | 1. All input stream classes can be manipulated with the following methods: 2731 | ```java 2732 | // InputStream and Reader 2733 | public boolean markSupported(); 2734 | public void mark(int readLimit); 2735 | public reset() throws IOException; 2736 | public long skip(long n) throws IOException; 2737 | ``` 2738 | 2739 | 1. The *mark()* and *reset()* methods return a stream to an earlier position. Make sure to call *markSupported()* on the stream to confirm the stream supports *mark()*. The *skip()* method reads data from the stream but discards the contents. 2740 | 2741 | 1. When data is written to an output stream, the underlying operating system does not guarantee that the data will make it to the file system immediately. The data may be cached in memory, with a write only occurring after a temporary cache is filled or after some amount of time has passed. If the application terminates unexpectedly, the data would be lost, because it was never written to the file system. To address this, all output stream classes provide a *flush()* method to request that all accumulated data be written immediately to disk. Note that each time it is used, it may cause a noticeable delay in the application, so it should only be used intermittently. 2742 | 2743 | 1. Some common, concrete I/O stream classes are shown below: 2744 | ```java 2745 | // byte streams 2746 | public FileInputStream(File file) throws FileNotFoundException; 2747 | public FileInputStream(String name) throws FileNotFoundException; 2748 | public FileOutputStream(File file) throws FileNotFoundException; 2749 | public FileOutputStream(String name) throws FileNotFoundException; 2750 | 2751 | // character streams 2752 | public FileReader(File file) throws FileNotFoundException; 2753 | public FileReader(String name) throws FileNotFoundException; 2754 | public FileWriter(File file) throws FileNotFoundException; 2755 | public FileWriter(String name) throws FileNotFoundException; 2756 | ``` 2757 | 2758 | 1. Buffered streams contain a number of performance improvements for managing data in memory: 2759 | ```java 2760 | // byte streams 2761 | public BufferedInputStream(InputStream in); 2762 | public FileInputStream(String name) throws FileNotFoundException; 2763 | public FileOutputStream(File file) throws FileNotFoundException; 2764 | public FileOutputStream(String name) throws FileNotFoundException; 2765 | 2766 | // character streams 2767 | public BufferedReader(Reader in); 2768 | public BufferedWriter(Writer out); 2769 | ``` 2770 | 2771 | 1. The following examples copies a file using these streams. In the first example, instead of reading one byte at a time, we read and write up to 1024 bytes at a time. In the second example, we use a String instead of a buffer array, and inserting a *newLine()* on every iteration of the loop: 2772 | ```java 2773 | // byte stream 2774 | void copyFileWithBuffer(File src, File dest) throws IOException { 2775 | try (var in = new BufferedInputStream(new FileInputStream(src)); 2776 | var out = new BufferedOutputStream( 2777 | new FileOutputStream(dest))) { 2778 | var buffer = new byte[1024]; 2779 | int lengthRead; 2780 | while ((lengthRead = in.read(buffer)) > 0) { 2781 | out.write(buffer, 0, lengthRead); 2782 | out.flush(); 2783 | } 2784 | } 2785 | } 2786 | 2787 | // character stream 2788 | void copyTextFileWithBuffer(File src, File dest) throws IOException { 2789 | try (var reader = new BufferedReader(new FileReader(src)); 2790 | var writer = new BufferedWriter(new FileWriter(dest))) { 2791 | String s; 2792 | while ((s = reader.readLine()) != null) { 2793 | writer.write(s); 2794 | writer.newLine(); 2795 | } 2796 | } 2797 | } 2798 | ``` 2799 | 2800 | 1. *Serialization* is the process of converting an in-memory object to a byte stream. Likewise, *deserialization* is the process of converting from a byte stream into an object. Serialization often involves writing an object to a stored or transmittable format, while deserialization is the reciprocal process. 2801 | 2802 | 1. To serialize an object using the I/O API, the object must implement the *java.io.Serializable* interface. Since *Serializable* is a marker interface, it does not have any methods. Generally speaking, you should only mark data-orientated classes serializable. The purpose of using this interface is to inform any process attempting to serialize the object that you have taken the proper steps to make the object serializable. 2803 | 2804 | 1. Oftentimes, the *transient* modifier is used for sensitive data of the class, such as a password. There are other objects it does not make sense to serialize, like the state of an in-memory thread. If the object is part of a serializable object, we just mark it *transient* to ignore these select instance members. When making a class serializable, every instance member of the class must be serializable, marked *transient*, or having a null value at the time of serialization. 2805 | 2806 | 1. The following classes are high level streams that operate on existing streams: 2807 | ```java 2808 | // serialize an object to a stream 2809 | public ObjectOutputStram(OutputStream out) throws IO Exception; 2810 | 2811 | // deserialize an object from a stream 2812 | public ObjectInputStream(InputStream in) throws IO Exception; 2813 | ``` 2814 | 2815 | 1. Two common methods using these methods and example usages are shown below: 2816 | ```java 2817 | // ObjectInputStream 2818 | public Object readObject() throws IOException, ClassNotFoundException 2819 | 2820 | // ObjectOutputStream 2821 | public void writeObject(Object obj) throws IOException; 2822 | 2823 | // save an object to file 2824 | void saveToFile(List gorillas, File dataFile) throws IOException { 2825 | try(var out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)))) { 2826 | for (Gorilla gorilla : gorillas) 2827 | out.writeObject(gorilla;) 2828 | } 2829 | } 2830 | 2831 | // read an object from file 2832 | List readFromFile(File dataFile) 2833 | throws IOException, ClassNotFoundException { 2834 | var gorillas = new ArrayList(); 2835 | try (var in = new ObjectInputStream( 2836 | new BufferedInputStream(new FileInputStream(dataFile)))) { 2837 | while (true) { 2838 | var object = in.readObject(); 2839 | if (object instanceof Gorilla) 2840 | gorillas.add((Gorilla) object); 2841 | } 2842 | 2843 | } catch (EOFException e) { 2844 | // File end reached 2845 | } 2846 | return gorillas; 2847 | } 2848 | ``` 2849 | 2850 | 1. It should be noted that the constructor and any instance initializations defined in the serialized class are ignored during the deserialization process. Java only calls the constructor of the first non-serializable parent class in the class hierarchy. 2851 | 2852 | 1. A diagram of I/O stream classes is shown below: 2853 |

2854 | 2855 |

2856 | 2857 | 1. The *java.io.Console* class is designed to handle user interactions. The below example will ask the user a series of questions and print the results based on this information: 2858 | ```java 2859 | public void readConsole() { 2860 | Console console = System.console(); 2861 | if (console == null) { 2862 | throw new RuntimeException("Console not available"); 2863 | } else { 2864 | String name = console.readLine("Please enter your name: "); 2865 | console.writer().format("Hi %s", name); 2866 | console.writer().println(); 2867 | console.format("What is your address?"); 2868 | String address = console.readLine(); 2869 | char[] password = console.readPassword( 2870 | "Enter a password " + "between %d and %d characters: ", 5, 2871 | 10); 2872 | char[] verify = console.readPassword("Enter the password again: "); 2873 | console.printf( 2874 | "Passwords " + (Arrays.equals(password, verify) ? "match" 2875 | : "do not match")); 2876 | } 2877 | } 2878 | ``` 2879 | 2880 | ### NIO.2 2881 | 2882 | 1. NIO.2 is an acronym that stands for the second version of the Non-blocking Input/Output API, and it is sometimes referred to as the "New I/O". NIO.2 allows us to do a lot more with files and directories than the original *java.io* API. At its core NIO.2 is a replacement for the legacy *java.io.File* class that aims to provide a more intuitive, more feature-rich API for working with files and directories. 2883 | 2884 | 1. The cornerstone of NIO.2 is the *java.nio.file.Path* interface. A *Path* represents a hierarchical path on the storage system to a file or directory. A *Path* can be thought of as a replacement for the *java.io.File* class, although it is used a bit differently. Unlike the *java.io.File* class, the *Path* interface contains support for symbolic links. NIO.2 includes full support for creating, detecting, and navigating symbolic links within the file system. 2885 | 2886 | 1. *Path* is an interface, and the JVM returns a file system-specific implementation when a *Path* is created, such as a Windows or Unix *Path* class. Examples for creating a *Path* object are shown below: 2887 | ```java 2888 | // Path factory method 2889 | public static Path of(String first, String... more); 2890 | 2891 | // example 1 2892 | Path path1 = Path.of("pandas/cuddly.png"); 2893 | Path path2 = Path.of("c:\\zooinfo\\November\\employees.txt"); 2894 | Path path3 = Path.of("/home/zoodirectory"); 2895 | 2896 | // example 2 2897 | Path path1 = Path.of("pandas", "cuddly.png"); 2898 | Path path2 = Path.of("c:", "zooinfo", "November", "employees.txt"); 2899 | Path path3 = Path.of("/", "home", "zoodirectory"); 2900 | ``` 2901 | 2902 | 1. Another way of obtaining a *Path* instance is from the *java.nio.file.Paths* factory class. Note the *s* at the end to distinguish it from the *Path* interface. Examples are shown below: 2903 | ```java 2904 | // Paths factory method 2905 | public static Path get(String first, String... more); 2906 | 2907 | Path path1 = Paths.get("pandas/cuddly.png"); 2908 | Path path2 = Paths.get("c:\\zooinfo\\November\\employees.txt"); 2909 | Path path3 = Paths.get("/", "home", "zoodirectory"); 2910 | ``` 2911 | 2912 | 1. A *Path* can also be obtained using the *Paths* class with a URI value. Examples are shown below: 2913 | ```java 2914 | // URI Constructor 2915 | public URI(String str) throws URISyntaxException; 2916 | 2917 | URI a = new URI("file://icecream.txt"); 2918 | Path b = Path.of(a); 2919 | Path c = Paths.get(a); 2920 | URI d = b.toUri(); 2921 | ``` 2922 | 2923 | 1. A *Path* can also be obtained using the *FileSystems* class. Examples are shown below: 2924 | ```java 2925 | // FileSystems factory method 2926 | public static FileSystem getDefault(); 2927 | 2928 | // FileSystem instance method 2929 | public Path getPath(String first, String... more); 2930 | 2931 | Path path1 = FileSystems.getDefault().getPath("pandas/cuddly.png"); 2932 | Path path2 = FileSystems.getDefault().getPath("c:\\zooinfo\\November\\employees.txt"); 2933 | Path path3 = FileSystems.getDefault().getPath("/home/zoodirectory"); 2934 | ``` 2935 | 2936 | 1. Finally, a *Path* instance can be obtained using the legacy *java.io.File* class. Examples are shown below: 2937 | ```java 2938 | // Path to File, using Path instance method 2939 | public default File toFile(); 2940 | 2941 | // File to Path, using java.io.File instance method 2942 | public Path toPath(); 2943 | 2944 | File file = new File("husky.png"); 2945 | Path path = file.toPath(); 2946 | File backToFile = path.toFile(); 2947 | ``` 2948 | 2949 | 1. The relationships between NIO.2 classes and interfaces are shown below: 2950 |

2951 | 2952 |

2953 | 2954 | 1. Many NIO.2 methods include a varargs that takes an optional list of value. The table below shows the commonly used arguments: 2955 |

2956 | 2957 |

2958 | 2959 | 1. An example is shown below: 2960 | ```java 2961 | void copy(Path source, Path target) throws IOException { 2962 | Files.move(source, target, LinkOption.NOFOLLOW_LINKS, 2963 | StandardCopyOption.ATOMIC_MOVE); 2964 | } 2965 | ``` 2966 | 2967 | 1. Common causes of an *IOException* include a loss of communication to the file system, inaccessibility of files or directories, an inability to overwrite a file, or a file or directory being required but not exist. 2968 | 2969 | 1. Other common *Path* instance methods are shown below: 2970 | ```java 2971 | Path of(String, String...); 2972 | Path getParent(); 2973 | URI toURI(); 2974 | boolean isAbsolute(); 2975 | String toString(); 2976 | Path toAbsolutePath(); 2977 | int getNameCount(); 2978 | Path relativize(); 2979 | Path getName(int); 2980 | Path resolve(Path); 2981 | Path subpath(int, int); 2982 | Path normalize(); 2983 | Path getFileName(); 2984 | Path toRealPath(LinkOption...); 2985 | ``` 2986 | 2987 | 1. The *Files* helper class can interact with real files and directories within the file system. Common static methods in the *Files* class are shown below: 2988 | ```java 2989 | boolean exists(Path, LinkOption...); 2990 | Path move(Path, Path, CopyOption...); 2991 | boolean isSameFile(Path, Path); 2992 | void delete(Path); 2993 | Path createDirectory(Path, FileAttribute...); 2994 | BufferedReader newBufferedReader(Path); 2995 | Path copy(Path, Path, CopyOption...); 2996 | BufferedWriter newBufferedWriter(Path, OpenOption...); 2997 | long copy(InputStream, Path, CopyOption...); 2998 | List readAllLines(Path); 2999 | long copy(Path, OutputStream); 3000 | ``` 3001 | 3002 | 1. The *Files* attribute also provides methods for accessing file and directory metadata, referred to as file attributes. These methods include: 3003 | ```java 3004 | public static boolean isDirectory(Path path, LinkOption... options); 3005 | public static boolean isSymbolicLink(Path path); 3006 | public static boolean isRegularFile(Path path, LinkOption... options); 3007 | public static boolean isHidden(Path path) throws IOException; 3008 | public static boolean isReadable(Path path); 3009 | public static boolean isWriteable(Path path); 3010 | public static boolean isExecutable(Path path); 3011 | public static long size(Path path) throws IOException; 3012 | public static FileTime getLastModifiedTime(Path path, LinkOption... options) throws IOException; 3013 | ``` 3014 | 3015 | 1. NIO.2 includes two methods for working with attributes. For each method, you need to provide a file system type object. The BasicFileAttributes type is the most used. Examples of reading and updating using these methods are shown below: 3016 | ```java 3017 | public static A readAttributes(Path path, 3018 | Class type, LinkOption... options) throws IOException; 3019 | public static V getFileAttributeView( 3020 | Path path, Class type, LinkOption... options); 3021 | 3022 | // read 3023 | var path = Paths.get("/turtles/sea.txt"); 3024 | BasicFileAttributes data = Files.readAttributes(path, BasicFileAttributes.class); 3025 | 3026 | // update 3027 | BasicFileAttributes view = Files.readAttributes(path, BasicFileAttributes.class); 3028 | BasicfileAttributes attributes = view.readAttributes(); 3029 | FileTime lastModifiedTime = FileTime.fromMi(attributes.lastModifiedTime().toMillis() + 10_000); 3030 | view.setTimes(lastModifiedTime, null, null); 3031 | ``` 3032 | 3033 | 1. The *Files* class contains useful Stream API methods that operate on files, directories, and directory trees. The example below performs a deep copy: 3034 | ```java 3035 | public void copyPath(Path source, Path target) { 3036 | try { 3037 | Files.copy(source, target); 3038 | if (Files.isDirectory(source)) { 3039 | try (Stream s = Files.list(source)) { 3040 | s.forEach( 3041 | p -> copyPath(p, target.resolve(p.getFileName()))); 3042 | } 3043 | } 3044 | } catch (IOException e) { 3045 | // handle exception 3046 | } 3047 | } 3048 | ``` 3049 | 3050 | 1. The *Files.lines()* method is preferable to the *Files.readAllLines()* method as it does not read the entire file into memory, instead it lazily processes each line and prints it as it is read. 3051 | 3052 | ### JDBC 3053 | 3054 | 1. There are two main ways to access a relational database from Java. Java Database Connectivity Language (JDBC) accesses data as rows and columns. Java Persistence API (JPA) accesses data through Java objects using a concept called Object-Relational Mapping (ORM). Databases that store their data in a format other than tables, such as key/value, are known as NoSQL databases. NoSQL is out of scope for this exam. 3055 | 3056 | 1. Create, Read, Update, Delete (CRUD) are the types of SQL statements. All database imports are in the *java.sql* package. 3057 | 3058 | 1. The JDBC interfaces are declared in the JDK. The concrete classes come from the JDBC driver, and each database has a different JAR file with these classes. The interfaces and an example implementation are shown below: 3059 |

3060 | 3061 |

3062 | 3063 | 1. *Driver* establishes a connection to the database, *Connection* sends commands to the database, *PreparedStatement* executes a SQL query, *CallableStatement* executes a SQL query, and *ResultSet* reads the results of a query. The example below shows end to end JDBC code: 3064 | ```java 3065 | public class MyFirstDatabaseConnection { 3066 | public static void main(String[] args) throws SQLException { 3067 | String url = "jdbc:derby:zoo"; 3068 | try (Connection conn = DriverManager.getConnection(url); 3069 | PreparedStatement ps = conn 3070 | .prepareStatement("SELECT name FROM animal"); 3071 | ResultSet rs = ps.executeQuery()) { 3072 | while (rs.next()) { 3073 | System.out.println(rs.getString(1)); 3074 | } 3075 | } 3076 | } 3077 | } 3078 | ``` 3079 | 3080 | 1. The JDBC URL format includes the protocol, subprotocol, and subname. Examples are shown below: 3081 | ```java 3082 | jdbc:postgresql://localhost/zoo 3083 | jdbc:oracle:thin@123,123,123,123:1521:zoo 3084 | jdbc:mysql://localhost:3306 3085 | jdbc:mysql://localhost:3306/zoo?profileSQL=true 3086 | ``` 3087 | 3088 | 1. A *Connection* can be established with *DriverManager* or *DataSource*. *DriverManager* is the only one relevant in the exam, but in the real world *DataSource* is much more powerful as it can pool connection and store the database connection information outside the application. 3089 | 3090 | 1. You have the choice of working with a *Statement*, *PreparedStatement*, or *CallableStatement*. *Statement* is an interface that both *PreparedStatement* and *CallableStatement* extend. A *PreparedStatement* takes parameters, while a *Statement* does not. A *CallableStatement* is for queries that are inside the database. 3091 | 3092 | 1. *PreparedStatement* is preferred to *Statement* as it provides increased performance, security, readability, and makes future use easier. The *ps.execute()*, *ps.executeQuery()*, and *ps.executeUpdate()* methods are used to run SQL statements. The *ps.execute()* method returns a Boolean result type and true for all SELECT operations and false for all other operations, the *ps.executeQuery()* method returns a ResultSet and the relevent rows and columns for a SELECT operation and n/a for other operations, and the *ps.executeUpdate()* method returns n/a for a SELECT operation and the number of rows added/changed/removed for other operations. 3093 | 3094 | 1. A *PreparedStatement* allows you to set parameters using the *setBoolean()*, *setDouble()*, *setInt*, *setLong*, *setObject*, and *setString* methods. It is important to note that the variables start counting from 1 and not from 0. An example is shown below: 3095 | ```java 3096 | public static void register(Connection conn, int key, int type, String name) 3097 | throws SQLException { 3098 | String sql = "INSERT INTO names VALUES(?,?,?)"; 3099 | try (PreparedStatement ps = conn.prepareStatement(sql)) { 3100 | ps.setInt(1, key); 3101 | ps.setString(3, name); 3102 | ps.setInt(2, type); 3103 | ps.executeUpdate(); 3104 | } 3105 | } 3106 | ``` 3107 | 3108 | 1. The *addBatch()* and *executeBatch()* methods can be used to run multiple statements in fewer trips to the database. As the database is often on a different machine than the machine the Java code runs, reducing the number of network calls can improve performance significantly. 3109 | 3110 | 1. A *CallableStatement* can be used to execute a stored procedure on a database. An example is shown below with the *read_e_names()* stored procedure: 3111 | ```java 3112 | String sql = "{call read_e_names()}"; 3113 | try(CallableStatement cs = conn.prepareCall(sql); 3114 | ResultSet rs = cs.executeQuery()) { 3115 | while(rs.next()) { 3116 | System.out.println(rs.getString(3)); 3117 | } 3118 | } 3119 | ``` 3120 | 3121 | 1. An example calling the *read_names_by_letter()* stored procedure which requires a prefix parameter is shown below: 3122 | ```java 3123 | var sql = "{call read_names_by_letter(?)}"; 3124 | try (var cs = conn.prepareCall(sql)) { 3125 | cs.setString("prefix", "Z"); 3126 | try (var rs = cs.executeQuery()) { 3127 | while (rs.next()) { 3128 | System.out.println(rs.getString(3)); 3129 | } 3130 | } 3131 | } 3132 | ``` 3133 | 3134 | 1. An example calling the *magic_number()* stored procedure which requires a Num parameter and returns an OUT parameter is shown below: 3135 | ```java 3136 | var sql = "?=call magic_number(?)}"; 3137 | try (var cs = conn.prepareCall(sql)) { 3138 | cs.registerOutParameter(1, Types.INTEGER); 3139 | cs.execute(); 3140 | System.out.println(cs.getInt("num")); 3141 | } 3142 | ``` 3143 | 3144 | 1. An example calling the *double_number()* stored procedure which requires a Num parameter and returns an INOUT parameter is shown below: 3145 | ```java 3146 | var sql = "{? = call double_number(?)}"; 3147 | try (var cs = conn.prepareCall(sql)) { 3148 | cs.setInt(1, 8); 3149 | cs.registerOutParameter(1, Types.INTEGER); 3150 | cs.execute(); 3151 | System.out.println(cs.getInt("num")); 3152 | } 3153 | ``` 3154 | 3155 | 1. JDBC resources, such as a *Connection*, are expensive to create. Not closing them creates a resource leak that will eventually slow down you program. The resources need to be closed in a specific order. The *ResultSet* is closed first, followed by the *PreparedStatement* (or *CallableStatement*), and then the *Connection*. Closing all three is not strictly necessary, as closing a JDBC resource should close any resources that it created. JDBC also automatically closes a *ResultSet* when you run another SQL statement from the same *Statement*, *PreparedStatement*, or *CallableStatement*. 3156 | 3157 | ### Security 3158 | 3159 | 1. A key security principle is to limit access as much as possible. This is known as the principle of least privilege. There are four levels of access control in Java, and the lowest level of accessible possible should be used. 3160 | 3161 | 1. If subclassing is not required, classes should use the *final* modified to prevent subclassing. 3162 | 3163 | 1. Object immutability should be used wherever possible. This can be achieved my marking the class *final*, marking all instance variables *private*, not defining any setter methods and marking parameters *final*, not allowing referenced mutable objects to be modified, and using a constructor to set all properties of the object (making a copy if needed). 3164 | 3165 | 1. A copy can be returned easily using the *clone()* method if the class implements the *Clonable* interface. A shallow copy is returned if no custom implementation is provided. If required, a deep copy implementation can be written for the class. 3166 | 3167 | 1. *Injection* is an attack where dangerous input runs in a program as part of a command. Sources of untrusted data include user input, reading from files, and retrieving data from a database. In the real world, any data that did not originate from your program should be considered suspect. 3168 | 3169 | 1. Consider the following example: 3170 | ```java 3171 | public int getOpening(Connection conn, String day) throws SQLException { 3172 | String sql = "SELECT opens FROM hours WHERE day = " + day + ""; 3173 | 3174 | try (var stmt = conn.createStatement(); 3175 | var rs = stmt.executeQuery(sql)) { 3176 | if (rs.next()) { 3177 | return rs.getInt("opens"); 3178 | } 3179 | } 3180 | return -1; 3181 | } 3182 | 3183 | // good 3184 | int opening = attack.getOpening(comm, "monday"); // 10 3185 | 3186 | // bad 3187 | int evil = attack.getOpening(conn, "monday' OR day IS NOT NULL OR day = 'sunday"); // 9 3188 | ``` 3189 | 3190 | 1. The second execution ran the following SQL statement which returns all rows (9 is returned as that happens to be the first result): 3191 | ```SQL 3192 | SELECT opens FROM hours 3193 | WHERE day = 'monday' 3194 | OR day IS NOT NULL 3195 | OR day = 'sunday' 3196 | ``` 3197 | 3198 | 1. Consider second execution with the following example where a *PreparedStatement* is used: 3199 | ```java 3200 | public int getOpening(Connection conn, String day) throws SQLException { 3201 | String sql = "SELECT opens FROM hours WHERE day = ?"; 3202 | 3203 | try (var ps = conn.prepareStatement(sql)) { 3204 | ps.setString(1, day); 3205 | try (var rs = ps.executeQuery()) { 3206 | if (rs.next()) { 3207 | return rs.getInt("opens"); 3208 | } 3209 | } 3210 | return -1; 3211 | } 3212 | } 3213 | 3214 | // bad 3215 | int evil = attack.getOpening(conn, "monday' OR day IS NOT NULL OR day = 'sunday"); // -1 3216 | ``` 3217 | 3218 | 1. The entire string is matched against the *day* column, and since there is no match, no rows are returned. 3219 | 3220 | 1. *Command injection* is another type that uses operating system commands to do something unexpected. Consider the following example: 3221 | ```java 3222 | Console console = System.console(); 3223 | String dirName = console.readLine(); 3224 | Path path = Paths.get("c:/data/diets/" + dirName); 3225 | try (Stream stream = Files.walk(path)) { 3226 | stream.filter(p -> p.toString().endsWith(".txt")) 3227 | .forEach(System.out::println); 3228 | } 3229 | ``` 3230 | 3231 | 1. When run with .. as the directory name, a *secrets* directory could be returned. A whitelist can be implemented to control what directories can be searched: 3232 | ```java 3233 | Console console = System.console(); 3234 | String dirName = console.readLine(); 3235 | if (dirName.equals("mammal") || dirName.equals("birds")) { 3236 | Path path = Paths.get("c:/data/diets/" + dirName); 3237 | try (Stream stream = Files.walk(path)) { 3238 | stream.filter(p -> p.toString().endsWith(".txt")) 3239 | .forEach(System.out::println); 3240 | } 3241 | } 3242 | ``` 3243 | 3244 | 1. When working on a project, you will often encounter confidential or sensitive data. Confidential information should not be put into a *toString()* method, as it is likely to wind up logged somewhere that you did not intend. You should be careful what methods you call in sensitive contexts such as writing to a log file, printing an exception or stack trace, *System.out* and *System.err* messages, or writing to data files. 3245 | 3246 | 1. You also need to be careful about what is in memory. If the application crashes, it may generate a dump file which contains the values of everything in memory. For example, when calling the *readPassword()* method on *Console*, it returns a *char[]* instead of a *String*. This is safer because it will not be placed in the String pool, where it could remain in memory after that code that used it is run, and you can *null* out the value of the array element yourself rather than waiting for the garbage collector to do it. The idea is to have confidential data in memory for as short a time as possible. 3247 | 3248 | 1. Imagine you are storing data in an *Employee* record. We want to write this data to a file and read this data back into memory, but we want to do this without writing any potentially sensitive data to disk. This can be achieved with serialization. Recall that Java skips calling the constructor when deserializing an object. This means validation performed in the constructor cannot be relied on. 3249 | 3250 | 1. Consider the following class: 3251 | ```java 3252 | import java.io.*; 3253 | 3254 | public class Employee implements Serializable { 3255 | private String name; 3256 | private int age; 3257 | 3258 | // Constructors/getters/setters 3259 | } 3260 | ``` 3261 | 3262 | 1. Recall that marking a field as *transient* prevents it from being serialized. Serialized fields can also be whitelisted by including them in a *ObjectStreamField [] serialPersistentFields* object. Security requirements may require us to implement custom serialization. We have a requirement to store the Social Security number, and we do need to serialize this information. However, we do not want to store it in plain text, so we will need to write some custom code. Consider the following updated class which uses custom read and write methods to securely encrypt and decrypt the Social Security number: 3263 | ```java 3264 | public class Employee { 3265 | private String name; 3266 | private String ssn; 3267 | private int age; 3268 | 3269 | // Constructors/getters/setters 3270 | 3271 | private static final ObjectStreamField[] serialPersistentFields = { 3272 | new ObjectStreamField("name", String.class), 3273 | new ObjectStreamField("ssn", String.class) }; 3274 | 3275 | private static String encrypt(String input) { 3276 | // Implementation omitted 3277 | } 3278 | 3279 | private static String decrypt(String input) { 3280 | // Implementation omitted 3281 | } 3282 | 3283 | private void writeObject(ObjectOutputStream s) throws Exception { 3284 | ObjectOutputStream.PutField fields = s.putFields(); 3285 | fields.put("name", name); 3286 | fields.put("ssn", encrypt(ssn)); 3287 | s.writeFields(); 3288 | } 3289 | 3290 | private void readObject(ObjectInputStream s) throws Exception { 3291 | ObjectInputStream.GetField fields = s.readFields(); 3292 | this.name = (String) fields.get("name", null); 3293 | this.ssn = decrypt((String) fields.get("ssn", null)); 3294 | } 3295 | } 3296 | ``` 3297 | 3298 | 1. Some fields are too sensitive even for custom serialization. A password should never be decryptable. When a password is set for a user, it should be converted to a *String* value using a salt (initial random value) using a one-way hashing algorithm. Databases of stored passwords can get stolen. Having them properly encrypted means the attacker cannot do much with them. 3299 | 3300 | 1. Sometimes an object can have different contents in memory versus on disk. When present, the *readResolve()* method is run after the *readObject()* method and is capable of replacing the reference of the object returned by deserialization. Similarly, if we want to write an object to disk but do not completely trust the instance we are holding, we will want to write the object in memory instead of what is in the *this* instance. When present, the *writeReplace()* method is run before *writeObject()* and allows us to replace the object that gets serialized. This is shown below: 3301 |

3302 | 3303 |

3304 | 3305 | 1. When constructing sensitive objects, you need to ensure that subclasses can't change the behaviour. This can be done by making methods *final*, making classes *final*, or making the constructor *private*. 3306 | 3307 | 1. The person running your program will have access to the bytecode (.class) files, typically bundled in a JAR file. With the bytecode, they can decompile your code and get source code. It is not as well written as the code you wrote but has equivalent information. Using an obfuscator makes your decompiled bytecode harder to read and therefore harder to reverse engineer, it doesn't provide any security. 3308 | 3309 | 1. A Denial of Service (DoS) attack can exploit poorly written code. This could include code that leaks resources (does not close a resource), creates very large resources, doesn't handle overflowing numbers correctly, or tries to exploit the limitations of data structures. 3310 | 3311 | ## Practise Tests 3312 | 3313 | - [Working with Java Data Types](#Working-with-Java-Data-Types) 3314 | - [Java Object Oriented Approach](#Java-Object-Oriented-Approach) 3315 | - [Controlling Program Flow](#Controlling-Program-Flow) 3316 | - [Arrays and Collections](#Arrays-and-Collections) 3317 | - [Exception Handlng](#Exception-Handlng) 3318 | - [Concurrency](#Concurrency) 3319 | - [Java I/O + NIO](#Java-IO-+-NIO) 3320 | - [Modules](#Modules) 3321 | - [JDBC](#JDBC) 3322 | - [Streams and Lambda](#Streams-and-Lambda) 3323 | - [Localization](#Localization) 3324 | - [Annotations](#Annotations) 3325 | - [Security](#Security) 3326 | 3327 | ### Working with Java Data Types 3328 | 3329 | 1. What will the following code print when run? 3330 | ```java 3331 | public class TestClass { 3332 | public static Integer wiggler(Integer x) { 3333 | Integer y = x + 10; 3334 | x++; 3335 | System.out.println(x); 3336 | return y; 3337 | } 3338 | 3339 | public static void main(String[] args) { 3340 | Integer dataWrapper = new Integer(5); 3341 | Integer value = wiggler(dataWrapper); 3342 | System.out.println(dataWrapper + value); 3343 | } 3344 | } 3345 | ``` 3346 | * The printed output is `6 20`. 3347 | 3348 | 1. What will the following code print when run? 3349 | ```java 3350 | public class TestClass { 3351 | public static void main(String args[]) { 3352 | Object obj1 = new Object(); 3353 | Object obj2 = obj1; 3354 | if (obj1.equals(obj2)) System.out.println("true"); 3355 | else System.out.println("false"); 3356 | } 3357 | } 3358 | ``` 3359 | * This code will not compile as there are no brackets around the if-else statement. If there was only an if statement, it would compile but brackets are recommended for code style. If there were brackets the code would output `true`. 3360 | 3361 | 1. Give the below code, what are the types of the variables `a` and `b`? 3362 | ```java 3363 | public class TestClass { 3364 | public void myMethod(String... params) { 3365 | var a = params; 3366 | var b = params[0]; 3367 | } 3368 | } 3369 | ``` 3370 | * The type of `a` is *String[]* and the type of `b` is *String*. 3371 | 3372 | 1. You want to find out whether two strings are equal or not, in terms of the actual characters within the strings. What is the best way to do this? 3373 | * Use String's equals method. 3374 | 3375 | 1. What will be the result of attempting to compile and run the following program? 3376 | ```java 3377 | public static void main(String args[]) { 3378 | StringBuilder sb = new StringBuilder("12345678"); 3379 | sb.setLength(5); 3380 | sb.setLength(10); 3381 | System.out.println(sb.length()); 3382 | } 3383 | ``` 3384 | * The printed output is `10`. 3385 | 3386 | 1. What will be the result of attempting to compile and run the following program? 3387 | ```java 3388 | public static void main(String args[]) { 3389 | int k = 1; 3390 | int[] a = { 1 }; 3391 | k += (k = 4) * (k + 2); 3392 | a[0] += (a[0] = 4) * (a[0] + 2); 3393 | System.out.println(k + " , " + a[0]); 3394 | } 3395 | ``` 3396 | * The printed output is `25 , 25`. 3397 | 3398 | 1. What will be the result of attempting to compile and run the following program? 3399 | ```java 3400 | public class SM { 3401 | public String checkIt(String s) { 3402 | if (s.length() == 0 || s == null) { 3403 | return "EMPTY"; 3404 | } 3405 | else return "NOT EMPTY"; 3406 | } 3407 | 3408 | public static void main(String args[]) { 3409 | SM a = new SM(); 3410 | System.out.println(a.checkIt(null)); 3411 | } 3412 | } 3413 | ``` 3414 | * A *NullPointerException* is thrown because `s` is operated upon before the null check. 3415 | 3416 | 1. Given the following class, which statements can be inserted at line 1 without causing the code to fail compilation? 3417 | ```java 3418 | public class TestClass { 3419 | int a; 3420 | int b = 0; 3421 | static int c; 3422 | 3423 | public void m() { 3424 | int d; 3425 | int e = 0; 3426 | // Line 1 3427 | } 3428 | } 3429 | ``` 3430 | * Variables `a`, `b`, `c`, and `e` can be incremented at Line 1. The variable `d` cannot be incremented as it has not been initialised. Unlike instance or static variables, local variables are not initialised automatically, and must be initialised if they are used. 3431 | 3432 | 1. Which of these are valid expressions to create a string of value "hello world"? 3433 | * The following are valid: 3434 | ```java 3435 | System.out.println(" hello world".trim()); 3436 | System.out.println("hello".concat(" world")); 3437 | System.out.println( 3438 | new StringBuilder("world").insert(0, "hello ").toString()); 3439 | ``` 3440 | 3441 | 1. Given the following class, what can be done to make this code compile and run? 3442 | ```java 3443 | public class Square { 3444 | private double side = 0; // Line 2 3445 | public static void main(String[] args) { // Line 4 3446 | Square sq = new Square(); // Line 5 3447 | side = 10; // Line 6 3448 | } 3449 | } 3450 | ``` 3451 | * Line 6 needs to be replaced with `sq.side = 10`. 3452 | 3453 | 1. What will the following program print when run? 3454 | ```java 3455 | public class Operators { 3456 | 3457 | public static int operators() { 3458 | int x1 = -4; 3459 | int x2 = x1--; 3460 | int x3 = ++x2; 3461 | if (x2 > x3) { 3462 | --x3; 3463 | } else { 3464 | x1++; 3465 | } 3466 | return x1 + x2 + x3; 3467 | } 3468 | 3469 | public static void main(String[] args) { 3470 | System.out.println(operators()); 3471 | } 3472 | } 3473 | ``` 3474 | * The output is `-10`. 3475 | 3476 | 1. What will be the result of attempting to compile or run the following class? 3477 | ```java 3478 | public class TestClass { 3479 | public static void main(String args[]) { 3480 | int i, j, k; 3481 | i = j = k = 9; 3482 | System.out.println(i); 3483 | } 3484 | } 3485 | ``` 3486 | * The code will compile and print a value of `9`. 3487 | 3488 | 1. What will the below program print if compiled and run using the `java Switcher 1 2 3` command line? 3489 | ```java 3490 | public class Switcher { 3491 | public static void main(String[] args) { 3492 | switch (Integer.parseInt(args[1])) // 1 3493 | { 3494 | case 0: 3495 | var b = false; // 2 3496 | break; 3497 | 3498 | case 1: 3499 | b = true; // 3 3500 | break; 3501 | } 3502 | 3503 | if (b) { // 4 3504 | System.out.println(args[2]); 3505 | } 3506 | } 3507 | } 3508 | ``` 3509 | * It will fail to compile because at line 4 `b` is not defined. 3510 | 3511 | 1. Which of the following operators can be used in conjunction with a String object? 3512 | * The `+`, `+=`, and `.` operators can be used with String objects. There are no `++` or `*` operators for String. 3513 | 3514 | 1. What will the following code print? 3515 | ```java 3516 | public static void main(String[] args) { 3517 | int i = 0; 3518 | int j = 1; 3519 | if ((i++ == 0) & (j++ == 2)) { 3520 | i = 12; 3521 | } 3522 | System.out.println(i + " " + j); 3523 | } 3524 | ``` 3525 | * It will print `1 2`. 3526 | 3527 | 1. What will the following code print? 3528 | ```java 3529 | public class TrimTest { 3530 | public static void main(String[] args) { 3531 | String blank = " "; // one space 3532 | String line = blank + "hello" + blank + blank; 3533 | line.concat("world"); 3534 | String newLine = line.trim(); 3535 | System.out.println((int) (line.length() + newLine.length())); 3536 | } 3537 | } 3538 | ``` 3539 | * It will print `13`. The concatenation does not assign a new value to `line`, and trim takes spaces from the beginning and end of a String. 3540 | 3541 | 1. Which of the following is not a primitive data value in Java? 3542 | * Strings and the Object class are not primitive data types. The primitive data types are boolean, byte, char, int, long, float, and double. 3543 | 3544 | 1. What will be the output of the following program? 3545 | ```java 3546 | public class SubstringTest { 3547 | public static void main(String args[]) { 3548 | String String = "string isa string"; 3549 | System.out.println(String.substring(3, 6)); 3550 | } 3551 | } 3552 | ``` 3553 | * It will print `ing` (note there is no space as the 6th character is excluded from the upper bound). 3554 | 3555 | 1. Consider the following lines of code, what variables can you put in place of `?` to cause the expression to evaluate to 'true'? 3556 | ```java 3557 | boolean greenLight = true; 3558 | boolean pedestrian = false; 3559 | boolean rightTurn = true; 3560 | boolean otherLane = false; 3561 | ((rightTurn && !pedestrian || otherLane) || (? && !pedestrian && greenLight)) == true; 3562 | ``` 3563 | * Any value is alright as the first part of the expression evaluates to `true`. 3564 | 3565 | 1. Which of the following expressions will evaluate to true if preceded by the following code? 3566 | ```java 3567 | String a = "java"; 3568 | char[] b = { 'j', 'a', 'v', 'a' }; 3569 | String c = new String(b); 3570 | String d = a; 3571 | ``` 3572 | * The expressions `a == d`, `a == "java"`, and `a.equals(c)` will all evaluate to `true`. The expression `b == d"` cannot be used as the objects are of different types. 3573 | 3574 | 1. What will the following code print 3575 | ```java 3576 | String abc = ""; 3577 | abc.concat("abc"); 3578 | abc.concat("def"); 3579 | System.out.print(abc); 3580 | ``` 3581 | * An empty String will be printed as the values as `abc` is not assigned any new value. 3582 | 3583 | 1. What will the following code print? 3584 | ```java 3585 | String string = " hello java guru ".strip(); 3586 | System.out.print(string); 3587 | ``` 3588 | * It will print `hello java guru` as `strip()` will remove the spaces from the beginning and end of `string`. 3589 | 3590 | 1. What will the following code print? 3591 | ```java 3592 | int i1 = 1, i2 = 2, i3 = 3; 3593 | int i4 = i1 + (i2 = i3); 3594 | System.out.println(i4); 3595 | ``` 3596 | * It will print `4`. 3597 | 3598 | 1. What will the following code print? 3599 | ```java 3600 | String str1 = "str1"; 3601 | String str2 = "str2"; 3602 | System.out.println(str1.concat(str2)); 3603 | System.out.println(str1); 3604 | ``` 3605 | * It will print `str1str2` and `str1`. 3606 | 3607 | 1. What type can be inserted in the code above so that the above code compiles and runs as expected? 3608 | ```java 3609 | Byte condition = 1; 3610 | switch (condition) { 3611 | case 1: 3612 | System.out.println("1"); 3613 | break; 3614 | case 2: 3615 | System.out.println("2"); 3616 | break; 3617 | case 3: 3618 | System.out.println("3"); 3619 | break; 3620 | } 3621 | ``` 3622 | * The types `var condition = new Integer("1")` and `Byte condition = 1` types would allow the code to compile and run. Only String, byte, char, short, int, and their wrapper classes, and enums can be used as types of a switch variable. 3623 | 3624 | 1. What will the following lines of code print? 3625 | ```java 3626 | System.out.println(1 + 5 < 3 + 7); 3627 | System.out.println((2 + 2) >= 2 + 3); 3628 | ``` 3629 | * It will print `true` and `false`. 3630 | 3631 | 1. Which of the following options will empty the contents of the StringBuilder referred to by variable `sb` and method *dumpLog()*? 3632 | ```java 3633 | public class Logger { 3634 | private StringBuilder sb = new StringBuilder(); 3635 | 3636 | public void logMsg(String location, String message) { 3637 | sb.append(location); 3638 | sb.append("-"); 3639 | sb.append(message); 3640 | } 3641 | 3642 | public void dumpLog() { 3643 | System.out.println(sb.toString()); 3644 | // Empty the contents of sb here 3645 | } 3646 | } 3647 | ``` 3648 | * The `sb.delete(0, sb.length())` method can be used. 3649 | 3650 | 1. Which of the above variables will have the value 45? 3651 | ```java 3652 | int expr1 = 3 + 5 * 9 - 7; 3653 | int expr2 = 3 + (5 * 9) - 7; 3654 | int expr3 = 3 + 5 * (9 - 7); 3655 | int expr4 = (3 + 5) * 9 - 7; 3656 | ``` 3657 | * None of these variables will have the value 45. 3658 | 3659 | 1. What will the following program print? 3660 | ```java 3661 | public class TestClass { 3662 | static String str = "Hello World"; 3663 | 3664 | public static void changeIt(String s) { 3665 | s = "Good bye world"; 3666 | } 3667 | 3668 | public static void main(String[] args) { 3669 | changeIt(str); 3670 | System.out.println(str); 3671 | } 3672 | } 3673 | ``` 3674 | * It will print `Hello World` as the value of `s` is not returned and assigned to `str`. 3675 | 3676 | 1. What will be the output of the following code snippet? 3677 | ```java 3678 | int a = 1; 3679 | int[] ia = new int[10]; 3680 | int b = ia[a]; 3681 | int c = b + a; 3682 | System.out.println(b = c); 3683 | ``` 3684 | * The output will be `1`. 3685 | 3686 | ### Java Object Oriented Approach 3687 | 3688 | ### Controlling Program Flow 3689 | 3690 | ### Arrays and Collections 3691 | 3692 | ### Exception Handlng 3693 | 3694 | ### Concurrency 3695 | 3696 | ### Java I/O + NIO 3697 | 3698 | ### Modules 3699 | 3700 | ### JDBC 3701 | 3702 | ### Streams and Lambda 3703 | 3704 | ### Localization 3705 | 3706 | ### Annotations 3707 | 3708 | ### Security --------------------------------------------------------------------------------