public enum Size
{ SMALL, MEDIUM, LARGE, EXTRA_LARGE };
. . .
Size s = Size.MEDIUM;
for (Size s: Size.values()) . . .
switch (s) {
case SMALL: . . .
case LARGE: . . .
}
@inponomarev
Ivan Ponomarev, Synthesized.io/MIPT
public enum Size
{ SMALL, MEDIUM, LARGE, EXTRA_LARGE };
. . .
Size s = Size.MEDIUM;
for (Size s: Size.values()) . . .
switch (s) {
case SMALL: . . .
case LARGE: . . .
}
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(); // returns S, M, L or XL
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)
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.).
Error
and subclasses — don’t handle (something catastrophic happened).
RuntimeException
and subclasses — fix the code, there should be none! (in fact, it’s more complex than that).
Checked exceptions — handle them.
public FileInputStream(String name) throws FileNotFoundException{
. . .
}
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 subclass of the other
Truth of Life: IDE does everything for you
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.)
We should not explicitly throw
Exception
RuntimeException
Throwable
Error
(J. Bloch, Effective Java, Item 72)
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.
try-catch
blocktry {
// . . . code . . .
// . . . more code . . .
// . . . more code . . .
} catch (ExceptionType e) {
// handler for this type of exception
// use e object to extract the data!
}
try {
//code that might throw exceptions
} catch (FileNotFoundException e) {
//emergency action for missing files
} catch (UnknownHostException e) {
// emergency action for unknown hosts
} catch (IOException e) {
// emergency action for all other I/O problems
}
try {
//code that might throw exceptions
} catch (FileNotFoundException | UnknownHostException e) {
//emergency action for missing files or unknown hosts
//e type is the most specific common supertype
//of the throwable types
}
. . . throws SQLException . . .
try {
//database access: only SQLException can be thrown here
} catch (Exception e) {
logger.log(level, message, e);
//Although Exception is a less specific than SQLException
//the compiler will be ok with this
throw e;
}
. . . throws ServletException . . .
try {
//database access: SQLException can be thrown here
} catch (SQLException e) {
ServletException se = new ServletException("database error");
//we save information about the initial cause
se.initCause(e);
throw se;
}
Exception can be of the same type or subtype
It is not prohibited if there will be no exception at all
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 |
|
|
| index is either less than minimum value or bigger than maximum value |
try {
...
} catch (Exception e) {
e.printStackTrace();
//And compiler is happy!
//(but your colleague who does the code review is not)
}
…but you should not do this!
Declare checked exception in the method
Wrap using initCause
or constructor argument
into the declared checked exception
into unchecked exception (InvalidStateException
, for example)
Better don’t do this: Lobmok’s @SneakyThrows
(the most arguable feature of Lombok)
Throw early, catch late.
Throw an exception as soon as it is clear that something is wrong.
Do not handle an exception until it is clear how exactly you should do it.
finally
blockInputStream in = new FileInputStream(. . .);
try {
// an exception can be thrown here
code that might throw exceptions
// and even return from the method will call the 'finally' block!
if (...)
return;
} catch (IOException e) {
// sometimes an exception is thrown
// during exception handling
show error message
} finally {
// finally block will work in any case!
in.close();
}
finally
block...throws IOException...
//One resource
BufferedReader br = new BufferedReader(new FileReader(path));
try {return br.readLine();}
finally {br.close();}
finally
block...throws IOException...
//Two resources
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();}
Common scheme
try (Resource res = . . .) {
work with res
}
Example:
try (Scanner in = new Scanner(
new FileInputStream("/usr/share/dict/words")), "UTF-8") {
while (in.hasNext())
System.out.println(in.next());
}
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//HORRIBLE. DON'T DO THIS
try {
int i = 0;
while (true)
range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e) {
}
//DO THIS!!
for (Mountain m: range)
m.climb();
//HORRIBLE. DON'T DO THIS
try {
Iterator<Foo> i = collection.iterator();
while (true)
Foo foo = i.next();
} catch (NoSuchElementException e) {
}
//DO THIS!!
for (Iterator<Foo> i = collection.iterator(); i.hasNext(); ) {
Foo foo = i.next();
. . .
}
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.
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]
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 standard 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
ThreadLocalRandom
//Bad: seed is calculated at each invocation. DON'T!!
double rnd = (new Random()).nextDouble();
//Before Java 7+ it was OK, but now DON'T!!
static final Random r = new Random();
static double random() {
return r.nextDouble();
}
//Java 7-16: 3.6 times faster, statistically better
double rnd = ThreadLocalRandom.current().nextDouble();
//Java 17+: JEP356. Not thread safe!
RandomGenerator generator = RandomGenerator.getDefault();
generator.nextDouble();
String e = ""; // an empty string
String java = "Java\u2122"; //Java™
Need another string? Build another one from scratch:
String greeting = "Hello!"
greeting = greeting.substring(0, 3) + "p!"; //Help!
Although there is an array inside the String, you cannot change its elements!
Before Java 9 — char[]
After Java 9 — byte[]
and byte coder
UTF-16 / Latin1 ('Compact 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 foo = "foo";
String bar = "bar";
System.out.println(foo + bar); //foobar
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 :-(
//Don't do this: the performance is poor!
String result = "";
for (int i = 0; i < numItems(); i++)
result += lineForItem(i);
return result;
StringBuilder
!//You can allocate an approximate number of characters in advance
StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH);
//And you can always use the default: new StringBuilder() (16 chars)
for (int i = 0; i < numItems(); i++)
b.append(lineForItem(i));
return b.toString();
String length in UTF-16 code units:
String greeting = "Hello";
int n = greeting.length(); // is 5.
«The real» string length in code points:
int cpCount = greeting.codePointCount(0, greeting.length());
//works at O(1) time
char first = greeting.charAt(0); // first is 'H'
char last = greeting.charAt(4); // last is 'o'
If you need code points:
int[] codePoints = str.codePoints().toArray();
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)
indexOf..
/ lastIndexOf..
replace..
split..
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.