CS F213 Objected Oriented Programming Labsheet 10
Spring 2024
Java NIO
Java.nio package was introduced in java 1.4. In contrast of java I/O in java NIO the buffer and channel oriented data flow for I/O operations is introduced which in result provide faster execution and better performance.
The central abstractions of the NIO APIs are following −
Channels
of various types,which represent connections to entities capable of performing I/O operationsBuffers
which are containers for data,charsets and their associated decoders and encoders,which translate between bytes and Unicode characters.Selectors
and selection keys, which together with selectable channels define a multiplexed, non-blocking I/O facility.
NIO- Channels
As name suggests channel is used as mean of data flow from one end to other.Here in java NIO channel act same between buffer and an entity at other end in other words channel are use to read data to buffer and also write data from buffer.
Java NIO channel is implemented primarily in following classes:
FileChannel
: used for reading the data from the files. It's object can be created only by calling thegetChannel()
method. We cannot create FileChannel object directly.
DatagramChannel
: The datagram channel can read and write the data over the network via UDP (User Datagram Protocol).
SocketChannel
: The Socket channel can read and write the data over the network via TCP (Transmission Control Protocol).
ServerSocketChannel
: The ServerSocketChannel allows user to listen the incoming TCP connections, same as a web server.
NIO- Buffers
Java NIO buffers are used for interacting with NIO channels. It is the block of memory into which we can write data, which we can later be read again. The memory block is wrapped with a NIO buffer object, which provides easier methods to work with the memory block.
In NIO the data transfer take place by using the buffers implemented in java.nio.Buffer class. It is similar to array, and having a fixed capacity.
In Java NIO the core Buffer used are given below:
CharBuffer
DoubleBuffer
IntBuffer
LongBuffer
ByteBuffer
ShortBuffer
FloatBuffer
Allocating a Buffer
Reading Data from a Buffer
There are two methods for reading the data from a Buffer:
Read the data from Buffer by using one of the
get()
method.
Read the data from Buffer into a Channel.
Writing Data to a Buffer
Similarly there are two methods for writing the data into a Buffer:
Write the data into Buffer by using one of the put() method.
Write the data from Channel into a Buffer.
Example
source.txt 1 2 3
Output
Explanation of the output:
The ASCII value of '1' is 49, '2' is 50, and '3' is 51.
The ASCII value of space is 32.
The ByteBuffer is allocated with a capacity of 8 bytes, so it reads the first 8 bytes from the file, which include the ASCII values of '1', space, '2', and space and so on.
Since the buffer is flipped, it prints all 8 bytes stored in it. The last few zeros indicate unused space in the buffer.
Java Assertions
Assertions in Java help to detect bugs by testing code we assume to be true. An assertion is made using the assert
keyword. Its syntax is:
Here, condition is a boolean expression that we assume to be true when the program executes.
Enabling Assertions
By default, assertions are disabled and ignored at runtime. To enable assertions, we use:
When assertions are enabled and the condition is true, the program executes normally. But if the condition evaluates to false while assertions are enabled, JVM throws an AssertionError
, and the program stops immediately.
Example
Output
Advantages of Assertion
Quick and efficient for detecting and correcting bugs.
Assertion checks are done only during development and testing. They are automatically removed in the production code at runtime so that it won’t slow the execution of the program.
It helps remove boilerplate code and make code more readable.
Refactors and optimizes code with increased confidence that it functions correctly.
Java instanceOf
The java instanceof operator is used to test whether the object is an instance of the specified type (class or subclass or interface).
The instanceof in java is also known as type comparison operator because it compares the instance with type. It returns either true or false. If we apply the instanceof operator with any variable that has null value, it returns false.
Note: In Java, all the classes are inherited from the Object class. So, instances of all the classes are also an instance of the Object class.
Java Records
In Java, a record is a special type of class declaration aimed at reducing the boilerplate code. Java records were introduced with the intention to be used as a fast way to create data carrier classes, i.e. the classes whose objective is to simply contain data and carry it between modules.
Consider a simple class Employee, whose objective is to contain an employee’s data such as its ID and name and act as a data carrier to be transferred across modules. To create such a simple class, you’d need to define its constructor, getter, and setter methods, and if you want to use the object with data structures like HashMap or print the contents of its objects as a string, we would need to override methods such as equals(), hashCode(), and toString(). Instead of writing over 80 lines of code for this we can write :
The constructor, getter methods, toString(), equals(), and hashCode() are generated by the Java compiler during compile time. One thing to note here is that records do not provide setter methods
, as it is expected that the value to instance variables is provided while creating the object.
Records also provide us the capability to:
Create our own constructors. In records, you can create a parameterized constructor, which calls the default constructor with the provided parameters inside its body.
Create instance methods. Like any other class, you can create and call instance methods for the record class.
Create static fields. Records restrict us to write the instance variables only as parameters but enable the use of static variables and static methods
Example
Java Sealed Classes
In Java, class hierarchies can become complex, with numerous subclasses extending a common superclass. This can make it challenging to maintain and control the hierarchy. Unintentional extensions and modifications of classes can lead to unexpected issues and bugs in the codebase.
The sealed
modifier is used to declare a class as sealed. Additionally, the classes that are permitted to be its direct subclasses are specified using the permits
keyword. Here’s an example:
In this example, the Animal class is declared as sealed and permits three subclasses: Dog, Cat and Bird. Any attempt to create a new subclass of Animal outside of this list will result in a compilation error.
Example
Java Records and Sealed Classes are only available in the later versions of java so you might need an IDE like Eclipse to access these features.
Java Generics
Java Generics allows us to create a single class, interface, and method that can be used with different types of data (objects). This helps us to reuse our code.
Note: Generics does not work with primitive types (int, float, char, etc).
Java Generics Class We can create a class that can be used with any type of data. Such a class is known as Generics Class. Here's is how we can create a generics class in Java:
Example
Here, T used inside the angle bracket <> indicates the type parameter.
Exercises
Exercise 1
Create a Java program that reads data from a source file using Java NIO and verifies its content using the assert keyword. For this exercise let the content be "Hello, World!". Note that the size is 13 bytes, so read the content of the file, use a ByteBuffer with a suitable capacity, verify that the content read from the file matches an expected value by using the assert keyword, if the content matches the expected value, print a message indicating successful verification. If not, print an error message.
Sample Output
source.txt : Hello, World!
source.txt : Hello, Worrd!!
Exercise 2
Create a program that represents different types of shapes - Circle, Rectangle, and Triangle using a sealed class Shape. Implement methods to calculate the area of each shape. Then, create an application that calculates the total area of all shapes and prints it.
Define a sealed abstract class Shape with subclasses Circle, Rectangle, and Triangle and an abstract method calcArea().
Each subclass should have appropriate fields and override the calcArea() method to calculate its area.
Create an application (Main) that creates instances of different shapes by creating an array of 3 shapes and calculates the total area of all shapes.
Use the instanceof keyword to check the type of each shape by if-else ladder, calculate its area, total area and print accordingly.
One shape (circle) class is below for reference
in Main you can use the following code for getting the name of the class after you checked by instanceOf
shape.getClass().getSimpleName() + "'s area: " + area
Sample Input (in the code)
Sample Output
Exercise 3
Design a program to manage student records using Java records. Each student record should contain the following information:
Student ID
Student Name
List of Course IDs the student is enrolled in
GPA (Grade Point Average)
Implement methods to perform the following operations:
Add a new student record.
Remove a student record by ID.
Update a student's GPA by ID.
Display all student records.
Calculate and display the average GPA of all students.
You should use the methods provided by record and not make all the methods yourself. You cannot create your own setter methods for record components. This is because records are designed to be immutable data holders, and their components are automatically final. Therefore, you cannot modify the values of record components after the record instance has been created.
If you need to modify the values of the record components, you would need to create a new record instance with the updated values.
Sample Input (in the code)
Sample Output
Exercise 4
Create a generic class
called Pair<K, V> that represents a simple key-value pair. Implement methods to set and get the key and value of the pair.
Instructions:
Define a generic class called Pair<K, V> with two type parameters K for the key and V for the value.
Provide methods to set and get the key and value of the pair.
Test your Pair<K, V> class by creating instances of it with different types for the key and value (e.g., Integer, String, String, Double) and performing various operations like setting and getting the key and value.
Sample Input (in the code)
Sample Output
Last updated