ocp

OCP Chapter 22

Security

LIMITING ACCESSIBILITY

package animals.security;
public class ComboLocks {
   public Map<String, String> combos;
}

combos object has public access. This is also poor encapsulation. A key security principle is to limit access as much as possible. Think of it as “need to know” for objects. This is called the principle of least privilege.

better:

package animals.security;
public class ComboLocks {
private Map<String, String> combos;
   public boolean isComboValid(String animal, String combo) {
      var correctCombo = combos.get(animal);
      return combo.equals(correctCombo);
} }

RESTRICTING EXTENSIBILITY

public class GrasshopperCage {
   public static void openLock(ComboLocks comboLocks, String
combo) {
      if (comboLocks.isComboValid("grasshopper", combo))
            System.out.println("Open!");
} }

// nu is mogelijk
public class EvilComboLocks extends ComboLocks {
   public boolean isComboValid(String animal, String combo) {
      var valid = super.isComboValid(animal, combo);
      if (valid) {
         // email the password to Hacker Harry
      }
      return valid;
   }
}

// nu is niet meer mogelijk

public **final** class ComboLocks { private Map<String, String> combos;
   // instantiate combos object
   public boolean isComboValid(String animal, String combo) {
      var correctCombo = combos.get(animal);
      return combo.equals(correctCombo);
} }

CREATING IMMUTABLE OBJECTS

Although there are a variety of techniques for writing an immutable class, you should be familiar with a common strategy for making a class immutable. 1. Mark the class as final. 2. Mark all the instance variables private. 3. Don't define any setter methods and make fields final. 4. Don't allow referenced mutable objects to be modified. 5. Use a constructor to set all properties of theo bject,making a copy if needed.

  import java.util.*;

  public final class Animal {

        private final ArrayList<String> favoriteFoods;

        public Animal() {
           this.favoriteFoods = new ArrayList<String>();
           this.favoriteFoods.add("Apples");

     }

    public List<String> getFavoriteFoods() {
        return favoriteFoods;
    }
}
// kwetsbaar omdat favoriteFoods gewijzigd kan worden bv:  getFavoriteFoods().clear()

// dit zou al helpen ipv vorige getter
public int getFavoriteFoodsCount() {
    return favoriteFoods.size();
}
public String getFavoriteFoodsElement(int index) {
   return favoriteFoods.get(index);
}

// of
public ArrayList<String> getFavoriteFoods() {
   return new ArrayList<String>(this.favoriteFoods);
}

Let's say we want to allow the user to provide the favoriteFoods data, so we implement the following:


public Animal(ArrayList<String> favoriteFoods) { if(favoriteFoods == null) throw new RuntimeException("favoriteFoods is this.favoriteFoods = favoriteFoods; } public int getFavoriteFoodsCount() { return favoriteFoods.size(); } public String getFavoriteFoodsElement(int index) { return favoriteFoods.get(index); } kan dan en dan is favfoods niet meer immutable: void modifyNotSoImmutableObject() { var favorites = new ArrayList<String>(); favorites.add("Apples"); var animal = new Animal(favorites); System.out.print(animal.getFavoriteFoodsCount()); favorites.clear(); System.out.print(animal.getFavoriteFoodsCount()); } // The solution is to use a copy constructor to make a copy of the list object containing the same elements. public Animal(List<String> favoriteFoods) { if(favoriteFoods == null) throw new RuntimeException("favoriteFoods is required"); this.favoriteFoods = new ArrayList<String> (favoriteFoods); }

CLONING OBJECTS

ava has a Cloneable interface that you can implement if you want classes to be able to call the clone() method on your object. This helps with making defensive copies.

this.favoriteFoods = (ArrayList) favoriteFoods.clone();

// dan Clonable gebruyiken:
public final class Animal implements Cloneable {

//en
public static void main(String... args) throws Exception {
   ArrayList<String> food = new ArrayList<>();
   food.add("grass");
   Animal sheep = new Animal(food);
   Animal clone = (Animal) sheep.clone();
   System.out.println(sheep == clone);
   System.out.println(sheep.favoriteFoods == clone.favoriteFoods);
}

By default, the clone() method makes a shallow copy of the data, which means only the top‐level object references and primitives are copied. No new objects from within the cloned object are created.

By contrast, you can write an implementation that does a deep copy and clones the objects inside. A deep copy does make a new ArrayList object. Changes to the cloned object do not affect the original.


public Animal clone() { ArrayList<String> listClone = (ArrayList) favoriteFoods.clone(); return new Animal(listClone); }

myObject.clone() | V Implements Clonable? -> No -> Throws Exception | V Overrides Clone() -> No -> Shallow copy | V Implementation dependent

In the last block, implementation‐dependent means you should probably check the Javadoc of the overridden clone() method before using it.

Shallow Copy

A shallow copy of an object is a new instance of that object where the fields of the original object are copied as they are. However, if the object contains references to other objects (i.e., non-primitive fields), only the references are copied, not the actual objects they refer to.

        Address address = new Address("New York");
        Person person1 = new Person("John", address);
        Person person2 = (Person) person1.clone();

        System.out.println(person1.address.city); // Outputs: New York
        System.out.println(person2.address.city); // Outputs: New York

        person2.address.city = "Los Angeles";

        System.out.println(person1.address.city); // Outputs: Los Angeles
        System.out.println(person2.address.city); // Outputs: Los Angeles

In this example, person1 and person2 are separate objects, but they share the same Address object. If you change the city in person2, it will also change in person1, demonstrating that only a shallow copy was made.

Deep Copy

A deep copy of an object involves creating a new object and also recursively copying all objects referenced by the original object. This means that the copy and the original object do not share references to any mutable objects. Any changes made to the deep-copied object will not affect the original object.

// nu in person.class:
@Override
    protected Object clone() throws CloneNotSupportedException {
        Person clonedPerson = (Person) super.clone();
        clonedPerson.address = new Address(this.address); // Deep copy of Address
        return clonedPerson;
    }

// en dan
Address address = new Address("New York");
        Person person1 = new Person("John", address);
        Person person2 = (Person) person1.clone();

        System.out.println(person1.address.city); // Outputs: New York
        System.out.println(person2.address.city); // Outputs: New York

        person2.address.city = "Los Angeles";

        System.out.println(person1.address.city); // Outputs: New York
        System.out.println(person2.address.city); // Outputs: Los Angeles

Shallow copying is faster and uses less memory, but deep copying ensures that the two objects are entirely independent of each other.

Introducing Injection and Input Validation

Injection is an attack where dangerous input runs in a program as part of a command. Kan bv met Statement met raw SQL.

"monday' OR day IS NOT NULL OR day = 'sunday"

An exploit is an attack that takes advantage of weak security.

There are many sources of untrusted data. For the exam, you need to be aware of 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.

Using PreparedStatement

If you remember only two things about SQL and security, remember to use a PreparedStatement and bind variables.

INVALIDATING INVALID INPUT WITH VALIDATION

SQL injection isn't the only type of injection. Command injection is another type that uses operating system commands to do something unexpected.

Console console = System.console();
String dirName = console.readLine();
Path path = Paths.get("c:/data/diets/" + dirName);
try (Stream<Path> stream = Files.walk(path)) {
   stream.filter(p -> p.toString().endsWith(".txt"))
      .forEach(System.out::println);
}
// als je input .. is dan zie je de secrets dir... dus validate input

// bv whitelist
`if (dirName.equals("mammal") || dirName.equals("birds")) {
`


A blacklist is a list of things that aren't allowed. In the previous example, we could have put the dot ( .) character on a blacklist. The problem with a blacklist is that you have to be cleverer than the bad guys. There are a lot of ways to cause harm. For example, you can encode characters.

By contrast, the whitelist is specifying what is allowed. You can supply a list of valid characters. Whitelisting is preferable to blacklisting for security because a whitelist doesn't need to foresee every possible problem.

Working with Confidential Information

GUARDING SENSITIVE DATA FROM OUTPUT

The first step is to avoid putting confidential information in a toString() method. Dus oppassen bij - Writing to a log file - Printing an exception or stack trace - System.out and System.err messages - Writing to data files

PROTECTING DATA IN MEMORY

if crashes:

When calling the readPassword() on Console, it returns a char[] instead of a String. This is safer for two reasons. - It is not stored as a String, so Java won't place it in the String pool, where it could exist in memory long after the code that used it is run. - You can null out the value of the array element rather than waiting for the garbage collector to do it.

Console console = System.console();
char[] password = console.readPassword();
Arrays.fill(password, 'x');

When the sensitive data cannot be overwritten, it is good practice to set confidential data to null when you're done using it. If the data can be garbage collected, you don't have to worry about it being exposed later.

LocalDate dateOfBirth = getDateOfBirth();
// use date of birth
dateOfBirth = null;

The idea is to have confidential data in memory for as short a time as possible.

LIMITING FILE ACCESS

Another way is to use a security policy to control what the program can access.

It is good to apply multiple techniques to protect your application. This approach is called defense in depth.

// permissie read only
grant {
   permission java.io.FilePermission
    "C:\\water\\fish.txt",
    "read";
};

// permissie read write
grant {
   permission java.io.FilePermission
    "C:\\water\\fish.txt",
    "read, write";
};


When looking at a policy, pay attention to whether the policy grants access to more than is needed to run the program. If our application needs to read a file, it should only have read permissions. This is the principle of least privilege we showed you earlier.

Serializing and Deserializing Objects

Imagine we 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 so without writing any potentially sensitive data to disk. From Chapter 19, you should already know how to do this with serialization.

import java.io.*;
public class Employee implements Serializable {
   private String name;
   private int age;
   // Constructors/getters/setters
}

SPECIFYING WHICH FIELDS TO SERIALIZE

marking a field as transient prevents it from being serialized.

private transient int age;

// Alternatively, you can specify fields to be serialized in an array.

private static final ObjectStreamField[]
serialPersistentFields =
   { new ObjectStreamField("name", String.class) };

You can think of serialPersistentFields as the opposite of transient. The former is a whitelist of fields that should be serialized, while the latter is a blacklist of fields that should not.

If you go with the array approach, make sure you remember to use the private, static, and final modifiers. Otherwise, the field will be ignored.

CUSTOMIZING THE SERIALIZATION PROCESS

Security may demand custom serialization.

Take a look at the following implementation that uses writeObject() and readObject() for serialization

import java.io.*;
public class Employee implements Serializable {
   private String name;
   private String ssn;
   private int age;

   // Constructors/getters/setters

   private static final ObjectStreamField[] serialPersistentFields =
            { new ObjectStreamField("name", String.class), new ObjectStreamField("ssn", String.class) };

    private static String encrypt(String input) {
      // Implementation omitted
    }
    private static String decrypt(String input) {
      // Implementation omitted
    }

    private void writeObject(ObjectOutputStream s) throws Exception {
          ObjectOutputStream.PutField fields = s.putFields();
          fields.put("name", name);
          fields.put("ssn", encrypt(ssn));
          s.writeFields();
    }

    private void readObject(ObjectInputStream s) throws Exception {
            ObjectInputStream.GetField fields = s.readFields();

            this.name = (String)fields.get("name", null);
            this.ssn = decrypt((String)fields.get("ssn", null));
    }

}

This version skips the age variable as before, although this time without using the transient modifier. It also uses custom read and write methods to securely encrypt/decrypt the Social Security number. Notice the PutField and GetField classes are used in order to write and read the fields easily.

PRE/POST‐SERIALIZATION PROCESSING

import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Employee implements Serializable {
   ...
   private Employee() {}
   private static Map<String,Employee> pool =
      new ConcurrentHashMap<>();
   public synchronized static Employee getEmployee(String
name) {
      if(pool.get(name)==null) {
         var e = new Employee();
         e.name = name;         pool.put(name, e);
}
      return pool.get(name);
   }
}

This method creates a new Employee if one does not exist. Otherwise, it returns the one stored in the memory pool.

Applying readResolve()

Now we want to start reading/writing the employee data to disk, but we have a problem. When someone reads the data from the disk, it deserializes it into a new object, not the one in memory pool. This could result in two users holding different versions of the Employee in memory!

Enter the readResolve() method. When this method is present, it is run after the readObject() method and is capable of replacing the reference of the object returned by deserialization.

import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Employee implements Serializable {
   ...
    public synchronized Object readResolve() throws ObjectStreamException {
      var existingEmployee = pool.get(name);
      if(pool.get(name) == null) {
        // New employee not in memory
        pool.put(name, this);
        return this;
     } else {
        // Existing user already in memory
        existingEmployee.name = this.name;
        existingEmployee.ssn = this.ssn;
        return existingEmployee;
    }
 }
}

If the object is not in memory, it is added to the pool and returned. Otherwise, the version in memory is updated, and its reference is returned. Notice that we added the synchronized modifier to this method. Java allows any method modifiers (except static) for the readResolve() method including any access modifier. This rule applies to writeReplace(), which is up next.

Applying writeReplace()

Now, what if we want to write an Employee record to disk but we don't completely trust the instance we are holding? For example, we want to always write the version of the object in the pool rather than the this instance. By construction, there should be only one version of this object in memory, but for this example let's pretend we're not 100 percent confident of that.

The writeReplace() method is run before writeObject() and allows us to replace the object that gets serialized.

import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Employee implements Serializable {
   ...
    public Object writeReplace() throws ObjectStreamException {
      var e = pool.get(name);
      return e != null ? e : this;
    }
}

This implementation checks whether the object is found in the pool. If it is found in the pool, that version is sent for serialization; otherwise, the current instance is used.

Return type Method Parameters Description
Object writeReplace None Allows replacement of object before serialization
void writeObject() ObjectInputStream Serializes optionally using PutField
void readObject() ObjectOutputStream Deserializes optionally using GetField
Object readResolve() None Allows replacement of object after deserialization

Making Methods final

Making Classes final

Making Constructor private

public class FoodOrder {
   private String item;
   private int count;

    private FoodOrder(String item, int count) { setItem(item);
        setCount(count);
    }
-> public FoodOrder getOrder(String item, int count) {
      return new FoodOrder(item, count);
   }
   public String getItem() { return item; }
   public void setItem(String item) { this.item = item; }
   public int getCount() { return count; }
   public void setCount(int count) { this.count = count; }
}

HOW TO PROTECT THE SOURCE CODE

jars.....

Preventing Denial of Service Attacks

A denial of service (DoS) attack is when a hacker makes one or more requests with the intent of disrupting legitimate requests. Most denial of service attacks require multiple requests to bring down their targets. Some attacks send a very large request that can even bring down the application in one shot.

By contrast, a distributed denial of service (DDoS) attack is a denial of service attack that comes from many sources at once. For example, many machines may attack the target.

LEAKING RESOURCES

READING VERY LARGE RESOURCES

Another source of a denial of service attacks is very large resources.

public void transform(Path in, Path out) throws IOException  {
   var list = Files.readAllLines(in);
   list.removeIf(s -> s.trim().isBlank());
   Files.write(out, list);
}

gaat mis op grote files want je geheugen loopt vol... To prevent this problem, you can check the size of the file before reading it.

INCLUDING POTENTIALLY LARGE RESOURCES

An inclusion attack is when multiple files or components are embedded within a single file. Any file that you didn't create is suspect. Some types can appear smaller than they really are. For example, some types of images can have a “zip bomb” where the file is heavily compressed on disk. When you try to read it in, the file uses much more space than you thought.

Extensible Markup Language (XML) files can have the same problem. One attack is called the “billion laughs attack” where the file gets expanded exponentially.

OVERFLOWING NUMBERS

When checking file size, be careful with an int type and loops. Since an int has a maximum size, exceeding that size results in integer overflow. Incrementing an int at the maximum value results in a negative number, so validation might not work as expected.

public static void main(String[] args) {
   System.out.println(enoughRoomToAddLine(100));
   System.out.println(enoughRoomToAddLine(2_000_000));
   System.out.println(enoughRoomToAddLine(Integer.MAX_VALUE));
}
public static boolean enoughRoomToAddLine(int requestedSize) {
   int maxLength = 1_000_000;
   String newLine = "END OF FILE";
   int newLineSize = newLine.length();
   return requestedSize + newLineSize < maxLength;
}

//true
//false
//true

WASTING DATA STRUCTURES

One advantage of using a HashMap is that you can look up an element quickly by key. Even if the map is extremely large, a lookup is fast as long as there is a good distribution of hashed keys.

  • Identify ways of preventing a denial of service attack. Using a try‐with‐ resources statement for all I/O and JDBC operations prevents resource leaks. Checking the file size when reading a file prevents it from using an unexpected amount of memory. Confirming large data structures are being used effectively can prevent a performance problem.
  • Protect confidential information in memory. Picking a data structure that minimizes exposure is important. The most common one is using char[] for passwords. Additionally, allowing confidential information to be garbage collected as soon as possible reduces the window of exposure.
  • Compare injection, inclusion, and input validation. SQL injection and command injection allow an attacker to run expected commands. Inclusion is when one file includes another. Input validation checks for valid or invalid characters from users.
  • Design secure objects. Secure objects limit the accessibility of instance variables and methods. They are deliberate about when subclasses are allowed. Often secure objects are immutable and validate any input parameters.
  • Write serialization and deserializaton code securely. The transient modifier signifies that an instance variable should not be serialized. Alternatively, serialPersistenceFields specifies what should be. The readObject(), writeObject(), readResolve(), and writeReplace() methods are optional methods that provide further control of the process.

prev next

Tags: 

OCP Chapter 21

JDBC

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).

A relational database is accessed through Structured Query Language (SQL). SQL is a programming language used to interact with database records. JDBC works by sending a SQL command to the database and then processing the response.
SQL keywords are case insensitive.

In addition to relational databases, there is another type of database called a NoSQL database. This is for databases that store their data in a format other than tables, such as key/value, document stores, and graph‐based databases. NoSQL is out of scope for the exam as well.

interfaces in the JDK: - Driver: Establishes a connection to the database - Connection: Sends commands to a database - PreparedStatement: Executes a SQL query - CallableStatement: Executes commands stored in the database - ResultSet: Reads results of a query

public class MyFirstDatabaseConnection {
   public static void main(String[] args) throws SQLException
   {
    String url = "jdbc:derby:zoo";
    try (Connection conn = DriverManager.getConnection(url);
        PreparedStatement ps = conn.prepareStatement( "SELECT name FROM animal");
        ResultSet rs = ps.executeQuery()) {
        while (rs.next())
        System.out.println(rs.getString(1));
    }
   }
}

GETTING A DATABASE CONNECTION

There are two main ways to get a Connection: DriverManager or DataSource. DriverManager is the one covered on the exam. Do not use a DriverManager in code someone is paying you to write. A DataSource has more features than DriverManager. For example, it can pool connections or store the database connection information outside the application.

Connection conn = DriverManager.getConnection("jdbc:derby:zoo");
System.out.println(conn);

It does not actually execute the query yet!

JDBC URL

Unlike web URLs, a JDBC URL has a variety of formats. They have three parts in common, as shown in Figure 21.3. The first piece is always the same. It is the protocol jdbc. The second part is the subprotocol, which is the name of the database such as derby, mysql, or postgres. The third part is the subname, which is a database‐specific format. Colons ( :) separate the three parts.

Notice the three parts. It starts with jdbc and then comes the subprotocol derby, and it ends with the subname, which is the database name. The location is not required, because Derby is an in‐memory database.

jdbc:derby:zoo // The location is not required, because Derby is an in‐memory database.


jdbc:postgresql://localhost/zoo
jdbc:oracle:thin:@123.123.123.123:1521:zoo
jdbc:mysql://localhost:3306
jdbc:mysql://localhost:3306/zoo?profileSQL=true

Statement, CallableStatement, PreparedStatement

            ┌───────────────────┐
            │     Statement     │
            └─────────▲─────────┘
          ┌───────────┴────────────┐
          │                        │
          │                        │
┌───────────────────┐    ┌───────────────────┐
│ PreparedStatement │    │ CallableStatement │
└───────────────────┘    └───────────────────┘


While it is possible to run SQL directly with Statement, you shouldn't. PreparedStatement is far superior for the following reasons: - Performance: In most programs, you run similar queries multiple times. A PreparedStatement figures out a plan to run the SQL well and remembers it. - Security: As you will see in Chapter 22, “Security,” you are protected against an attack called SQL injection when using a PreparedStatement correctly - Readability: It's nice not to have to deal with string concatenation in building a query string with lots of parameters. - Future use: Even if your query is being run only once or doesn't have any parameters, you should still use a PreparedStatement. That way future editors of the code won't add a variable and have to remember to change to PreparedStatement then.

Using the Statement interface is also no longer in scope for the JDBC exam, so we do not cover it in this book.

Prepared Statement

Passing a SQL statement when creating the object is mandatory.

try (var ps = conn.prepareStatement()) { // DOES NOT COMPILE }

correct:

try (
    PreparedStatement ps = conn.prepareStatement( "SELECT * FROM exhibits")) {
        // work with ps
}

Modifying Data with executeUpdate()

10: var insertSql = "INSERT INTO exhibits VALUES(10, 'Deer',
3)";
11: var updateSql = "UPDATE exhibits SET name = '' " +
12:    "WHERE name = 'None'";
13: var deleteSql = "DELETE FROM exhibits WHERE id = 10"; 14:
15: try (var ps = conn.prepareStatement(insertSql)) {
16: int result = ps.executeUpdate();
17:    System.out.println(result); // 1
18: }
19:
20: try (var ps = conn.prepareStatement(updateSql)) {
21: int result = ps.executeUpdate();
22:    System.out.println(result); // 0
23: }
24:
25: try (var ps = conn.prepareStatement(deleteSql)) {
26: int result = ps.executeUpdate();
27:    System.out.println(result); // 1
28: }

Reading Data with executeQuery()

30: var sql = "SELECT * FROM exhibits";
31: try (var ps = conn.prepareStatement(sql); 32: ResultSet rs = ps.executeQuery() ) { 33:
34: // work with rs
35: }

Processing Data with execute()

boolean isResultSet = ps.execute();
if (isResultSet) {
    try (ResultSet rs = ps.getResultSet()) {
        System.out.println("ran a query");
    }
} else {
    int result = ps.getUpdateCount();
    System.out.println("ran an update");
}

PreparedStatment Methods

Method DELETE INSERT SELECT UPDATE Return Type What is returned for SELECT What is returned for DELETE/INSERT/UPDATE
ps.execute() Yes Yes Yes Yes boolean true false
ps.executeQuery() No No Yes No ResultSet The rows and columns returned n/a
ps.executeUpdate() Yes Yes No Yes int n/a Number of rows added/changed/removed

WORKING WITH PARAMETERS

let op: conn.prepareStatment(sql) VS PreparedStatement ps = conn.prepareStatement(sql)

String sql = "INSERT INTO names VALUES(?, ?, ?)";


public static void register(Connection conn, int key,int type, String name) throws SQLException {

    String sql = "INSERT INTO names VALUES(?, ?, ?)";
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setInt(1, key);
        ps.setString(3, name);
        ps.setInt(2, type);
        ps.executeUpdate(); // GEEN SQL meesturen!!!
    }
}

Remember that JDBC starts counting columns with 1 rather than 0. A common exam (and interview) question tests that you know this!

When not having all parameters defined: The code compiles, and you get a SQLException. The message may vary based on your database driver.

PreparedStatments Methods

  • setBoolean
  • setDouble
  • setInt
  • setLong
  • setObject (Any type)
  • setString

COMPILE VS. RUNTIME ERROR WHEN EXECUTING

ps.setObject(1, key);
ps.setObject(2, type);
ps.setObject(3, name);
ps.executeUpdate(sql);  // INCORRECT

The problem is that the last line passes a SQL statement. With a PreparedStatement, we pass the SQL in when creating the object. More interesting is that this does not result in a compiler error. Remember that PreparedStatement extends Statement. The Statement interface does accept a SQL statement at the time of execution, so the code compiles. Running this code gives SQLException. The text varies by database.

UPDATING MULTIPLE TIMES

var sql = "INSERT INTO names VALUES(?, ?, ?)";
try (var ps = conn.prepareStatement(sql)) {
   ps.setInt(1, 20);
   ps.setInt(2, 1);
   ps.setString(3, "Ester");
   ps.executeUpdate();

   ps.setInt(1, 21);
   ps.setString(3, "Elias");
   ps.executeUpdate();
}// WORKS!!

BATCHING STATEMENTS

JDBC supports batching so you can run multiple statements in fewer trips to the database. For example, if you need to insert 1,000 records into the database, then inserting them as a single network call (as opposed to 1,000 network calls) is usually a lot faster. You don't need to know the addBatch() and executeBatch() methods for the exam, but they are useful in practice.

   public static void register(Connection conn, int firstKey, int type, String... names) throws SQLException {
      var sql = "INSERT INTO names VALUES(?, ?, ?)";
      var nextIndex = firstKey;
      try (var ps = conn.prepareStatement(sql)) {
         ps.setInt(2, type);
        for(var name: names) {
            ps.setInt(1, nextIndex);
            ps.setString(3, name);
            ps.addBatch();
            nextIndex++;
        }
        int[] result = ps.executeBatch();
        System.out.println(Arrays.toString(result));
      }
}

register(conn, 100, 1, "Elias", "Ester");

Getting Data from a ResultSet

begin met rs.next()

-> 0 leeg
* row1 * row2

daarom: while(rs.next()

String sql = "SELECT id, name FROM exhibits";
Map<Integer, String> idToNameMap = new HashMap<>();

try (var ps = conn.prepareStatement(sql);
    ResultSet rs = ps.executeQuery()) {
    while (rs.next()) {
        int id = rs.getInt("id");
        String name = rs.getString("name");
        idToNameMap.put(id, name);
    }
    System.out.println(idToNameMap);

}
// results: {1=African Elephant, 2=Zebra}

kan ook met index column. LET OP begint met 1!!!

    int id = rs.getInt(1);
    String name = rs.getString(2);

Attempting to access a column name or index that does not exist throws a SQLException, as does getting data from a ResultSet when it isn't pointing at a valid row.

var sql = "SELECT * FROM exhibits where name='Not in table'";
try (var ps = conn.prepareStatement(sql);
   var rs = ps.executeQuery()) {
   rs.next();
   rs.getInt(1); // SQLException
}
// fout: staat rs.next() staaat nog op 0
var sql = "SELECT count(*) FROM exhibits";
try (var ps = conn.prepareStatement(sql);
   var rs = ps.executeQuery()) {
   rs.getInt(1); // SQLException
}
// fout column 0 bestaat NIET
var sql = "SELECT count(*) FROM exhibits";
try (var ps = conn.prepareStatement(sql);
   var rs = ps.executeQuery()) {
   if (rs.next())
      rs.getInt(0); // SQLException
}

var sql = "SELECT name FROM exhibits";
try (var ps = conn.prepareStatement(sql);
   var rs = ps.executeQuery()) {
   if (rs.next())
   rs.getInt("badColumn"); // SQLException
}

To sum up this section, it is important to remember the following: - Always use an if statement or while loop when calling rs.next(). - Column indexes begin with 1.

ResultSet Return from getObject

16: var sql = "SELECT id, name FROM exhibits";
17: try (var ps = conn.prepareStatement(sql);
18:    var rs = ps.executeQuery()) {
19:
20:    while (rs.next()) {
21: Object idField = rs.getObject("id");
22: Object nameField = rs.getObject("name");
23:       if (idField instanceof Integer) {
24:          int id = (Integer) idField;
25:          System.out.println(id);
26: }
27:       if (nameField instanceof String) {
28:          String name = (String) nameField;

USING BIND VARIABLES

Wanneer er vars gezet moeten worden en uitgepakt dan nesten met try.

30: var sql = "SELECT id FROM exhibits WHERE name = ?";
31:
32: try (var ps = conn.prepareStatement(sql)) {
        ps.setString(1, "Zebra");
        try (var rs = ps.executeQuery()) {
           while (rs.next()) {
              int id = rs.getInt("id");
              System.out.println(id);
           }
        }
41: }

Calling a CallableStatement

Sometimes you want your SQL to be directly in the database instead of packaged with your Java code. This is particularly useful when you have many queries and they are complex. A stored procedure is code that is compiled in advance and stored in the database. Stored procedures are commonly written in a database‐specific variant of SQL, which varies among database software providers.

CALLING A PROCEDURE WITHOUT PARAMETERS

A stored procedure is called by putting the word call and the procedure name in braces ( {}).

12: String sql = "{call read_e_names()}";
13: try (CallableStatement cs = conn.prepareCall(sql);
14: ResultSet rs = cs.executeQuery()) {
15:
16:    while (rs.next()) {
17:       System.out.println(rs.getString(3));
18: }
19: }

PASSING AN IN PARAMETER

We have to pass a ? to show we have a parameter. This should be familiar from bind variables with a PreparedStatement.

25: var sql = "{call read_names_by_letter(?)}"; 26: try (var cs = conn.prepareCall(sql)) {
        cs.setString("prefix", "Z");
        try (var rs = cs.executeQuery()) {
           while (rs.next()) {
              System.out.println(rs.getString(3));
           }
        }
}

// => cs.setString(1, "Z"); === cs.setString("prefix", "Z");

RETURNING OUT PARAM

40: var sql = "{?= call magic_number(?) }";
41: try (var cs = conn.prepareCall(sql)) {
42: cs.registerOutParameter(1, Types.INTEGER);
43: cs.execute();
44:    System.out.println(cs.getInt("num"));
45: }

On line 40, we included two special characters ( ?=) to specify that the stored procedure has an output value. This is optional since we have the OUT parameter, but it does aid in readability.

On line 42, we register the OUT parameter. This is important. It allows JDBC to retrieve the value on line 44. Remember to always call registerOutParameter() for each OUT or INOUT parameter (which we will cover next).

WORKING WITH AN INOUT PARAMETER

50: var sql = "{call double_number(?)}"; 51: try (var cs = conn.prepareCall(sql)) {
52: cs.setInt(1, 8);
53: cs.registerOutParameter(1, Types.INTEGER);
54: cs.execute();
55:    System.out.println(cs.getInt("num"));
56: }

COMPARING CALLABLE STATEMENT PARAMETERS

Stored procedure parameter types IN OUT INOUT
Used for input Yes No Yes
Used for output No Yes Yes
Must set parameter value Yes No Yes
Must call registerOutParameter() No Yes Yes
Can include ?= No Yes Yes

Closing Database Resources

it is important to close resources when you are finished with them. This is true for JDBC as well. JDBC resources, such as a Connection, are expensive to create. Not closing them creates a resource leak that will eventually slow down your program.

Closing a JDBC resource should close any resources that it created. In particular, the following are true: - Closing a Connection also closes PreparedStatement (or CallableStatement) and ResultSet. - Closing a PreparedStatement (or CallableStatement) also closes the ResultSet

you learned that it is possible to declare a type before a try‐with‐resources statement. Do you see why this method is bad? ``` 40: public void bad() throws SQLException { 41: var url = "jdbc:derby:zoo"; 42: var sql = "SELECT not_a_column FROM names"; 43: var conn = DriverManager.getConnection(url); 44: var ps = conn.prepareStatement(sql); 45: var rs = ps.executeQuery(); 46: 47: try (conn; ps; rs) { 48: while (rs.next()) 49: System.out.println(rs.getString(1)); 50: } 51: }

Suppose an exception is thrown on line 45. The try‐with‐resources block is never entered, so we don't benefit from automatic resource closing. That means this code has a resource leak if it fails. Do not write code like this.


There's another way to close a ResultSet. JDBC automatically closes a ResultSet when you run another SQL statement from the same Statement. This could be a PreparedStatement or a CallableStatement.

How many are closed?

14: var url = "jdbc:derby:zoo"; 15: var sql = "SELECT count(*) FROM names where id = ?"; 16: try (var conn = DriverManager.getConnection(url); 17: var ps = conn.prepareStatement(sql)) { 18: 19: ps.setInt(1, 1); 20: 21: var rs1 = ps.executeQuery(); 22: while (rs1.next()) { 23: System.out.println("Count: " + rs1.getInt(1)); 24: } 25: 26: ps.setInt(1, 2); 27: 28: var rs2 = ps.executeQuery(); 29: while (rs2.next()) { 30: System.out.println("Count: " + rs2.getInt(1)); 31: } 32: rs2.close();

``` !!!The correct answer is four!!! On line 28, rs1 is closed because the same PreparedStatement runs another query. On line 32, rs2 is closed in the method call. Then the try‐with‐resources statement runs and closes the PreparedSatement and Connection objects.

prev next

Tags: 

OCP Chapter 20

NIO

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.”. At its core, NIO.2 is a replacement for the legacy java.io.File class you learned about in Chapter 19. The goal of the API is to provide a more intuitive, more feature‐rich API for working with files and directories.

The cornerstone of NIO.2 is the java.nio.file.Path interface. A Path instance represents a hierarchical path on the storage system to a file or directory. You can think of a Path as the NIO.2 replacement for the java.io.File class, although how you use it is a bit different. Both java.io.File and Path objects may refer to an absolute path or relative path within the file system. In addition, both may refer to a file or a directory.

Unlike the java.io.File class, the Path interface contains support for symbolic links. A symbolic link is a special file within a file system that serves as a reference or pointer to another file or directory. In general, symbolic links are transparent to the user, as the operating system takes care of resolving the reference to the actual file. NIO.2 includes full support for creating, detecting, and navigating symbolic links within the file system.

CREATING PATHS

Since Path is an interface, we can't create an instance directly. After all, interfaces don't have constructors! Java provides a number of classes and methods that you can use to obtain Path objects.

why is Path an interface? When a Path is created, the JVM returns a file system–specific implementation, such as a Windows or Unix Path class. In the vast majority of circumstances, we want to perform the same operations on the Path, regardless of the file system. By providing Path as an interface using the factory pattern, we avoid having to write complex or custom code for each type of file system.

` // Path factory method public static Path of(String first, String... more)

// impl Path path1 = Path.of("pandas/cuddly.png"); Path path2 = Path.of("c:\zooinfo\November\employees.txt"); Path path3 = Path.of("/home/zoodirectory"); ` The first example creates a reference to a relative path in the current working directory. The second example creates a reference to an absolute file path in a Windows‐based system. The third example creates a reference to an absolute directory path in a Linux or Mac‐based system.

ABSOLUTE VS. RELATIVE PATHS

  • If a path starts with a forward slash ( /), it is absolute, with / as the root directory. Examples: /bird/parrot.png and /bird/../data/./info
  • If a path starts with a drive letter ( c:), it is absolute, with the drive letter as the root directory. Examples: c:/bird/parrot.png and d:/bird/../data/./info
  • Otherwise, it is a relative path. Examples: bird/parrot.png and bird/../data/./info

The Path.of() method also includes a varargs to pass additional path elements. The values will be combined and automatically separated by the operating system–dependent file separator.

Path path1 = Path.of("pandas", "cuddly.png");
Path path2 = Path.of("c:", "zooinfo", "November", "employees.txt");
Path path3 = Path.of("/", "home", "zoodirectory");

Obtaining a Path with the Paths Class (met zonder S)

The Path.of() method is actually new to Java 11. Another way to obtain a Path instance is from the java.nio.file.Paths factory class. Note the s at the end of the Paths class to distinguish it from the Path interface.

Path path1 = Paths.get("pandas/cuddly.png");
Path path2 = Paths.get("c:\\zooinfo\\November\\employees.txt");
Path path3 = Paths.get("/", "home", "zoodirectory");

Obtaining a Path with a URI Class

Another way to construct a Path using the Paths class is with a URI value. A uniform resource identifier (URI) is a string of characters that identify a resource. It begins with a schema that indicates the resource type, followed by a path value. Examples of schema values include file:// for local file systems, and http://, https://, and ftp:// for remote file systems. The java.net .URI class is used to create URI values.

// URI Constructor

public URI(String str) throws URISyntaxException

// URI to Path, using Path factory method

public static Path of(URI uri)

// URI to Path, using Paths factory method

public static Path get(URI uri)

// Path to URI, using Path instance method

public URI toURI()

The following examples all reference the same file:

URI a = new URI("file://icecream.txt");
Path b = Path.of(a);
Path c = Paths.get(a);
URI d = b.toUri();

Some of these examples may actually throw an IllegalArgumentException at runtime, as some systems require URIs to be absolute. The URI class does have an isAbsolute() method, although this refers to whether the URI has a schema, not the file location.

A URI can be used for a web page or FTP connection.

Path path5 = Paths.get(new URI("http://www.wiley.com"));
Path path6 = Paths.get(new URI("ftp://username:secret@ftp.example.com"));

Obtaining a Path from the FileSystem Class

NIO.2 makes extensive use of creating objects with factory classes. As you saw already, the Paths class creates instances of the Path interface. Likewise, the FileSystems class creates instances of the abstract FileSystem class.

Path path1 = FileSystems.getDefault().getPath("pandas/cuddly.png");
Path path2 = FileSystems.getDefault().getPath("c:\\zooinfo\\November\\employees.txt");
Path path3 = FileSystems.getDefault().getPath("/home/zoodirectory");

CONNECTING TO REMOTE FILE SYSTEMS

While most of the time we want access to a Path object that is within the local file system, the FileSystems class does give us the freedom to connect to a remote file system, as follows:

// FileSystems factory method
public static FileSystem getFileSystem(URI uri)

The following shows how such a method can be used:

FileSystem fileSystem = FileSystems.getFileSystem( new URI("http://www.selikoff.net"));
Path path = fileSystem.getPath("duck.txt");

This code is useful when we need to construct Path objects frequently for a remote file system. NIO.2 gives us the power to connect to both local and remote file systems, which is a major improvement over the legacy java.io.File class.

Obtaining a Path from the java.io.File Class

Last but not least, we can obtain Path instances using the legacy java.io.File class. In fact, we can also obtain a java.io.File object from a Path instance.

// Path to File, using Path instance method
public default File toFile()
// File to Path, using java.io.File instance method
public Path toPath()

File file = new File("husky.png");
Path path = file.toPath();
File backToFile = path.toFile();

Reviewing NIO.2 Relationships

By now, you should realize that NIO.2 makes extensive use of the factory pattern. You should become comfortable with this paradigm. Many of your interactions with NIO.2 will require two types: an abstract class or interface and a factory or helper class. The diagram shows the relationships among NIO.2 classes, as well as select java.io and java.net classes. diagram:

              Creates

┌─────────────┐    ┌─────────────┐                       ┌─────────────┐
│ FileSystems │────▶ FileSystems │◀──────────┐         ▲ │java.io.File │
└─────────────┘    └─────────────┘           │         │ └─────────────┘
                                             ├─────────┘
                                             │
                                             ▼
                                      ┌─────────────┐
                             Creates  │             │Converts
                                  ┌──▶│    Path     │◀─┐
                                  │   │             │  │
                 ┌─────────────┐  │   └─────────────┘  │  ┌─────────────┐
                 │    Paths    │──┘          ▲         └─▶│java.net.URI │
                 └─────────────┘             │            └─────────────┘
                                        Uses │
                                      ┌─────────────┐
                                      │    Files    │
                                      └─────────────┘

When working with NIO.2, keep an eye on whether the class name is singular or plural. The classes with plural names include methods to create or operate on class/interface instances with singular names. Remember, a Path can also be created from the Path interface, using the static factory of() method.

Included in the diagram is the class java.nio.file.Files. For now, you just need to know that it is a helper or utility class that operates primarily on Path instances to read or modify actual files and directories.

UNDERSTANDING COMMON NIO.2 FEATURES

Applying Path Symbols

Absolute and relative paths can contain path symbols. A path symbol is a reserved series of characters that have special meaning within some file systems.

Symbol Description
. A reference to the current directory
.. A reference to the parent of the current directory

Providing Optional Arguments

Many of the methods in this chapter include a varargs that takes an optional list of values.

Common NIO.2 method arguments

Enum type Interface inherited Enum value Details
LinkOption Copy Option Open Option NOFOLLOW_LINKS Do not follow symbolic links.
Standard CopyOption Copy Option ATOMIC_MOVE Move file as atomic file system operation.
Standard CopyOption Copy Option COPY_ATTRIBUTES Copy existing attributes to new file.
Standard CopyOption Copy Option REPLACE_EXISTING Overwrite file if it already exists.
Standard OpenOption Open Option APPEND If file is already open for write, then append to the end.
Standard OpenOption Open Option CREATE Create a new file if it does not exist.
Standard OpenOption Open Option CREATE_NEW Create a new file only if it does not exist, fail otherwise.
Standard OpenOption Open Option READ Open for read access.
Standard OpenOption Open Option TRUNCATE_EXISTING If file is already open for write, then erase file and append to beginning.
Standard OpenOption Open Option WRITE Open for write access.
FileVisitOption N/A FOLLOW_LINK Follow symbolic links.

Path path = Paths.get("schedule.xml");
boolean exists = Files.exists(path, LinkOption.NOFOLLOW_LINKS);

The Files.exists() simply checks whether a file exists. If the parameter is a symbolic link, though, then the method checks whether the target of the symbolic link exists instead. Providing LinkOption.NOFOLLOW_LINKS means the default behavior will be overridden, and the method will check whether the symbolic link itself exists.

some methods accept a variety of enums types. For example, the Files.move() method takes a CopyOption vararg so it can take enums of different types.

void copy(Path source, Path target) throws IOException {
   Files.move(source, target,
    LinkOption.NOFOLLOW_LINKS,
    StandardCopyOption.ATOMIC_MOVE);
}

Many of the NIO.2 methods use a varargs for passing options, even when there is only one enum value available. The advantage of this approach, as opposed to say just passing a boolean argument, is future‐proofing. These method signatures are insulated from changes in the Java language down the road when future options are available.

Handling Methods That Declare IOException

  • Loss of communication to underlying file system.
  • File or directory exists but cannot be accessed or modified.
  • File exists but cannot be overwritten.
  • File or directory is required but does not exist.

In general, methods that operate on abstract Path values, such as those in the Path interface or Paths class, often do not throw any checked exceptions. On the other hand, methods that operate or change files and directories, such as those in the Files class, often declare IOException. There are exceptions to this rule, as we will see. For example, the method Files.exists() does not declare IOException. If it did throw an exception when the file did not exist, then it would never be able to return false!

Interacting with Paths

Just like String values, Path instances are immutable. In the following example, the Path operation on the second line is lost since p is immutable:

Path p = Path.of("whale");
p.resolve("krill");
System.out.println(p);  // whale (!)

// chaining
Path.of("/zoo/../home").getParent().normalize().toAbsolutePath();

VIEWING THE PATH WITH TOSTRING(), GETNAMECOUNT(), AND GETNAME()

public String toString()
public int getNameCount()
public Path getName(int index)

The first method, toString(), returns a String representation of the entire path. In fact, it is the only method in the Path interface to return a String. Many of the other methods in the Path interface return Path instances.

The getNameCount() and getName() methods are often used in conjunction to retrieve the number of elements in the path and a reference to each element, respectively. These two methods do not include the root directory as part of the path.

Path path = Paths.get("/land/hippo/harry.happy");
System.out.println("The Path Name is: " + path);
for(int i=0; i<path.getNameCount(); i++) {
    System.out.println(" Element " + i + " is: " + path.getName(i));
}

The Path Name is: /land/hippo/harry.happy
   Element 0 is: land
   Element 1 is: hippo
   Element 2 is: harry.happy

Even though this is an absolute path, the root element is not included in the list of names. As we said, these methods do not consider the root as part of the path.

var p = Path.of("/");
System.out.print(p.getNameCount()); // 0
System.out.print(p.getName(0)); // IllegalArgumentException

CREATING A NRE PATH WITH SUBPATH()

The references are inclusive of the beginIndex, and exclusive of the endIndex. The subpath() method is similar to the previous getName() method, except that subpath() may return multiple path components, whereas getName() returns only one. Both return Path instances, though.

public Path subpath(int beginIndex, int endIndex)


var p = Paths.get("/mammal/omnivore/raccoon.image");
System.out.println("Path is: " + p);
for (int i = 0; i < p.getNameCount(); i++) {
    System.out.println(" Element " + i + " is: " + p.getName(i));
}
System.out.println();
System.out.println("subpath(0,3): " + p.subpath(0, 3));
System.out.println("subpath(1,2): " + p.subpath(1, 2));
System.out.println("subpath(1,3): " + p.subpath(1, 3));

Path is: /mammal/omnivore/raccoon.image
   Element 0 is: mammal
   Element 1 is: omnivore
   Element 2 is: raccoon.image
subpath(0,3): mammal/omnivore/raccoon.image
subpath(1,2): omnivore
subpath(1,3): omnivore/raccoon.image

Like getNameCount() and getName(), subpath() is 0‐indexed and does not include the root. Also like getName(), subpath() throws an exception if invalid indices are provided.

var q = p.subpath(0, 4); // IllegalArgumentException
var x = p.subpath(1, 1); // IllegalArgumentException

The first example throws an exception at runtime, since the maximum index value allowed is 3. The second example throws an exception since the start and end indexes are the same, leading to an empty path value.

ACCESSING PATH ELEMENTS WITH GETFILENAME(), GETPARENT(), AND GETROOT()

The getFileName() returns the Path element of the current file or directory, while getParent() returns the full path of the containing directory. The getParent() returns null if operated on the root path or at the top of a relative path. The getRoot() method returns the root element of the file within the file system, or null if the path is a relative path.

public void printPathInformation(Path path) {
    System.out.println("Filename is: " + path.getFileName());
    System.out.println(" Root is: " + path.getRoot());
    Path currentParent = path;
    while((currentParent = currentParent.getParent()) != null)
    {
      System.out.println("   Current parent is: " + currentParent);
    }
}

printPathInformation(Path.of("zoo"));
printPathInformation(Path.of("/zoo/armadillo/shells.txt"));
printPathInformation(Path.of("./armadillo/../shells.txt"));


Filename is: zoo
   Root is: null
Filename is: shells.txt
   Root is: /
   Current parent is: /zoo/armadillo
   Current parent is: /zoo
   Current parent is: /
Filename is: shells.txt
   Root is: null
   Current parent is: ./armadillo/..
   Current parent is: ./armadillo
   Current parent is: .

The while loop in the printPathInformation() method continues until getParent() returns null.

CHECKING PATH TYPE WITH ISABSOLUTE() AND TOABSOLUTEPATH()

The first method, isAbsolute(), returns true if the path the object references is absolute and false if the path the object references is relative. As discussed earlier in this chapter, whether a path is absolute or relative is often file system–dependent, although we, like the exam writers, adopt common conventions for simplicity throughout the book. The second method, toAbsolutePath(), converts a relative Path object to an absolute Path object by joining it to the current working directory. If the Path object is already absolute, then the method just returns the Path object.

var path1 = Paths.get("C:\\birds\\egret.txt");
System.out.println("Path1 is Absolute? " + path1.isAbsolute());
System.out.println("Absolute Path1: " + path1.toAbsolutePath());
var path2 = Paths.get("birds/condor.txt");
System.out.println("Path2 is Absolute? " + path2.isAbsolute());
System.out.println("Absolute Path2 " + path2.toAbsolutePath());

Path1 is Absolute? true
Absolute Path1: C:\birds\egret.txt
Path2 is Absolute? false
Absolute Path2 /home/work/birds/condor.txt

JOINING PATHS WITH RESOLVE()

public Path resolve(Path other)
public Path resolve(String other)

Path path1 = Path.of("/cats/../panther");
Path path2 = Path.of("food");
System.out.println(path1.resolve(path2));
/cats/../panther/food

Path path3 = Path.of("/turkey/food");
System.out.println(path3.resolve("/tiger/cage"));
// Since the input parameter path3 is an absolute path, the output would be the following:
/tiget/cage

If an absolute path is provided as input to the method, then that is the value that is returned. Simply put, you cannot combine two absolute paths using resolve(). when you see Files.resolve(), think concatenation.

DERIVING A PATH WITH RELATIVIZE()

The Path interface includes a method for constructing the relative path from one Path to another, often using path symbols.

var path1 = Path.of("fish.txt");
var path2 = Path.of("friendly/birds.txt");
System.out.println(path1.relativize(path2));
System.out.println(path2.relativize(path1));

../friendly/birds.txt
../../fish.txt

The idea is this: if you are pointed at a path in the file system, what steps would you need to take to reach the other path? For example, to get to fish.txt from friendly/birds.txt, you need to go up two levels (the file itself counts as one level) and then select fish.txt.

If both path values are relative, then the relativize() method computes the paths as if they are in the same current working directory.

Remember, most methods defined in the Path interface do not require the path to exist.

The relativize() method requires that both paths are absolute or both relative and throws an exception if the types are mixed.

Path path1 = Paths.get("/primate/chimpanzee");
Path path2 = Paths.get("bananas.txt");
path1.relativize(path2); // IllegalArgumentException

CLEANING UP A PATH WITH NORMALIZE()

So far, we've presented a number of examples that included path symbols that were unnecessary. Luckily, Java provides a method to eliminate unnecessary redundancies in a path

var p1 = Path.of("./armadillo/../shells.txt"); System.out.println(p1.normalize()); // shells.txt
var p2 = Path.of("/cats/../panther/food"); System.out.println(p2.normalize()); // /panther/food
var p3 = Path.of("../../fish.txt"); System.out.println(p3.normalize()); // ../../fish.txt


var p1 = Paths.get("/pony/../weather.txt");
var p2 = Paths.get("/weather.txt");
System.out.println(p1.equals(p2)); // false
System.out.println(p1.normalize().equals(p2.normalize())); // true

he equals() method returns true if two paths represent the same value. In the first comparison, the path values are different. In the second comparison, the path values have both been reduced to the same normalized value, /weather.txt. This is the primary function of the normalize() method, to allow us to better compare different paths.

RETRIEVING THE FILE SYSTEM PATH WITH TOREALPATH()

This method is similar to normalize(), in that it eliminates any redundant path symbols. It is also similar to toAbsolutePath(), in that it will join the path with the current working directory if the path is relative.

Unlike those two methods, though, toRealPath() will throw an exception if the path does not exist. In addition, it will follow symbolic links, with an optional varargs parameter to ignore them.

System.out.println(Paths.get("/zebra/food.txt").toRealPath());
System.out.println(Paths.get(".././food.txt").toRealPath());

/horse/food.txt

REVIEWING PATH METHODS

Path methods Path methods
Path of(String, String...) Path getParent()
URI toURI() Path getRoot()
File toFile() boolean isAbsolute()
String toString() Path toAbsolutePath()
int getNameCount() Path relativize()
Path getName(int) Path resolve(Path)
Path subpath(int, int) Path normalize()
Path getFileName() Path toRealPath(LinkOption...)

Operating on Files and Directories

Most of the methods we covered in the Path interface operate on theoretical paths, which are not required to exist within the file system. What if you want to rename a directory, copy a file, or read the contents of a file? Enter the NIO.2 Files class. The Files helper class is capable of interacting with real files and directories within the system. Because of this, most of the methods in this part of the chapter take optional parameters and throw an IOException if the path does not exist. The Files class also replicates numerous methods found in the java.io.File, albeit often with a different name or list of parameters.

CHECKING FOR EXISTENCE WITH EXISTS()

The first Files method we present is the simplest. It just checks whether the file exists. This method does not throw an exception if the file does not exist, as doing so would prevent this method from ever returning false at runtime.

public static boolean exists(Path path, LinkOption... options)

var b1 = Files.exists(Paths.get("/ostrich/feathers.png"));
System.out.println("Path " + (b1 ? "Exists" : "Missing"));

var b2 = Files.exists(Paths.get("/ostrich"));
System.out.println("Path " + (b2 ? "Exists" : "Missing"));

TESTING UNIQUENESS WITH ISSAMEFILE()

Since a path may include path symbols and symbolic links within a file system, it can be difficult to know if two Path instances refer to the same file.

The method takes two Path objects as input, resolves all path symbols, and follows symbolic links. Despite the name, the method can also be used to determine whether two Path objects refer to the same directory. While most usages of isSameFile() will trigger an exception if the paths do not exist, there is a special case in which it does not. If the two path objects are equal, in terms of equals(), then the method will just return true without checking whether the file exists.

public static boolean isSameFile(Path path, Path path2) throws IOException


This isSameFile() method does not compare the contents of the files. Two files may have identical names, content, and attributes, but if they are in different locations, then this method will return false.

MAKING DIRECTORIES WITH CREATEDIRECTORY() AND CREATEDIRECTORIES()

The createDirectory() will create a directory and throw an exception if it already exists or the paths leading up to the directory do not exist. The createDirectories() works just like the java.io.File method mkdirs(), in that it creates the target directory along with any nonexistent parent directories leading up to the path. If all of the directories already exist, createDirectories() will simply complete without doing anything. This is useful in situations where you want to ensure a directory exists and create it if it does not.

Both of these methods also accept an optional list of FileAttribute<?> values to apply to the newly created directory or directories. We will discuss file attributes more later in the chapter.

Files.createDirectory(Path.of("/bison/field"));
Files.createDirectories(Path.of("/bison/field/pasture/green"));

The first example creates a new directory, field, in the directory /bison, assuming /bison exists; or else an exception is thrown. Contrast this with the second example, which creates the directory green along with any of the following parent directories if they do not already exist, including bison, field, and pasture.

COPYING FILES WITH COPY()

The method copies a file or directory from one location to another using Path objects. The following shows an example of copying a file and a directory:

Files.copy(Paths.get("/panda/bamboo.txt"), Paths.get("/panda-save/bamboo.txt"));
Files.copy(Paths.get("/turtle"), Paths.get("/turtleCopy"));

When directories are copied, the copy is shallow. A shallow copy means that the files and subdirectories within the directory are not copied. A deep copy means that the entire tree is copied, including all of its content and subdirectories.

Copying and Replacing Files

By default, if the target already exists, the copy() method will throw an exception. You can change this behavior by providing the StandardCopyOption enum value REPLACE_EXISTING to the method. The following method call will overwrite the movie.txt file if it already exists:

Files.copy(Paths.get("book.txt"), Paths.get("movie.txt"), StandardCopyOption.REPLACE_EXISTING);

Copying Files with I/O Streams

The first method reads the contents of a stream and writes the output to a file. The second method reads the contents of a file and writes the output to a stream. They are quite convenient if you need to quickly read/write data from/to disk.

public static long copy(InputStream in, Path target, CopyOption... options) throws IOException
public static long copy(Path source, OutputStream out) throws IOException

try (var is = new FileInputStream("source-data.txt")) { // Write stream data to a file
    Files.copy(is, Paths.get("/mammals/wolf.txt"));
}
Files.copy(Paths.get("/fish/clown.xsl"), System.out);

While we used FileInputStream in the first example, the streams could have been any valid I/O stream including website connections, in‐memory stream resources, and so forth. The second example prints the contents of a file directly to the System.out stream.

Copying Files into a Directory

For the exam, it is important that you understand how the copy() method operates on both files and directories. For example, let's say we have a file, food.txt, and a directory, /enclosure. Both the file and directory exist.

var file = Paths.get("food.txt");
var directory = Paths.get("/enclosure");
Files.copy(file, directory);

If you said it would create a new file at /enclosure/food.txt, then you're way off!!! It actually throws an exception. The command tries to create a new file, named /enclosure. Since the path /enclosure already exists, an exception is thrown at runtime.

On the other hand, if the directory did not exist, then it would create a new file with the contents of food.txt, but it would be called /enclosure. Remember, we said files may not need to have extensions, and in this example, it matters.

This behavior applies to both the copy() and the move() methods, the latter of which we will be covering next.

correct:

var file = Paths.get("food.txt");
var directory = Paths.get("/enclosure/food.txt");
Files.copy(file, directory);

// or
var directory = Paths.get("/enclosure").resolve(file.getFileName());

MOVING OR RENAMING PATHS WITH MOVE()

Files.move(Path.of("c:\\zoo"), Path.of("c:\\zoo-new"));
Files.move(Path.of("c:\\user\\addresses.txt"), Path.of("c:\\zoo-new\\addresses2.txt"));

The first example renames the zoo directory to a zoo‐new directory, keeping all of the original contents from the source directory. The second example moves the addresses.txt file from the directory user to the directory zoo‐new, and it renames it to addresses2.txt.

Similarities between move() and copy()

Like copy(), move() requires REPLACE_EXISTING to overwrite the target if it exists, else it will throw an exception. Also like copy(), move() will not put a file in a directory if the source is a file and the target is a directory. Instead, it will create a new file with the name of the directory.

Performing an Atomic Move

Another enum value that you need to know for the exam when working with the move() method is the StandardCopyOption value ATOMIC_MOVE.

Files.move(Path.of("mouse.txt"), Path.of("gerbil.txt"), StandardCopyOption.ATOMIC_MOVE);

You may remember the atomic property from Chapter 18, “Concurrency,” and the principle of an atomic move is similar. An atomic move is one in which a file is moved within the file system as a single indivisible operation. Put another way, any process monitoring the file system never sees an incomplete or partially written file. If the file system does not support this feature, an AtomicMoveNotSupportedException will be thrown.

Note that while ATOMIC_MOVE is available as a member of the StandardCopyOption type, it will likely throw an exception if passed to a copy() method.

DELETING A FILE WITH DELETE() AND DELETEIFEXISTS()

The Files class includes two methods that delete a file or empty directory within the file system.

To delete a directory, it must be empty. Both of these methods throw an exception if operated on a nonempty directory. In addition, if the path is a symbolic link, then the symbolic link will be deleted, not the path that the symbolic link points to.

The methods differ on how they handle a path that does not exist. The delete() method throws an exception if the path does not exist, while the deleteIfExists() method returns true if the delete was successful, and false otherwise. Similar to createDirectories(), deleteIfExists() is useful in situations where you want to ensure a path does not exist, and delete it if it does.

READING AND WRITING DATA WITH NEWBUFFEREDREADER() AND NEWBUFFEREDWRITER()

NIO.2 includes two convenient methods for working with I/O streams.

public static BufferedReader newBufferedReader(Path path) throws IOException
public static BufferedWriter newBufferedWriter(Path path, OpenOption... options) throws IOException

There are overloaded versions of these methods that take a Charset. You can wrap I/O stream constructors to produce the same effect, although it's a lot easier to use the factory method.

var path = Path.of("/animals/gopher.txt");
try (var reader = Files.newBufferedReader(path)) {
   String currentLine = null;
   while((currentLine = reader.readLine()) != null)
    System.out.println(currentLine);
}

This example reads the lines of the files using a BufferedReader and outputs the contents to the screen.

var list = new ArrayList<String>();
list.add("Smokey");
list.add("Yogi");
var path = Path.of("/animals/bear.txt");
try (var writer = Files.newBufferedWriter(path)) {
   for(var line : list) {
      writer.write(line);
      writer.newLine();
   }
}

This code snippet creates a new file with two lines of text in it. Did you notice that both of these methods use buffered streams rather than low‐ level file streams?

READING A FILE WITH READALLLINES()

The Files class includes a convenient method for turning the lines of a file into a List. The following sample code reads the lines of the file and outputs them to the user:

var path = Path.of("/animals/gopher.txt");
final List<String> lines = Files.readAllLines(path);
lines.forEach(System.out::println);

Be aware that the entire file is read when readAllLines() is called, with the resulting List storing all of the contents of the file in memory at once. If the file is significantly large, then you may trigger an OutOfMemoryError trying to load all of it into memory.

Files Methods Files Methods
boolean exists(Path, LinkOption...) Path move(Path, Path, CopyOption...)
boolean isSameFile(Path,Path) void delete(Path)
Path createDirectory(Path, FileAttribute...) | boolean deleteIfExists(Path) | | Path createDirectories(Path, FileAttribute...) BufferedReader newBufferedReader(Path)
Path copy(Path, Path, CopyOption...) BufferedWriter newBufferedWriter( Path, OpenOption...)
long copy(InputStream,Path, CopyOption...) ListreadAllLines(Path)
long copy(Path,OutputStream)

Managing File Attributes

The Files class also provides numerous methods for accessing file and directory metadata, referred to as file attributes. A file attribute is data about a file within the system, such as its size and visibility, that is not part of the file contents.

Reading Common Attributes with isDirectory(), isSymbolicLink(), and isRegularFile()

The Files class includes three methods for determining type of a Path.

public static boolean isDirectory(Path path, LinkOption... options)
public static boolean isSymbolicLink(Path path)
public static boolean isRegularFile(Path path, LinkOption... options)

The isDirectory() and isSymbolicLink() methods should be self‐ explanatory, although isRegularFile() warrants some discussion. Java defines a regular file as one that can contain content, as opposed to a symbolic link, directory, resource, or other nonregular file that may be present in some operating systems. If the symbolic link points to an actual file, Java will perform the check on the target of the symbolic link. In other words, it is possible for isRegularFile() to return true for a symbolic link, as long as the link resolves to a regular file.

System.out.print(Files.isDirectory(Paths.get("/canine/fur.jpg" )));
System.out.print(Files.isSymbolicLink(Paths.get("/canine/coyote")));
System.out.print(Files.isRegularFile(Paths.get("/canine/types.txt")));

The first example prints true if fur.jpg is a directory or a symbolic link to a directory and false otherwise. The second example prints true if /canine/coyote is a symbolic link, regardless of whether the file or directory it points to exists. The third example prints true if types.txt points to a regular file or alternatively a symbolic link that points to a regular file.

While most methods in the Files class declare IOException, these three methods do not. They return false if the path does not exist.

Checking File Accessibility with isHidden(), isReadable(), isWritable(), and isExecutable()

In many file systems, it is possible to set a boolean attribute to a file that marks it hidden, readable, or executable. The Files class includes methods that expose this information.

public static boolean isHidden(Path path) throws IOException public static boolean isReadable(Path path)
public static boolean isWritable(Path path)
public static boolean isExecutable(Path path)

A hidden file can't normally be viewed when listing the contents of a directory. The readable, writable, and executable flags are important in file systems where the filename can be viewed, but the user may not have permission to open the file's contents, modify the file, or run the file as a program, respectively.

System.out.print(Files.isHidden(Paths.get("/walrus.txt")));
System.out.print(Files.isReadable(Paths.get("/seal/baby.png")) );
System.out.print(Files.isWritable(Paths.get("dolphin.txt")));
System.out.print(Files.isExecutable(Paths.get("whale.png")));

If the walrus.txt exists and is hidden within the file system, then the first example prints true. The second example prints true if the baby.png file exists and its contents are readable. The third example prints true if the dolphin.txt file is able to be modified. Finally, the last example prints true if the file is able to be executed within the operating system. Note that the file extension does not necessarily determine whether a file is executable. For example, an image file that ends in .png could be marked executable in some file systems. With the exception of isHidden(), these methods do not declare any checked exceptions and return false if the file does not exist.

Reading File Size with size()

The Files class includes a method to determine the size of the file in bytes.

public static long size(Path path) throws IOException

The size returned by this method represents the conceptual size of the data, and this may differ from the actual size on the persistent storage device.

The Files.size() method is defined only on files. Calling Files.size() on a directory is undefined, and the result depends on the file system. If you need to determine the size of a directory and its contents, you'll need to walk the directory tree.

Checking for File Changes with getLastModifiedTime()

Most operating systems support tracking a last‐modified date/time value with each file. Some applications use this to determine when the file's contents should be read again. In the majority of circumstances, it is a lot faster to check a single file metadata attribute than to reload the entire contents of the file. The Files class provides the following method to retrieve the last time a file was modified:

public static FileTime getLastModifiedTime(Path path, LinkOption... options) throws IOException

The method returns a FileTime object, which represents a timestamp. For convenience, it has a toMillis() method that returns the epoch time, which is the number of milliseconds since 12 a.m. UTC on January 1, 1970. The following shows how to print the last modified value for a file as an epoch value:

final Path path = Paths.get("/rabbit/food.jpg");
System.out.println(Files.getLastModifiedTime(path).toMillis());

IMPROVING ATTRIBUTE ACCESS

Up until now, we have been accessing individual file attributes with multiple method calls. While this is functionally correct, there is often a cost each time one of these methods is called. Put simply, it is far more efficient to ask the file system for all of the attributes at once rather than performing multiple round‐trips to the file system. Furthermore, some attributes are file system–specific and cannot be easily generalized for all file systems.

NIO.2 addresses both of these concerns by allowing you to construct views for various file systems with a single method call. A view is a group of related attributes for a particular file system type. That's not to say that the earlier attribute methods that we just finished discussing do not have their uses. If you need to read only one attribute of a file or directory, then requesting a view is unnecessary.

Understanding Attribute and View Types

NIO.2 includes two methods for working with attributes in a single method call: a read‐only attributes method and an updatable view method. For each method, you need to provide a file system type object, which tells the NIO.2 method which type of view you are requesting. By updatable view, we mean that we can both read and write attributes with the same object.

Retrieving Attributes with readAttributes()

The Files class includes the following method to read attributes of a class in a read‐only capacity:

public static <A extends BasicFileAttributes> A readAttributes(Path path,Class<A> type,LinkOption... options) throws IOException

Applying it requires specifying the Path and BasicFileAttributes.class parameters.

var path = Paths.get("/turtles/sea.txt");
BasicFileAttributes data = Files.readAttributes(path,BasicFileAttributes.class);


System.out.println("Is a directory? " + data.isDirectory());
System.out.println("Is a regular file? " +
data.isRegularFile());
System.out.println("Is a symbolic link? " +
data.isSymbolicLink());
System.out.println("Size (in bytes): " + data.size());
System.out.println("Last modified: " +
data.lastModifiedTime());

The BasicFileAttributes class includes many values with the same name as the attribute methods in the Files class. The advantage of using this method, though, is that all of the attributes are retrieved at once.

Modifying Attributes with getFileAttributeView()

The following Files method returns an updatable view:

public static <V extends FileAttributeView> V
getFileAttributeView(
   Path path,
Class<V> type, LinkOption... options)

We can use the updatable view to increment a file's last modified date/time value by 10,000 milliseconds, or 10 seconds.

// Read file attributes
var path = Paths.get("/turtles/sea.txt"); BasicFileAttributeView view = Files.getFileAttributeView(path,
   BasicFileAttributeView.class);
BasicFileAttributes attributes = view.readAttributes();
// Modify file last modified time
FileTime lastModifiedTime = FileTime.fromMillis(
attributes.lastModifiedTime().toMillis() + 10_000); view.setTimes(lastModifiedTime, null, null);

After the updatable view is retrieved, we need to call readAttributes() on the view to obtain the file metadata. From there, we create a new FileTime value and set it using the setTimes() method.

// BasicFileAttributeView instance method public void setTimes(FileTime lastModifiedTime,
   FileTime lastAccessTime, FileTime createTime)

This method allows us to pass null for any date/time value that we do not want to modify. In our sample code, only the last modified date/time is changed.

Not all file attributes can be modified with a view. For example, you cannot set a property that changes a file into a directory. Likewise, you cannot change the size of the object without modifying its contents.

Applying Functional Programming

LISTING DIRECTORY CONTENTS

Let's start with a simple Stream API method. The following Files method lists the contents of a directory:

public static Stream<Path> list(Path dir) throws IOException

The Files.list() is similar to the java.io.File method listFiles(), except that it returns a Stream rather than a File[]. Since streams use lazy evaluation, this means the method will load each path element as needed, rather than the entire directory at once.

Printing the contents of a directory is easy.

try (Stream<Path> s = Files.list(Path.of("/home"))) { s.forEach(System.out::println);}

Let's do something more interesting, though. Earlier, we presented the Files.copy() method and showed that it only performed a shallow copy of a directory. We can use the Files.list() to perform a deep copy.

public void copyPath(Path source, Path target) {
   try {
      Files.copy(source, target);
      if(Files.isDirectory(source))
try (Stream<Path> s = Files.list(source)) { s.forEach(p -> copyPath(p,
               target.resolve(p.getFileName())));
         }
   } catch(IOException e) {
      // Handle exception
   }
}

The method first copies the path, whether it be a file or a directory. If it is a directory, then only a shallow copy is performed. Next, it checks whether the path is a directory and, if it is, performs a recursive copy of each of its elements. What if the method comes across a symbolic link? Don't worry, we'll address that topic in the next section. For now, you just need to know the JVM will not follow symbolic links when using this stream method.

CLOSING THE STREAM

Did you notice that in the last two code samples, we put our Stream objects inside a try‐with‐resources method? The NIO.2 stream‐based methods open a connection to the file system that must be properly closed, else a resource leak could ensue. A resource leak within the file system means the path may be locked from modification long after the process that used it completed. If you assumed a stream's terminal operation would automatically close the underlying file resources, you'd be wrong. There was a lot of debate about this behavior when it was first presented, but in short, requiring developers to close the stream won out. On the plus side, not all streams need to be closed, only those that open resources, like the ones found in NIO.2. For instance, you didn't need to close any of the streams you worked with in Chapter 15. Finally, the exam doesn't always properly close NIO.2 resources. To match the exam, we will sometimes skip closing NIO.2 resources in review and practice questions. Please, in your own code, always use try‐with‐resources statements with these NIO.2 methods.

TRAVERSING A DIRECTORY TREE

Traversing a directory, also referred to as walking a directory tree, is the process by which you start with a parent directory and iterate over all of its descendants until some condition is met or there are no more elements over which to iterate.

DON'T USE DIRECTORYSTREAM AND FILEVISITOR

While browsing the NIO.2 Javadocs, you may come across methods that use the DirectoryStream and FileVisitor classes to traverse a directory. These methods predate the existence of the Streams API and were even required knowledge for older Java certification exams. Even worse, despite its name, DirectoryStream is not a Stream API class. The best advice we can give you is to not use them. The newer Stream API–based methods are superior and accomplish the same thing often with much less code.

Selecting a Search Strategy

There are two common strategies associated with walking a directory tree: a depth‐first search and a breadth‐first search. A depth‐first search traverses the structure from the root to an arbitrary leaf and then navigates back up toward the root, traversing fully down any paths it skipped along the way. The search depth is the distance from the root to current node. To prevent endless searching, Java includes a search depth that is used to limit how many levels (or hops) from the root the search is allowed to go. Alternatively, a breadth‐first search starts at the root and processes all elements of each particular depth, before proceeding to the next depth level. The results are ordered by depth, with all nodes at depth 1 read before all nodes at depth 2, and so on. While a breadth‐first tends to be balanced and predictable, it also requires more memory since a list of visited nodes must be maintained.

you just need to be aware that the NIO.2 Streams API methods use depth‐first searching with a depth limit, which can be optionally changed.

Walking a Directory with walk()

That's enough background information; let's get to more Steam API methods. The Files class includes two methods for walking the directory tree using a depth‐first search.

public static Stream<Path> walk(Path start, FileVisitOption... options) throws IOException
public static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options) throws IOException

Like our other stream methods, walk() uses lazy evaluation and evaluates a Path only as it gets to it. This means that even if the directory tree includes hundreds or thousands of files, the memory required to process a directory tree is low. The first walk() method relies on a default maximum depth of Integer.MAX_VALUE, while the overloaded version allows the user to set a maximum depth. This is useful in cases where the file system might be large and we know the information we are looking for is near the root.

Java uses an int for its maximum depth rather than a long because most file systems do not support path values deeper than what can be stored in an int. In other words, using Integer.MAX_VALUE is effectively like using an infinite value, since you would be hard‐ pressed to find a stable file system where this limit is exceeded.

Rather than just printing the contents of a directory tree, we can again do something more interesting. The following getPathSize() method walks a directory tree and returns the total size of all the files in the directory:

private long getSize(Path p) {
   try {
     return  Files.size(p);
   } catch (IOException e) {
      // Handle exception
   }
    return 0L;
}
public long getPathSize(Path source) throws IOException {
    try (var s = Files.walk(source)) {
      return s.parallel()
            .filter(p -> !Files.isDirectory(p))
            .mapToLong(this::getSize)
            .sum();
    }
}

var size = getPathSize(Path.of("/fox/data")); System.out.format("Total Size: %.2f megabytes", (size/1000000.0));
Total Directory Tree Size: 15.30 megabytes

The getSize() helper method is needed because Files.size() declares IOException, and we'd rather not put a try/ catch block inside a lambda expression.

Applying a Depth Limit

Let's say our directory tree was quite deep, so we apply a depth limit by changing one line of code in our getPathSize() method.

try (var s = Files.walk(source, 5)) {

This new version checks for files only within 5 steps of the starting node. A depth value of 0 indicates the current path itself. Since the method calculates values only on files, you'd have to set a depth limit of at least 1 to get a nonzero result when this method is applied to a directory tree.

Avoiding Circular Paths

Many of our earlier NIO.2 methods traverse symbolic links by default, with a NOFOLLOW_LINKS used to disable this behavior. The walk() method is different in that it does not follow symbolic links by default and requires the FOLLOW_LINKS option to be enabled. We can alter our getPathSize() method to enable following symbolic links by adding the FileVisitOption:

try (var s = Files.walk(source, FileVisitOption.FOLLOW_LINKS)) {

When traversing a directory tree, your program needs to be careful of symbolic links if enabled. For example, if our process comes across a symbolic link that points to the root directory of the file system, then every file in the system would be searched!

Worse yet, a symbolic link could lead to a cycle, in which a path is visited repeatedly. A cycle is an infinite circular dependency in which an entry in a directory tree points to one of its ancestor directories.

Be aware that when the FOLLOW_LINKS option is used, the walk() method will track all of the paths it has visited, throwing a FileSystemLoopException if a path is visited twice.

SEARCHING A DIRECTORY WITH FIND()

In the previous example, we applied a filter to the Stream object to filter the results, although NIO.2 provides a more convenient method.

public static Stream<Path> find(Path start,int maxDepth, BiPredicate<Path,BasicFileAttributes> matcher, FileVisitOption... options) throws IOException

The find() method behaves in a similar manner as the walk() method, except that it takes a BiPredicate to filter the data. It also requires a depth limit be set. Like walk(), find() also supports the FOLLOW_LINK option.

READING A FILE WITH LINES()

Earlier in the chapter, we presented Files.readAllLines() and commented that using it to read a very large file could result in an OutOfMemoryError problem. Luckily, NIO.2 solves this problem with a Stream API method.

public static Stream<String> lines(Path path) throws IOException

The contents of the file are read and processed lazily, which means that only a small portion of the file is stored in memory at any given time.

Path path = Paths.get("/fish/sharks.log"); try (var s = Files.lines(path)) {
   s.forEach(System.out::println);
}

Taking things one step further, we can leverage other stream methods for a more powerful example.

Path path = Paths.get("/fish/sharks.log"); try (var s = Files.lines(path)) {
   s.filter(f -> f.startsWith("WARN:"))
      .map(f -> f.substring(5))
      .forEach(System.out::println);
}
This sample code searches a log for lines that start with WARN:, outputting the text that follows. Assuming that the input file sharks.log is as follows:

INFO:Server starting
DEBUG:Processes available = 10
WARN:No database could be detected
DEBUG:Processes available reset to 0
WARN:Performing manual recovery
INFO:Server successfully started

=>
No database could be detected
Performing manual recovery

FILES.READALLLINES() VS. FILES.LINES()

For the exam, you need to know the difference between readAllLines() and lines(). Both of these examples compile and run:

Files.readAllLines(Paths.get("birds.txt")).forEach(System .out::println);
Files.lines(Paths.get("birds.txt")).forEach(System.out::p rintln);

The first line reads the entire file into memory and performs a print operation on the result, while the second line lazily processes each line and prints it as it is read. The advantage of the second code snippet is that it does not require the entire file to be stored in memory at any time.

You should also be aware of when they are mixing incompatible types on the exam. Do you see why the following does not compile?

Files.readAllLines(Paths.get("birds.txt"))
   .filter(s -> s.length()> 2)
   .forEach(System.out::println);

The readAllLines() method returns a List, not a Stream, so the filter() method is not available.

Legacy I/O File method NIO.2 method
file.delete() Files.delete(path)
file.exists() Files.exists(path)
file.getAbsolutePath() path.toAbsolutePath()
file.getName() path.getFileName()
file.getParent() path.getParent()
file.isDirectory() Files.isDirectory(path)
file.isFile() Files.isRegularFile(path)
file.lastModified() Files.getLastModifiedTime(path)
file.length() Files.size(path)
file.listFiles() Files.list(path)
file.mkdir() Files.createDirectory(path)
file.mkdirs() Files.createDirectories(path)
file.renameTo(otherFile) Files.move(path,otherPath)

prev next

Tags: 

OCP Chapter 19

OCP Chapter 19 I/O

  • read write data console + file using I/O streams
  • use IO stream to read write files
  • read write object by using serialization

using java.io API to interact with files and streams

I/O streams zijn niet gelijk of hebben niets te maken met streams voor collections....

Files Directories

  • root
  • path
  • path seperator character / or \
  • data is stored in bytes

File Class

The File class is used to read information about existing files AND directories, list the contents of a directory, and create/delete files and directories. An instance of a File class represents the path to a particular file or directory on the file system. The File class can NOT read or write data within a file, although it can be passed as a reference to many stream classes to read or write data.

Creating a File Object

A File object often is initialized with a String containing either an absolute or relative path to the file or directory within the file system. The absolute path of a file or directory is the full path from the root directory to the file or directory, including all subdirectories that contain the file or directory. Alternatively, the relative path of a file or directory is the path from the current working directory to the file or directory.

absolute path:
/home/tiger/data/stripes.txt

relative path from /home/tiger
data/stripes.txt

var zooFile1 = new File("/home/tiger/data/stripes.txt");
System.out.println(zooFile1.exists()); // true if the file exists

Constructors

public File(String pathname)
public File(File parent, String child)
public File(String parent, String child)
File zooFile3 = new File("stripes.txt");
System.out.println(zooFile3.exists()); // File object is niet de file zelf
//                                       ...alleen t path naar de file.....

// dan is zooFile3.createNewFile() nodig
        File zooFile3 = new File("stripes.txt");
        try {
            if (zooFile3.createNewFile()) {
                System.out.println("File created: " + zooFile3.getName());
            } else {
                System.out.println("File already exists.");
            }
        } catch (IOException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        System.out.println("File exists: " + zooFile3.exists());

Commonly used java.io.File methods

  • boolean delete() Deletes the file or directory and returns true only if successful. If this instance denotes a directory, then the directory must be empty in order to be deleted.
  • boolean exists() Checks if a file exists
  • String getAbsolutePath() Retrieves the absolute name of the file or directory within the file system
  • String getName() Retrieves the name of the file or directory.
  • String getParent() Retrieves the parent directory that the path is contained in or null if there is none
  • boolean isDirectory() Checks if a File reference is a directory within the file system
  • boolean isFile() Checks if a File reference is a file within the file system
  • long lastModified() Returns the number of milliseconds since the epoch (number of milliseconds since 12 a.m. UTC on January 1, 1970) when the file was last modified
  • long length() Retrieves the number of bytes in the file
  • File[] listFiles() Retrieves a list of files within a directory
  • boolean mkdir() Creates the directory named by this path
  • boolean mkdirs() Creates the directory named by this path including any nonexistent parent directories
  • boolean renameTo(File dest) Renames the file or directory denoted by this path to dest and returns true only if successful

Let op!!! Kan file maar ook Directory zijn!!!

/data/zoo.txt
// Print current working directory
System.out.println("Current working directory: " + System.getProperty("user.dir"));

// Ensure the directory exists
java.io.File dir = new java.io.File("hjh");
if (!dir.exists()) {
    boolean created = dir.mkdirs();
    System.out.println("Directory created: " + created);
} else {
    System.out.println("Directory already exists.");
}

// Ensure the file exists
java.io.File file = new java.io.File("hjh/test.txt");
try {
    if (file.createNewFile()) {
        System.out.println("File created: " + file.getName());
    } else {
        System.out.println("File already exists.");
    }
} catch (java.io.IOException e) {
    System.err.println("An error occurred while creating the file.");
    e.printStackTrace();
}

// Verify the directory
if (dir.exists() && dir.isDirectory()) {
    System.out.println("Directory contents:");
    for (String f : dir.list()) {
        System.out.println(f);
    }
} else {
    System.out.println("Directory 'hjh' does not exist.");
}

// Verify the specific file
System.out.println("File exists: " + file.exists());

Current working directory: /Users/hjhubeek/Development/jupyter/IJava
Directory already exists.
File already exists.
Directory contents:
stripes.txt
test.txt
File exists: true

File parent = new File("hjh"); // Ensure the parent directory exists if (!parent.exists()) { boolean dirCreated = parent.mkdirs(); if (dirCreated) { System.out.println("Directory 'hjh' created."); } else { System.out.println("Failed to create directory 'hjh'."); } } else { System.out.println("Directory 'hjh' already exists."); } File zooFile3 = new File(parent,"stripes.txt"); try { if (zooFile3.createNewFile()) { System.out.println("File created: " + zooFile3.getName()); } else { System.out.println("File already exists."); } } catch (IOException e) { System.out.println("An error occurred."); e.printStackTrace(); } System.out.println("abs path: " + zooFile3.getAbsolutePath()); System.out.println("File exists: " + zooFile3.exists());
Directory 'hjh' already exists.
File already exists.
abs path: /Users/hjhubeek/Development/jupyter/IJava/hjh/stripes.txt
File exists: true

Byte Stream vs Character Streams

  • Byte streams read/write binary data ( 0s and 1s) and have class names that end in InputStream or OutputStream.
  • Character streams read/write text data and have class names that end in Reader or Writer.

The API frequently includes similar classes for both byte and character streams, such as FileInputStream and FileReader.

character encoding determines how characters are encoded and stored in bytes in a stream and later read back or decoded as characters.

The character encoding can be specified using the Charset class by passing a name value to the static Charset.forName()

Charset usAsciiCharset = Charset.forName("US-ASCII");
Charset utf8Charset = Charset.forName("UTF-8");
Charset utf16Charset = Charset.forName("UTF-16");

For character encoding, just remember that using a character stream is better for working with text data than a byte stream. The character stream classes were created for convenience, and you should certainly take advantage of them when possible.

Input vs. Output Streams

Most Reader classes have a corresponding Writer class. For example, the FileWriter class writes data that can be read by a FileReader.

There are exceptions to this rule. For the exam, you should know that PrintWriter has no accompanying PrintReader class. Likewise, the PrintStream is an OutputStream that has no corresponding InputStream class. It also does not have Output in its name.

Low‐Level vs. High‐Level Streams

A low‐level stream connects directly with the source of the data, such as a file, an array, or a String. Low‐level streams process the raw data or resource and are accessed in a direct and unfiltered manner. For example, a FileInputStream is a class that reads file data one byte at a time.

Alternatively, a high‐level stream is built on top of another stream using wrapping. Wrapping is the process by which an instance is passed to the constructor of another class, and operations on the resulting instance are filtered and applied to the original instance.

try (var br = new BufferedReader(new FileReader("zoo- data.txt"))) {
   System.out.println(br.readLine());
}

Fileread is the low-level stream readeer and BufferedReader is the high-level stream that takes the FileReader as input.

High‐level streams can take other high‐level streams as input. For example, although the following code might seem a little odd at first, the style of wrapping a stream is quite common in practice:

try (var ois = new ObjectInputStream( new BufferedInputStream(
new FileInputStream("zoo-data.txt")))) { System.out.print(ois.readObject());
}

Stream Base Classes

The java.io library defines four abstract classes that are the parents of all stream classes defined within the API: - InputStream - OutputStream - Reader - Writer

new BufferedInputStream(new FileReader("z.txt"));  // DOES NOT COMPILE
new BufferedWriter(new FileOutputStream("z.txt")); // DOES NOT COMPILE
new ObjectInputStream(   new FileOutputStream("z.txt")); // DOES NOT COMPILE
new BufferedInputStream(new InputStream()); // DOES NOT COMPILE

The first two examples do not compile because they mix Reader/ Writer classes with InputStream/ OutputStream classes, respectively. The third example does not compile because we are mixing an OutputStream with an InputStream. Although it is possible to read data from an InputStream and write it to an OutputStream, wrapping the stream is not the way to do so. As you will see later in this chapter, the data must be copied over, often iteratively. Finally, the last example does not compile because InputStream is an abstract class, and therefore you cannot create an instance of it.

Decoding I/O Class Names

Pay close attention to the name of the I/O class on the exam, as decoding it often gives you context clues as to what the class does.

Review of java.io Class Name Properties

  • A class with the word InputStream or OutputStream in its name is used for reading or writing binary or byte data, respectively.
  • A class with the word Reader or Writer in its name is used for reading or writing character or string data, respectively.
  • Most, but not all, input classes have a corresponding output class.
  • A low‐level stream connects directly with the source of the data.
  • A high‐level stream is built on top of another stream using wrapping.
  • A class with Buffered in its name reads or writes data in groups of bytes or characters and often improves performance in sequential file systems.
  • With a few exceptions, you only wrap a stream with another stream if they share the same abstract parent.
Class Name Description
InputStream Abstract class for all input byte streams
OutputStream Abstract class for all output byte streams
Reader Abstract class for all input character streams
Writer Abstract class for all output character streams
Class Name Low/High Level Description
FileInputStream Low Reads file data as bytes
FileOutputStream Low Writes file data as bytes
FileReader Low Reads file data as characters
FileWriter Low Writes file data as characters
Buffered InputStream High Reads byte data from an existing InputStream in a buffered manner, which improves efficiency and performance
Buffered OutputStream High Writes byte data to an existing OutputStream in a buffered manner, which improves efficiency and performance
Buffered Reader High Reads character data from an existing Reader in a buffered manner, which improves efficiency and performance
Buffered Writer High Writes character data to an existing Writer in a buffered manner, which improves efficiency and performance
ObjectInputStream High Deserializes primitive Java data types and graphs of Java objects from an existing InputStream
ObjectOutputStream High Serializes primitive Java data types and graphs of Java objects to an existing OutputStream
PrintStream High Writes formatted representations of Java objects to a binary stream
PrintWriter High Writes formatted representations of Java objects to a character stream

Common I/O Stream Operations

Reading and Writing Data

I/O streams are all about reading/writing data, so it shouldn't be a surprise that the most important methods are read() and write().

// InputStream and Reader
public int read() throws IOException

// OutputStream and Writer
public void write(int b) throws IOException

Why do the methods use int instead of byte? Remember, the byte data type has a range of 256 characters. They needed an extra value to indicate the end of a stream. The authors of Java decided to use a larger data type, int, so that special values like ‐1 would indicate the end of a stream. The output stream classes use int as well, to be consistent with the input stream classes.

Closing the Stream

// All I/O stream classes
public void close() throws IOException

In many file systems, failing to close a file properly could leave it locked by the operating system such that no other processes could read/write to it until the program is terminated. Throughout this chapter, we will close stream resources using the try‐with‐resources syntax since this is the preferred way of closing resources in Java.

What about if you need to pass a stream to a method? That's fine, but the stream should be closed in the method that created it.

The following example is valid and will result in three separate close() method calls but is unnecessary:

try (var fis = new FileOutputStream("zoo-
banner.txt"); // Unnecessary
         var bis = new BufferedOutputStream(fis);
         var ois = new ObjectOutputStream(bis)) {
       ois.writeObject("Hello");
}

Instead, we can rely on the ObjectOutputStream to close the BufferedOutputStream and FileOutputStream. The following will call only one close() method instead of three:

try (var ois = new ObjectOutputStream(
      new BufferedOutputStream(
         new FileOutputStream("zoo-banner.txt")))) {
   ois.writeObject("Hello");
}

MANIPULATING INPUT STREAMS

All input stream classes include the following methods to manipulate the order in which data is read from a stream:

// InputStream and Reader
public boolean <b>markSupported()</b>
public void void mark(int readLimit)
public void reset() throws IOException
public long skip(long n) throws IOException

mark() and reset()

The mark() and reset() methods return a stream to an earlier position. Before calling either of these methods, you should call the markSupported() method, which returns true only if mark() is supported. The skip() method is pretty simple; it basically reads data from the stream and discards the contents.

public void readData(InputStream is) throws IOException {
System.out.print((char) is.read()); // L
    if (is.markSupported()) {
       is.mark(100); // Marks up to 100 bytes
       System.out.print((char) is.read()); // I
       System.out.print((char) is.read()); // O
       is.reset(); // Resets stream to position before I
    }
}
System.out.print((char) is.read()); // I 
System.out.print((char) is.read()); // O 
System.out.print((char) is.read()); // N

skip()

System.out.print ((char)is.read()); // T
is.skip(2); // Skips I and G
is.read(); // Reads E but doesn't output it
System.out.print((char)is.read()); // R
System.out.print((char)is.read()); // S

This code prints TRS at runtime. We skipped two characters, I and G. We also read E but didn't store it anywhere, so it behaved like calling skip(1).

The return parameter of skip() tells us how many values were actually skipped. For example, if we are near the end of the stream and call skip(1000), the return value might be 20, indicating the end of the stream was reached after 20 values were skipped.

FLUSHING OUTPUT STREAMS

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. In many operating systems, the data may be cached in memory, with a write occurring only after a temporary cache is filled or after some amount of time has passed. If the data is cached in memory and 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, which requests that all accumulated data be written immediately to disk.

// OutputStream and Writer
public void flush() throws IOException

The flush() method helps reduce the amount of data lost if the application terminates unexpectedly. It is not without cost, though. Each time it is used, it may cause a noticeable delay in the application, especially for large files. Unless the data that you are writing is extremely critical, the flush() method should be used only intermittently. For example, it should not necessarily be called after every write.

Common I/O Stream Methods

Stream Class Method Name Description
All streams void close() Closes stream and releases resources
All input streams int read() Reads a single byte or returns ‐1 if no bytes were available
InputStreamReader int read(byte[] b) int read(char[] c) Reads values into a buffer. Returns number of bytes read
InputStream int read(byte[]b, int offset,int length) Reads up to length values into a buffer starting from position offset. Returns number of bytes read
Reader int read(char[]c, int offset,int length)
All OutputStreams void write(int) Writes a single byte
OutputStream Writer void write(byte[] b) void write(char[] c) Writes an array of values into the stream
OutputStream void write(byte[] b, int offset, int length) Writes length values from an array into the stream, starting with an offset index
Writer void write(char[] c, int offset, int length) All input streams
All input streams boolean markSupported() Returns true if the stream class supports mark()
All input streams mark(int readLimit) Marks the current position in the stream
All input streams void reset() Attempts to reset the stream to the mark() position
All input streams long skip(long n) Reads and discards a specified number of characters
All output streams void flush() Flushes buffered data through the stream

Remember that input and output streams can refer to both byte and character streams throughout this chapter.

Working with I/O Stream Classes

READING AND WRITING BINARY DATA

the most basic file stream classes, FileInputStream and FileOutputStream

They are used to read bytes from a file or write bytes to a file, respectively.

public FileInputStream(File file) throws FileNotFoundException
public FileInputStream(String name) throws FileNotFoundException
public FileOutputStream(File file) throws FileNotFoundException
public FileOutputStream(String name) throws FileNotFoundException

If you need to append to an existing file, there's a constructor for that. The FileOutputStream class includes overloaded constructors that take a boolean append flag. When set to true, the output stream will append to the end of a file if it already exists. This is useful for writing to the end of log files, for example.

void copyFile(File src, File dest) throws IOException { try (var in = new FileInputStream(src);
var out = new FileOutputStream(dest)) { int b;
while ((b = in.read()) != -1) { out.write(b);
} }
}

If the source file does not exist, a FileNotFoundException, which inherits IOException, will be thrown. If the destination file already exists, this implementation will overwrite it, since the append flag was not sent. The copy() method copies one byte at a time until it reads a value of ‐1.

BUFFERING BINARY DATA

While our copyFile() method is valid, it tends to perform poorly on large files.

public BufferedInputStream(InputStream in)
public BufferedOutputStream(OutputStream out)
void copyFileWithBuffer(File src, File dest) throws IOException {
    try (var in = new BufferedInputStream(new FileInputStream(src));
            var out = new BufferedOutputStream(new FileOutputStream(dest))) {
        var buffer = new byte[1024];
        int lengthRead;
        while ((lengthRead = in.read(buffer))> 0) {
            out.write(buffer, 0, lengthRead);
            out.flush();
        }
    }
}

Instead of reading the data one byte at a time, we read and write up to 1024 bytes at a time. The return value lengthRead is critical for determining whether we are at the end of the stream and knowing how many bytes we should write into our output stream. We also added a flush() command at the end of the loop to ensure data is written to disk between each iteration.

READING AND WRITING CHARACTER DATA

The FileReader and FileWriter classes, along with their associated buffer classes, are among the most convenient I/O classes because of their built‐in support for text data. They include constructors that take the same input as the binary file classes. The following is an example of using these classes to copy a text file:

public FileReader(File file) throws FileNotFoundException
public FileReader(String name) throws FileNotFoundException
public FileWriter(File file) throws FileNotFoundException
public FileWriter(String name) throws FileNotFoundException
void copyTextFile(File src, File dest) throws IOException {
 try (var reader = new FileReader(src);
      var writer = new FileWriter(dest)) {
    int b;
    while ((b = reader.read()) != -1) { writer.write(b);
    }
 }
}

Wait a second, this looks identical to our copyFile() method with byte stream! Since we're copying one character at a time, rather than one byte, it is.

The FileReader class doesn't contain any new methods you haven't seen before. The FileWriter inherits a method from the Writer class that allows it to write String values.

```// Writer public void write(String str) throws IOException


For example, the following is supported in FileWriter but not FileOutputStream: ```writer.write("Hello World");``` ### BUFFERING CHARACTER DATA Java includes high‐level buffered character streams that improve performance. The constructors take existing Reader and Writer instances as input.

public BufferedReader(Reader in) public BufferedWriter(Writer out)

They add two new methods, readLine() and newLine(), that are particularly useful when working with String values.

void copyTextFileWithBuffer(File src, File dest) throws IOException { try (var reader = new BufferedReader(new FileReader(src)); var writer = new BufferedWriter(new FileWriter(dest))) { String s; while ((s = reader.readLine()) != null) { writer.write(s); writer.newLine(); } } }

In this example, each loop iteration corresponds to reading and writing a line of a file. Assuming the length of the lines in the file are reasonably sized, this implementation will perform well.

There are some important distinctions between this method and our earlier copyFileWithBuffer() method that worked with byte streams. First, instead of a buffer array, we are using a String to store the data read during each loop iteration. By storing the data temporarily as a String, we can manipulate it as we would any String value. For example, we can call replaceAll() or toUpperCase() to create new values.

Next, we are checking for the end of the stream with a null value instead of ‐1. Finally, we are inserting a newLine() on every iteration of the loop. This is because readLine() strips out the line break character. Without the call to newLine(), the copied file would have all of its line breaks removed.



## SERIALIZING DATA

**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.

To serialize an object using the I/O API, the object must implement the java.io.Serializable interface. The Serializable interface is a marker interface, similar to the marker annotations you learned about in Chapter 13, “Annotations.” By marker interface, it means the interface does not have any methods. Any class can implement the Serializable interface since there are no required methods to implement.

The purpose of using the Serializable interface is to inform any process attempting to serialize the object that you have taken the proper steps to make the object serializable. All Java primitives and many of the built‐in Java classes that you have worked with throughout this book are Serializable.

import java.io.Serializable;

public class Gorilla implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; private Boolean friendly;

private transient String favoriteFood; // Constructors/Getters/Setters/toString() omitted }

The Gorilla class contains three instance members ( name, age, friendly) that will be saved to a stream if the class is serialized. 

Any field that is marked transient will not be saved to a stream when the class is serialized.

### Marking Data transient

Oftentimes, the transient modifier is used for sensitive data of the class, like 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.

### Ensuring a Class Is Serializable

Any process attempting to serialize an object will throw a NotSerializableException if the class does not implement the Serializable interface properly.

- The class must be marked Serializable.
- Every instance member of the class is serializable, marked transient, or has a null value at the time of serialization.

public class Cat implements Serializable { private Tail tail = new Tail(); } public class Tail implements Serializable { private Fur fur = new Fur(); } public class Fur {}

Cat contains an instance of Tail, and both of those classes are marked Serializable, so no problems there. Unfortunately, Tail contains an instance of Fur that is not marked Serializable.
Either of the following changes fixes the problem and allows Cat to be serialized:

public class Tail implements Serializable { private transient Fur fur = new Fur(); } public class Fur implements Serializable {}

We could also make our tail or fur instance members null, although this would make Cat serializable only for particular instances, rather than all instances.

### Storing Data with ObjectOutputStream and ObjectInputStream
The ObjectInputStream class is used to deserialize an object from a stream, while the ObjectOutputStream is used to serialize an object to a stream. They are high‐level streams that operate on existing streams.

public ObjectInputStream(InputStream in) throws IOException public ObjectOutputStream(OutputStream out) throws IOException

While both of these classes contain a number of methods for built‐in data types like primitives, the two methods you need to know for the exam are the ones related to working with objects.

// ObjectInputStream public Object readObject() throws IOException, ClassNotFoundException // ObjectOutputStream public void writeObject(Object obj) throws IOException


void saveToFile(List gorillas, File dataFile) throws IOException { try (var out = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream(dataFile)))) { for (Gorilla gorilla : gorillas) out.writeObject(gorilla); } }


Once the data is stored in a file, we can deserialize it using the following method:

List readFromFile(File dataFile) throws IOException, ClassNotFoundException { var gorillas = new ArrayList(); try (var in = new ObjectInputStream( new BufferedInputStream( new FileInputStream(dataFile)))) { while (true) { var object = in.readObject(); if (object instanceof Gorilla) gorillas.add((Gorilla) object); } } catch (EOFException e) { // File end reached } return gorillas; }

Since the return type of readObject() is Object, we need an explicit cast to obtain access to our Gorilla properties. Notice that readObject() declares a checked ClassNotFoundException since the class might not be available on deserialization.

var gorillas = new ArrayList(); gorillas.add(new Gorilla("Grodd", 5, false)); gorillas.add(new Gorilla("Ishmael", 8, true)); File dataFile = new File("gorilla.data"); saveToFile(gorillas, dataFile); var gorillasFromDisk = readFromFile(dataFile); System.out.print(gorillasFromDisk);



## Understanding the Deserialization Creation Process When you deserialize an object, the constructor of the serialized class, along with any instance initializers, is not called when the object is created. Java will call the no‐arg constructor of the first nonserializable parent class it can find in the class hierarchy. As we stated earlier, any static or transient fields are ignored. Values that are not provided will be given their default Java value, such as null for String, or 0 for int values. make sure you understand 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. ## PRINTING DATA PrintStream and PrintWriter are high‐level output print streams classes that are useful for writing text data to a stream. We cover these classes together, because they include many of the same methods. Just remember that one operates on an OutputStream and the other a Writer. The print stream classes have the distinction of being the only I/O stream classes we cover that do not have corresponding input stream classes. And unlike other OutputStream classes, PrintStream does not have Output in its name.

public PrintStream(OutputStream out) public PrintWriter(Writer out)

For convenience, these classes also include constructors that automatically wrap the print stream around a low‐level file stream class, such as FileOutputStream and FileWriter.

public PrintStream(File file) throws FileNotFoundException public PrintStream(String fileName) throws FileNotFoundException

public PrintWriter(File file) throws FileNotFoundException public PrintWriter(String fileName) throws FileNotFoundException


Furthermore, the PrintWriter class even has a constructor that takes an OutputStream as input. This is one of the few exceptions in which we can mix a byte and character stream.

public PrintWriter(OutputStream out)

Besides the inherited write() methods, the print stream classes include numerous methods for writing data including print(), println(), and format(). Unlike the majority of the other streams we've covered, the methods in the print stream classes do not throw any checked exceptions. If they did, you would have been required to catch a checked exception anytime you called System.out.print()! The stream classes do provide a method, checkError(), that can be used to check for an error after a write.

When working with String data, you should use a Writer, so our examples in this part of the chapter use PrintWriter. Just be aware that many of these examples can be easily rewritten to use a PrintStream.

### print()

The most basic of the print‐based methods is print(). The print stream classes include numerous overloaded versions of print(), which take everything from primitives and String values, to objects. Under the covers, these methods often just perform String.valueOf() on the argument and call the underlying stream's write() method to add it to the stream.

### println()

The next methods available in the PrintStream and PrintWriter classes are the println() methods, which are virtually identical to the print() methods, except that they also print a line break after the String value is written. These print stream classes also include a no‐argument version of println(), which just prints a single line break.

System.getProperty("line.separator"); System.lineSeparator();


### format() Each print stream class includes a format() method, which includes an overloaded version that takes a Locale.

// PrintStream public PrintStream format(String format, Object args...) public PrintStream format(Locale loc, String format, Object args...)

// PrintWriter public PrintWriter format(String format, Object args...) public PrintWriter format(Locale loc, String format, Object args...)


The method parameters are used to construct a formatted String in a single method call, rather than via a lot of format and concatenation operations. They return a reference to the instance they are called on so that operations can be chained together. As an example, the following two format() calls print the same text:

String name = "Lindsey"; int orderId = 5; // Both print: Hello Lindsey, order 5 is ready

System.out.format("Hello "+name+", order "+orderId+" is ready");

System.out.format("Hello %s, order %d is ready", name, orderId);


|Symbol |Description| |-|-| |%s|Applies to any type, commonly String values| |%d|Applies to integer values like int and long| |%f|Applies to floating‐point values like float and double| |%n|Inserts a line break using the system‐dependent line separator| Mixing data types may cause exceptions at runtime. For example, the following throws an exception because a floating‐point number is used when an integer value is expected:

System.out.format("Food: %d tons", 2.0); // IllegalFormatConversionException


### USING FORMAT() WITH FLAGS Besides supporting symbols, Java also supports optional flags between the % and the symbol character. In the previous example, the floating‐point number was printed as 90.250000. By default, %f displays exactly six digits past the decimal. If you want to display only one digit after the decimal, you could use %.1f instead of %f. The format() method relies on rounding, rather than truncating when shortening numbers. For example, 90.250000 will be displayed as 90.3 (not 90.2) when passed to format() with %.1f. The format() method also supports two additional features. You can specify the total length of output by using a number before the decimal symbol. By default, the method will fill the empty space with blank spaces. You can also fill the empty space with zeros, by placing a single zero before the decimal symbol. The following examples use brackets, [], to show the start/end of the formatted value:

var pi = 3.14159265359; System.out.format("[%f]",pi); // [3.141593] System.out.format("[%12.8f]",pi); // [ 3.14159265] System.out.format("[%012f]",pi); // [00003.141593] System.out.format("[%12.2f]",pi); // [ 3.14] System.out.format("[%.3f]",pi); // [3.142]

The format() method supports a lot of other symbols and flags. You don't need to know any of them for the exam beyond what we've discussed already.




### INPUTSTREAMREADER AND OUTPUTSTREAMWRITER

Most of the time, you can't wrap byte and character streams with each other, although as we mentioned, there are exceptions. The InputStreamReader class wraps an InputStream with a Reader, while the OutputStreamWriter class wraps an OutputStream with a Writer.

try (Reader r = new InputStreamReader(System.in); Writer w = new OutputStreamWriter(System.out)) { }


These classes are incredibly convenient and are also unique in that they are the only I/O stream classes to have both InputStream/ OutputStream and Reader/ Writer in their name. # Interacting with Users ## PRINTING DATA TO THE USER Java includes two PrintStream instances for providing information to the user: System.out and System.err. While System.out should be old hat to you, System.err might be new to you. The syntax for calling and using System.err is the same as System.out but is used to report errors to the user in a separate stream from the regular output information.

try (var in = new FileInputStream("zoo.txt")) { System.out.println("Found file!"); } catch (FileNotFoundException e) { System.err.println("File not found!"); }


How do they differ in practice? In part, that depends on what is executing the program. For example, if you are running from a command prompt, they will likely print text in the same format. On the other hand, if you are working in an integrated development environment (IDE), they might print the System.err text in a different color. Finally, if the code is being run on a server, the System.err stream might write to a different log file. ### USING LOGGING APIS While there are many logging APIs available, they tend to share a number of similar attributes. First, you create a static logging object in each class. Then, you log a message with an appropriate logging level: debug(), info(), warn(), or error(). The debug() and info() methods are useful as they allow developers to log things that aren't errors but may be useful. The log levels can be enabled as needed at runtime. For example, a server might only output warn() and error() to keep the logs clean and easy to read. If an administrator notices a lot of errors, then they might enable debug() or info() logging to help isolate the problem. Finally, loggers can be enabled for specific classes or packages. While you may be interested in a debug() message for a class you write, you are probably not interested in seeing debug() messages for every third‐party library you are using. ## READING INPUT AS A STREAM

var reader = new BufferedReader(new InputStreamReader(System.in)); String userInput = reader.readLine(); System.out.println("You entered: " + userInput);


## CLOSING SYSTEM STREAMS You might have noticed that we never created or closed System.out, System.err, and System.in when we used them. In fact, these are the only I/O streams in the entire chapter that we did not use a try‐with‐ resources block on! Because these are static objects, the System streams are shared by the entire application. The JVM creates and opens them for us. They can be used in a try‐with‐resources statement or by calling close(), although closing them is not recommended. Closing the System streams makes them permanently unavailable for all threads in the remainder of the program.

try (var out = System.out) {} System.out.println("Hello");

Nothing. It prints nothing. Remember, the methods of PrintStream do not throw any checked exceptions and rely on the checkError() to report errors, so they fail silently.

try (var err = System.err) {} System.err.println("Hello");

This one also prints nothing. Like System.out, System.err is a PrintStream. Even if it did throw an exception, though, we'd have a hard time seeing it since our stream for reporting errors is closed! Closing System.err is a particularly bad idea, since the stack traces from all exceptions will be hidden.

var reader = new BufferedReader(new InputStreamReader(System.in)); try (reader) {} String data = reader.readLine(); // IOException

It prints an exception at runtime. Unlike the PrintStream class, most InputStream implementations will throw an exception if you try to operate on a closed stream.

## ACQUIRING INPUT WITH CONSOLE
The java.io.Console class is specifically designed to handle user interactions. After all, System.in and System.out are just raw streams, whereas Console is a class with numerous methods centered around user input.
The Console class is a singleton because it is accessible only from a factory method and only one instance of it is created by the JVM.

For example, if you come across code on the exam such as the following, it does not compile, since the constructors are all private:
```Console c = new Console();  // DOES NOT COMPILE```

The following snippet shows how to obtain a Console and use it to retrieve user input:

Console console = System.console(); if (console != null) { String userInput = console.readLine(); console.writer().println("You entered: " + userInput); } else { System.err.println("Console not available"); }

The Console object may not be available, depending on where the code is being called. If it is not available, then System.console() returns null. It is imperative that you check for a null value before attempting to use a Console object!

### reader() and writer()
The Console class includes access to two streams for reading and writing
data.

public Reader reader() public PrintWriter writer()

Accessing these classes is analogous to calling System.in and System.out directly, although they use character streams rather than byte streams. In this manner, they are more appropriate for handling text data.

### format()
For printing data with a Console, you can skip calling the writer().format() and output the data directly to the stream in a single call.

Console console = System.console(); if (console == null) { throw new RuntimeException("Console not available"); } else { console.writer().println("Welcome to Our Zoo!"); console.format("It has %d animals and employs %d people", 391, 25); console.writer().println(); console.printf("The zoo spans %5.1f acres", 128.91); } //Welcome to Our Zoo! //It has 391 animals and employs 25 people //The zoo spans 128.9 acres.


#### USING CONSOLE WITH A LOCALE Unlike the print stream classes, Console does not include an overloaded format() method that takes a Locale instance. Instead, Console relies on the system locale. Of course, you could always use a specific Locale by retrieving the Writer object and passing your own Locale instance, such as in the following example:

Console console = System.console(); console.writer().format(new Locale("fr", "CA"), "Hello World");


### readLine() and readPassword() The Console class includes four methods for retrieving regular text data from the user.

public String readLine() public String readLine(String fmt, Object... args)

public char[] readPassword() public char[] readPassword(String fmt, Object... args)


Like using System.in with a BufferedReader,the Console readLine() method reads input until the user presses the Enter key. The overloaded version of readLine() displays a formatted message prompt prior to requesting input. The readPassword() methods are similar to the readLine() method with two important differences. - The text the user types is not echoed back and displayed on the screen as they are typing. - The data is returned as a char[] instead of a String. The first feature improves security by not showing the password on the screen if someone happens to be sitting next to you. The second feature involves preventing passwords from entering the String pool. ### Reviewing Console Methods

Console console = System.console(); if (console == null) { throw new RuntimeException("Console not available"); } else { String name = console.readLine("Please enter your name: "); console.writer().format("Hi %s", name); console.writer().println(); console.format("What is your address? "); String address = console.readLine(); char[] password = console.readPassword("Enter a password " + "between %d and %d characters: ", 5, 10); char[] verify = console.readPassword("Enter the password again: "); console.printf("Passwords " + (Arrays.equals(password, verify) ? "match" : "do notmatch")); } Please enter your name: Max Hi Max What is your address? Spoonerville Enter a password between 5 and 10 digits: Enter the password again: Passwords match ```

prev next

Tags: 

OCP Chapter 18

Concurrency

Threads

  • a Thread is the smallest unit of execution that can be scheduled by the operation system.
  • a process is a group of associated threads that execute in th esame shared environment.
  • single-threaded process have only one thread
  • multithreaded process uses one or more threads
  • shared environment share the same memory space and can directly communicate with one another.
  • a task is a single unit of work performed by a thread.
  • a task can be implemented as an lambda expression
  • a thread can complete multiple independent tasks but only task at a time.
  • for simplicity we refer to threads that contain only a single user defined thread as a single-threaded application. Since we are uninterested in the system threads.
  • the property of executing multiple threads and processes at the same time is actually referred as concurrency.
  • CPU uses thread scheduler to accomplish threads running
  • a thread scheduler may employ a round-robin schedule
  • when a thread alloted time is complete but the thread has not finished processing a context switch occurs. A context-switch is the process of storing a threads current state
  • thread priority

Thread Types

  • system thread is created by the JVM and runs in the background of the application. fe garbage collection
  • user-defined thread is one created by the applicationdeveloper to accopmplish a specific task.
  • deamon thread is one that will not prevent the JVM from exiting when the program finishes. Both sysem an user defined threads can be marked as daemon threads

Task with Runnable

@FunctionalInterface public interface Runnable {
   void run();
}

Summary

This chapter introduced you to threads and showed you how to process tasks in parallel using the Concurrency API. The work that a thread performs can be expressed as lambda expressions or instances of Runnable or Callable.

For the exam, you should know how to concurrently execute tasks using ExecutorService. You should also know which ExecutorService instances are available, including scheduled and pooled services.

Thread‐safety is about protecting data from being corrupted by multiple threads modifying it at the same time. Java offers many tools to keep data safe including atomic classes, synchronized methods/blocks, the Lock framework, and CyclicBarrier. The Concurrency API also includes numerous collections classes that handle multithreaded access for you. For the exam, you should also be familiar with the concurrent collections including the CopyOnWriteArrayList class, which creates a new underlying structure anytime the list is modified.

When processing tasks concurrently, a variety of potential threading issues can arise. Deadlock, starvation, and livelock can result in programs that appear stuck, while race conditions can result in unpredictable data. For the exam, you need to know only the basic theory behind these concepts. In professional software development, however, finding and resolving such problems is often quite challenging.

Finally, we discussed parallel streams and showed you how to use them to perform parallel decompositions and reductions. Parallel streams can greatly improve the performance of your application. They can also cause unexpected results since the results are no longer ordered. Remember to avoid stateful lambda expressions, especially when working with parallel streams.

(new Thread(new PrintData())).start();

// Will compile but not start a new thread!!

(new PrintData()).run();

Polling with Sleep

Polling is the process of intermittently checking data at some fixed interval.

public class CheckResults {
   private static int counter = 0;
   public static void main(String[] args) {
      new Thread(() -> {
         for(int i = 0; i < 500; i++) CheckResults.counter++;
}).start();
      while(CheckResults.counter < 100) {
System.out.println("Not reached yet");
      }
      System.out.println("Reached!");

Concurrency API

  • ExecutorService interface which creates and manage threads.
import java.util.concurrent.*;
public class ZooInfo {
   public static void main(String[] args) {
      ExecutorService service = null;
      Runnable task1 = () ->
         System.out.println("Printing zoo inventory");
      Runnable task2 = () -> {for(int i = 0; i < 3; i++)
            System.out.println("Printing record: "+i);};
try {
    service = Executors.newSingleThreadExecutor();
    System.out.println("begin");
    service.execute(task1);
    service.execute(task2);
    service.execute(task1);
    System.out.println("end");
} finally {
    if(service != null) service.shutdown();
} }
}
  • SHUTDOWN NIET VERGETEN!!!
  • shutdown stopt lopende threads niet, die lopen uit. dan gebruik shutdownNow()
  • shutdownNow() returns a list with tasks that were submitted to the thread executor but that were never started.

Submitting tasks

  • fire-and-forget
  • execute()
  • submit() returns a Future, can check if task is completed.

|Method name |Description| |-|-| |void execute(Runnable command)|Executes a Runnable task at some point in the future| |Future submit(Runnable task)|Executes a Runnable task at some point in the future and returns a Future representing the task| | Future submit(Callable task) | Executes a Callable task at some point in the future and returns a Future representing the pending results of the task| | List> invokeAll(Collection>tasks) throwsInterruptedException|Executes the given tasks and waits for all tasks to complete. Returns a List of Future instances, in the same order they were in the original collection| | T invokeAny(Collection> tasks) throws InterruptedException,ExecutionException|Executes the given tasks and waits for at least one to complete. Returns a Future instance for a complete task and cancels any unfinished tasks| ## Waiting for Results Future Methods |Method name |Description| |-|-| |boolean isDone()|Returns true if the task was completed, threw an exception, or was cancelled| |boolean isCancelled()|Returns true if the task was cancelled before it completed normally| |boolean cancel(boolean mayInterruptIfRunning)|Attempts to cancel execution of the task and returns true if it was successfully cancelled or false if it could not be cancelled or is complete| |V get()|Retrieves the result of a task, waiting endlessly if it is not yet available| |V get(long timeout, TimeUnit unit)|Retrieves the result of a task, waiting the specified amount of time. If the result is not ready by the time the timeout is reached, a checked TimeoutException will be thrown.| ## Callable - V call() throws Exception; - Future result = service.submit(()->1+2); result.get() //3 ## Wait for finish - do shutdown() - awaitTermination() - if awaitTermination is called before shutdown than full timeout value moet doorloopen worden ## submitting task collections - List list = service.invokeAll(List.of(task,task,task)); - invokeAny() executes a collection of tasks and returns the result of one of the tasks that succesfully completes executions, canceling al unfinished tasks! - invokeAll() will wait for all tasks to complete undefinitly untill all tasks are complete - invokeAll() en involeAny() hebben ook overload met timeout. ## Scheduling tasks - ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); + ScheduledFuture r1 = service.schedule(task1, 2, TimeUnit.SECONDS); - Executors.newSingleThreadScheduledExecutor loopt in de toekomst met delay

schedule(Callable<V> callable, long delay, TimeUnit unit) // after period
schedule(Runnable command, long delay, TimeUnit unit)
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) // every period
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

scheduleAtFixedRate

-service.scheduleAtFixedRate(command, 5, 1, TimeUnit.MINUTES);

Concurrency with Pools

  • ExecutorService newSingle ThreadExecutor()
  • ScheduledExecutorService newSingleThreadScheduledExecutor()
  • ExecutorService newCachedThreadPool()
  • ExecutorService newFixedThreadPool(int)
  • ScheduledExecutorService newScheduledThreadPool(int)

  • single threaded executor will wait for a thread to become available

  • a pooled-thread can execute the next task concurrently until the pool is filled then awaits etc

Determine the thread pool size

  • Runtime.getRuntime().availableProcessors()

Writing Thread-Safe Code

  • race-condition

Protect Data with Atomic Classes

  • atomic is the property of an operation to be carried out as a single unit of execution without any interference by another thread.

  • AtomicBoolean

  • AtomicInteger
  • AtomicLong

Improveing Acces with Synchronized Blocks

  • Atomic alleen helpt nog niets in de volgordelijkheid daarom hulp van monitor that supports mutual exclusion

Synchronizing Methods

ipv synchronize a var kan je ook de hele methode synchronizen

Lock Framework

  • ReentrantLock()
// Implementation #1 with a synchronized block
Object object = new Object();
synchronized(object) {
// Protected code
}

// Implementation #2 with a Lock
Lock lock = new ReentrantLock();
try {
   lock.lock();
// Protected code
} finally {
   lock.unlock();
}
  • void lock()
  • void unlock()
  • boolean tryLock()
  • booelean tryLock(long, TimeUnit)

tryLock()

  • used in try/finally block
Lock lock = new ReentrantLock();
new Thread(() -> printMessage(lock)).start(); if(lock.tryLock()) {
   try {
      System.out.println("Lock obtained, entering protected
code");
   } finally {
      lock.unlock();
}
} else {
   System.out.println("Unable to acquire lock, doing something
else");
}

Duplicate Lock Requests

  • critical to release all the locks!
  • ReentrantReadWriteLock niet voor t examen maar wel handig!

Orchestrating Tasks with a CyclicBarrier

  • taken met CyclicBarrier kunnen gegroepeerd en op volgorde van groepen worden uitgevoerd.
  • let op groote threadpool
  • let op deadlock als je rules of poolgrootes regelt
import java.util.concurrent.*;

public class CyclicBarrierExample {
    public static final int NUMBER_OF_THREADS = 4;
    public static final int NUMBER_OF_STEPS = 3;

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS, new Runnable() {
            @Override
            public void run() {
                System.out.println("All threads completed a step. Moving to the next step...\n");
            }
        });

        ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS);

        for (int i = 0; i < NUMBER_OF_THREADS; i++) {
            executorService.submit(new Worker(barrier));
        }

        executorService.shutdown();
    }
}

class Worker implements Runnable {
    private CyclicBarrier barrier;

    public Worker(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            for (int step = 1; step <= CyclicBarrierExample.NUMBER_OF_STEPS; step++) {
                performTask(step);
                System.out.println(Thread.currentThread().getName() + " completed step " + step);
                barrier.await(); // Wait for other threads
            }
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    private void performTask(int step) {
        // Simulating task for each step
        System.out.println(Thread.currentThread().getName() + " is performing task for step " + step);
        try {
            Thread.sleep((long) (Math.random() * 1000)); // Simulate varying task durations
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Concurrent Collections

  • om om memory consistency errors te vermijden. wanneer twee threads verschillende data krijgen die gelijk zou moeten zijn.
  • concurrentModificationException at runtime
        var foodData = new HashMap<String, Integer>();
        foodData.put("penguin", 1);
        foodData.put("flamingo", 2);
        for (String key : foodData.keySet()) {
            foodData.remove(key); // Key klopt niet meer na key penguin removed
        }

// wel met:

        var foodData = new ConcurrentHashMap<String, Integer>();
        foodData.put("penguin", 1);
        foodData.put("flamingo", 2);
        for (String key : foodData.keySet()) {
            System.out.println("remove key " + key);
            foodData.remove(key);

        }

In principe hetzelfde als de niet concurrent collecties

Class name Java Collections Framework interfaces Elements ordered? Sort? Blocking?
ConcurrentHashMap ConcurrentMap No No No
ConcurrentLinkedQueue Queue Yes No No
ConcurrentSkipListMap ConcurrentMap SortedMap NavigableMap Yes Yes No
ConcurrentSkipListSet SortedSet NavigableSet Yes Yes No
CopyOnWriteArrayList List Yes No No
CopyOnWriteArraySet Set No No No
LinkedBlockingQueue BlockingQueue Yes No Yes

Deleting when looping

List<String> birds = new CopyOnWriteArrayList<>();
birds.add("hawk");
birds.add("hawk");
birds.add("hawk");
for (String bird : birds) birds.remove(bird);
System.out.print(birds.size()); // 0

// kan dus ook met arraylist en iterator.

var iterator = birds.iterator();
while(iterator.hasNext()) {
   iterator.next();
   <b>iterator.remove()</b>;
}
System.out.print(birds.size());  // 0

Blocking Queue waiting methods

Method name Description
offer(E e,long timeout,TimeUnit unit) Adds an item to the queue, waiting the specified time and returning false if the time elapses before space is available
poll(long timeout,TimeUnit unit) Retrieves and removes an item from the queue waiting the specified time and returning null if the time elapses before the item is available
try {
var blockingQueue = new LinkedBlockingQueue<Integer>(); blockingQueue.offer(39);
blockingQueue.offer(3, 4, TimeUnit.SECONDS); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll(10,
TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
   // Handle interruption
}

Obtaining Synchronized Collections

  • synchronizedCollection(Collection c)
  • synchronizedList(List list)
  • synchronizedMap(Map<K,V> m)
  • synchronizedNavigableMap(NavigableMap<K,V> m)
  • synchronizedNavigableSet(NavigableSet s)
  • synchronizedSet(Set s)
  • synchronizedSortedMap(SortedMap<K,V> m)
  • synchronizedSortedSet(SortedSet s)

  • If you know at the time of creation that your object requires synchronization, then you should use one of the concurrent collection classes listed in this table.

Identifying Threading Problems

  • liveness is ability of an application to be able to execute in a timely manner.
  • kind of stuck state

  • deadlock

  • starvation
  • livelock

Deadlock

  • Deadlock occurs when two or more threads are blocked forever, each waiting on the other.

Starvation

  • Starvation occurs when a single thread is perpetually denied access to a shared resource or lock.

Livelock

  • Livelock occurs when two or more threads are conceptually blocked forever, although they are each still active and trying to complete their task.
  • gebeurt vaak als resultaat wanneer je een deadlock wilt voorkomen

Race Conditions

  • A race condition is an undesirable result that occurs when two tasks, which should be completed sequentially, are completed at the same time.
  • For the exam, you should understand that race conditions lead to invalid data if they are not properly handled. Even the solution where both participants fail to proceed is preferable to one in which invalid data is permitted to enter the system.

Parallel Streams

  • A parallel stream is a stream that is capable of processing results concurrently, using multiple threads.

Calling parallel() on an existing Stream

Stream<Integer> s1 = List.of(1,2).stream();
Stream<Integer> s2 = s1.parallel();

Calling parallelStream() on a Collection Object

Stream<Integer> s3 = List.of(1,2).parallelStream();

PERFORMING A PARALLEL DECOMPOSITION


public static void main(String[] args) { // feitelijk achter elkaar // long start = System.currentTimeMillis(); // List.of(1,2,3,4,5) // .stream() // .map(w -> doWork(w)) // .forEach(s -> System.out.print(s + " ")); // 12345 // feitelijk naast elkaar long start = System.currentTimeMillis(); List.of(1,2,3,4,5) .parallelStream() .map(w -> doWork(w)) .forEach(s -> System.out.print(s + " ")); //3 4 5 2 1 // naast elkaar maar in volgorde! List.of(5,2,1,4,3) .parallelStream() .map(w -> doWork(w)) .forEachOrdered(s -> System.out.print(s + " ")); // 5 2 1 4 3 } private static int doWork(int input) { try { Thread.sleep(5000); } catch (InterruptedException e) {} return input; }

PROCESSING PARALLEL REDUCTIONS

  • Since order is not guaranteed with parallel streams, methods such as findAny() on parallel streams may result in unexpected behavior.
  • Besides possibly improving performance and modifying the order of operations, using parallel streams can impact how you write your application. Reduction operations on parallel streams are referred to as parallel reductions. The results for parallel reductions can be different from what you expect when working with serial streams.
        System.out.println(List.of(1,2,3,4,5,6)
                .stream() .findAny().get());  // 1

        System.out.println(List.of(1,2,3,4,5,6)
                .parallelStream() .findAny().get()); // (vaak)4 (soms)1

        System.out.println(
                List.of(1,2,3,4,5,6).stream().unordered().findAny()); //Optional[1]
// unordedered can improve performance

Combining Results with reduce()

System.out.println(List.of('w', 'o', 'l', 'f') .parallelStream()
.reduce("",
(s1,c) -> s1 + c,
(s2,s3) -> s2 + s3)); // wolf

` - Intermediate result (s1 + c): o - Intermediate result (s1 + c): f - Intermediate result (s1 + c): w - Intermediate result (s1 + c): l - Combined result (s2 + s3): wo - Combined result (s2 + s3): lf - Combined result (s2 + s3): wolf - Final result: wolf

`

Order Order!

      System.out.println(List.of("w","o","l","f") .parallelStream()
                .reduce("X", String::concat)); // XwXoXlXf
        System.out.println(List.of("w","o","l","f") .stream()
                .reduce("X", String::concat)); // Xwolf

Combining Results with collect()


<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) Stream<String> stream = Stream.of("w", "o", "l", "f").parallel(); SortedSet<String> set = stream.collect(ConcurrentSkipListSet::new, Set::add, Set::addAll); System.out.println(set); // [f, l, o, w] natural soring dus alphabetish!

Requirements for Parallel Reduction with collect()

  • The stream is parallel.
  • The parameter of the collect() operation has the Characteristics.CONCURRENT characteristic.
  • Either the stream is unordered or the collector has the characteristic Characteristics.UNORDERED.

stream.collect(Collectors.toSet()); // Not a parallelreduction

The Collectors class includes two sets of static methods for retrieving collectors, toConcurrentMap() and groupingByConcurrent(), that are both UNORDERED and CONCURRENT.

Parallel reduction on a Collector

  • Every Collector instance defines a characteristics() method that returns a set of Collector.Characteristics attributes. When using a Collector to perform a parallel reduction, a number of properties must hold true. Otherwise, the collect() operation will execute in a single‐threaded fashion.

Requirements for Parallel Reduction with Collect()

  • The stream is parallel.
  • The parameter of the collect() operation has the Characteristics.CONCURRENT characteristic.
  • Either the stream is unordered or the collector has the characteristic Characteristics.UNORDERED.
stream.collect(Collectors.toSet());  // Not a parallel reduction Set is unorded but is not concurrent characteristic

The Collectors class includes two sets of static methods for retrieving collectors, UNORDERED and CONCURRENT - toConcurrentMap() - groupingByConcurrent()

Stream<String> ohMy =
Stream.of("lions","tigers","bears").parallel();
ConcurrentMap<Integer, String> map = ohMy
.collect(Collectors.toConcurrentMap(String::length, k -> k,
(s1, s2) -> s1 + "," + s2)); System.out.println(map); // {5=lions,bears, 6=tigers}
 System.out.println(map.getClass());
// java.util.concurrent.ConcurrentHashMap

// groupingby
var ohMy = Stream.of("lions","tigers","bears").parallel();
ConcurrentMap<Integer, List<String>> map = ohMy.collect(
Collectors.groupingByConcurrent(String::length));
System.out.println(map); // {5=[lions, bears], 6=[tigers]}

Avoiding Stateful Operations

Side effects can appear in parallel streams if your lambda expressions are stateful. A stateful lambda expression is one whose result depends on any state that might change during the execution of a pipeline. On the other hand, a stateless lambda expression is one whose result does not depend on any state that might change during the execution of a pipeline.

public static List<Integer> addValues(IntStream source) {
   return source.filter(s -> s % 2 == 0)
                .boxed() .collect(Collectors.toList()); // zonder lambda
}

prev next

Tags: 

OCP Chapter 17

Modular Applications

Derivative Description
exports Allows all modules to access the package
exports to Allows a specific module to access the package
requires Indicates module is dependent on another module
requires transitive Indicates the module and that all modules that use this module are dependent on another module
uses Indicates that a module uses a service
provides with Indicates that a module provides an implementation of a service

Comparing types of Modules

  • named modules
  • automatic modules
  • unnamed modules

Named Modules

  • contains modul-info.java file
  • is in the root of the jar
  • default name is mudule when not named

Automatic Modules

  • no module-info.java file
  • alleen de jar
  • property call Automatic-Module-Name in the MANIFEST>MF
  • based on the filename of the jar file
  • converts - to . als de jar name omgezet wordt

Unnamed Modules

  • net zoals een automatic
  • alleen op de classpath ipv module path
  • treated as old code and second class citizen
Property Named Automatic Unnamed
A ______ module contains a module‐info file? Yes No Ignored if present
A ______ module exports which packages to other modules? Those in the module‐info file All packages No packages
A ______ module is readable by other modules on the module path? Yes Yes No
A ______ module is readable by other JARs on the classpath? Yes Yes Yes

JDK Dependencies

  • java.base
  • java.desktop
  • java.loggin
  • java.sql
  • java.xml

beginnen allemaal met java.

jdeps

  • jdeps -s bla.jar is summary mode.
  • jdeps --jdk-internals asd.jar details about the unsupported APIs
  • -jdk-internals === --jdk-internals provide details about unsupported APIs

Migrating an Application

Strategies to connect to the world of modules. - determing the order - exploring bottom Up migration strategy - Top down strategy - split up and migration migration

Determing the order

-need to know how the packages and libraries are structured - not allowed cyclic dependencies

Bottom UP Migration Strategy

  1. Pick the lowest‐level project that has not yet been migrated.(Remember the way we ordered them by dependencies in the previous section?)
  2. 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 a requires directive for any modules it depends on.
  3. Move this newly migrated named module from the class path to the module path.
  4. Ensure any projects that have not yet been migrated stay as unnamed modules on the classpath.
  5. Repeat with the next‐lowest‐level project until you are done.

advantages: - more easy than other migrations - it encourage care in what is exposed - when having control of every jar in the project

Top-down Migration Strategy

  1. Place all projects on the module path.
  2. Pick the highest‐level project that has not yet been migrated.
  3. Add a module‐info file to that project to convert the automatic module into a named module. Again, remember to add any exports or requires directives. You can use the automatic module name of other modules when writing the requires directive since most of the projects on the module path do not have names yet.
  4. Repeat with the next‐lowest‐level project until you are done.
Category Bottom‐Up Top‐Down
A project that depends on all others Unnamed module on the classpath Named module on the module path
A project that has no dependencies Named module on the module path Automatic module on the module path

Creating a Service

Service Locator

  • uses load()

Invoking a Cosumer

Adding a Service provider

Merging Service Locator and Consumer

Reviewing Services

Artifact Part of the service Directives required in module‐info.java
Service provider interface Yes exports
Service provider No requires provides
Service locator Yes exports requires uses
Consumer No requires

prev next

Tags: 

OCP Chapter 16

Reviewing Exceptions

Handling Exceptions

        try {
            // protected code
        } catch (IOException e) {
            // handler
        } catch (ArithmeticException | IllegalArgumentException e) {
            // multi catch
        } finally {
            // allways run this
        }

throw throws

public String getDataFromDatabase() throws SQLException {
    throw new UnsupportedOperationException();
}

Exception category

  • all exception inherit from Throwable
  • handling exceptions from Exception
  • checked exception MUST be handled or declared
  • unchecked exceptions does not need to be handled or declared

Unchecked Exceptions inherit RUntomeException

  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • ArrayStoreException
  • ClassCastException
  • IllegalArgumentException
  • IllegalStateException
  • MissingResourceException
  • NullPointerException
  • NumberFormatException
  • UnsupportedOperationException

  • ! NumberFormatException inherits from IllegalArgumentException

Checked Exception

  • FileNotFoundException
  • IOException
  • NotSerializableException
  • ParseException
  • SQLException

  • ! FileNotFoundException and NotSerializableException inherits from IOException

  • broad to specific exception=> DNC

try {
   throw new IOException();
} catch (IOException | FileNotFoundException e) {} // DOES NOT COMPILE (parent + child...)

try {
   throw new IOException();
} catch (IOException e) {
} catch (FileNotFoundException e) {} // DOES NOT COMPILE (Order...)

Creating Custom Exceptions

When creating your own exception, you need to decide whether it should be a checked or unchecked exception. While you can extend any exception class, it is most common to extend Exception (for checked) or RuntimeException (for unchecked).

class CannotSwimException extends Exception {}

  class DangerInTheWater extends RuntimeException {}

  class SharkInTheWaterException extends DangerInTheWater {}

  class Dolphin {
   public void swim() throws CannotSwimException {

   }
}


// constructors

    public class CannotSwimException extends Exception {

    public CannotSwimException() {
      super();  // Optional, compiler will insert automatically
    }

    public CannotSwimException(Exception e) {
      super(e);
    }

    public CannotSwimException(String message) {
          super(message);
    }
}

Printing Stack Traces

  • e.printStackTrace();

Automating resource managment

  • constructing with try-with-resources statements
  • AutoClosable interface
  • OMGEKEERDE volgorde van opruimen...
  • try scope blijft in try scope!!
public class MyFileReader implements AutoCloseable { private String tag;
    public MyFileReader(String tag) { this.tag = tag;}
    @Override public void close() {
      System.out.println("Closed: "+tag);
    }
}

try (var bookReader = new MyFileReader("monkey")) {
    System.out.println("Try Block");
} finally {
   System.out.println("Finally Block");
}

//Try Block
//Closed: monkey
//Finally Block

// scoping

try (Scanner s = new Scanner(System.in)) {
 s.nextLine();
 } catch(Exception e) {
 s.nextInt(); // DOES NOT COMPILE
 } finally {
 s.nextInt(); // DOES NOT COMPILE
}

effectively Final Feature

it is possible to use resources declared prior to the try‐with‐resources statement, provided they are marked final or effectively final.

public void relax() {

final var bookReader = new MyFileReader("4");
MyFileReader movieReader = new MyFileReader("5");

try (bookReader;
     var tvReader = new MyFileReader("6");
movieReader) {
    System.out.println("Try Block");
} finally {
   System.out.println("Finally Block");
}

Suppressed Exceptions

  • de eerste wordt als main exception gezien
  • die daarna erbij komen als suppressed
public class TurkeyCage implements AutoCloseable { public void close() {
      System.out.println("Close gate");
   }
public static void main(String[] args) {
    try (var t = new TurkeyCage()) {
         System.out.println("Put turkeys in");
    }
} }

Assertions

  • assert test_value;
  • assert test_value : message;
  • assert => false => AssertionError
  • AssertionError is fatal and ends the program
  • JVM heeft moet -enableassertions of -ea flag hebben
  • -ea zonder iets dan geldt t voor alles in de running package
  • -ea:com.demos... voor alles in demos en eronder
  • -ea:comdemos.TestColors my.program.Main voor specifiek die Class

  • -disableassertions of -da om uit te zetten

// correct!
assert 1 == age;
assert(2 == height);
assert 100.0 == length : "Problem with length";
assert ("Cecelia".equals(name)): "Failed to verify user data";


// DNC
assert(1);
assert x -> true;
assert 1==2 ? "Accept" : "Error";
assert.test(5> age);
public class Party {

    public static void main(String[] args) {
       int numGuests = -5;
       assert numGuests> 0;
       System.out.println(numGuests);


    }
 }

Assertions should never alter outcomes.

int x = 10;
assert ++x> 10;

Dates and Times

Dates beginnen met een 1 NIET een 0... gebruik daarom Month.October

Class Description Example
java.time.LocalDate Date with day, month, year Birth date
java.time.LocalTime Time of day Midnight
java.time.LocalDateTime Day and time with no time zone 10 a.m. next Monday
java.time.ZonedDateTime Date and time with a specific time zone 9 a.m. EST on 2/20/2021

.now()

// hebben allemaal .now()
System.out.println(LocalDate.now());     // 2020-10-14
System.out.println(LocalTime.now());     // 12:45:20.854
System.out.println(LocalDateTime.now()); // 2020-10-14T12:45:20.854
System.out.println(ZonedDateTime.now()); // 2020-10-14T12:45:20.854-04:00[America/New_York]

.of()

LocalDate date1 = LocalDate.of(2020, Month.OCTOBER, 20);
LocalDate date2 = LocalDate.of(2020, 10, 20);

formatting

  • format()
  • getDayOfWeek()
  • getMonth()
  • getYear()
  • getDayOfYear()

DateTimeFormatter

LocalDate date = LocalDate.of(2020, Month.OCTOBER, 20);
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DAT E));

// or custom
var f = DateTimeFormatter.ofPattern("MMMM dd, yyyy 'at' hh:mm");
System.out.println(dt.format(f)); // October 20, 2020 at 11:12

// of de oude classes
DateFormat s = new SimpleDateFormat("MMMM dd, yyyy'at' hh:mm");
System.out.println(s.format(new Date()));  // October 20, 2020 at 06:15
  • y Year 20, 2020
  • M Month 1, 01, Jan, January
  • d Day 5, 05
  • h Hour 9, 09
  • m Minute 45
  • s Second 52
  • a a.m./p.m. AM, PM
  • z Time Zone Name Eastern Standard Time,EST
  • Z Time Zone Offset ‐0400

M Month m minute

Supported data/time symbols

Symbol LocalDate LocalTime LocalDateTime ZonedDateTime
y
M
d
h
m
s
a
z
Z

  • LocalDate : YMd
  • LocalTime : hsma
  • LocaldateTime : YMdhmsa
  • ZonedDateTime : alles

Custom Text values

  • concat
  • use '' 'var f = DateTimeFormatter.ofPattern("MMMM dd, yyyy 'at' hh:mm");'
  • escape ' met ' 'var g1 = DateTimeFormatter.ofPattern("MMMM dd', Party''s at' hh:mm");'

Internationalization i18n

Locale

Locale locale = Locale.getDefault();
System.out.println(locale);
  • first lowercase language
  • language is allways required
  • then _ with UPPERcase country
  • Locale.GERMAN => de
  • Locale.GERMANY => de_DE

met builder()

Locale l1 = new Locale.Builder()
                .setLanguage("en")
                .setRegion("US")
                .build();

specific for your program

in scope van program, niet machine

System.out.println(Locale.getDefault()); // en_US
Locale locale = new Locale("fr");
Locale.setDefault(locale); // change the default
System.out.println(Locale.getDefault()); // fr

Localing Dates

  • DateTimeFormatter.ofLocalizedDate(dateStyle)
  • DateTimeFormatter.ofLocalizedTime(timeStyle)
  • DateTimeFormatter.ofLocalizedDateTime(dateStyle, timeStyle)

  • .SHORT .MEDIUM .FULL

Localizing Numbers

  • java.text
  • eerst formatten dan pas number va maken

Factory methods to get a NumberFormat |Description| Using default Locale and a specified Locale| |-|-| |A general‐purpose formatter|NumberFormat.getInstance()| | | NumberFormat.getInstance(locale)| |Same as getInstance|NumberFormat.getNumberInstance()| | |NumberFormat.getNumberInstance(locale)| |For formatting monetary amounts|NumberFormat.getCurrencyInstance()| | |NumberFormat.getCurrencyInstance(locale)| |For formatting percentages|NumberFormat.getPercentInstance()| | |NumberFormat.getPercentInstance(locale)| |Rounds decimal values before displaying|NumberFormat.getIntegerInstance()| | |NumberFormat.getIntegerInstance(locale)| Once you have the NumberFormat instance, you can call format() to turn a number into a String, or you can use parse() to turn a String into a number.

double price = 48;
var myLocale = NumberFormat.getCurrencyInstance();
System.out.println(myLocale.format(price));

String s = "23.45";
var en = NumberFormat.getInstance(Locale.US);
System.out.println(en.parse(s)); // 23.45

// LET OP!!

String s = "23.45";
var en = NumberFormat.getInstance(Locale.FRANCE);
System.out.println(en.parse(s)); //  !! 23 vanwege de .

// currency

String s = "$23.45"; // "23.45" DNC !!!
var en = NumberFormat.getCurrencyInstance(Locale.US);
System.out.println(en.parse(s));


Custom Number Formatter

  • # , Omit the position if no digit exists for it. , $2.2

  • 0 - Put a 0 in the position if no digit exists for it - $002.20

 double d = 1234567.467;
 NumberFormat f1 = new DecimalFormat("###,###,###.0");
 System.out.println(f1.format(d)); // 1,234,567.5

 NumberFormat f2 = new DecimalFormat("000,000,000.00000");
 System.out.println(f2.format(d)); // 001,234,567.46700

 NumberFormat f3 = new DecimalFormat("$#,###,###.##");
 System.out.println(f3.format(d)); // $1,234,567.47

Locale Category

  • DISPLAY
  • FORMAT
  • Locale.setDefault() doet DISPLAY en FORMAT
    public static void printCurrency(Locale locale, double money) {
        System.out.println(
                NumberFormat.getCurrencyInstance().format(money) + ", " + locale.getDisplayLanguage());
    }

    public static void main(String[] args) throws ParseException {
        var spain = new Locale("es", "ES");
        var money = 1.23;

        // Print with default locale
        Locale.setDefault(new Locale("en", "US"));
        printCurrency(spain, money); // $1.23, Spanish

        // Print with default locale and selected locale display
        Locale.setDefault(Locale.Category.DISPLAY, spain);
        printCurrency(spain, money); // $1.23, espaÑol  ! let op NUMBERFORMAT is changed...

        // Print with default locale and selected locale format
        Locale.setDefault(Locale.Category.FORMAT, spain);
        printCurrency(spain, money); // 1,23 €, espaÑol
    }

Resource Bundle

  • op goede locatie...
ResourceBundle.getBundle("name");          // default locale
ResourceBundle.getBundle("name", locale);
  1. Lookfortheresourcebundlefortherequestedlocale,followedbytheone for the default locale.
  2. Foreachlocale,checklanguage/country,followedbyjustthelanguage.
  3. Usethedefaultresourcebundleifnomatchinglocalecanbefound.

having: - Zoo_en.properties - Zoo_hi.properties - Zoo.properties

Locale.setDefault(new Locale("hi"));
ResourceBundle rb = ResourceBundle.getBundle("Zoo", new Locale("en"));

Worden ALLE drie geladen!

Formatting Messages

  • {0} {1} als markers

Properties Class

  • HashMap<String, String>
System.out.println(props.getProperty("camel")); // null
System.out.println(props.getProperty("camel", "Bob")); // Bob is default waarde omdat camel er niet is

prev next

Tags: 

OCP Chapter 15

Functional Programming

Functional Interfaces

  • Supplier
  • Consumer, BiConsumer
  • Predicate, BiPredicate
  • Function apply(T), BiFunction aply(T,U), UnaryOperator apply(T), BinaryOperator apply(T,T)

en voor concurrency - Callable - Runnable

Func Iinterface method
Supplier T get()
Consumer, BiConsumer void accept()
Predicate, BiPredicate Boolean test(T t)
Function apply(T), BiFunction aply(T,U), UnaryOperator apply(T), BinaryOperator apply(T,T) apply()

Supplier

used when you want to generate or supply values whitout taking any input.

T get();

Supplier<LocalDate> s1 = LocalDate::now;
Supplier<LocalDate> s2 = () -> LocalDate.now();

LocalDate d1 = s1.get();
LocalDate d2 = s2.get();

Consumer & BiConsumer

used when you want to do something with a param but void return

void accept(T t);

BiConsumer<String, Integer> printBiConsumer = (s, i) -> System.out.println(s + " " + i);

printBiConsumer.accept("Age:", 30);
printBiConsumer.accept("Score:", 100);

Predicate & BiPredicate

used when filtering or matching.

boolean test(T t);
boolean test(T t, U u);

        Predicate<String> p1 = String::isEmpty;
        System.out.println(p1.test(""));
        System.out.println(p1.test("asd"));

        BiPredicate<String, String> p2 = String::startsWith;
        System.out.println(p2.test("asd", "as"));

Function UnaryOperator BinaryOperator

R apply(T t);
R apply(T t, U u);

Function<String, Integer> f1 = String::length;
System.out.println(f1.apply("hjh"));

BiFunction<String, String, String> f2 = String::concat;
System.out.println(f2.apply("hj","hubeek"));

UnaryOperator<String> u1 = String::toUpperCase;
System.out.println(u1.apply("hjh"));

BinaryOperator<String> b1 = String::concat;
System.out.println(b1.apply("hj","Hubeek"));

Convinience methods

  • Consumer, Function andThen()
  • Function, compose()
  • Predicate, and() negate() or()
Function<Integer, Integer> before = x -> x+1;
Function<Integer, Integer> after = x -> x+2;
Function<Integer, Integer> combined = after.compose(before); // gefore runs first...
System.out.println(combined.apply(3));


Predicate<String> egg = s -> s.contains("egg");
Predicate<String> brown = s -> s.contains("brown");
Predicate<String> brownEggs = egg.and(brown);
System.out.println(brownEggs.test("brownegg"));

Optional

  • When Optional is null it throws an NullPointerException...use if met .isPresent()
  • Optional.empty()
  • Optional.of(double)
  • Optional.ofNullable(value)
Optional o = Optional.ofNullable(null);
System.out.println(o.isEmpty());

Optional o = null;
System.out.println(o.isEmpty()); // nullpointer exception

Method When Optional is empty When Optional contains value
get() throws Exception returns value
ifPresent(Consumer c) does nothing calls consumer with value
ifPresent() return false returns true
orElse(T other) return other param returns value
orElseGet(Supplier s) return result Supplier returns value
orElseThrow() Throws NoSuchElementException returns value
orElseThrow(Supplier s) Throws exception created by calling Supplier returns value

Streams

  • a sequence of data
  • stream pipeline consists of operations that run on a stream to produce a result
  • source -> operations -> terminal operation

Finite Streams

Stream<String> empty = Stream.empty();
Stream<Integer> singleElem = Stream.of(4);
Stream<Integer> moreElem = Stream.of(4,54,78);

var list = List.of(234,354,564);
Stream<Integer> fromList = list.stream();
Stream<Integer> fromListParallel = list.parallelStream();

Infinite Streams

// random numbers
Stream<Double> random = Stream.generate(Math::random);
Iterator<Integer> iterator = list.iterator();
random.forEach(System.out::println);

// odd numbers
Stream<Integer> oddNumbers = Stream.iterate(1, n -> n + 2);
Iterator<Integer> iterator = list.iterator();

oddNumbers.forEach(System.out::println);

Stream Creation Methods

  • finite streams Stream.empty(), Stream.of(), coll.stream()
  • infinite streams Stream.generate(), Stream.iterate()
Method Finite or infinite notes
Stream.empty() Finite Creates Stream with zero elements
Stream.of(varargs) Finite Creates Stream with elements listed
coll.stream() Finite Creates Stream from a Collection
coll.parallelStream() Finite Creates Stream from a Collection where the stream can run in parallel
Stream.generate(supplier) Infinite Creates Stream by calling the Supplier for each element upon request
Stream.iterate(seed, unaryOperator) Infinite Creates Stream by using the seed for the first element and then calling the UnaryOperator for each subsequent element upon request
Stream.iterate(seed, predicate,unaryOperator) Finite or infinite Creates Stream by using the seed for the first element and then calling the UnaryOperator for each subsequent element upon request. Stops if the Predicate returns false
// Stream.iterate(seed, unaryOperator)
// Generate an infinite stream of even numbers starting from 0
Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);

// Limit the stream to the first 10 elements and print them
evenNumbers.limit(10).forEach(System.out::println);

Intermediata Operations

  • filter()
  • distinct()
  • limit() skip()
  • map()
  • flatMap()
  • sorted()
  • peek()
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
                                   .filter(n -> n % 2 == 0)
                                   .collect(Collectors.toList());
// evenNumbers will be [2, 4, 6]

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinctNumbers = numbers.stream()
                                       .distinct()
                                       .collect(Collectors.toList());
// distinctNumbers will be [1, 2, 3, 4, 5]


List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> firstThreeNumbers = numbers.stream()
                                         .limit(3)
                                         .collect(Collectors.toList());
// firstThreeNumbers will be [1, 2, 3]


List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> afterSkipTwo = numbers.stream()
                                    .skip(2)
                                    .collect(Collectors.toList());
// afterSkipTwo will be [3, 4, 5, 6]

List<String> words = Arrays.asList("hello", "world");
List<Integer> wordLengths = words.stream()
                                 .map(String::length)
                                 .collect(Collectors.toList());
// wordLengths will be [5, 5]


List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("a", "b", "c"),
    Arrays.asList("d", "e", "f"));
List<String> flatList = nestedList.stream()
                                  .flatMap(List::stream)
                                  .collect(Collectors.toList());
// flatList will be ["a", "b", "c", "d", "e", "f"]


List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
List<Integer> sortedNumbers = numbers.stream()
                                     .sorted()
                                     .collect(Collectors.toList());
// sortedNumbers will be [1, 2, 3, 4, 5]

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
       .peek(System.out::println) // prints each element
       .map(n -> n * 2)
       .collect(Collectors.toList());
// Output will be each number printed to the console

// map element -> transformed element
List<Long> ids = people.stream()
                               .map(Person::getId)
                               .collect(Collectors.toList());

// flatMap -> multiple streams naar nieuwe stream
List<List<Integer>> listOfLists = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5),
            Arrays.asList(6, 7, 8, 9)
        );

        // Use flatMap to flatten the list of lists into a single list
        List<Integer> flattenedList = listOfLists.stream()
                                                 .flatMap(List::stream)
                                                 .collect(Collectors.toList());

Common Terminal Operations

  • count()
  • min() max()
  • findAny() findFirst()
  • allMatch() anyMatch() noneMatch()
  • forEach()
  • reduce()
  • collect()
Optional<Integer> maxNumber = numbers.stream().max(Integer::compareTo);
Optional<String> firstName = names.stream().findFirst();
boolean hasEvenNumber = numbers.stream().anyMatch(n -> n % 2 == 0);
Method what happens for infinite streams return values reduction
count() Does not terminate long Yes
min() max() Does not terminate Optional Yes
findAny() findFirst() Terminates Optional No
allMatch() anyMatch() noneMatch() Sometimes terminates boolean No
forEach() Does not terminate void No
reduce() Does not terminate Varies Yes
collect() Does not terminate Varies Yes
  • count() in a finite stream number of elements. for infinite it never ends.
  • min() max() : Optional return type.
  • findAny() findFirst(): Optional
  • allMatch(), anyMatch() noneMatch():

  • foreach()
  • reduce()

  • filter()

  • distinct() werkt met equals()
  • limit()
  • skip()
  • map()
  • flatMap()
  • sorted()
  • peek() voor debugging

Primitive Streams

creating

  • IntStream
  • LongStream
  • DoubleStream
Stream<Integer> stream = Stream.of()1,2,3;
sout(stream.mapToInt(x->x).sum());

IntStream intStream = IntStream.of(1,2,3);
OptionalDouble avg = intStream.average();
  • OptionalDouble average()
  • Stream boxed() naar Wrapper Class (!)
  • OptionalInt OptionalDouble OptionalLong max() min()
  • IntDoubleLongStream range(int a, int b)
  • IntDoubleLong rangeClosed(a, b)
  • int long double sum()
  • IntSummaryStatistics summaryStatistics() -> numerous statistics

Mapping

Source streamclass To create Stream To create DoubleStream To create IntStream To create LongStream
Stream map() mapToDouble() mapToInt() mapToLong( )
DoubleStream mapToObj() map() mapToInt() mapToLong()
IntStream mapToObj () mapToDouble() map() mapToLong()
LongStream mapToObj () mapToDouble() mapToInt() map()
Stream<String> objStream = Stream.of("penguin", "fish");
IntStream intStream = objStream.mapToInt(s -> s.length());

Using Optional with Primitive Streams

  • OptionalDouble voor double optionals
  • Optional voor Double optionals ;-)
var stream = IntStream.rangeClosed(1,10);
OptionalDouble optional = stream.average();

Functional Interface for Primitives

Functional Interfaces for boolean

BooleanSupplier b1 = () -> true;
BooleanSupplier b2 = () -> Math.random()> .5;
System.out.println(b1.getAsBoolean()); // true
System.out.println(b2.getAsBoolean()); // false

Working with advanced Stream Concepts

var cats = new ArrayList<String>();
 cats.add("Annie");
 cats.add("Ripley");
 var stream = cats.stream();
 cats.add("KC");
 System.out.println(stream.count()); // 3 van wege lazy evaluated

Chaining Optionals

private static void threeDigit(Optional<Integer> optional) {
   optional.map(n -> "" + n)            // part 1
      .filter(s -> s.length() == 3)     // part 2
      .ifPresent(System.out::println);  // part 3
}

prev next

Tags: 

OCP Chapter 14

Generics and Collections

Functional Interfaces

Functional interface return type method name params
Supplier T get() 0
Consumer void accept() 1(T)
BiConsumer<T, U> void accept(T,U) 2(T,U)
Predicate boolean test(T) 1(T)
BiPredicate<T, U> boolean test(T, U) 2(T, U)
Functio<T, R> R apply(T) 1(T)
BiFunctio<T, U, R> R apply(T, U) 2(T, U)
Unary T apply(T) 1(T)

Method references

  • static methods
  • instance methods on a particular instance
  • instance methods on a parameter to be determined at runtime
  • constructors

Static Methods

.sort() is static method on Collections. Eerst vb : // Java magie om die ene param om te zetten naar input param en method overloading keuze. bij compiler twijfel -> Error

Consumer <List<Integer>> methodRef = Collections::sort 
Consumer<List<Integer>> lambda = Collections.sort(x);

instance method

var str = "asd";
Predicate<String> methodRef = str::startsWith;
Predicate<String> lambda = str.startsWith(s);

instance method on a parameter

Predicate<String> methodRef = str::isEmpty;
Predicate<String> lambda = str.isEmpty();

Calling constructor

Supplier<List<String>> methodRef = ArrayList::new;
Supplier<List<String>> lmbda = () -> new ArrayList();

Function<Integer,List<String>> methodRef = ArrayList::new;
Function<Integer, List<String>> lmbda = () -> new ArrayList();

Using wrapper class

voor primitives .valueOf()

Diamond operator

  • ONLY right side
var list = new ArrayList<>(); // Worden List<Object> als niet gespecificieerd
var list = new ArrayList<Integer>(); // Worden List<Integer>
List<> list2 = new ArrayList<Integer>(); // DNC!!

List Maps Sets and Queues

4 interfacces:

  • list: ordered collections with int index
  • set: collection without dupes UNordered
  • queue: coolection that orders elements for operation in typical order
  • map: collection maps keys + values. no dupe key allowed.

Collection is parent of all except map!

Common collections methods

  • add()
  • remove() (let op bij enhanced for loop for(var a: coll) {} )
  • isEmpty, size()
  • clear()
  • contains()
  • removeIf()
  • forEach()
list.forEach(System.out::println);
list.forEach(element -> System.out.println(element));

List interface

No adding or deleting: - Arrays.asList(varargs) , fixed size, replace yes - List.of(varargs) , immutable list - List.copyOf(varargs), imuutable with copies of ori values

List methods

  • boolean add(E element), adds at the end
  • void add(int index, E elem) adds at index moves rest to end
  • E get(in t index)
  • E remove(int index) removes at index and moves to front
  • void replaceAll(UnaryOperator op) replaces
  • set(int index, E e) replaces and returns original(!)
for (String string: list) {
System.oiut.println(string);
}
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String string = iter.next();
System.out.println(string);
}

Queue Interface

  • ordered

Queue methods

  • boolean add(E e) -> (can throw Exc)
  • E element() ->(can throw Exc)
  • boolean offer(E e)
  • E remove() -> (can throw Exc)
  • E poll()
  • E peek()

Map

unordered
Hashmap
TreeMap

Map methods

  • void clear()
  • boolean containsKey
  • boolean conatinsVlaue
  • Set<Map.entry<K,V>> entrySet()
  • void forEach(BiConsumer(K,V))
  • V get(Object key)
  • V getOrDefault(Object key, V defaultValue)
  • boolean isEmpty
  • Set keySet()
  • V merge(K key, V value, Function(<V,V,V> func))
  • put(K,V)
  • V putIfAbsent(K,V)
  • V remove(Object k)
  • V replace(K,V)
  • void replaceAll(BiFunction<K,V,V> func)
  • int size()
  • Collection values()
map.forEach((k,v) -> System.out.println(v));
map.values().forEach(System.out::println);
map.entrySet().forEach(e->System.out.println(e.getKey() +e.getValue()))

Comparing CollectionTypes

Type Dupes Ordered KV Add/Remove in specifi order
List Yes Yes(by index) No No
Set No No No No
Queue Yes Yes(retrieved in defined order) No Yes
Map Yes No Yes No

Which data structures allow null

  • die gesorteerd kunnen worden mogen geen null hebben...

Oude collections

  • Vector -> List
  • Hashtable -> HashMap
  • Stack -> Queue

Sorting

  • Collections.sort() returns void because sorts method param.
  • Numbers > letters
  • Uppercase > lowercase
  • Pas op Comparable <-> Comparator interface
var list = new ArrayList<String>();
        list.add("asda");
        list.add("123asda");
        list.add("ASD");
        list.sort(null);
        System.out.println(list); //[123asda, ASD, asda]

Comparable class

  • id - a.id ASC
  • a.id - id DESC

Comparator class

  • uit java.util
  • specifiek gemaakte vergelijking
        Comparator byWeight = new Comparator<Duck>() {
            @Override
            public int compare(Duck o1, Duck o2) {
                return o1.getWeight() - o2.getWeight();
            }
        };
        Comparator<Duck> byWeight = (d1, d2) -> d1.getWeight() - d2.getWeight();
        Comparator<Duck> byWeight = Comparator.comparing(Duck::getWeight);
        Collections.sort(ducks, byWeight);

Belangrijk

Difference Comparable Comparator
package name java.lang java.util
interface must be implemented by class comparing Yes No
method name compareTo() compare()
Number of params 1 2
Common to declare using a lambda No Yes

Multiple fields

public class Squirrel {
    private int weight;
    private String species;
// all the getters setter constructorsetc
}

public class MultiFieldComparator implements Comparator<Squirrel> {
    public int compare(Squirrel s1, Squirreel s2) {
        int result = s1.getSpecies().compareTo(s2.s2.getSpecies());
        if(result != 0) {return result;}
        return s1.getWeight()-s2.getWeight();
    }
}

// OF

Comparator<Squirrel> c = Comparator.comapre(Squirrel::getSpecies).thenComparingIn(Squirrel::getWeight);

// kan ook met .reversed()

Methods

  • ---- helper static methods ----
  • comparing(function)
  • comparingDouble(func)
  • comparingInt(func)
  • comparingLong(func)
  • naturalOrder()
  • reversedOrder()
  • ---- helper default methods ----
  • reversed()
  • thenComparing(func)
  • thenComparingDouble/Int/Long(func)

  • Collections.binarySearch() !!!

  • binarySearch requires a sorted List.

TreeSet needs sorted collection

init

Set<Rabbit> rabbits = new TreeSet<> ((r1,r2) -> r1.id =r2.id);
rabbits.add(new Rabbit()); //Werkt nu wel

Generics

  • E element
  • K map Key
  • V map Value
  • N number
  • T generic data type
  • S,U,V for multiple generic types

type erasure

Alles onderwater na compilen Object geworden. en dan verdwijnt dus eigenlijk t class-type

Generic Interfaces

interface can declare formal type param.

public interface Abc<T> {
    void doSomething(T t);
}

No nos with Generic Types

  • calling constructor T() als constructor is not allwed
  • creating an array of that generic type
  • calling instanceOf (want alles is onderwater natuurlijk een Object...)
  • using the primitive type as a generic type param... moet dan dus met de wrapper class...
  • static variable T no no

Generic methods

staat voor return type: public <T> int calc(){return -1;}

public class Handler {
    public static <T> void prepare(T t) {

    sout("T: "+ t);
    }
}

confusing T tricky

class T is not same as method T!!!

public class Crate<T> { //

    public <T> T tricky(T t) {
        return t;
    }
}

Bounding Generic Types - generics are treated as Object... - ? wildcard - *bounded param type* - *wildcard generic type* - List en ArrayList<>

Types of generic wildcard bounds

  • unbounded wildcard, ? , any type is OK
  • upper bound, ? extends type ?
  • lower bound, ? super type
// unbounded
List<?> l = new ArrayList<String>();

// upper bounded
List<? extends Exception> l = new ArrayList<RuntimeException>();

// lower bounded
List<? super Exception> l = new ArrayList<Object>();

unbounded

not the same!

List<?> q = new ArrayList<>();
var q2 = new ArrayList<>();

upper bounded

  • list becomes immutable...
    public static long total(List<? extends Number> nums) {
        long result = 0;
        for (Number num: nums) {
            result += num.longValue();
        }
        return result;
    }

// of voor beter resultaat met floats erin

    public static double total(List<? extends Number> nums) {
        BigDecimal result = BigDecimal.ZERO;
        for (Number num: nums) {
            result = result.add(new BigDecimal(num.toString()));
        }
        return result.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

lower bound

  • zelfde class of subclasses...
  • let op met exceptions

collections in de rebound

List

  • arrayList
  • LinkedList

Set

  • HashSet, unorded elements
  • TreeSet, sorted, null NOT allowed

Queue

  • LinkedList

Map

  • HashMap
  • TreeMap, sorted, null NOT allowed

prev next

Tags: 

OCP Chapter 13

Annotations

  • assign metadata tp classes methods variables and other Java types
  • an annotation ascribes custom information on the declaration where it is defined
  • annotations zal compileren niet in de weg zitten
  • case sensitive!
  • zijn impliciet abstract =. dus geen protected, private en final
  • required element, optional element, constant element
  • all requiremed elements must be provided
  • marker annotation has no elements
// creeerr Annotatie
public @interface Exercise {}

// gebruik m
@Exercise public class Blah{}

annotation element

  • zonder value => dan dus REQUIRED
public @interface Exercise {
 int hoursPerDay();
}

// =>

@Exercise(hoursPerDay=34)public class Blah {}

providing an optional element with default

  • NOT NULL
  • type mag primitive, Class, enum, another annotation or an array of these types
  • leeg mag wel => "" als empty String
public @interface Exercise {
 int hoursPerDay();
 int startHour() default 6; // optional element
}

Adding a constant var

public @interface Exercise {
 int MINVALUE = 21;
}

Applying Annotations

prev next

Tags: 

OCP Chapter 12

applying final modifier

  • not needed to assign a value when final variable is declared.
  • abstract <=> final. of t 1 of t ander. public abstract final bla(); DNC
  • final kan niet meer veranderen, one time assignement, not more
  • variables + static variables
  • final methods cannot be overriden by subclass
  • na final class geen extends meer...
  • final interface DNC...

Enums

  • an enum is a type of class that mainly contains static members
  • values gescheiden door komma ,
  • it contains helper func .values(), .ordinal(), .toString(), .compareTo(s), .equals(s)
  • can create (abstract) methods
  • enum value can be composed! END with semicolon ;!
  • wanneer enum value composed dan constructors for fields and methods

Nested Classes

  • inner class
  • static nested class
  • local class
  • anonymous class

can acces members of outer class

kan ook zo geinstantieerd worden...

Outer outer = Outer();
Inner inner = outer.new Inner();

scoping vars inner class

De private vars blijven bereikbaar vanuit nested en parent.


public class Outer { private int x = 10; Outer() { Inner inner = new Inner(); Inner.InnerDeeper inDeep = inner.new InnerDeeper();// niet Inner.new InnerDeeper().. } class Inner { private int x = 20; Inner() { System.out.println("constructor inner"); System.out.println("this private x"); System.out.println(x); System.out.println(this.x); } public void doSomething() { System.out.println("inner does something"); InnerDeeper innerDeeper = new InnerDeeper(); } class InnerDeeper { private int x = 30; InnerDeeper() { System.out.println("In innerdeeper"); System.out.println(x); System.out.println(this.x); System.out.println(Inner.this.x); System.out.println(Outer.this.x); } } } public void callInner() { Inner i = new Inner(); i.doSomething(); } }
public class A {

    private int x = 10;

    class B {

        private int x = 20;

        class C {

            private int x = 30;

            public void allTheX(){
                System.out.println(x);//30
                System.out.println(this.x);//30
                System.out.println(B.this.x);//20
                System.out.println(A.this.x);//10
            }

        }
    }
}

Static nested class

  • can be instantiated without instance enclosing class
  • cant access instance variables or methods in outer class directly. explicit reference to outer class is needed.
  • enclosing class wordt als namespace gezien bij imports

Local Class

  • gewoon als local variable
  • GEEN acces modifier
  • toegang tot alle vars (als final, effectifly final) en methods in enclosing class
  • Als local Class in Method dan vars in de method moeten effectively final zijn!!!

Anonymous Class

  • vergeet de ; niet!!

modifiers in nested classes

permitted Modifiers Inner Class static nested class Local class Anonymous class
acces modifiers All All None None
abstract Yes Yes Yes No
final Yes Yes Yes No

members in nested classes

permitted Members Inner Class static nested class Local class Anonymous class
Instance Methods Yes Yes Yes YES
Instance Variables Yes Yes Yes YES
static Methods No Yes No NO
static Variables Yes (if final) Yes Yes (if final) Yes (if final)

nested class acces rules

Inner class static nested class Local class Anonymous class
can extend any class or implement any number of interfaces Yes Yes Yes No - exact 1 superclass or interface
can acces instance memebrs of enclosing class without reference Yes No Yes(if declared in instance method) Yes(if declared in instance method)
can access local var of enclosing method N/A N/A Yes(if final) No - exact 1 superclass or interface

Default Interface Method Definition Rules

  1. default method may be declared only within an interface
  2. default method must be marked with default keyword and include a body
  3. default method is assumed to be public
  4. default cannot be marked abstract, final or static
  5. default may be overriden by a class that implements the interface
  6. if a class inherits two or more default methods with the same method signature the the class must override the method
  • default method is a method defined in an interface with default keyword and includes a method body

  • default method alleen in interfaces!

Inheriting duplicate default methods

  • geen duplicate default methods wanneer er meerdere worden implemented
  • wel duplicate als de dubbele word overriden

static Interface methods

  1. static method must be marked with static keyword and include method BODY
  2. static method without acces modifier is public
  3. static method kan niet abstract of final zijn
  4. static method is not inherited and cannot be accessed in a class implementing the interface without a reference to the interface name...

private interface methods

  • tegen code duplication en als helper
  1. private interface method must be marked with private modifier and include BODY
  2. private interface method may be called by default and private (non-static) methods

private static interface methods

  1. must be marked private static + BODY
  2. can be used by the other methods in the interface.

Functional Programming

  • annotations are optional
  • Functional Interface is an interface that contains a single abstract method
  • SAM regel Single Abstract Method
  • any functional interface can be implemented as lambda expression

(in interface als method geen body heeft is ie impliciet abstract...)

Object parent heeft methods

  • toString()
  • equals(Object)
  • hashCode()
  • DEZE TELLEN NIET MEE in SAM regel

prev next

Tags: 

OCP Chapter 11

Modules

Custom Java Builds

jlink used to create runtime images.

Improved performance

unique package enforcement

  • anders twee packages met zelfde naam maar verschillende code

Creating and Running a Modular program

  • code files nofidig
  • module-info.java file nodig
  • module-info.java file can be empty
  • (weetje ) zelfs empty .java class kan in project. De compiler skipt m dan
  • javac optie -d specifies de dir in which t compiles
  • javac optie --module-path indicates location of custom module files
  • javac optie -p is gelijk aan --module-path

javac -d

javac -p is gleijk aan javac --module-path

Module-info file operators

  • exports
  • requires
  • provides
  • uses
  • opens

Exports

  • possible to exports to specific module. exports a.b.c to a.d
  • access control via private, default (package-private), protected, public

Requires transitive

  • requires modeulName specifies that the current module depends on moduleName
  • requires transitive specifies any module that requires this module will also depend on moduleName
  • requires and requires transitive met dezelfde ref mogen niet samen in module-info

provides, uses, opens

  • hoef je alleen te weten dat ze bestaan
  • provides specifies that a class provides an implementation of a service (zoals een interface...)
  • uses specifies that a module is relying on a service.
  • opens is voor runtime inspection... moet apart worden opgegeven ivm security

java Command

3 module related options: - describing with --describe-module (vermeld dan ook java.base module) - listing --list-modules - module resolution --show-module-resolution

jar Command

  • --file of -f beschrijf module (feitelijk hetzelfde als java --describe-module)

jdeps Command

  • info about dependencies within the module.
  • jdeps -s of jdeps -summary (maar 1 minnetje...)
  • kan ook zonder -s of -summary
  • --module-path kan niet worden afgekort in jdeps.... maar moet je helemaal uitschrijven...

jmods Command

  • jmod is only working with jmod files

prev next

Tags: 

OCP Chapter 9a

Advanceed CLass Design

Abstract classes

  • Abstract class is a class that cannot be instantiated and may contain abstract methods.
  • Abstract methods is a method that doesnot define an implementation when it is declared.
  • Abstract class cannot instantiated
  • Abstract methods alleen in Abstract Classess!!!
  • abstract keyword VOOR class/return type keyword
  • Geen final + abstract!! //DNC
  • Geen private + abstract!! //DNC
abstract class Asd {

    public static main(String[] args) {
        var a = new Asd(); //DNC
    }
}
public class abstract Zxc {    //DNC
    public int abstract ert(); //DNC
}

public abstract class Turtle {

    public abstract long eat()       //DNC
    public abstract void swim() {};  //DNC
    public abstract int getAge() { return 10;}   //DNC
    public void sleep;  //DNC
    public void goInShell(); //DNC
}

public abstract final class Turtle{ //DNC
    public anstract final void walk(); //DNC
}

public abstract class Asd {

    public void bla() {}

}
public class Qwe extends Asd {

}
var q = new Qwe();
q.bla();

Abstract Class Rules

  • abstract classes cannot be instanciated
  • all top-level types including abstract classes cannot be marked protected or private.
  • Abstract classes cannot be marked final
  • abstract classes may include zero or more abstract and nonabstract methods
  • abstract class that extends another abstract class inherits all of its abstract methods
  • the first concrete class that extends an abstract class must provide an implementation for all of the inherited abstract methods
  • abstract class constructors follow the same rules for initialization as regular constructors except they can be called only as part of the initilization of a subclass

Abstract Method Definition Rules

  • Abstract methods can be defined only in abstract classes or interfaces
  • Abstract methods cannot be declared private or final
  • Abstract methods must not provide a method body/implementation in the abstract class in wich they are declared.
  • Implementing an abstract method in a subclass follows the same rules for overriding a method including covariant return types exception declarations etc

implementing Interfaces

  • any number of interfaces can be implemented
  • interface is an abstract data type tyhat declares a list of abstract methods that any class implementing the interface must provide.
  • interface can include constant variables.
  • abstract methods and constant vars included with the interface are implicitly assumed to be public
  • interface cannot be marked final
public/default abstract interface CanPurr{ 
    public abstract Float getSpeed(int start)
    public static final int MIN  = 2;
}
  • abstract is implicit modifier. implicit modifier is a modifier that the compiler automatically adds to a class, interface, method or variable declaration.

Implicit modifiers

  • interfaces are assumed to be abstract
  • interface variables are assumed to be public, static and final
  • interface methods without a body are assumed to be abstract and public
  • interface can implement another interface
  • meerdere interfaces met dezelfde methods moeten compatible zijn
public interface Soar {
 int MAX_HEIGHT = 10;
 final static boolean UNDERWATER = true;
 void fly(int speed);
 abstract void takeoff();
 public abstract double dive();
}

public *abstract* interface Soar {
 *public static final* int MAX_HEIGHT = 10;
 *public* final static boolean UNDERWATER = true;
 *public abstract* void fly(int speed);
 *public* abstract void takeoff();
 public abstract double dive();
}

interface Dance {
 private int count = 4;// DNC
 protected void step(); //DNC
}

Interfaces vs Abstract Classes

  • interfaces make use of implicit modifiers

Interface Definition Rules

  • interfaces cannot be instantiated
  • all top level types including interfaces, cannot be marked protected or private
  • interfaces are assumed to be abstract and cannot be marked final
  • interfaces may include zero or more abstract methods
  • an interface can extend any number of interfaces
  • an interface refenrece may be cast to any reference that inherits the interface although this may produce an exception at runtime if the classes arent related.
  • the compiler will only report an unrelated type error for an instanceof operation with an interface on the right side if th ref on the left side is final classth at doesnot inherit the interface
  • an interface method with a body must be marked default, private, static ot private static.

abstract interface methods rules

  • abstract methods can be defined only in abstract classes or interfaces
  • abstract methods cannot be declared private or final
  • abstract methods must not provide a method body/implementation in th eabstract class in which it is declared
  • implementing an abstract method in a subclass follows the same rules for overriding a method including covariant return types, exceptios,
  • interface methods without a body are assumed to be abstract public

Interface Variables Rules

  • interface variables are assumed to be public, static and final
  • because interface vars are marked final the must initialze with a value when they are declared.

Inner Classes


prev next

Tags: 

OCP Chapter 10

Exceptions

  • Throwable superclass
  • Throwable is parent van Exception en Error
  • Exception is parent van RuntimeException

  • RuntimeException kan altijd gegooid worden. De andere Exceptions moeten worden gechecked

  • Exception hoeft niet bad te zijn
  • checked exception => CHECKED by the compiler
  • unchecked exception => NOTCHECKED by the compiler

  • Bij overriding methods only same or less exceptions NOT more exceptions

Throwable
  Exception
    RuntimeException

  Error
    OutOfMemoryError
    StackOverflowError

exceptions

![exceptions.png](OCP Chapter 10_files/c8216fb0-f902-4794-92cd-7ac0b87ee4dc.png)

Exception Types

  • Throwable
  • Exception
  • Error

Checked Exceptions

  • must be declared or handled by the application where it is thrown
  • checked exception inherit Exception but not RuntimeException
  • try catch blocks
  • handle or declare rule

Unchecked Exceptions

  • checked exceptions
  • unchecked exceptions (Runtime exceptions)
  • unchecked do not need to be declared or handled where it is thrown. RuntimeExceptions or Error
  • unexpected but not fatal.

throws and throw

Recognizing Exception Classes

  • RuntimeException
  • checked Exception (oplossen!)
  • Error
type how to recognize okay for program to catch Is program required to handle or declare
Runtime Exception Subclass of RuntimeException YES No
Checked Exception Subclass of Exception but not RuntimeException YES YES
Runtime Exception Subclass of Error No No
RuntimeException e  = new RuntimeException();
throw e;
Exception e  = new Exception(); 
throw e; // DNC

public class MyRuntimeException extends RuntimeException {

    public MyRuntimeException() {
        super("My runtime exception");
    }
}

public static void main(String[] args) {
        MyCheckedException e = new MyCheckedException(); // DNC
        throw e; //DNC
    }

Checked exception - program continues na catch

RuntimeException - continues in try catch - stops als niet in try catch

Error stopt altijd

Recognizing Exception CLasses

  • ArithmeticException - Thrown when code attempts to divide by zero
  • ArrayOutOfBoundsException - array en illegal index
  • ClassCastException - illegal cast to another class
  • NullPointerException - when there is a null reference when object is required..
  • IllegalArgumentException - method input illegal (handig als guard met specifieke error)
  • NumberFormatException - "123abc" is een string die niet naar nummer kan SUBCLASS van IllegalArgumentException

ClassCastException

let op:

String s = "sdf";
        Object o = s;
        Integer i = (Integer) o;
// bouwt wel maar runtime error

String s = "sdf";
Integer i = (Integer) s; // bouwt niet.

Checked Exceptions Classes

  • IOException while reading/writing file
  • FileNotFoundException subclass of IOException

Error Classes

  • ExceptionInInitializerError static init throws
  • StackOverflowError endless loop in memry
  • NoClassDeffoundError

Handling Exceptions

  • eerst subclass dan parent anders unreachable code => DNC
  • multicatch met | => catch( exception1 | exception2 error) {}
  • multicatch met 1x error variable!
  • multicatch niet met sub and parent classes DNC
  • exception kan maar in 1 catch voorkomen
  • catch niet verplicht kan maar moet niet

Finally

  • als geen catch dan finally
  • finally will always run!
  • finally kan zonder catch
  • allen System.exit(0); kan finally doen ontlopen
  • finally is handig om resources af te knopen

Closing resources

  • try-with-resources statement or automatically cloase all resources openend in a try clause.
  • mag catch en finnally overslaan
  • implicit finally runs FIRST
  • 1x finally is max
  • declaring multiple resources gescheiden door ;

Scope of try-with-resources

  • Scope is alleen in de try. Niet catch en finally....
  • resources are closed after try en before catch&finally
  • resource closed in reveoerse order of creation

Declaring and Overriding Methods with exceptions

  • minder CHECKED exceptions mag in child NIET in parent
  • child mag ook child van exception
  • unchecked exception mag minder of meer in childs en parents

printing exceptions

  • sout(e)
  • sout(e.message())
  • e.printStackTrace()

prev next

Tags: 

OCP Chapter 09

Class Design

Inheritance

  • Overerving
  • is proces by which subclass automatically includes any public or protected memebrs of the class
  • including primitives, objects, methods
  • Package private memebrs are available if the child is in the same package as the parent
  • private memebrs are restricted to the class they are defined in and never available via inherintance.
  • PREVENT inheritance with final keyword

Single vs Multiple Inheritance

  • class may inherit from only one direct parent class
  • class may also inherit from another class that inherits from another class

  • by design java doesnt support multiple inheritance. Only one exception: Multiple interfaces

// Steeds 1 parent
class Animal {}
class VierFooter {}
class Dog extends VierFooter{}
class Puppy extends Dog{}

Inheriting Object

  • java.lang.Object is parent van alles java classes

Creating Classes

public - default

abstract - final

public/default abstract/final class ClassName extends ANotherClass{}

Class Access Modifiers

  • top-level-class is a class that is not defined inside another class
  • inner-class is a class defined inside of another class

this.

private String color;
public void setColor(String color) {
    color = color; // gaat dan field zetten met zichzelf -> null...
}

Daarom this.color

super.

  • moet als eerste worden uitgevoerd.
  • parent class func uitvoeren
  • mag maar 1x worden gebruikt in constructor
  • verwijst naar de dichtst bijzijnde.

Declaring constructors

  • geen return type!

    public class Bunny {
    public Bunny(){}
    }
    
    
    public class Bunny {
    public bunny() {} // DNC
    public void Bunny() {} // is geen constructor maar compiles
    }
    

Overloading Constructors this()

  • this() is de eerste lijn in de contructor, MOET als eerste worden uitgevoerd.
  • geen this aanroep loop -> DNC

Missing Default No-Argument Constructor

public class Mammal {}

public class Elephant extends Mammal {} // DNC

public class Rabbit {

    public Rabbit(){}
    private Rabbit(boolean b){}
}


Rabbit b = new Rabbit();


public class Mammal {
    public Mammal(int age){}
}

public class Elephant extends Mammal {
    public Elephant() {
        //super(); DNC
        super(2);

    }
}
Elephant e = new Elephant();

Constructors and final fields

  • let op volgorde {} blocks dan pas constructor
  • final mag maar 1x

Order of Initialization

  • super class first
  • process all static variables in order of appearancce
  • process all static inits in order
public class Animal {
    static { System.out.println("A");}
}

public class Hippo extends Animal {
    static { System.out.println("B");}

    public static void main(String[] args){
        System.out.println("B");
        new Hippo();
        new Hippo();
        new Hippo();
    }
}

  • constructor start met this(); of super();
  • geen this or super dan no-arg super()
  • this() of super als niet eerste -> DNC
  • if parent hasnt no-arg constr dan child moet starten met super of this()
  • if parent hasnt no-arg constr en child not define constr -> DNC
  • private constructors cannot dooorgegeven aan child
  • final only once...

Inheriting Members

  • super.method() gebruiken (anders recusive overflow duh)

  • the method moet zelde signature hebben asl parent

  • same accessible as the parent method
  • geen nieuwe exception toevoegen
  • same return value / subtype covariant return types

A subclass is a subtype but not all subtypes are subclasses... (interfaces!)

Overloading vs Overriding

Overriding Generic Method

  • cannot overload methods changing the generic type. -> type erasure

Generic Method Params

  • kan! maar moeten kloppen
  • bij kleiner class scope dan wordt t overload

Generic Return Types

  • the return types must be covariant

Redeclaring private methods

  • permits to redeclare the same method

Hiding Static Methods

  • hidden method when a child class defines a static method with the same name asn sign as an inherited static method in the parent
  • similar as overriding but not the same... Is meer masking dezelfde parent method met eigen implentatie
  • let op : niet kleiner maken access...

final Methods

  • final cannot be replaced
  • KAN NOOIT

Hiding Variables

  • java doesnt allow variable to be overriden.

Polymorphism

  • object can be referenced with the same type as the object, a superclass or an interface that is implemented.

Interface

  • can define abstract methods
  • a class can implement any number of interfaces
  • a class implements an interface by overriding the onherited abstract methods
  • an object that implements an interface can be assigned to a reference for that interface.

Object vs Reference

  • In java all object are accessed by reference.
Lemur l = new Lemur();
Object lAsObject = l; // is gecast naar super object Object
  • the type of the objevt determines which properties exists in memory
  • the type of reference to the obj determines which methods and vars are accessible to the Java program

Casting

  • implicit
  • explicit
  • kan van parent naar child
  • kan NIET impliciet van child naar parent alleen met explicit casting...
Primate primate = new Lemur();
Lemur lemur2 = primate; // DNC

Lemur lemur3 =  (Lemur) primate; // Nu wel
  1. Casting a refenrece from subtype to supertype doesnt require explicit casst
  2. Casting a reference from a supertype to a subtype require epliciti cast
  3. compiler disallows casts to an unrelated class
  4. at runtime an invalid cast results in ClassCastException
public class Rodent{}

public class Capybara extends Rodent {
    public static void main(String[] args) {
        Rodent rodent = new Rodent();

        Capybara capybara = (Capybara)rodent; //Compiles but ClassCastException at runtime...
    }

}

Instanceof

Kan niet gebruikt worden op ongerelateerd type....

class Bird {}
class Fish {}
Fish fish = new Fish();
//if(fish instanceof Bird) { //DNC
//    Bird bird = (Bird) fish; //DNC
//}

Implement vs extends

  • Use extends for class-to-class inheritance. (1x!)
  • Use implements for class-to-interface relationships, allowing a class to commit to performing certain actions as defined by one or more interfaces.

Polymorphism and Method Overriding

  • polymorphism states that when you override a method you replace all calls to even those defined in the parent class.
  • parent func met super hergebruiken.

Overriding vs Hiding Members

  • static methods and static vars cannot be replaced by override!!

ocp 08 ocp 10

Tags: 

OCP Chapter 08

Class Design

Inheritance is the process by which a subclass automatically includes any public or protected members of the class, including primitives, objects, or methods, defined in the parent class.

For illustrative purposes, we refer to any class that inherits from another class as a subclass or child class, as it is considered a descendant of that class. Alternatively, we refer to the class that the child inherits from as the superclass or parent class, as it is considered an ancestor of the class.

access levels: - public - protected - package-private - private

When one class inherits from a parent class, all public and protected members are automatically available as part of the child class.

Package-private members are available if the child class is in the same package as the parent class.

Last but not least, private members are restricted to the class they are defined in and are never available via inheritance.

SINGLE VS. MULTIPLE INHERITANCE

Java supports single inheritance, by which a class may inherit from only one direct parent class. Java also supports multiple levels of inheritance, by which one class may extend another class, which in turn extends another class. You can have any number of levels of inheritance, allowing each descendant to gain access to its ancestor’s members.

multiple inheritance: by which a class may have multiple direct parents. By design, Java doesn’t support multiple inheritance in the language

PREVENT FROM BEING EXTENDED

It is possible in Java to prevent a class from being extended by marking the class with the final modifier.

INHERITING OBJECT

Object is the only class that doesn’t have a parent class.

The key is that when Java sees you define a class that doesn’t extend another class, it automatically adds the syntax extends java.lang.Object to the class definition.

Primitive types such as int and boolean do not inherit from Object, since they are not classes.

Extending a Class

format of a class definition: < abstract / final (optional) > class(required) {ClassName(required)}

access modifier - class modifier - class declaration - inheritance

Remember that final means a class cannot be extended.

Access Modifier Summary

Access Modifier Class Package Subclass World
public Yes Yes Yes Yes
protected No Yes Yes No
package-private (default) No Yes No No
private No No No No

APPLYING CLASS ACCESS MODIFIERS

In Java, a top-level class is a class that is not defined inside another class.

Applying public access to a class indicates that it can be referenced and used in any class. Applying default (package-private) access, which you’ll remember is the lack of any access modifier, indicates the class can be accessed only by a class within the same package.

An inner class is a class defined inside of another class and is the opposite of a top-level class. In addition to public and package- private access, inner classes can also have protected and private access.

ACCESSING THE THIS REFERENCE

let op met this.___

public class Flamingo {

    private String color;
    private String color2;

    public void setColor(String color) {
        color = color; // zet NIET de color field van Flaming => die is nu nog null....
        this.color = color;
        color  = this.color; // werkt wel maar this.color blijft null....en color als inputvar is dat nu ook...
    }

    public void setColor2(String color) {
        color2 = color; // werkt nu WEL want andere naam....
    }

    public static void main(String[] args) {
        Flamingo flamingo = new Flamingo();
        flamingo.setColor("HJH");
        flamingo.setColor2("HJH");
        System.out.println(flamingo.color);
        System.out.println(flamingo.color2);
    }
}

Declaring Constructors

public class Bunny {
   public bunny() { }     // DOES NOT COMPILE
   public void Bunny() { } // COMPILES maar is geen constructor
}

class Bonobo {
   public Bonobo(var food) { // DOES NOT COMPILE 
   }
}

Like method parameters, constructor parameters can be any valid class, array, or primitive type, including generics, but may not include var.

nog een keer: GEEN var in constructor!

A class can have multiple constructors, so long as each constructor has a unique signature.

Like methods with the same name but different signatures, declaring multiple constructors with different signatures is referred to as constructor overloading.

Constructors are used when creating a new object. This process is called instantiation because it creates a new instance of the class. A constructor is called when we write new followed by the name of the class we want to instantiate.

CALLING THE SUPER REFERENCE

class Mammal {
    String type = "mammal";
}

public class Bat extends Mammal {
    String type = "bat";

    public String getType() {
        return super.type + ":" + this.type;
    }

    public static void main(String... zoo) {
        System.out.print(new Bat().getType());
    }
}

The program prints mammal:bat

DEFAULT CONSTRUCTOR

Every class in Java has a constructor whether you code one or not. If you don’t include any constructors in the class, Java will create one for you without any parameters.

This Java-created constructor is called the default constructor and is added anytime a class is declared without any constructors.

public class Rabbit1 {} => ENIGE die een default constructor krijgt: public Rabbit() {}


public class Rabbit2 {
   public Rabbit2() {}
}

public class Rabbit3 {
   public Rabbit3(boolean b) {}
}

public class Rabbit4 {
   private Rabbit4() {}
}

CALLING OVERLOADED CONSTRUCTORS WITH THIS()

a single class can have multiple constructors. This is referred to as constructor overloading because all constructors have the same inherent name but a different signature.


public class Hamster { private String color; private int weight; public Hamster(int weight) { // FIRST this.weight = weight; color = "brown"; } public Hamster(int weight, String color) { // SECOND this.weight = weight; this.color = color; } } // kan dus beter public class Hamster { private String color; private int weight; public Hamster(int weight) { // FIRST this.weight = weight; this.color = "brown"; } public Hamster(int weight, String color) { // SECOND this(weight); this.color = color; } public static void main(String[] args) { Hamster h =new Hamster(12, "green"); System.out.println(h.color); System.out.println(h.weight); } }

INFINITE CONSTRUCTOR

Will not compile => infinite constructions....


public class Gopher { public Gopher() { this(5); // DOES NOT COMPILE } public Gopher(int dugHoles) { this(); // DOES NOT COMPILE } }

this vs this()

this, refers to an instance of the class, while the second, this(), refers to a constructor call within the class.

SUPER()

super() can only be called once as the first statement of the constructor


public class Zoo { public Zoo() { System.out.println("Zoo created"); super(); // DOES NOT COMPILE } } public class Zoo { public Zoo() { super(); System.out.println("Zoo created"); super(); // DOES NOT COMPILE } }

SUPER vs SUPER()

Like this and this(), super and super() are unrelated in Java. The first, super, is used to reference members of the parent class, while the second, super(), calls a parent constructor.

ARE CLASSES WITH ONLY PRIVATE CONSTRUCTORS CONSIDERED FINAL?

Remember, a final class cannot be extended. What happens if you have a class that is not marked final but only contains private constructors—can you extend the class? The answer is “yes,” but only an inner class defined in the class itself can extend it. An inner class is the only one that would have access to a private constructor and be able to call super(). Other top-level classes cannot extend such a class. Don’t worry—knowing this fact is not required for the exam.

Missing default No-Argument Constructor

public class Mammal {
   public Mammal(int age) {}
}
public class Elephant extends Mammal {  // DOES NOT COMPILE
}

Als er al een constructor is dan gaat de compiler er niet de standaard aan toevoegen.....

Since Elephant does not define any constructors, the Java compiler will attempt to insert a default no-argument constructor. As a second compile- time enhancement, it will also auto-insert a call to super() as the first line of the default no-argument constructor.

You should be wary of any exam question in which a class defines a constructor that takes arguments and doesn’t define a no-argument constructor. Be sure to check that the code compiles before answering a question about it, especially if any classes inherit it

CONSTRUCTORS AND FINAL FIELDS

By the time the constructor completes, all final instance variables must be assigned a value.

public class MouseHouse {
   private final int volume;
   private final String type;
   {
      this.volume = 10;
   }
   public MouseHouse(String type) {
      this.type = type;
   }
   public MouseHouse() {  // DOES NOT COMPILE
      this.volume = 2;    // DOES NOT COMPILE
   }
}

ORDER OF INITIALIZATION

Class Init

First, you need to initialize the class, which involves invoking all static members in the class hierarchy, starting with the highest superclass and working downward. This is often referred to as loading the class. The JVM controls when the class is initialized, although you can assume the class is loaded before it is used. The class may be initialized when the program first starts, when a static member of the class is referenced, or shortly before an instance of the class is created.

  1. If there is a superclass Y of X,then initialize class Y first.
  2. Process all static variable declarations in the order they appear in the class.
  3. Process all static initializers in the order they appear in the class.

Instance Init

  1. If there is a superclass Y of X,then initialize the instance of Y first.
  2. Process all instance variable declarations in the order they appear in the class.
  3. Process all instance initializers in the order they appear in the class.
  4. Initialize the constructor including any overloaded constructors referenced with this().
public class ZooTickets {
    private String name = "BestZoo";

    {
        System.out.print(name + "-");
    }

    private static int COUNT = 0;

    static {
        System.out.print(COUNT + "-");
    }

    static {
        COUNT += 10;
        System.out.print(COUNT + "-");
    }

    public ZooTickets() {
        System.out.print("z-");
    }

    public static void main(String... patrons) {
        new ZooTickets();
    }
}

0-10-BestZoo-z-


class Primate { public Primate() { System.out.print("Primate-"); } } class Ape extends Primate { public Ape(int fur) { System.out.print("Ape1-"); } public Ape() { System.out.print("Ape2-"); } } public class Chimpanzee extends Ape { public Chimpanzee() { super(2); System.out.print("Chimpanzee-"); } public static void main(String[] args) { new Chimpanzee(); } }

Primate-Ape1-Chimpanzee-

REVIEWING CONSTRUCTOR RULES

  1. The first statement of every constructor is a call to an overloaded constructor via this(), or a direct parent constructor via super().
  2. If the first statement of a constructor is not a call to this() or super(), then the compiler will insert a no-argument super() as the first statement of the constructor.
  3. Calling this() and super() after the first statement of a constructor results in a compiler error.
  4. If the parent class doesn’t have a no-argument constructor,then every constructor in the child class must start with an explicit this() or super() constructor call.
  5. If the parent class doesn’t have a no-argument constructor and the child doesn’t define any constructors, then the child class will not compile.
  6. If a class only defines private constructors, then it cannot be extended by a top-level class.
  7. All final instance variables must be assigned a value exactly once by the end of the constructor. Any final instance variables not assigned a value will be reported as a compiler error on the line the constructor is declared.

Key Points about Constructors

  • Same Name as the Class: The constructor's name must exactly match the class name.

  • No Return Type: Constructors do not have a return type, not even void. This distinguishes them from other methods.

  • Automatic Invocation: A constructor is automatically called when an object of the class is created.

  • Default Constructor: If no constructor is explicitly defined in a class, the Java compiler automatically provides a default constructor, which is a no-argument constructor. This constructor initializes the object with default values (e.g., 0, null, false).

  • Parameterized Constructors: Constructors can take parameters, allowing you to create objects with custom initial values.

  • Overloading Constructors: Like other methods in Java, constructors can be overloaded. This means you can have multiple constructors in the same class with different parameter lists.

  • Constructor Chaining: Within a class, one constructor can call another constructor of the same class using the this() keyword. This is called constructor chaining. If you want to call the constructor of the superclass, you use the super() keyword.

INHERITING METHODS

Overriding a Method

To override a method, you must follow a number of rules. The compiler performs the following checks when you override a method:

  1. The method in the child class must have the same signature as the method in the parent class.
  2. The method in the child class must beat least as accessible as the method in the parent class.
  3. The method in the child class may not declare a checked exception that is new or broader than the class of any exception declared in the parent class method.
  4. If the method returns a value,it must be the same or a subtype of the method in the parent class, known as covariant return types.

DEFINING SUBTYPE AND SUPERTYPE

When discussing inheritance and polymorphism, we often use the word subtype rather than subclass, since Java includes interfaces. A subtype is the relationship between two types where one type inherits the other. If we define X to be a subtype of Y, then one of the following is true: - X and Y are classes, and X is a subclass of Y. - X and Y are interfaces, and X is a subinterface of Y. - X is a class and Y is an interface, and X implements Y (either directly or through an inherited class).

Likewise, a supertype is the reciprocal relationship between two types where one type is the ancestor of the other. Remember, a subclass is a subtype, but not all subtypes are subclasses.


public class Bird { public void fly() { System.out.println("Bird is flying"); } public void eat(int food) { System.out.println("Bird is eating " + food + " units of food"); } } public class Eagle extends Bird { public int fly(int height) { System.out.println("Bird is flying at " + height + " meters"); return height; } public int eat(int food) { // DOES NOT COMPILE System.out.println("Bird is eating " + food + " units of food"); return food; } }

the method is being overridden, the return type of the method in the Eagle class must be compatible with the return type for the method in the Bird class. In this example, the return type int is not a subtype of void; therefore, the compiler will throw an exception on this method definition.

public class Camel {
   public int getNumberOfHumps() {
return 1; }
}
public class BactrianCamel extends Camel {
   private int getNumberOfHumps() {  // DOES NOT COMPILE
return 2; }
}
public class Rider {
   public static void main(String[] args) {
      Camel c = new BactrianCamel();
      System.out.print(c.getNumberOfHumps());
   }
}

In this example, BactrianCamel attempts to override the getNumberOfHumps() method defined in the parent class but fails because the access modifier private is more restrictive than the one defined in the parent version of the method.

The third rule says that overriding a method cannot declare new checked exceptions or checked exceptions broader than the inherited method. This is done for similar polymorphic reasons as limiting access modifiers. In other words, you could end up with an object that is more restrictive than the reference type it is assigned to, resulting in a checked exception that is not handled or declared.

public class Reptile {
   protected void sleepInShell() throws IOException {}
   protected void hideInShell() throws NumberFormatException{}
   protected void exitShell() throws FileNotFoundException {}
}

public class GalapagosTortoise extends Reptile {
   public void sleepInShell() throws FileNotFoundException {}
   public void hideInShell() throws IllegalArgumentException{}
   public void exitShell() throws IOException {} // DOES NOT COMPILE
}

The third overridden exitShell() method declares IOException, which is a superclass of the exception declared in the inherited method, FileNotFoundException. Since these are checked exceptions and IOException is broader, the overridden exitShell() method does not compile in the GalapagosTortoise class.

The fourth and final rule around overriding a method is probably the most complicated, as it requires knowing the relationships between the return types. The overriding method must use a return type that is covariant with the return type of the inherited method.

public class Rhino {
   protected CharSequence getName() {
      return "rhino";
   }
   protected String getColor() {
      return "grey, black, or white";
    }
}
class JavanRhino extends Rhino {
   public String getName() {
      return "javan rhino";
   }
   public CharSequence getColor() {  // DOES NOT COMPILE
      return "grey";
    }
}

the overridden getColor() method does not compile because CharSequence is not a subtype of String. To put it another way, all String values are CharSequence values, but not all CharSequence values are String values....

A simple test for covariance is the following: Given an inherited return type A and an overriding return type B, can you assign an instance of B to a reference variable for A without a cast? If so, then they are covariant. This rule applies to primitive types and object types alike. If one of the return types is void, then they both must be void, as nothing is covariant with void except itself.

Overriding a Generic Method

Overriding methods is complicated enough, but add generics to it and things only get more challenging.

Review of Overloading a Generic Method

public class LongTailAnimal {
   protected void chew(List<Object> input) {}
   protected void chew(List<Double> input) {}  // DOES NOT COMPILE
}

only one of the two methods is allowed in a class because type erasure will reduce both sets of argumentsto(List input).

Generic Method Parameters

public class LongTailAnimal {
   protected void chew(List<Object> input) {}
}

public class Anteater extends LongTailAnimal {
   protected void chew(List<Double> input) {}  // DOES NOT COMPILE
}

Both of these examples fail to compile because of type erasure. In the compiled form, the generic type is dropped, and it appears as an invalid overloaded method.

Generics and Wildcards

void sing1(List<?> v) {}                 // unbounded wildcard
void sing2(List<? super String> v) {}    // lower bounded wildcard
void sing3(List<? extends String> v) {}  // upper bounded wildcard

Generic Return Types

When you’re working with overridden methods that return generics, the return values must be covariant. In terms of generics, this means that the return type of the class or interface declared in the overriding method must be a subtype of the class defined in the parent class. The generic parameter type must match its parent’s type exactly.

public class Mammal {
   public List<CharSequence> play() { ... }
   public CharSequence sleep() { ... }
}
public class Monkey extends Mammal {
   public ArrayList<CharSequence> play() { ... }
}
public class Goat extends Mammal {
   public List<String> play() { ... }  // DOES NOT COMPILE
   public String sleep() { ... }
}

Redeclaring private Methods

In Java, you can’t override private methods since they are not inherited. Just because a child class doesn’t have access to the parent method doesn’t mean the child class can’t define its own version of the method. It just means, strictly speaking, that the new method is not an overridden version of the parent class’s method.

Java permits you to redeclare a new method in the child class with the same or modified signature as the method in the parent class. This method in the child class is a separate and independent method, unrelated to the parent version’s method, so none of the rules for overriding methods is invoked.

public class Camel {
   private String getNumberOfHumps() {
      return "Undefined";
   }
}
public class DromedaryCamel extends Camel {
   private int getNumberOfHumps() {
return 1; }
}

Notice that the return type differs in the child method from String to int. In this example, the method getNumberOfHumps() in the parent class is redeclared, so the method in the child class is a new method and not an override of the method in the parent class.

Hiding Static Methods

A hidden method occurs when a child class defines a static method with the same name and signature as an inherited static method defined in a parent class. Method hiding is similar but not exactly the same as method overriding. The previous four rules for overriding a method must be followed when a method is hidden. In addition, a new rule is added for hiding a method:

  1. The method defined in the child class must be marked as static if it is marked as static in a parent class.
public class Bear {
   public static void eat() {
      System.out.println("Bear is eating");
   }
}
public class Panda extends Bear {
   public static void eat() {
      System.out.println("Panda is chewing");
   }
   public static void main(String[] args) {
      eat();
    }
}

werkt! Allebei static methods en als je er bij Panda eat() weghaald dan wordt die van Bear gebruikt. Because they are both marked as static, this is not considered an overridden method. That said, there is still some inheritance going on. If you remove the eat() method in the Panda class, then the programprints"Bear is eating"atruntime.

public class Bear {
   public static void sneeze() {
      System.out.println("Bear is sneezing");
   }
   public void hibernate() {
      System.out.println("Bear is hibernating");
   }
   public static void laugh() {
      System.out.println("Bear is laughing");
   }
}
public class Panda extends Bear {
   public void sneeze() {           // DOES NOT COMPILE
      System.out.println("Panda sneezes quietly");
   }
   public static void hibernate() { // DOES NOT COMPILE
      System.out.println("Panda is going to sleep");
   }
   protected static void laugh() {  // DOES NOT COMPILE
      System.out.println("Panda is laughing");
   }
}

In this example, sneeze() is marked static in the parent class but not in the child class. The compiler detects that you’re trying to override using an instance method. However, sneeze() is a static method that should be hidden, causing the compiler to generate an error.

In the second method, hibernate() is an instance member in the parent class but a static method in the child class. In this scenario, the compiler thinks that you’re trying to hide a static method. Because hibernate() is an instance method that should be overridden, the compiler generates an error.

Finally, the laugh() method does not compile. Even though both versions of method are marked static, the version in Panda has a more restrictive access modifier than the one it inherits, and it breaks the second rule for overriding methods. Remember, the four rules for overriding methods must be followed when hiding static methods.

Creating final Methods

We conclude our discussion of method inheritance with a somewhat self- explanatory rule—final methods cannot be replaced. By marking a method final, you forbid a child class from replacing this method. This rule is in place both when you override a method and when you hide a method. In other words, you cannot hide a static method in a child class if it is marked final in the parent class.

public class Bird {
   public final boolean hasFeathers() {
      return true;
   }
   public final static void flyAway() {}
}
public class Penguin extends Bird {
   public final boolean hasFeathers() {  // DOES NOT COMPILE
      return false;
   }
   public final static void flyAway() {}  // DOES NOT COMPILE
}

This rule applies only to inherited methods. For example, if the two methods were marked private in the parent Bird class, then the Penguin class, as defined, would compile. In that case, the private methods would be redeclared, not overridden or hidden.

HIDING VARIABLES

Java doesn’t allow variables to be overridden. Variables can be hidden, though.

A hidden variable occurs when a child class defines a variable with the same name as an inherited variable defined in the parent class. This creates two distinct copies of the variable within an instance of the child class: one instance defined in the parent class and one defined in the child class.

class Carnivore {
   protected boolean hasFur = false;
}
public class Meerkat extends Carnivore {
   protected boolean hasFur = true;
   public static void main(String[] args) {
      Meerkat m = new Meerkat();
      Carnivore c = m;
      System.out.println(m.hasFur);
      System.out.println(c.hasFur);
    }
}

It prints true followed by false. Confused? Both of these classes define a hasFur variable, but with different values. Even though there is only one object created by the main() method, both variables exist independently of each other. The output changes depending on the reference variable used.

overriding a method replaces the parent method on all reference variables (other than super), whereas hiding a method or variable replaces the member only if a child reference type is used.

Understanding Polymorphism

Java supports polymorphism, the property of an object to take on many different forms. To put this more precisely, a Java object may be accessed using a reference with the same type as the object, a reference that is a superclass of the object, or a reference that defines an interface the object implements, either directly or through a superclass. Furthermore, a cast is not required if the object is being reassigned to a super type or interface of the object.

Interfaces

  • An interface can define abstract methods.
  • A class can implement any number of interfaces.
  • A class implements an interface by overriding the inherited abstract methods.
  • An object that implements an interface can be assigned to a reference for that interface.
public class Primate {
   public boolean hasHair() {
      return true;
   }
}

public interface HasTail {
   public abstract boolean isTailStriped();
}

public class Lemur extends Primate implements HasTail {
   public boolean isTailStriped() {
      return false;
   }
   public int age = 10;
   public static void main(String[] args) {
      Lemur lemur = new Lemur();
      System.out.println(lemur.age);
      HasTail hasTail = lemur;
      System.out.println(hasTail.isTailStriped());
        => System.out.println(hasTail.age);             // DOES NOT COMPILE, kan nu aleen bij zn type blijven
      Primate primate = lemur;
      System.out.println(primate.hasHair());
   }
}

Polymorphism enables an instance of Lemur to be reassigned or passed to a method using one of its supertypes, such as Primate or HasTail.

OBJECT VS. REFERENCE

In Java, all objects are accessed by reference, so as a developer you never have direct access to the object itself. Conceptually, though, you should consider the object as the entity that exists in memory, allocated by the Java runtime environment. Regardless of the type of the reference you have for the object in memory, the object itself doesn’t change.

Lemur lemur = new Lemur();
Object lemurAsObject = lemur;

Even though the Lemur object has been assigned to a reference with a different type, the object itself has not changed and still exists as a Lemur object in memory. What has changed, then, is our ability to access methods within the Lemur class with the lemurAsObject reference.

  1. The type of the object determines which properties exist within the object in memory.
  2. The type of the reference to the object determines which methods and variables are accessible to the Java program.

Denk aan Class van type Bla in memory. Daar referen als Bla of Object levert andere mogelijk funcs+fields op

CASTING OBJECTS

Primate primate = new Lemur();  // Implicit Cast
Lemur lemur2 = primate;         // DOES NOT COMPILE
System.out.println(lemur2.age);
Lemur lemur3 = (Lemur)primate;  // Explicit Cast
System.out.println(lemur3.age);

When casting objects, you do not need a cast operator if the current reference is a subtype of the target type. This is referred to as an implicit cast or type conversion. Alternatively, if the current reference is not a subtype of the target type, then you need to perform an explicit cast with a compatible type. If the underlying object is not compatible with the type, then a ClassCastException will be thrown at runtime.

  1. Casting a reference from a subtype to a supertype doesn’t require an explicit cast.
  2. Casting a reference from a supertype to a subtype requires an explicitcast.
  3. The compiler disallows casts to an unrelated class.
  4. At runtime, an invalid cast of a reference to an unrelated type results in a ClassCastException being thrown.
public class Bird {}
public class Fish {
   public static void main(String[] args) {
      Fish fish = new Fish();
      Bird bird = (Bird)fish;  // DOES NOT COMPILE
   }
}

In this example, the classes Fish and Bird are not related through any class hierarchy that the compiler is aware of; therefore, the code will not compile. While they both extend Object implicitly, they are considered unrelated types since one cannot be a subtype of the other.

public class Rodent {}
public class Capybara extends Rodent {
   public static void main(String[] args) {
      Rodent rodent = new Rodent();
      Capybara capybara = (Capybara)rodent;  // ClassCastException
    }
}

This code creates an instance of Rodent and then tries to cast it to a subclass of Rodent, Capybara. Although this code will compile, it will throw a ClassCastException at runtime since the object being referenced is not an instance of the Capybara class. The thing to keep in mind in this example is the Rodent object created does not inherit the Capybara class in any way.

THE INSTANCEOF OPERATOR

the instanceof operator, which can be used to check whether an object belongs to a particular class or interface and to prevent ClassCastExceptions at runtime.

``` if(rodent instanceof Capybara) { Capybara capybara = (Capybara)rodent; }

Just as the compiler does not allow casting an object to unrelated types, it also does not allow instanceof to be used with unrelated types.

public static void main(String[] args) { Fish fish = new Fish(); if (fish instanceof Bird) { // DOES NOT COMPILE Bird bird = (Bird) fish; // DOES NOT COMPILE } }


## POLYMORPHISM AND METHOD OVERRIDING In Java, polymorphism states that when you override a method, you replace all calls to it, even those defined in the parent class.

class Penguin { public int getHeight() { return 3; } public void printInfo() { System.out.print(this.getHeight()); } } public class EmperorPenguin extends Penguin { public int getHeight() { return 8; } public static void main(String []fish) { new EmperorPenguin().printInfo(); } }

Penguin3 en EmperorPenguin 8  

The facet of polymorphism that replaces methods via overriding is one of the most important properties in all of Java. It allows you to create complex inheritance models, with subclasses that have their own custom implementation of overridden methods. It also means the parent class does not need to be updated to use the custom or overridden method. If the method is properly overridden, then the overridden version will be used in all places that it is called.

Remember, you can choose to limit polymorphic behavior by marking methods final, which prevents them from being overridden by a subclass.

### CALLING THE PARENT VERSION OF AN OVERRIDDEN METHOD

there is one exception to overriding a method where the parent method can still be called, and that is when the super reference is used. How can you modify our EmperorPenguin example to print 3, as defined in the Penguin getHeight() method? You could try calling super.getHeight() in the printInfo() method of the Penguin class. Unfortunately, this does not compile, as super refers to the superclass of Penguin, in this case Object. The solution is to override printInfo() in the EmperorPenguin class and use super there.

public void printInfo() { System.out.print(super.getHeight()); }

This new version of EmperorPenguin uses the getHeight() method declared in the parent class and prints 3.




## OVERRIDING VS. HIDING MEMBERS

While method overriding replaces the method everywhere it is called, static method and variable hiding does not. Strictly speaking, hiding
members is not a form of polymorphism since the methods and variables maintain their individual properties. Unlike method overriding, hiding members is very sensitive to the reference type and location where the member is being used.

class Penguin { public static int getHeight() { return 3; } public void printInfo() { System.out.println(this.getHeight()); } } public class CrestedPenguin extends Penguin { public static int getHeight() { return 8; } public static void main(String... fish) { new CrestedPenguin().printInfo(); } }

The CrestedPenguin example is nearly identical to our previous EmporerPenguin example, although as you probably already guessed, it prints 3 instead of 8. The getHeight() method is static and is therefore hidden, not overridden. The result is that calling getHeight() in CrestedPenguin returns a different value than calling it in the Penguin, even if the underlying object is the same. Contrast this with overriding a method, where it returns the same value for an object regardless of which class it is called in.

class Marsupial { protected int age = 2;

public static boolean isBiped() { return false; } }

public class Kangaroo extends Marsupial { protected int age = 6;

public static boolean isBiped() { return true; }

public static void main(String[] args) { Kangaroo joey = new Kangaroo(); Marsupial moey = joey; System.out.println(joey.isBiped()); // true System.out.println(moey.isBiped()); //false System.out.println(joey.age); //6 System.out.println(moey.age); // 2 } } ``` Remember, in this example, only one object, of type Kangaroo, is created and stored in memory. Since static methods can only be hidden, not overridden, Java uses the reference type to determine which version of isBiped() should be called, resulting in joey.isBiped() printing true and moey.isBiped() printing false. Likewise, the age variable is hidden, not overridden, so the reference type is used to determine which value to output. This results in joey.age returning 6 and moey.age returning 2.

DON’T HIDE MEMBERS IN PRACTICE

When you’re defining a new variable or static method in a child class, it is considered good coding practice to select a name that is not already used by an inherited member. Redeclaring private methods and variables is considered less problematic, though, because the child class does not have access to the variable in the parent class to begin with.

ocp 07 ocp 09

Tags: 

OCP Chapter 07

Methods and Encapsulation

acces modifier optional specifier return type method name ( params) exception (optional)
public final void nap ( int minutes ) throws Exception
public final void nap(int minutes) throws Exception {
    // do a nap
}
  • method declaration
  • method signature (name and param list)
Element value in nap() Required
Acces modifier public NO
optional specifier final NO
RETURN TYPE void YES
METHOD NAME nap YES
Param list (int minutes) YES () can be empty
Optional Exception List public NO
METHOD BODY public YES {} can be empty

Access Modifiers

  • private , only from the same class
  • default (Package Private) Acces, from same package (no keyword!)
  • protected , same package or subclasses , bij extends Clazz
  • public, can be called from any class
public void walk() {}
default void walk() {} // DNC
void public walk() {} // DNC
void walk() {}

Optional Specifiers

  • may be multiple!
  • static,
  • abstract, when a method body is not provided.
  • final not allowed to be overriden by subclass
  • synchronized, multithreaded code
  • native , when interacting with code from another language
  • strictfp , floating point calc portable
public void walk() {}
public final void walk() {}
public static final void walk() {}
public final static void walk() {}
public modifier void walk() {} //DNC
public void final walk() {} //DNC
final public void walk() {}

Return Type

  • must have, moet er zijn mag er niet uit, gebruik void
public void walk() {}
public void walk() {return;}
public String walk() {return "":}
public String walk() {} //DNC
public walk() {} // DNC
public String int walk() {} //DNC
String walk(int a) {if (a==4) return "";} //DNC

Method Name

  • alleen letter, nummers $ of _ maar eerst character is niet nummer, en geen reserved words
  • _ alleen mag ook niet
  • begint met kleine letter
public void walk() {}
public void 2walk() {} //DNC
public walk3 void() {} //DNC
public void Walk_$() {}
public _() {} //DNC
public void () {} //DNC

Parameter list

  • not required 0 of meer
  • separeer met komma
public void walk() {}
public void walk {} //DNC
public void walk(int a) {}
public void walk(int a; int b) {} //DNC
public void walk(int a, int b) {}

Optional Exception List

  • 0 of meer exceptions, komma gescheiden

Method Body

  • moet er zijn, mag leeg

Varargs

  • moet laatste element vd param list zijn
  • mag dus maar 1 zijn
  • als laatste
  • met varargs kanje ook komma gescheiden aanleveren
public void walk(int... nums){}
public void walk(int start, int... nums){}
public void walk(int... nums, int start){} // DNC
public void walk(int... nums, int...start){} // DNC

public static void walk(int start, int... nums) {
    System.out.println(nums.length);
}
public static void main(String[] args){

walk(1); // 0
walk(1,2); //1
walk(1,2,3);//2
walk(1, new int[]{4,5});//2


}

Let op!

package pond.shore;
public class Bird {
    protected String text = "floating";
    protected void floatInWater() {
        System.out.println(text);
    }
}

package pond.swan;
import pond.Bird;
public class Swan extends Bird {
    public void smwin() {
        floatInWater();            //sublcass access to super class
        System.out.println(text);  //sublcass access to super class
    }


    public void helpOtherSwanSwim() {
        Swan other = new Swan();
        other.floatInWater();            //sublcass access to super class
        System.out.println(other.text);  //sublcass access to super class
    }

    public void helpOtherBirdSwim() {
        Bird other = new Bird();         // <-- !!! Bird ref en Bird is in ander package.
        other.floatInWater();            //DNC
        System.out.println(other.text);  //DNC
    }

}
method in __ can access a __ meber private default protected public
same class YES YES YES YES
same package NO YES YES YES
subclass in diff package NO NO YES YES
unrelated + diff package NO NO NO YES
public class ASD {

    public static void main(String[] args) {
        long a = test();
        System.out.println("longe int");
    }

    static long test() {
        System.out.println("int past wel in long");
        return 12;
    }
}
ASD a = new ASD();
a.test();

int past wel in long





12

Static keywordp

  • dont require an instance of a class
  • shared, independed lifecycle
  • for utility or helper methods like uppcase()
  • for state that is shared like a counter
  • from static cannot to non static.

Static vs Instance

  • only one static method. no copys.

Static variables

  • static final is a constant

Static Initialization

  • static block runs first when class is first used..

Static imports

  • for members of classes
import static java.util.Arrays.asList;
import static statics.A.TYPE;
import static statics.B.TYPE; // DNC mag niet dezelfde naam hebben

Passing Data

Pass By Value

  • Java is pass by value language
  • assigning a new primitive or reference to a parameter doesnt change the caller
  • Calling methods on a reference to an Object DO AFFECT the caller!
public static void main(String[] args) {
    int ori1 = 1;
    int ori2 = 2;
    String letters = "asd";
    swap(ori1,ori2);
    voegToe(letters);
    System.out.println(ori1); // GEBEURT NIX!!! GEEN SWAP
    System.out.println(ori2);
    System.out.println(letter);
}

public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;

}
public static void voegToe(String letters) {
    letters += "d";


}

TODO

public static void main(String[] args) {
    int ori1 = 1;
    int ori2 = 2;
    String letters = "asd";
    swap(ori1,ori2);
    voegToe(letters);
    System.out.println(ori1); // GEBEURT NIX!!! GEEN SWAP
    System.out.println(ori2);
    System.out.println("na voegtoe "+letters);
    letters = voegToe2(letters);
    System.out.println("na voegtoe2 "+letters);
    ori1 = voegToe1(ori1);
    System.out.println(ori1);
}

public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;

}
public static void voegToe(String letters) {
    letters += "d";
}

public static int voegToe(int a) {
    a += 8;
    return a;
}

public static String voegToe2(String letters) {
    letters += "d";
    return letters;
}
main(new String[]{}); // ZIJN DUS NIET GESWAPPED!!!!

1
2
na voegtoe asd
na voegtoe2 asdd
9
public class Koala {
    public static int count = 0;
    public static void main(String[] args) {
       System.out.println("init "+count); 
    }
}

Koala k = new Koala();
System.out.println("first "+k.count); 
k = null;
System.out.println("second "+k.count); 
Koala.count = 4;
Koala k1 = new Koala();
Koala k2 = new Koala();
Koala.count = 6;
Koala.count = 5;
System.out.println("last "+k.count); 
first 5
second 5
last 5

Overloading Methods

  • same name in the same class
  • same name different signature (method input params)
  • diff acces modifiers
  • specifiers
  • return types (return type is niet onderdeel vd signature, als param list niet gelijk is)
  • exceptions

  • Werkt niet met niet-static en static

Varargs

let op! array en varargs zijn hetzelfde nl array...

public void fly(int[] lengths){}
public void fly(int... lengths){} // DNC want is hetzelfde al hierboven...

Autoboxing

  • Java will match the correct version if there is an option
public void fly(int length) {
    System.out.println("int "); 
}

public void fly(Integer length) {
    System.out.println("Integer "); 
}
// Autoboxing....
fly(new Integer(12));
fly(12);
//Integer
//in

Reference types

  • Java try to find a direct match

Primitives

  • Java tries to find the most matching overload.

Generics

public static void walk(int[] a){}
public static void walk(Integer[] a){}

// Type Erasue 
public void walk(List<String> str) {}
public void walk(List<Integer> int) {} //DNC

// want na compile ziet t t er zo uit
public void walk(List str) {}
public void walk(List str) {}
// En zijn dan de dezelfde paramlists....

Only 1 conversion...

public class TooManyConversions{
    public static void play(Long l){}
    public static void play(Long... l){}
    public static void main(String[] args) {
        play(4);// DNC want int -> long -> Long !!!!
    }
}

Encapsulating Data

  • access control mbv private fields
  • getters setters
  • setter == mutator
public static void walk(int[] a){}
public static void walk(Integer[] a){}
class Move {
public void walk(List<String> str) {}

//public void walk(List<Integer> int) {} //DNC
}
Move a = new Move();
public void fly(int length) {
    System.out.println("int "); 
}

public void fly(Integer length) {
    System.out.println("Integer "); 
}
// Autoboxing....
fly(new Integer(12));
fly(12);
Integer 
int 

ocp 06 ocp 08

Tags: 

OCP Chapter 06

Chapter 06 Lambdas

  • Alles tussen curly braces...
  • work with interface with only one abstract method. (functional interface)
  • SAM Single Abstract Method (SAM rule)
a -> a.canHop()
// zelfde als:
(Animal a) -> {return a.canHop();}
  • single param
  • arrow operator to seperate param and body
  • one or more lines of code, incl semicolo and return statment.

Geen {} betekent dat ie returned. Gaat niet met 2 of meer return statements.

wel valide....

s->{}
()-> true
a-> a.startsWith("test")
(a,b)-> a.startsWith("test")
(String a, String b) ->a.startsWith("test")

NIET valide

a,b -> a.startsWith("test") // missende boogjes
a -> {a.startsWith("test");} // missende return...
a -> {return a.startsWith("test")} // missende semicolon ;

Functional Interfaces

  • Predicate
  • Consumer
  • Supplier
  • Comparator

Predicate

public interface Predicate<T> {
    boolean test(T t);
}

Consumer

void accept(T t)

Supplier

T get()
//
Supplier<Integer> number = () -> 42;
Supplier<Integer> random = () -> newRandom().nextInt();

Comparator

int compate(T o1, T o2)
// ascending sort
Comparator<Integer> ints = (i1,i2) -> i1 -i2;
// descending
Comparator<String> strings = (s1,s2) -> s2.compareTo(s1);
Comparator<String> moreStrings = (s1,s2) -> - s1.compareTo(s2);

Func interface params return type
Comparator Two int
Consumer One void
Predicate One boolean
Supplier None One

Variables in Lambas

Predicate<String> p = x -> true;
Predicate<String> p = (var x) -> true;
Predicate<String> p = (Stringx) -> true;

Local variable in Lambda body

(a,b) -> {init c = 0; return 5;} (a,b) -> {init a = 0; return 5;} // DNC

Variable referenced from the labmda body

  • can access instance variable, method params, or local variables
  • Method params and local variables are allowed if they are effectively final. (no change after set...)
Variable type Rule
instance variable allowed
static variable allowed
local variable allowed (if eff final)
method variable allowed (if eff final)
lambda param allowed

Calling APIs with Lambdas

RemoveIf()

NOT on a Map! Map heeft keys and values...

sort()

begint met twee vars en returns an int!!

Not a sort on a Set of Map

forEach()

Kan wel op Set of Map, maar dan over de Keys of Values ;-)

ocp 05 ocp 07

Tags: 

OCP Chapter 05

Core Java APIs

Strings

Concat with numbers

  • if both numbers => addition
  • if one is String => concat
  • expressio is evaluated from left to right.
System.out.println("1+2");          // 3  
System.out.println("a" + "b");      // ab  
System.out.println("a" + "b" + 3);  // ab3  
System.out.println(1+2+"a");        // 3a 
System.out.println("a"+1+2);        // a12
System.out.println("a"+1+2+3);   
a123

Immutability

Once created it is not allowed to change.

String s1 = "1";
String s2 = s1.concat("2");
s2.concat("3");
System.out.println(s2);
12

String methods

.length()

.charAt() let op StringIndexOutOfBoundsException

.indexOf() -1 when not found

.substring() optional endIndex is er 1 voorbij end!!!

String s1 = "abcdef";
System.out.println(s1.length()); // 6
String s2 = s1.substring(2);
System.out.println(s2); // cdef

String s3 = s1.substring(2, 6); // 6 (is dus 1 er voorbij....)
System.out.println(s3); // cdef

String s4 = s1.substring(2, 3); 
System.out.println(s4); // c

String s5 = s1.substring(2, 2); 
System.out.println(s5); // EMPTY STRING


// s1.substring(2, 1); EXCEPTION 
// s1.substring(2, 7); EXCEPTION 
6
cdef
cdef
c

.toLowerCase() .toUpperCase()

.equals() .equalsIgnoreCase()

.startsWIth() .endsWith() !! Case sensitive !!

.replace()

.contains()

"asd".contains('a') // Incompatible types

System.out.println("asd".contains("a"));
true

.trim()

.strip() .stripLeading() .stripTrailing() Supports unicode!

.intern()

System.out.println("asd".intern());
asd

StringBuilder

new StringBuilder;
new StringBuilder("saasdf");
new StringBuilder(12);

.charAt(), .indexOf(), .length(), .substring()
.append()
.insert()

.delete()
.deleteCharAt()

String str = "asd"; str.deleteCharAt(5) // exception

StringBuilder sb = new StringBuilder("asd"); sb.delete(1,100);


StringBuilder sb = new StringBuilder("asd"); sb.delete(1,100);
a

.replace()

.reverse()

.toString()

Equality

equals() vs ==

  • if class doesnt have equals method Java checks refences are equal when == is operator...
  • == heeft zelfde type nodig
  • == checking for reference equality

<,,,<

StringBuilder one = new StringBuilder();
StringBuilder two = new StringBuilder();
StringBuilder three = one.append("a"); // one and three are referencing to the same object! == works

System.out.println(one == two);//false
System.out.println(one == three);//true

String x = "hello";
String z = " hello";

System.out.println(x.equals(z.trim())); // true

// var a = x == one; // twee verschillende types... DNC
var b = x.equals(one) ? true : false; // false....twee verschillende types . doet t wel...
System.out.println(b); // 
false
true
true
false

String Pool

Strings are immutable!!!!!

  • contins literal values and constants
  • "name" is literal
  • myObject.toString() is a string but not a literal so it doesnt go to the stringpool
  • Strings are reused
String x = "asd";
String y = "asd";
System.out.println(x == y); // true!!! 
String x = "asd";
String y = " asd".trim();
System.out.println(x == y); // false!!! computed at runtime...
String x = "Hello";
String y = new String("Hello");
System.out.println(x == y); // false!!! 
String x = "Hello";
String y = new String("Hello").intern();
System.out.println(x == y); // TRUE!!! 

.intern() en == is alleen voor examen. Gebruik nooit in je code.

Arrays

  • char is primitive. char[] niet is een ref naar array van chars.
  • array is ordered list
  • array is van primitives of objects
  • contents equality is with: Arrays.equals(array1, array2)

Array of primitives

int[] nums = new int[3];

int nums2 = new int[] {1,2,3}; // inline

int[] nums;
int [] nums;
int []nums;
int nums[];
int nums [];
int[] nums = new int[3];

for(int i: nums) {
    System.out.println("asd");
} // array heeft dus 3 0-en!!

int ids[], types;

String[] strings = {"asdasd"};
Object[] objects = strings;
String[] againStrings = (String[]) objects;
// againStrings[0] = new StringBuilder(); // DNC String[] != StringBuiler class
// objects[0] = new StringBulder(); // Nu String[] in Object dus ook andere class
asd
asd
asd

Sorting

import java.util.*;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collections;

int [] nums = {5,3,1};
Arrays.sort(nums);
for(int num: nums) {
    System.out.println(num);
}
    System.out.println("---\n");
Integer[] numsObj = Arrays.stream(nums).boxed().toArray(Integer[]::new);
Arrays.sort(numsObj,  Collections.reverseOrder());
for(int num: numsObj) {
    System.out.println(num);
}
1
3
5
---

5
3
1

Searching

.binarySearch()

  • alleen als array is sorted()!!!!
  • levert de index van gevonden elem of -1 op.
int [] numbers = {5,3,1};
Arrays.binarySearch(numbers, 1);// 2

Comparing

  • a negative number mean the first array is smaller than the second
  • a zero means that they are equal
  • positive means that the first array is larger than the second
// 1 - 1 => 0
System.out.println(Arrays.compare(new int[]{1}, new int[] {1})); // 0
// 1 - 2 => -1
System.out.println(Arrays.compare(new int[]{1}, new int[] {2})); // -1
// 2 - 1 => 1
System.out.println(Arrays.compare(new int[]{2}, new int[] {1})); // 1
// 1,1 - 1 => 1
System.out.println(Arrays.compare(new int[]{1,1}, new int[] {1})); // 1
// 1 - 1,1 => -1
System.out.println(Arrays.compare(new int[]{1}, new int[] {1,1})); // -1
// 3 - 1,1 => 1
System.out.println(Arrays.compare(new int[]{3}, new int[] {1,1})); // 1
System.out.println(Arrays.compare(new int[]{3}, new int[] {4,1})); // -1
System.out.println(Arrays.compare(new int[]{35,2}, new int[] {4,1})); // 1
System.out.println(Arrays.compare(new int[]{35,2}, new int[] {4,4})); // 1
//System.out.println(Arrays.compare(new int[]{1}, new char[] {'A'})); // DNC verschillende types
0
-1
1
1
-1
1
-1
1
1

.mismatch()

  • if equal => -1
  • anders index van de index waar ze het eerst ongelijk zijn!

Varargs

bv String... args

Multidimensional Arrays

int[][] vars; // 2d
int vars [][]; // 2d array
int[] vars[]; // 2d
int[] var1 [], var2 [][];// 2d en 3d array!!!

ArrayList

ArrayList a = new ArrayList();
ArrayList b = new ArrayList(12);
ArrayList c = new ArrayList(a);

new ArraList<>(); // Is van type Object !!!

You can store an ArrayList in a List maar niet ANDERSOM!

List<String> list6 = new ArrayList<>();
ArrayList<String> list7 = new List<>(); // DNC

ArrayList

.add(E element)

.add(, elem)

.remove(int index)

bij meerder dan eerst de eerste!

.set(int index, E newElem)

.isEmpty .size()

.clear()

.comtains()

return Boolean

.equals()

ArrayList one = new ArrayList<>();
ArrayList two = new ArrayList<>();
System.out.println(one.equals(two));// true

Wrapper Classes

  • int Integer etc
  • wrapper classes are immutable
  • has methods convert back to primitive

"".parseInt()

  • returns a primitive => int

"".valueOf()

  • returns wrapperClass => Integer
int primitive = Integer.parseInt("123");
Integer wrapper = Integer.valueOf("123");

if String is passed but not correct value => NumberFormatException!

ArrayList list = new ArrayList<>();
list.add("asd");
list.add("qwe");
list.add("asd");
list.add(Boolean.TRUE);
System.out.println(list);

ArrayList numlist = new ArrayList<>();
numlist.add(1);
numlist.add(2.0f);
numlist.add(2.001);
System.out.println(numlist);

list.remove("asd");
System.out.println(list);
[asd, qwe, asd, true]
[1, 2.0, 2.001]
[qwe, asd, true]

Autoboxing and Unboxing

unbox an null en je krijgt de NULLPOIINTEREXCEPTION

List<Integer> list = new ArrayList<>();
list.add(null);
int h = list.get(0); // NULL POINTER EXCEPTION
List<Integer> list = new ArrayList<>();
list.add(null);
//int h = list.get(0); // NULLPOINTER EXCEPTION!!!
true

Converting tussen array en list

  • list.toArray();
  • Arrays.asList(array);
List<String> l1 = Arrays.asList("234","wer");
List<String> l1 = List.of("234","wer");

Je hebt nu wel fixed length!

List<String> fixed = Arrays.asList("a","b","c");
List<String> expandable = new ArrayList<>(fixed);


Sorting Collections.sort(numbers)

Sets

  • Set set = new HashSet<>();

Maps

Map<String, String> map = new HashMap<>();
map.put("asd","zxc");

MAth APIs

Math.min(1,2);
Math.max(1,2);
Math.round(1.232);
Math.pow(5,2);
Math.random(1,2);

ocp 04 ocp 06

Tags: 

OCP Chapter 04

Chapter 04 Making Decisions

a Java statement is a complete unit of execution in Java terminated with a semicolon.
Control flow statement break up th e flow execution by using decision making, looping branching aollowing breakup the flow to selectively exewcute segments of code.

  • statement => bla + ;
  • control flow statement => single expression naar code block;

Desicion Making Statements

if(a >1)  
b++;  


if(c>1) {  
  d++;    
}   

if statement

if(booleaExpression) {

}

else

if(booleaExpression) {

} else {

}
if(booleaExpression) {

} else if(booleaExpression2) {

}

check of expression evaluetes in boolean

0 en 1 zijn GEEN booleans...

int a = 1;  
if(a) {} //DNC!!! moet boelean result zijn... 1 is geen boolean  

if(b=2){}//DNC ook geen boolean expression....

Switch

  • switch statement has a target variable that is not evaluated until runtime.
  • NOT boolean long float double EXCLUDE from switch statments
  • enums kunnen in switch
  • int Integer
  • short Short
  • char Character
  • String
  • enum
  • var

wordt pas tijdens runtime geevolueerd!

switch (varToTest) {
  case constanExpression:
  // ...
    break;
    case constanExpression2:
  // ... // hier hoeft niet iets verplichts, mag leeg...
    break;
    default: // default is optionally!
    // ...
}
int a = 1;
switch(a){

} // GAAT GOED

switch(a) {
    case 1 | 2: 
}

int a = 5;
switch a {} // DNC BOOGJES om a!!!

switch (a)
    case 1: sout(); // DNC curly braces!!!

switch(a) {
    case 1: 2: sout(""); //DNC case voor 2:
}

switch(a) {
    case 1 || 2: sout(""); //DNC boolean expression mag niet
}


switch(a){
    case a == 1 ://zelfde als hierboven GEEN INT MAAR BOOLEAN
        break;
}

Switch werkt met: - int Integer - byte Byte - short Short - char Character - String - enum - var met 1 van hierboven

!! Niet MET: - boolean - long - float - double

class A {int a = 1;}
A aa = new A();
switch (aa.a) {
    case 1:
        System.out.println("werkt");
}
werkt

alleen final var in case gedeelte DNC

While

while (booleanExpression) {

}

Do While

do {

} while (booleanExpression)

For loops

C style

for (init ; boolean; update) {}

  • init value must be same type!

infinit loop

for (;;) {}

multiple terms

for (long x = 0, y =1, z = 2; x<4 && z <<6; x++,y++, z++) {}

Redecalring fails

int x = 0;
for(int x = 4;x<5;x++){} //DNC

For-each loops

for (datatype instance ; collection) {}

  • built in java araay
  • an onject whose type implements java.lang.Iterable
String[] names = ...;
for (String name ; names) {}
// String[] names = new String[3];
// for(String name; names) { 
//     System.out.println(name);
// }
|   for(String name; names) { 

';' expected

Optional Labels

### break

optionalLabel: while(boolExpr) {
  break optionalLabel;
}

Continue statement

ocp 03 ocp 05

Tags: 

OCP Chapter 02

Java Building blocks

p. 38

  • Primitive Data Types

Creating Objects

instanciate a class:
Park p = new Park(); p stores the refe to object
constructor:
- the name of the constructor matches the name of the class. - NO return type. purpose of a constructor is to initialize fields.

initialize on line
or
initialize in constructor

public classs Chick {
    public Chick(){
        // do something..
    }
}
public classs Chick {
    public void Chick(){} // NOT A CONSTRUCTOR (return type...)
}

Volgorde init telt

  • Order of appearance!
  • constructor runs after all fields and instance init blocks have run!

Werkt niet

public class ObjectsInits {
    public static void main(String[] args) {
        Name n = new Name();
        System.out.println(n.full);
    }
}

class Name {
    String full = first + " " + last; // first en last zijn er nog niet... Illegal forward reference
    String first = "hj";
    String last = "hubeek";

}

Werkt wel

class Name {
    String first = "hj";
    String last = "hubeek";
    String full = first + " " + last; // nu wel ...
}
class Name {

    String first = "hj";
    String last = "hubeek";
    String full = first + " " + last; // speel met hier of hierboven plaatsen... spoiler hierboven gaat t mis ivm illegal forward referrence
}
Name n = new Name();
System.out.println(n.full);
hj hubeek

blocks in classes

gaan op volgorde van de code
dan constructor!!!

Niet correcte open en closing braces => wont compile
balanced parentheses problem

count blocks => tel de {}
count instance initializers NIET IN METHOD!!!

class BlockesInMyClass{
    String name;
    {System.out.println("vanuit block boven constructor...");}
    BlockesInMyClass() {
        System.out.println("vanuit constructor...");
        this.name = "hjh";
    }
    {System.out.println("vanuit block onder constructor...");}
    public void func(){
        // do something...
        {System.out.println("block in func...");}

    }
}

BlockesInMyClass b = new BlockesInMyClass();
System.out.println(b.name);
b.func();

vanuit block boven constructor...
vanuit block onder constructor...
vanuit constructor...
hjh
block in func...

2 Types: Primitive & Reference Types

8 primitive types
a primitive is just a single value in memory
String is NOT a primitive maar object

Type Bits Standard Default Minimum Value Maximum Value
boolean varies false N/A N/A
byte 8-bit 0 -128 127
short 16-bit 0 -32768 32767
char 16-bit '\u0000' 0 ('\u0000') 65535 ('\uffff')
int 32-bit 0 -2147483648 2147483647
float 32-bit 0.0f -3.4e+38 3.4e+38
double 64-bit 0.0d -1.79e+308 1.79e+308
long 64-bit 0L -9223372036854775808 9223372036854775807

.MAX_VALUE float en double hebben ook .POSITIVE_INFINITY and NaN

alle numeric types zijn SIGNED dus 1 bit voor negative value

range kennen van byte -128 tot 127

char is UNSIGNED

char dus geen negative waarden

char kan dus (2x) grotere waarden bevatten dan short

short d = 'd';
char mammal = (short) 83;
int i = 0b10;

System.out.println(d);
System.out.println(mammal);
System.out.println(i);
100
S
2

literals

when a numbers is represented in code dan heet t n literal.

long max = 3123123123123; does not compile want is int(!)

long max = 3123123123123L; nu wel want is long

long max = 3123123123123l; nu wel (Met kleine letter l!!!!!)NIET DOEN!!! LEEST ALS een 1 ws... GRGRG!

Octal

zet er een 0 voor 017

Hex

0x of 0X ervoor 0xde

Binairy

0b od 0B ervoor 0b10

underscore

Niet als eerste of laatste character

Niet voor of na de komma in t numme

double notAtStart = _1000.00; // DOES NOT COMPILE

double notAtEnd = 1000.00_; // DOES NOT COMPILE

double notByDecimal = 1000_.00; // DOES NOT COMPILE

double notByDecimal = 1000._00; // DOES NOT COMPILE

double magWel = 10_0_0.0_0; // WERKT WEL MAAR LELIJK

double magWel2 = 1________2; // WERKT WEL MAAR LELIJK

Reference Types

reference types refers to an object.

a ref points to an object by storing its memory address. pointer.

a value is assigned :
- to another obj of the same or compatible type
- to a new object using the new keyword.

Reference type can be null; NULL
Primitive Types NEVER can be NULL!!!!

int v = null; //DOES NOT COMPILE

String s = null;// wel!

Types beginnen standaard met null;

Will je null of een helper method voor bool, byte, short, int, char,double, float, long use wrapper classes: Boolean, Byte, Short, Integer, Character, Double, Float, Long

long l = null;

|   long l = null;

incompatible types: <nulltype> cannot be converted to long

Declaring vars

initializing a var

Identifiers

naming

snake_case
cameCase


variablelen beginnen met:

letter
$ Dollar sign
_ Underscore

geen number als start
_ alleen als naam mag niet meer....
geen reserved keyword...


MAG NIET:
int 3DjqkoewarClass; NIET MET NUMBER STARTEN
byte holly@vine; GEEN @
String *$coffee; GEEN *
boudle public; RESERVED keyword
shirt _; geen underscore..... is geen swift

Declaring multiple vars

int i1, i2, i3 =0; 3 declared 1 initialized.

int num, String value; DOES NOT COMPILE DIFFERENT TYPE in same statement...

double d1, double d2; MAG OOK NIET

Local vars

public int notValid() { int y= 10; int x; int reply = x+ y; // DOES NOT COMPILE return reply; }

public int isValid() { int y= 10; int x; x = 3 int reply = x+ y; // DOES COMPILE return reply; }

Passing Constructor and Method Params

moeten declared and init zijn

Var keyword

voor leesbaarheid...

assign en define on same line!!!

var is reserved TYPEname not a reserved word!!!! Dus kan als class, interface of enum...niet doen dus...

public class VarKeyWord{ var tricky = "this is a string";// DOEs NOT COMPILE }

public void twoTypes() { int a, var b = 3; /// DNC var n = null; ///DNC }

var o = (String)null;//Doet t wel...


Can be used: Local variable declaration inside methods or initializers. Local variables in for-loops.

Cannot be used: Class or instance variable declaration. Method parameters. Method return types. Constructor parameters. Multiple var declaration


Var scope

scope binnen
constructor
method
initializer block

public void eat(int pieces){ int bites = 1; } // heeft dus 2 vars

public void eatIfHungry(boolean hungry){
if(hungry) {
int cheese = 1;
}
System.out.println(cheese); // DNC !!!
}

public int bla(var a, var b) {} // DNC!!! alleen BINNEN constructor Method init blockkk!!!!!

public classs Var {
public void var() {
var var = "var";
}
public void Var() {
Var var = new Var();
}

}
COMILES!!!!! Var is niet een system keyword....

public class var { public var(){} } ?? DNC !!!!!!!!

DUS var NIET VOOR CLASS INTERFACE EN ENUM!!!!!!

Garbage Collection

System.gc();

not garanteed to do anything.

  • the object no longer has any ref to it.
  • all refs to obj are out of scope.

finalize() is deprecated in java 9. can run zero or 1 times. never twice.

var Rules

  1. var is used as a local variable in a constructor, method or initializer block
  2. var cannot be used in params of constructor, method, instance vars, class variables...
  3. var is always initialized on the same line where it is declared. declared + initiialized.
  4. value var can change, type NEVER
  5. var cannot be init with null
  6. var niet in multiple-variable declaration
  7. var is reserved type name not reserved word. can be used as an identifier for class, interface or enum name...

ocp 01 ocp 03

Tags: 

OCP Chapter 01

  • Java Development Kit with compiler javac.
  • javac converts .java to .class files.
  • Also archiver for .jar files and javadoc

Javac

javac HelloWorld.java

Jar

jar cvf HelloWorld.jar HelloWorld.class
  • c: Creates a new archive file.
  • v: Generates verbose output to standard output.
  • f: Specifies the archive file name.

Javadoc

javadoc -d doc HelloWorld.java

-d: Specifies the destination directory for the generated documentation.

Jdeps

is a command-line tool provided by the Java Development Kit (JDK) to analyze Java class files. ``` jdeps myapp.jar // basic analysis jdeps --verbose myapp.jar // verbose output jdeps --summary myapp.jar // summary jdeps --jdk-internals myapp.jar // detecting internal usage jdeps --generate-module-info . myapp.jar // general module info


- Code Quality: Helps improve code quality by identifying unnecessary dependencies and dependencies on unstable internal APIs. - Migration to Modules: Facilitates the migration of legacy codebases to the Java module system. - Maintenance: Assists in maintaining and refactoring large codebases by providing clear insights into dependencies. ## JVM Java Virtual Machine ## JRE Java Runtime Enviroment is not anymore in there jlink ## Java Assuming you have compiled a Java program named HelloWorld that is in a file named HelloWorld.class, you can run this program using the java command as follows:

java HelloWorld

Assuming your class file is in ./com/example/HelloWorld.class, you would run:

java com.example.HelloWorld

If you're using a JAR file, you can run your application with the -jar option, like so:

java -jar YourApplication.jar


### Common Options - -cp or -classpath: Specifies a list of directories, JAR archives, and ZIP archives to search for class files. - -Xmx: Specifies the maximum size of the memory allocation pool (for example, -Xmx512m for 512 megabytes). - -Xms: Specifies the initial size of the memory allocation pool. - -version: Displays version information and exits. - -verbose: Enables verbose output. ## Benefits of Java - Object Oriented - Encapsulation - Platform Independent - Robust - Simple - Secure - Multithreaded - Backwards Compatibility **OEPRSSMB** **SPERMBOS** ## Class - Java Class with which you create an Object. - An Object is a runtime instance in memory. - reference is a variable that points to an object. Class members: - fields and methods - method is an operation that can be called. - method signature = methodName + params - method declaration has return type - return type

public class Demo { }

word with special meaing is a **keyword**
public + class is keyword

A java file can have more classes! But max one needs to be public + same as the file name!



public class Demo {

String name;

public getName() {
    return name;
}

publi void setName(String newName) {
    name = newName;
}

}


### Comments

// until end of line

/* * Multi line comment */

/** * javadoc */


## Main() public static void main(String[] args) is startup of a java process managed by the JVM. JVM allocate memory CPU access file and so on. toegestane declaratie vd de argumenten: String[] args String args [] String... args ## Java Version

javac -version java -version




## Run a Java program ```Java public class Zoo { public static void main(String[] args) { } }

compile and run

javac Zoo.java
java Zoo

vanaf 11 kan ook meteen in 1 file runnen : java Zoo.java maar dan alleen JDK imports

to compile a .java file is needed name of the file must be the same as the containing class results in bytecode in the .class file

public is access modifier static results in Zoo.main()

voor java applicatie static main func is obligotory

String[] args

String args[]

String... args

Als argumenten niet kloppen met run command dan ArrayIndexOutOfBoundException

Random r = new Random();
System.out.println(r.nextInt(12));
4

Imports

Er zijn Java built-in classes

andere classes zijn te vinden in packages die je kan importeren

Conflicts

double same named Class will not compile

Static imports

Redundant imports

Same Name

dan bv 1 importeren en de andere helemaal uitschrijven.

import java.util.Data;

public class Conflicts {
    Date date;
    java.sql.Date sqlDate;

  // .... etc ...
}

Alternate dir for compiling code

javac -d classes packagea/ClassA.java packageb/ClassB.java

zowel bij java als bij javac werken alle drie: - java -cp classes packageb.ClassB - java -classpath classes packageb.ClassB - java --class-path classes packageb.ClassB - -d

Order packages imports

Package needs to be the first element

Imports after package

JDK Java Development Kit
java
javac
jar
javadoc
Java Virtual Machine JVM
JRE Java Runtime Enviroment
jlink jdeps (!) API APplication Programming Interfaces

Class
Object
reference
members
fields
methods
comments
type
returntype
void

next ocp 02

Tags: 
Subscribe to RSS - ocp