What is HATEOAS?

HATEOAS stands for Hypermedia As The Engine Of Application State. It is a technique that is often used in conjunction with REST APIs.

The basic idea of HATEOAS is that when your API returns a resource, the representation should include links that allow the consumer of the API to discover resources related to the current one. It’s similar to the way websites work. When people open a web page, it contains links to further pages, and they can use these links to navigate without any other information.

Let’s see a simple example where we are getting back the details of a user:

{
    "id": 19,
    "name": "JohnDoe",
    "email": "john.doe@gmail.com",
    "links": [{
            "href": "/users/19/comments",
            "rel": "comments",
            "method": "GET"
        },
        {
            "href": "/users/19",
            "rel": "delete",
            "method": "DELETE"
        }
    ]
}

As you can see we have a links array, which contains two links. One for getting the comments made by the user, and one for deleting the user. Because these links are provided, the consumer of the API do not need to know them. It only has to know where to find them. And of course, it needs to know the possible actions about a resource. In this case, it needs to know in advance that a user has comments and that it can be deleted, otherwise, the rel attribute wouldn’t make too much sense to the client.

This is not the only syntax that can be used for providing the links, but it’s a fairly standard one.

Here is a more real life example from the PayPal API:

{  
  "links": [{
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832",
    "rel": "self",
    "method": "GET"
  }, {
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832/refund",
    "rel": "refund",
    "method": "POST"
  }, {
    "href": "https://api.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
    "rel": "parent_payment",
    "method": "GET"
  }]
}

Why use HATEOAS?

The biggest advantage of this approach is that consumers of the API do not have to hardcode the API URLs into their code. They just have to know where to find them. This allows the API developers to freely change the URL structure as it won’t break the clients.

Limitations

In an optimal case, HATEOAS would allow a consumer to start from a “root” resource and just by going through the links, discover the URL for any endpoint and use it. To an extent, it can work. For example, if we are trying to get the likes received by a user for a specific post, a client could automatically

  • GET the user
  • GET the posts of the user
  • GET the specific post of the user
  • and finally, GET the likes for that specific post

All this based on the links returned in the response for each request.

This approach would not work if we need some extra information to be able to submit the request. For instance, if I want to create a post for a user, I need to know what fields do I have to submit. I can get the target URL from the response, but getting all the information about the fields would be pretty tricky. Even if I would get back the list of fields in the response, I would need to have a logic in place that can determine the correct value for each field.

Summary

The HATEOAS technique can be very useful if we are not trying to overuse it. It’s great when we would like to automatically discover content/endpoints to decouple the clients from the API. But we should not try to construct an API that is fully describing itself using this technique because in most cases, this would not be possible in a dynamic way.

Exception Handling in Java

Exception handling is the process of handling exceptional situations disrupting the normal flow of an application. This topic is fairly small and even newcomers to Java will come across exceptions pretty soon in their career, so knowing how you can handle exceptions is a must have even for people who consider themselves “junior” java developers.

Java provides a well thought out, object oriented way of handling exceptions. Let’s see the details in the following sections.

Exception handling overview

As I mentioned above, exception handling is the process of handling unexpected situations. These situations can happen for a lot of different reasons. For example, the programmer used nonexisting indexes on an array or tries to call methods on a nonexisting object, the database server could be down, or we could have lost network connection.

In Java, when a situation like this happens, an exception object is created and the JRE will try to find a piece of code that can handle the exception. This process is called throwing an exception. The exception object itself contains a lot of information about what happened. Where did the exception happen, what caused the exception and a complete stack trace that is basically the call hierarchy that led to this exception.

The logic for finding the piece of code that can handle the exception is pretty straightforward. It starts from the method where the exception is thrown and checks if that method can handle the exception. If it cannot handle it, it will go up the call hierarchy until it can find a suitable handler or the end of the call hierarchy is reached. When a handler is found we call it catching the exception. If no handler is found in the call chain, then the application will terminate and print information about the exception.

So now you have a general overview, let’s see the specifics.

Exception hierarchy

The first important part about exceptions is knowing what classes represent them in Java. The hierarchy looks like the following:

The starting point of the hierarchy is Throwable. All exceptions must be the descendant of this class. There are two direct child classes of Throwable: Error and Exception. Also, we have a special type of exception called a RuntimeException.

Exceptions that are of class Exception or any of its subclasses (except RuntimeException) are called checked exceptions because you have to do something (we’ll see later what this is) with them in your code if there is a possibility that they are thrown. On the other hand, exceptions that are instances of RuntimeException or one of its subclasses are called unchecked exceptions because you are not forced to deal with them in your code, but of course, you have the possibility. Errors are usually exceptions that the application cannot recover from.

Checked vs unchecked exceptions

There are two fundamentally different exceptions in Java: checked and unchecked exceptions.

Checked exceptions (Exception and its subclasses)  are exceptions that usually happen because of some outside circumstance. For example, you are trying to open a file but someone has deleted it. Or you are trying to access a database, but the server is down. Most of these situations although not necessarily common, but has a pretty significant chance of happening at one point or another in the life of your application so you have to be prepared for them so your app can act gracefully in these cases.

If your code has a statement that can throw a checked exception, you have to do something with it. You have to either:

  • Catch the exception and handle it in a try-catch block.
  • Declare the exception in the header of your method using the throws keyword.

If you are not doing one of the above, you will be presented with a compilation error.

Unchecked exceptions (RuntimeException and its subclasses) are mostly caused by programming mistakes. For example, you are trying to access a nonexisting index in an array or you are trying to call a method on a null value. Because these can happen at almost any line in your code, handling them at every point would be really inefficient. This is why it is not mandatory to handle them. It would cause a lot of overhead and your code would be unreadable because of excess exception handling logic.

Of course, it won’t hurt if your application can handle these types of exceptions as well. A good practice to this is to handle them somewhere higher in the call hierarchy in a central place. This way, you only have the handler logic at one place but you are doing something with the exception and don’t let the situation escalate to a point where the user sees a stack trace.

The try-catch block

In Java, you can use the so called try-catch block to handle exceptions. This consists of two blocks. The try block contains the code that can throw the exception and the catch block contains code that will handle the exception. Let’s see a simple example.

public class CustomException extends Exception {
}
RiskyClass riskyClass = new RiskyClass();

try {
    riskyClass.performRiskyAction();
} catch (CustomException e) {
    // Handle the exception.
}

As you can see the try block contains some code that can throw an exception, and there is a catch block that will catch any CustomException thrown in the try block. For catch blocks it is mandatory to specify what exceptions it can catch.

Catching multiple exceptions

You can have multiple catch blocks if needed, like this:

try {
    riskyClass.performRiskyAction();
} catch (CustomException e) {
    // Handle the exception.
} catch (IOException e) {
    // Handle the exception.
}

In case of multiple catch blocks, the JVM will start from the first one and goes until it finds one that can handle the exception. A catch block can handle an exception if the exception specified matches the thrown exception’s class or is a parent of it.

There is an important rule to the ordering of catch blocks. You have to start with the most specific and go towards the least specific. This is because if a less specific one would catch all the exceptions in the beginning, it would be impossible for the code to reach the remaining catch blocks. Here is an example of a correct ordering:

try {
    riskyClass.performRiskyAction();
} catch (CustomException e) {
    // Handle the exception.
} catch (IOException e) {
    // Handle the exception.
} catch (Exception e) {
    // Handle any Exception.
}

The first common superclass for CustomException and IOException is Exception so they can be in any order, but Exception must be specified after them because it is less specific.

Catching multiple exceptions in one block

Since Java 7, you can catch multiple exceptions in a single catch block by separating them with the pipe character. Here’s an example:

try {
    riskyClass.performRiskyAction();
} catch (CustomException | IOException e) {
    // Handle the exception.
} catch (Exception e) {
    // Handle any Exception.
}

The only rule for the exceptions specified in the catch block is that none of them can be the other’s child/parent class as it would mean unnecessary code.

The finally block

You can extend the try-catch block with a finally block. Actually when you have a finally block, you can even omit the catch part.

In the finally block you can specify code that you would like to run regardless of whether an exception was thrown or not.

One of the most common cases is when you are opening a resource (like file stream) in the try block and you would like to be sure that it will be closed even if an exception happens. But that is not the only case. For example, the following code prints how long did the method take to complete in case an exception happens or not.

long startTime = System.currentTimeMillis();
try {
    System.out.println("We are in try.");
} catch (Exception e) {
    System.out.println("We are in catch.");
} finally {
    System.out.println("We are in finally.");
    System.out.println("Total time: " + (System.currentTimeMillis() - startTime) + " ms");
}

Remember, the finally block always executes except when:

  • System.exit() is called.
  • The JVM crashes.
  • The try block never ends (e.g. endless loop).

Nested exception handlers

You can nest try-catch-finally blocks as deep as you would like. So you can have these blocks inside another try/catch/finally block.

Using try with resources

Before Java 7, it was pretty cumbersome to use some resources that needed to be closed after usage. A resource like this is a BufferedReader that uses a buffered input stream to read characters from for example a file.  Let’s see an example how much code did you need to use it.

BufferedReader bufferedReader = null;
String line;
try {
    bufferedReader = new BufferedReader(new FileReader("file.txt"));

    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

You can see that a good amount of code was needed just to handle the closing of BufferedReader to free up resources taken by it. You can see that this is boilerplate code as you have to do it the same way every time you are using a resource that needs to be closed.

Java 7 came to the rescue by introducing try with resources. It is basically a special version of the try block where you can specify the resource that you would like to use and the JVM will automatically close it for you.

String line;
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("file.txt"))) {
    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

As you can see, we could reduce the code to about half of its original size. You can specify as many resources as you want by separating them with semicolons. The only requirement for an object to be listed as a resource is that it has to implement the java.lang.AutoCloseable interface so the compiler can be sure that the classes used to implement a method know how to close the resource.

Declaring unhandled checked exceptions

If one of your methods can throw a checked exception and it is not handled in the method’s code, you have to declare it in the header of the method using the throws keyword.

public void testMethod() throws IOException {
    String line;
    try (BufferedReader bufferedReader = new BufferedReader(new FileReader("file.txt"))) {
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
    }
}

It is also a good idea to document these exceptions in JavaDoc like this:

/**
 * Test method.
 *
 * @throws IOException Thrown when the IO operation is unsuccessful.
 */
public void testMethod() throws IOException {

Throwing exceptions

Not only code that is coming from external libraries can throw exceptions. Using the throw keyword you can throw an exception any time you want. This is actually pretty often used for signaling to the caller that an exceptional situation has happened in your logic.

public void divide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("You cannot divide by zero.");
    }
    // ...
}

public void performBusinessLogic(int input) throws CustomException {
    if(input < 0) {
        throw new CustomException("Cannot perform operation with input less than zero");
    }
    // ...
}

In the first example we are throwing an unchecked exception so it’s not declared in the method’s header. In the second example, a checked exception is thrown so we needed to declare it.

Wrapping exceptions

When you catch an exception it can often be a good idea to throw a new exception in the catch block. In these cases you should wrap the original exception in the new one, so when the new exception will be logged, the original one is logged as well.

try {
    throw new UserNotFoundException("Cannot perform operation with input less than zero");
} catch (UserNotFoundException e) {
    throw new OtherException("Other exception", e);
}

We called this constructor:

public UserNotFoundException(String message, Throwable cause) {
    super(message, cause);
}

Custom exceptions

When you are throwing exceptions, those often should be custom exception classes made by you. Here are some examples:

public class UserNotFoundException extends Exception {

    public UserNotFoundException() {
    }

    public UserNotFoundException(String message) {
        super(message);
    }
}
public class UserAlreadyExistsException extends Exception {

    public UserAlreadyExistsException() {
    }

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

You can extend any of the existing exception classes (even yours) and create custom exception hierarchies.

Making and throwing custom exceptions allows you to be as specific as you want about the situation happening and you are able to handle the different exceptional cases in different ways.

Best practices

Logging

Properly logging the reason of the exceptions is one of the most important ways to keep your code as easily debuggable as possible. When you are catching an exception it is always a good idea to log a message stating the most concrete info that you know about the exception and you should also print the exception object itself as well. This will print the stack trace as well into the logs. It will come in very handy when someone wants to debug the code.

Of course, printing the exception information to the user is a bad idea. Usually, he won’t understand what is a NullPointerException for example and it can give away information to hackers. For users just print a friendly message that something went wrong and you’ll look into it.

Fail fast

If a method will throw an exception it is best if it throws as early as possible. So for example, if you have some conditions in your method that will result in an exception if not fulfilled, it’s best to check them as soon as you can, so the method won’t run unnecessarily for a longer period of time. This makes debugging easier as well.

Catch only when able to handle

You should only catch an exception when you can act on it properly. Just catching the exception and doing nothing will make debugging your application really hard when an exception happens.

Naming conventions

Your custom exceptions should be named in a way that they end in “Exception” like “CustomException”.

What is REST?

REST stands for Representational State Transfer. 

It is an architectural style (or software design pattern) that is used for designing web applications or web services. Well, that is not entirely true, because REST can be used in case of any networked application, but because the overwhelming majority of these are web applications/services, we are going to refer to them in this manner.

When you are using REST to build a web service, you can say that it is a RESTful web service. If you are referring to the API that this web service provides, it’s a RESTful API.

REST Guidelines

REST does not have a specification or concrete rules on how it must be implemented. It is a collection of guidelines that you can use if you would like to develop a RESTful API. You can choose to follow all or just a subset of the guidelines depending on how RESTful you want your API to be.

This approach might become more clear if we compare it to the SOAP communication protocol that is also a commonly used in case of web services. This protocol uses XML format for communication. The request/response formats need to follow strict rules and there is a separate descriptor language called WSDL to describe the formats used by the web service. REST, on the other hand, does not tie your hand regarding what format you should use (XML/JSON, etc.), what should the request or response contain. It is just a set of guidelines to use when building your web service. Although it’s worth mentioning that JSON is the most commonly used format.

So let’s see what are the guidelines that we need to follow if we would like to implement a RESTful web service:

  • Objects as resources: Server objects are treated as resources and are exposed using a uniform URL structure and are manipulated using a common set of verbs (HTTP request methods).
  • Well chosen HTTP status codes
  • Stateless client-server communication
  • Representations: Representations of resources are not dependent on which resource we would like to get, but it is determined by the request (with the use of request headers).
  • Cacheable resources

Objects as resources

In regular web applications that you can access through your browser, you can usually see URLs that refer to some action like:

  • /users/show-profile
  • /results/processResults.php
  • etc.

As you can see these describe some kind of action, they tell the server what it needs to do.

REST has a quite different approach to this. It does not talk about actions, it talks about resources. Objects on the server side (e.g. blog posts, users etc.) are treated as resources, and you can reference them using a standard URL format. The operation you would like to perform on a resource is determined by the HTTP method that you use.

The purpose of the HTTP methods are:

  • GET: Retrieve a resource.
  • POST: Create a resource.
  • PUT: Update a resource.
  • DELETE: Remove a resource.

We can differentiate instance and collection resources depending on whether we are talking about one or more objects. Let’s see some examples of the standard URL structure and the HTTP methods that we can use.

URLHTTP methodDescription
/messagesGETReturn all the messages.
/messages/{messageID}GETReturn a single message.
/messagesPOSTCreate a new message.
/messages/{messageID}PUTUpdate a message.
/messages/{messageID}DELETEDelete a message.
/messages/{messageID}/likesGETReturn all the likes for a particular message.
/messages/{messageID}/likes/{likeID}GETReturn information about a specific like for a specific message.
/messages/{messageID}/likesPOSTCreate a new like for a particular message.
/messages/{messageID}/likes/{likeID}PUTUpdate a like for a particular message.
/messages/{messageID}/likes/{likeID}DELETEDelete a like for a particular message.
/messages/{messageID}/likesDELETEDelete all likes for a given message.

As you can see, some of the URLs are identical, but we are using a different HTTP method, so the server will be able to decide what we would like to do.

HTTP method idempotence

We can say, that an HTTP method is idempotent if issuing the same HTTP request multiple times results in the same outcome as if we have just issued it a single time.

The HTTP methods GET, PUT, DELETE are idempotent, if used properly. Let’s see why:

  • Issuing a GET request multiple times will just return the same response multiple times. No issue is caused by this, it can be easily seen. Of course, if the data on the server changes in the meantime, a different result set will be returned.
  • If you issue multiple PUT requests, you are updating the resource the same way multiple times to the same state. The end result will be the same as if you would have just made one call.
  • Making a DELETE request multiple times won’t cause issues because the requests other than the first one won’t find the resource and will not be able to delete it. So the end result is the same as if there were only one call.

On the other hand, POST is not idempotent as making multiple POST requests with the same content will result in the creating of duplicate resources (with different IDs).

There is catch, however. For the above to be true, you have to design your API correctly. Here are some examples that would violate these idempotence rules and should be avoided when designing a RESTful API:

  • You have a GET endpoint that creates a resource. GET should be idempotent, but this case it is not.
  • You have a PUT endpoint that allows users increase the amount of votes for a picture. PUT should be idempotent, but in this case it is not.
    • Because you increase the votes by incrementing the current vote count, issuing this request multiple times will cause the votes to increase multiple times. Possible solutions:
      • Use POST in a RESTful way by creating a new “vote” object every time a user votes.
      • Use the less known PATCH HTTP method (not the scope of this tutorial).

Through a final example, you’ll easily see how important it is to keep to these rules. When you are submitting a form using a POST request and try to refresh the browser, you are presented with a message asking if you are sure that you want to issue the same request again. This is because of the fact that POST is not idempotent and issuing it multiple times might cause unwanted side effects like buying two products instead of one. For other HTTP methods, there is no such warning, because they are supposed to be idempotent.

Well chosen HTTP status codes

Becuase REST APIs are consumed by computers and not humans, it is important to always use the proper HTTP status code in the response, so the consumer code is able to decide if the request was successful or not. If the operation failed, the status code can tell some details about the reason of the failure and the client code can use that to decide what should it do (e.g. retry the request).

Stateless client-server communication

In case of REST, the distinction between the client and server is important. These need to be independent of each other, so we can easily scale or relocate them to different servers without affecting the other.

Also, REST relies on a stateless protocol (that is almost always HTTP). This means that every request should contain every information that is needed for the server to process it, so the requests must not depend on anything that was sent by a previous request.

If we take this seriously, we cannot even store anything in the session in a RESTful application, the client has to track this kind of information.

In case of APIs that does not require authentication, this is easily feasible. Most REST APIs, however, use some form of authentication. In this case, no storage on the server side is needed if the clients send their credentials with every request. However, it is not always the best way because in this case we always need to re-authenticate the user. Usually, there is some kind of token generated that the server accepts as authentication for a given amount of time. This slightly violates the stateless principle but some sacrifices have to be made if we would like to make a properly working system.

Representations

Each resource (e.g. blog post) can have many different representations. The most common ones are JSON and XML. REST says that the returned representation of the resource is not dependent on which resource we would like to get back, but on the HTTP request, we send. The request should contain information about what representation we would like to get back.

The headers used are:

  • Content-Type: The client and the server both use it to specify the format of the data that is being sent.
  • Accept: The client uses this to specify what format it expects from the server.

Cacheable resources

In REST, resources have to be explicitly declared cacheable or not. Caching a resource can eliminate some of the calls to the server as we can just use the locally cached version.

Documenting a REST API

The first step to a good documentation is to follow the REST principle as much as possible. This will allow users to more quickly understand how they can use your API. Of course, you have to create a documentation as well. There are a lot of various ways how you can structure the documentation, here is one example:

  • Description of the endpoint
  • URL
  • HTTP method
  • URL parameters
  • Data parameters (in request body)
  • Request / response format
  • Success response
  • Error responses
  • Sample call
  • Notes

REST in the real world

When we refer to REST, it can mean different things. As an example, it can be an API that fully conforms to the REST principles or it can be a web application that only uses some ideas from REST.

For example, we could just have some endpoints in a web application that use the REST URL structure and follows the HTTP method usage guidelines but uses the session to store details about the currently logged in user. This obviously violates the stateless principles, because we are depending on data stored on the server side.

Java Interview Questions About Strings

What are Strings in Java?

Strings represent a sequence of characters. In Java, String is not a primitive data type like int or long, they are objects and the characters are stored internally as a char array.

The String class is used to represent strings and provides some methods that can be used to manipulate them and perform various operations.

How can you create Strings in Java?

The most common way to create a String is to use a literal like so:

String myString = "Hello World";

You will use this 99% of the time, but there can be cases when you would like to use one of the constructors of the String class (like when creating a String from a character array).

What is important to remember here is not to use this constructor:

new String("Hello World")

This will first create a new String from the literal that you specified (“Hello World”), it will then pass this to the constructor and that will create another String object with the same value. You have 2 objects instead of one, this is really inefficient.

How to check if two Strings are equal?

There are two ways to check for String equality.

The first way is to use the double equal sign (==). In most cases however this will not provide the expected result, because it will compare the references of the two Strings and those might not be the same. Two different String objects could exist with the same value, but their reference will not be the same.

The recommended way is to use the equals() method. It will work as expected because it will only compare the value of the two String objects.

Why there are classes like StringBuilder or StringBuffer?

Performing a lot of operations (like concatenation) on a String until you reach it’s final form can result in creating a lot of String objects, because each of these operations have the chance of creating a new object because of String’s immutability.

StringBuilder and StringBuffer helps in this situation by keeping track of the String you are building as a character array. It will only produce a String object when you are done with the building of the String and ask StringBuilder/StringBuffer to return the result.

Let’s see an example. Without StringBuilder:

String stringByConcatenation = "Hello";

stringByConcatenation += " ";
stringByConcatenation += "World";
stringByConcatenation += "!";

After executing these lines we will have the following String objects:

  • 4 String literals we see in the code.
  • 3 intermediate results during the concatenation.

With StringBuilder:

StringBuilder builder = new StringBuilder("Hello");
builder.append(" ");
builder.append("World");
builder.append("!");

String stringFromBuilder = builder.toString();

After executing these lines we will have the following String objects:

  • 4 String literals we see in the code.
  • 1 extra String when we call toString().

As you can see in this example we have 5 instead of 7 strings. In more complex examples, the win would be even higher. For 1000 strings, it would be 1999 (without StringBuilder) vs 1001 (with StringBuilder).

What is the difference between StringBuilder and StringBuffer?

StringBuffer is synchronized, StringBuilder is not.

What does it mean that a String is immutable?

It means that once a String object is created, it’s value cannot be changed. More on that here: What is the difference between final and immutable in Java?

Are Strings thread safe?

Strings are thread safe, because they are immutable so their state cannot change after they are created.

What is String interning?

See our separate article on this topic: What is String interning in Java?

Bonus: Why could String’s substring() method cause a memory leak in JDK6?

In JDK6 the String class contained three fields: value, offset, count.

  • value – The characters of the String as a char array.
  • offset – The first index of the array.
  • count – The number of characters in the String.

When the substring() method was called, it just created a new String object that contained the same value, but used a different offset and count. As a result the new object referenced the old object and made it impossible for it to be garbage collected. If you had a lot of huge Strings, this wasted memory could add up to large amounts.

Here is the String constructor that causes the mentioned problem in JDK6.

String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

In JDK7, this issue has been resolved. The String class no longer has the offset and count fields. When the substring() method is used, a new String is created by copying the required characters to a new object.

What is the Difference Between Final and Immutable in Java?

Final

If you declare a field or variable final, it means, that you cannot change the object reference stored in it. It will always point to the same object. While you cannot substitute the stored reference with another one, you can modify the referenced object (for example update its fields).

For classes, final means that you cannot create a subclass of it.

Making something final is just a matter of adding a keyword, reaching immutability is a bit more complex.

Immutable

If an object is immutable, it’s state/value cannot change over time. A good example for this is the String or the BigDecimal class.

BigDecimal for example, has a number of “manipulation” methods like add(), but these methods will not modify the original object, but they will return a new one.

public BigInteger add(BigInteger val) {
        // ...
        return new BigInteger(resultMag, cmp == signum ? 1 : -1);
    }

Making an object immutable is the responsibility of the programmer. It cannot be achieved just by putting there a keyword like in case of final.

Final and immutable

Of course, it is possible for an object to be final and immutable at the same time. A good example for this is the String class.