17. Give examples of good and bad use of Optional
Suppose we have some series of values that we pass through with a stream, and as a result we get some Optional as a result:Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
.filter(str -> str.length() >= 3)
.findAny();
We, as expected, need to get a value from this Optional . Just using get() is a bad way:
String result = stringOptional.get();
But this method should get the value from Optional and return it to us, right? This, of course, is true, but if it has a value. Well, if the values in the stream were different, and as a result we got an empty Optional , when trying to take a value from it using the get() method , it will be thrown: Which is not good. In this case, it is better to use the constructions:
-
String result = null; if (stringOptional.isPresent()) { stringOptional.get(); }
In this case, we are checking to see if the element is in the Optional . If not, the resulting string has its old value.
-
String result = stringOptional.orElse("default value");
In this case, we specify some default value that will be set to the resulting string in the case of an empty Optional .
-
String result = stringOptional.orElseThrow(() -> new CustomException());
In this case, we ourselves throw an exception with an empty Optional .
18. Is it possible to declare the main method as final?
Yes, of course, nothing prevents us from declaring the main() method as final . The compiler will not throw errors. But it is worth remembering that any method after declaring it as final will become the last method - not overridden. Although, who will override main ???19. Is it possible to import the same package/class twice? What could be the consequences?
Yes, you can. Consequences? We will have a couple of unnecessary imports that Intelijj IDEA will display as gray, i.e. unused.20. What is Casting? When can we get a ClassCastException?
Casting, or type casting , is the process of converting one data type to another data type: manually (implicit cast) or automatically (explicit type casting). The automatic conversion is performed by the compiler, while the manual conversion is performed by the developer. Type casting for primitives and classes is somewhat different, so we will consider them separately. Primitive types An example of automatic casting of primitive types:int value = 17;
double convertedValue = value;
As you can see, no additional manipulations besides the = sign are needed here. An example of manual casting of primitive types:
double value = 17.89;
int convertedValue = (int)value;
In this case, we can observe a manual cast, which is implemented using (int) , in which case the comma part will be discarded, and the convertedValue will have a value of - 17. Read more about casting primitive types in this article . Now, let's move on to objects. Reference types For reference types, automatic casting is possible for descendant classes to parent classes. This is also called polymorphism . Let's say we have a Lion class that inherits from a Cat class . In this case, the automatic conversion will look like this:
Cat cat = new Lion();
But with an explicit cast , everything is somewhat more complicated, because there is no functionality for cutting off the excess, like primitives. And by simply explicitly converting the view:
Lion lion= (Lion)new Cat();
You will get an error: In fact, you can add methods to the Lion successor class that were not originally in the Cat class , and then try to call them, because the object type will become Lion . Well, there is no logic in this. Therefore, type narrowing is only possible when the original object is of type Lion , but was later cast to the parent class:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
Also, for greater reliability, narrowing casts for objects is recommended using the instanceOf construct :
if (cat instanceof Lion) {
newLion = (Lion)new Cat();
}
Read more about reference type casts in this article .
21. Why do modern frameworks mostly use only unchecked exceptions?
I think this is all because the handling of checked exceptions is still that spaghetti code that is repeated everywhere, while not really needed in all cases. In such cases, it is easier to do the processing inside the framework, so as not to once again shift it onto the shoulders of developers. Yes, of course, an emergency situation can arise, but these very uncheked exceptions can be handled in a more convenient way, without bothering with processing in try-catch and without passing further through the methods. It is enough just to convert the exception into some HTTP response in the exceptionHandler .22. What is static import?
When using static data (methods, variables), you can not create the object itself, but do it by the class name, but in this case, we need a reference to the class. With it, everything is simple: it is added using normal imports. But what if we go to use a static method without writing the class name, as if it were a static method of the current class? This is possible with static imports! In this case, we must write static import and a link to that method. Like, for example, a static method of the Math class for calculating the cosine value:import static java.lang.Math.cos;
As a result, we can use the method without specifying the class name:
double result = cos(60);
Also elementarily, we can load all the static methods of the class at once using static import:
import static java.lang.Math.*;
23. What is the relationship between hashCode() and equals() methods?
According to Oracle , there is the following rule: If two objects are equal (i.e. equals() returns true ), they must have the same hashcode. At the same time, do not forget that two different objects can have the same hash code. To understand why equals() and hashCode() are always overridden as a pair, consider the following cases:-
Both methods are overridden.
In this case , two different objects with the same internal states will return true for equals () , while hashCode() will both return the same number.
It turns out that everything is OK, because the rule is being fulfilled.
-
Both methods are not overridden.
In this case, two different objects with the same internal states will return false with equals() , since the comparison is by reference through the == operator .
The hashCode() method will also return different values (most likely) since it returns the converted value of the address of the memory location. But for the same object, this value will be the same, just as equals() in this case will return true only when the references point to the same object.
It turns out, and in this case everything is OK and the rule is fulfilled.
-
Override equals() , not override hashCode() .
In such a case, for two different objects with the same internal states, equals() will return true , and hashCode() will (most likely) return different values.
There is a violation of the rule, so it is not recommended to do this.
-
Not overridden equals() , overridden hashCode() .
In this case, for two different objects with the same internal states, equals() will return false , and hashCode() will return the same values.
There is a violation of the rule, so the approach is wrong.
24. When are the BufferedInputStream and BufferedOutputStream classes used?
An InputStream is used to read data from a resource byte by byte, and an OutputStream is used to write it byte by byte. But byte-by-byte operations can be very inconvenient and require additional processing (in order to read / write texts normally). Actually, to simplify such byte records, we introduced BufferedOutputStream , and for reading BufferedInputStream . These classes are nothing more than buffers that accumulate data, allowing you to work with data not byte by byte, but whole data packets (arrays). When creating a BufferedInputStream , it takes an instance of the InputStream type in the constructor , from which the data is read:BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in is an object of type InputStream that reads data from the console. That is, using this BufferedInputStream object , we can read data from the InputStream , writing it to the passed array. It turns out a kind of wrapper for the InputStream class . The arr array from this example is the array that gets the data from the BufferedInputStream . That, in turn, reads data from the InputStream with another array, which by default has a size of 2048 bytes. Similarly for BufferedOutputStream : an instance of the OutputStream type must be passed to the constructor, in which we will write data in whole arrays:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out is an object of type OutputStream that writes data to the console. The flush() method sends data from the BufferedOutputStream to the OutputStream , while clearing the BufferedOutputStream . Without this method, nothing will be recorded. And similar to the previous example: arr is an array from which data is written to the BufferedOutputStream . From there, they are written to the OutputStream in another array, which by default has a size of 512 bytes. Read more about these two classes in the article .
25. What is the difference between the java.util.Collection and java.util.Collections classes?
Collection is an interface that is the head of the collection hierarchy. It represents classes that allow you to create, contain and modify entire groups of objects. Many methods are provided for this, like add() , remove() , contains() and others. The main interfaces of the Collection class are:-
Set is an interface that describes a set that contains unordered unique (non-repeating) elements.
-
List is an interface that describes a data structure that stores an ordered sequence of objects. These objects get their own index (number), using which you can interact with them: take, delete, change, overwrite.
-
Queue is an interface that describes a data structure with storing elements in the form of a queue that follows the rule - FIFO - First In First Out .
-
addAll(Collection<? super T> collection, T...element) - adds the passed elements of type T to the collection .
-
copy(List<? super T> dest, List<? extends T> src) - copies all elements from list src to list in dest .
-
emptyList() - returns an empty list.
-
max(Collection<? extends T> collection, Comparator<? super T> comp) - Returns the maximum element of this collection according to the order specified by the specified comparator.
-
unmodifiableList(List<? extends T> list) - Returns an immutable representation of the given list.
GO TO FULL VERSION