Core Java. Lecture #4
Enumerations. Exceptions. Strings
Ivan Ponomarev, Synthesized.io/MIPT
Enumeration Classes
public enum Size
{ SMALL, MEDIUM, LARGE, EXTRA_LARGE };
. . .
Size s = Size.MEDIUM;
for (Size s: Size.values()) . . .
switch (s) {
case SMALL: . . .
case LARGE: . . .
}
Fields, methods and constructors for enum classes
public enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private final String abbreviation;
private Size(String abbreviation) {
this.abbreviation = abbreviation;
}
public String getAbbreviation() {
return abbreviation;
}
}
. . .
Size s = . . .
s.getAbbreviation();
Basic principles of class design
Minimize the scope (use private
wherever you can)
Minimize mutability (use final
wherever you can)
Document extension points or prohibit inheritance (final
or Javadoc on methods)
Exceptions
Program errors (bugs): null pointer dereference, running out of array bounds, invalid typecasts, division by zero etc.
Invalid input, user’s errors
Hardware and network: cannot open a file, running out of memory / out of disk space etc.
Exception is an unambiguous way to tell the user that the call was finished unsuccessfully (unlike return codes etc.).
Exception classes hierarchy
What should we do when we got an exception?
Error
and sublasses — don’t handle (something catastrophic happened).
RuntimeException
and sublasses — fix the code, there should be none! (in fact, it’s more complex than that).
Checked exceptions — handle them.
Declaring checked exceptions
public FileInputStream(String name) throws FileNotFoundException{
. . .
}
Declaring more than one exception
class MyAnimation {
. . .
public Image loadImage(String s)
throws FileNotFoundException, EOFException {
. . .
}
}
No need to declare unchecked exceptions
No need to declare two exceptions, if one is a sublass of the othern
Truth of Life: IDE does everything for you
Throwing exceptions
throw new EOFException();
(If we throw a checked exception, the compiler will let us do it only if it’s either declared or handled in the same method.)
Throwing exceptions
We should not explicitly throw
Exception
RuntimeException
Throwable
Error
(J. Bloch, Effective Java, Item 72)
What base class should we choose for our exception? This is the question
If we inherit it from Exception
— we should be ready to either declare or catch it everywhere. Is it good or bad?
We can inherit from RuntimeException
. And forget to catch it.
In the age of lambdas and streams, checked exceptions are rather an unneeded headache.
People are still arguing about checked exceptions.
How to catch an exception: the try-catch
block
try {
} catch (ExceptionType e) {
}
How to catch several types of exceptions
try {
} catch (FileNotFoundException e) {
} catch (UnknownHostException e) {
} catch (IOException e) {
}
try-multicatch
try {
} catch (FileNotFoundException | UnknownHostException e) {
}
Rethrowing exceptions (with smart type check)
. . . throws SQLException . . .
try {
} catch (Exception e) {
logger.log(level, message, e);
throw e;
}
Wrapping exceptions
. . . throws ServletException . . .
try {
} catch (SQLException e) {
ServletException se = new ServletException("database error");
se.initCause(e);
throw se;
}
Overriding methods with declared exceptions
Standard reusable types of unchecked exceptions
J. Bloch 'Effective Java', Item 72:
| invalid non-null method argument |
| internal state of the object doesn’t fit for the invocation of this method |
| null reference is passed to the method that does not expect `null`s in its arguments
|
IndexOutOfBoundsException
| index is either less than minimum value or bigger than maximum value |
You will be tempted to do this…
try {
...
} catch (Exception e) {
e.printStackTrace();
}
…but you shoud not do this!
If you can’t understand what to do with the exception
Declare checked exception in the method
Wrap using initCause
or constructor argument
Better don’t do this: Lobmok’s @SneakyThrows
(the most arguable feature of Lombok)
finally
block
InputStream in = new FileInputStream(. . .);
try {
code that might throw exceptions
if (...)
return;
} catch (IOException e) {
show error message
} finally {
in.close();
}
A problem with finally
block
...throws IOException...
BufferedReader br = new BufferedReader(new FileReader(path));
try {return br.readLine();}
finally {br.close();}
A problem with finally
block
...throws IOException...
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)out.write(buf, 0, n);}
finally {out.close();}}
finally {in.close();}
try-with-resources
try (Resource res = . . .) {
work with res
}
try (Scanner in = new Scanner(
new FileInputStream("/usr/share/dict/words")), "UTF-8") {
while (in.hasNext())
System.out.println(in.next());
}
Multiple resources
try (Scanner in = new Scanner(
new FileInputStream("/usr/share/dict/words"), "UTF-8");
PrintWriter out = new PrintWriter("out.txt")) {
while (in.hasNext())
out.println(in.next().toUpperCase());
}
Closeable
and AutoCloseable
interfaces
Exceptions are for exceptional cases only!
try {
int i = 0;
while (true)
range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e) {
}
for (Mountain m: range)
m.climb();
Exceptions are for exceptional cases only!
try {
Iterator<Foo> i = collection.iterator();
while (true)
Foo foo = i.next();
} catch (NoSuchElementException e) {
}
for (Iterator<Foo> i = collection.iterator(); i.hasNext(); ) {
Foo foo = i.next();
. . .
}
Don’t use exceptions for control flow
It masks the real errors and makes the code difficult to support.
It is resource consuming (exceptions have huge stack traces attached).
It is slow: compiler never optimizes for exceptions.
Exception is in production. What should we do?
2019-08-24 11:14:55.545 ERROR 30413 --- [0.1-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at guess.service.AnswerServiceImpl.setAnswer(AnswerServiceImpl.java:37) ~[classes!/:na]
at guess.controller.AnswerController.addAnswer(AnswerController.java:31) ~[classes!/:na]
at sun.reflect.GeneratedMethodAccessor75.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_222]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_222]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar!/:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_222]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_222]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.21.jar!/:9.0.21]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_222]
Analyze → Analyze Stack Trace…
Here we make a pause in learning the language features in favor of learning the library
J. Bloch, 'Effective Java': 'By using a standard library, you take advantage of the knowledge of the experts who wrote it and the experience of those who used it before you… Numerous features are added to the libraries in every major release, and it pays to keep abreast of these additions.'
Besides "fresh" classes the snandard library still holds many outdated classes for the sake of backwards compatibility.
You will meet Vector
, Date
, File
, StringBuffer
, Random
and others which I will not describe in my lectures.
Be careful: the fact that you saw them in legacy code, some outdated tutorial on Internet etc. does not justify their usage in the new code
A specific example from 'Effective Java' book: ThreadLocalRandom
double rnd = (new Random()).nextDouble();
static final Random r = new Random();
static double random() {
return r.nextDouble();
}
double rnd = ThreadLocalRandom.current().nextDouble();
RandomGenerator generator = RandomGenerator.getDefault();
generator.nextDouble();
String class
String e = "";
String java = "Java\u2122";
String is an immutable object.
Need another string? Build another one from scratch:
String greeting = "Hello!"
greeting = greeting.substring(0, 3) + "p!";
Although there is an array inside the String, you cannot change its elements!
Internal representation of strings
Internal String constants pool
String name1 = "John Doe";
String name2 = "John Doe";
String name3
= new String("John Doe");
| |
Comparing strings
if (a == "John Doe")
— wrong, novice’s error.
if (a.equals("John Doe"))
— wrong, we’ll get NPE, if a == null
.
if ("John Doe".equals(a))
— this is how experienced folks do it.
if ("John Doe".equalsIgnoreCase(a))
— case insensitive comparision.
if(str != null && !str.isEmpty())
— there is something in this string!
if(str != null && !str.isBlank())
— there is something in this string besides spaces, tabs, carriage returns etc.!
String interning
String name1 = "John Doe";
String name2 =
new String("John Doe");
String name3 =
new String("Jane Roe");
| |
String interning
name1 = name1.intern();
name2 = name2.intern();
name3 = name3.intern();
| |
String concatenation
String foo = "foo";
String bar = "bar";
System.out.println(foo + bar);
The situation is following:
Unlike other languages, there is no operators overloading for users in Java.
+
operator is overloaded for strings on language level.
==
operator is not overloaded, although its non-overloaded version does not make sense :-(
String concatenation: use StringBuilder
!
StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH);
for (int i = 0; i < numItems(); i++)
b.append(lineForItem(i));
return b.toString();
String length
String length in UTF-16 code units:
String greeting = "Hello";
int n = greeting.length();
«The real» string length in code points:
int cpCount = greeting.codePointCount(0, greeting.length());
Character at a given position
char first = greeting.charAt(0);
char last = greeting.charAt(4);
int[] codePoints = str.codePoints().toArray();
Useful methods
int length()
boolean isEmpty() / boolean isBlank()
char charAt(int index)
int compareTo(String anotherString)
boolean equals(Object anObject)
boolean equalsIgnoreCase(String anotherString)
boolean startsWith(String prefix)
boolean endsWith(String suffix)
String toLowerCase() / String toUpperCase()
String trim()
String join(CharSequence delimiter, CharSequence... elements)
Harmful methods
In combination with substring
and concatenation they are sometimes used for string parsing/transformation.
Don’t do that. This is the way for endless pain, endless bugs and endless shame. Use definite state machines or regular expressions instead.