preface
Recently, I read geek Time’s “100 Common Mistakes in Java Business Development”, and then combined with some common code holes, I wrote a summary, I hope it is helpful to everyone, thank you for reading ~
Github address, thanks to each star
Github.com/whx123/Java…
Public number: a boy picking up snails
1. Six types of typical null pointer problems
- Null-pointer problem for wrapper type
- Null-pointer problem for cascading calls
- Null pointer problem to the left of Equals method
- ConcurrentHashMap Similar containers do not support the value of K-V null.
- Collection, array gets elements directly
- Object gets attributes directly
1.1 The issue of null Pointers for packaging types
public class NullPointTest { public static void main(String[] args) throws InterruptedException { System.out.println(testInteger(null)); } private static Integer testInteger(Integer i) { return i + 1; }}Copy the code
1.2 Null-pointer problem of cascade call
Public class NullPointTest {public static void main (String [] args) {/ / fruitService getAppleService () may be empty, Cause problems of null pointer fruitService. GetAppleService () getWeight (.) the equals (" 18 "); }}Copy the code
1.3 Null pointer to Equals (
public class NullPointTest { public static void main(String[] args) { String s = null; If (s.quals ("666")) {// if (s.quals ("666")) {system.out.println ("666"); }}}Copy the code
1.4 ConcurrentHashMap The value of K-V cannot be null.
public class NullPointTest { public static void main(String[] args) { Map map = new ConcurrentHashMap<>(); String key = null; String value = null; map.put(key, value); }}Copy the code
1.5 sets, arrays get elements directly
public class NullPointTest { public static void main(String[] args) { int [] array=null; List list = null; System.out.println(array[0]); System.out.println(list.get(0)); // null pointer field}}Copy the code
1.6 Objects directly obtain attributes
public class NullPointTest { public static void main(String[] args) { User user=null; System.out.println(user.getAge()); // null pointer exception}}Copy the code
2. Date pit set in YYYY format
In daily development, it is often necessary to format the date, but when the year is set to YYYY uppercase, there is a hole.
Example:
Calendar calendar = Calendar.getInstance(); calendar.set(2019, Calendar.DECEMBER, 31); Date testDate = calendar.getTime(); SimpleDateFormat dtf = new SimpleDateFormat("YYYY-MM-dd"); Println ("2019-12-31 to YYYY-MM-DD "+ dtf.format(testDate));Copy the code
Running results:
2019-12-31 transfer to YYYY-MM-DD format after 2020-12-31Copy the code
Resolution:
Why is it December 31, 2019, and then it’s December 31, 2020? The week begins on Sunday and ends on Saturday. As long as the week is New Year’s day, that week counts as the year of the next year. The correct posture is to use yyyy format.
Is:
Calendar calendar = Calendar.getInstance(); calendar.set(2019, Calendar.DECEMBER, 31); Date testDate = calendar.getTime(); SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd"); Println ("2019-12-31 to YYYY-MM-DD "+ dtf.format(testDate));Copy the code
3. The amount of numerical calculation precision pit
Take a look at this floating point calculation example:
Public class DoubleTest {public static void main(String[] args) {system.out.println (0.1+0.2); System. The out. Println (1.0 0.8); System. The out. Println (4.015 * 100); System. The out. Println (123.3/100); Double amount1 = 3.15; Double amount2 = 2.10; If (amount1-amount2 == 1.05){system.out.println ("OK"); }}}Copy the code
Running results:
0.30000000000000004
0.19999999999999996
401.49999999999994
1.2329999999999999
Copy the code
As you can see, the settlement results are not what we expected because computers store values in binary, even for floating-point numbers. To a computer, 0.1 can’t be expressed exactly, which is why floating point numbers tend to be inaccurate. Therefore, sums are generally calculated using the BigDecimal type
For the above example, let’s change to BigDecimal and see how it works:
System. The out. Println (new BigDecimal (0.1). The add (new BigDecimal (0.2))); System. The out. Println (new BigDecimal (1.0), subtract (new BigDecimal (0.8))); System. The out. Println (new BigDecimal (4.015), multiply (new BigDecimal (100))); System. The out. Println (new BigDecimal (123.3). Divide (new BigDecimal (100)));Copy the code
Running results:
0.3000000000000000166533453693773481063544750213623046875
0.1999999999999999555910790149937383830547332763671875
401.49999999999996802557689079549163579940795898437500
1.232999999999999971578290569595992565155029296875
Copy the code
The result is still incorrect. In fact, to represent and evaluate floating point numbers with BigDecimal, you must initialize BigDecimal using the string constructor, as shown below:
Public class DoubleTest {public static void main(String[] args) {system.out.println (new BigDecimal("0.1").add(new) BigDecimal (" 0.2 "))); System. The out. Println (new BigDecimal (" 1.0 "). Subtract (new BigDecimal (" 0.8 "))); System. The out. Println (new BigDecimal (" 4.015 "), multiply (new BigDecimal (" 100 "))); System. The out. Println (new BigDecimal (" 123.3 "). Divide (new BigDecimal (" 100 "))); }}Copy the code
When using BigDecimal to calculate amounts, we also need to be aware of the number of decimal points in BigDecimal, as well as its eight rounding patterns.
Static variables rely on Spring to instantiate variables, which may lead to initialization errors
I saw a project with similar code earlier. Static variables depend on the Beans of the Spring container.
private static SmsService smsService = SpringContextUtils.getBean(SmsService.class);
Copy the code
This static smsService may not be available because the class loading order is not fixed. The correct way to write this is as follows:
private static SmsService smsService =null; Public static SmsService getSmsService(){if(SmsService ==null){SmsService = SpringContextUtils.getBean(SmsService.class); } return smsService; }Copy the code
5. The default encoding of FileReader causes garbled characters
public class FileReaderTest { public static void main(String[] args) throws IOException { Files.deleteIfExists(Paths.get("jay.txt")); Files.write(path.get (" jay-txt "), "Hello, little boy who pick up snail ".getBytes(charset.forname ("GBK"))); System.out.println(" System default encoding: "+ charset.defaultCharset ()); char[] chars = new char[10]; String content = ""; try (FileReader fileReader = new FileReader("jay.txt")) { int count; while ((count = fileReader.read(chars)) ! = -1) { content += new String(chars, 0, count); } } System.out.println(content); }}Copy the code
Running results:
The default encoding is UTF-8,���� ݵ� с �к�Copy the code
From the running result, you can know that the system default encoding is UTF8, read out in demo, appear garbled. Why is that?
FileReader reads files using the current machine’s default character set. If you want to specify a character set, you need to use InputStreamReader and FileInputStream directly.
For example:
public class FileReaderTest { public static void main(String[] args) throws IOException { Files.deleteIfExists(Paths.get("jay.txt")); Files.write(path.get (" jay-txt "), "Hello, little boy who pick up snail ".getBytes(charset.forname ("GBK"))); System.out.println(" System default encoding: "+ charset.defaultCharset ()); char[] chars = new char[10]; String content = ""; try (FileInputStream fileInputStream = new FileInputStream("jay.txt"); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, Charset.forName("GBK"))) { int count; while ((count = inputStreamReader.read(chars)) ! = -1) { content += new String(chars, 0, count); } } System.out.println(content); }}Copy the code
6. Integer Indicates the cache pit
public class IntegerTest { public static void main(String[] args) { Integer a = 127; Integer b = 127; System.out.println("a==b:"+ (a == b)); Integer c = 128; Integer d = 128; System.out.println("c==d:"+ (c == d)); }}Copy the code
Running results:
a==b:true
c==d:false
Copy the code
Why is an Integer value 128 not equal? The compiler converts Integer a = 127 to integer.valueof (127). Let’s look at the source code.
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Copy the code
You can see that I, up to a certain point, returns the cache.
By default, the cache interval is [-128, 127], so we need to pay attention to this pit when we compare Integer values. -xx :AutoBoxCacheMax=1000 can be used to set the JVM parameters
7. With ThreadLocal, threads reuse information-distorting pits
Using ThreadLocal to cache information, it is possible for information to be corrupted. Look at this example.
private static final ThreadLocal<Integer> currentUser = ThreadLocal.withInitial(() -> null); @getMapping ("wrong") public Map wrong(@requestParam ("userId") Integer userId) {query user information String in ThreadLocal before setting user information beforeUser = Thread.currentThread().getName() + ":" + currentUser.get(); ThreadLocal CurrentUser.set (userId); String afterUser = thread.currentThread ().getName() + ":" + currentUser.get(); Map Map = new HashMap(); map.put("before", beforeUser); map.put("after", afterUser); return map; }Copy the code
Ideally, the beforeUser should be null each time, but the program is running in Tomcat, and the thread executing the program is Tomcat’s worker thread, which is based on the thread pool.
A thread pool reuses a fixed number of threads, and once a thread reuses, it is likely that the first value fetched from ThreadLocal is a leftover value from a previous request by another user. In this case, the user information in ThreadLocal is the information of other users.
Set the Tomcat worker thread to 1
server.tomcat.max-threads=1
Copy the code
User 1 will get the following results, as expected:
User 2 requests to come in and gets the following result,Fall short of expectations:
Therefore, when using a tool like ThreadLocal to store some data, you need to take special care to explicitly clear the set data after the code runs, as shown in the following example:
@GetMapping("right") public Map right(@RequestParam("userId") Integer userId) { String beforeUser = Thread.currentThread().getName() + ":" + currentUser.get(); currentUser.set(userId); try { String afterUser = Thread.currentThread().getName() + ":" + currentUser.get(); Map map = new HashMap(); map.put("before", beforeUser); map.put("after", afterUser); return map; } finally {// Remove the ThreadLocal data from the finally block to ensure that the data is not currentUser.remove(); }}Copy the code
8. Neglect the return and break of switch
This should not be a bad thing, strictly speaking, but when you write code, some of your friends tend to neglect it.
/* * public account: Public class SwitchTest {public static void main(String[] args) throws InterruptedException {public static void main(String[] args) throws InterruptedException { System.out.println("testSwitch result: "+testSwitch("1")); } private static String testSwitch(String key) { switch (key) { case "1": System.out.println("1"); case "2": System.out.println(2); return "2"; case "3": System.out.println("3"); Default: system.out.println (" return default value "); return "4"; }}}Copy the code
Output result:
TestSwitch 1 2 testSwitch result: 2Copy the code
The switch matches all the way down the case until a return or break is encountered. So, when writing code, pay attention to whether you want the result.
9. Arrays
9.1 Primitive types cannot be used as arguments to the arrays.aslist method, otherwise they will be treated as arguments.
public class ArrayAsListTest { public static void main(String[] args) { int[] array = {1, 2, 3}; List list = Arrays.asList(array); System.out.println(list.size()); }}Copy the code
Running results:
1
Copy the code
Arrays.aslist:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
Copy the code
9.2 Arrays.asList The List returned by arrays. asList does not support addition or deletion operations.
public class ArrayAsListTest { public static void main(String[] args) { String[] array = {"1", "2", "3"}; List list = Arrays.asList(array); list.add("5"); System.out.println(list.size()); }}Copy the code
Running results:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at object.ArrayAsListTest.main(ArrayAsListTest.java:11)
Copy the code
AsList doesn’t return the java.util.ArrayList we’d expect, but ArrayList, the inner class of Arrays. The inner class’s ArrayList does not implement add, but the parent class’s implementation of add, which throws an exception.
9.3 When using Arrays. AsLis, the modification to the original array will affect the List we obtained
public class ArrayAsListTest { public static void main(String[] args) { String[] arr = {"1", "2", "3"}; List list = Arrays.asList(arr); arr[1] = "4"; System.out.println(" primitive Array "+ array.toString (arr)); System.out.println("list array "+ list); }}Copy the code
Running results:
[1, 4, 3] list array [1, 4, 3]Copy the code
ArrayList(Array.asList (arr)); ArrayList(array.asList (arr));
10. Arraylist.toarray () strong turn pit
public class ArrayListTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(1); List.add (" public account: a little boy picking up snails "); String[] array21 = (String[])list.toArray(); // type conversion exception}}Copy the code
Because Object is returned, a ClassCastException occurs when an Object array is forcibly converted to a String array. The solution is to override toArray(T[] a) with toArray().
String[] array1 = list.toArray(new String[0]); // It works properlyCopy the code
11. Several pits for abnormal use
11.1 Don’t lose your stack exception information
public void wrong1(){ try { readFile(); }} public void wrong2(){try {readFile(); } catch (IOException e) {log.error(' LLDB read error, {} ', LLDB read message ()); }}Copy the code
The correct way to print, it should be
public void right(){ try { readFile(); } catch (IOException e) {log. Error (" file read error ", e); }}Copy the code
11.2 Do not define exceptions as static variables
public void testStaticExeceptionOne{ try { exceptionOne(); } catch (Exception ex) { log.error("exception one error", ex); } try { exceptionTwo(); } catch (Exception ex) { log.error("exception two error", ex); }} private void exceptionOne() {throw Exceptions.ONEORTWO; } private void exceptionTwo() {// Throw Exceptions.ONEORTWO; }Copy the code
ExceptionTwo exceptionOne exceptionTwo exceptionOne Exception The proper use of methods, not static variables, should be a new one.
Private void exceptionTwo() {throw new BusinessException(" BusinessException ", 0001); }Copy the code
11.3 Do not use e.printStackTrace() in production environments;
public void wrong(){ try { readFile(); } catch (IOException e) {// Production environment do not use it e.printStackTrace(); }}Copy the code
Because it takes up too much memory, it locks up, and the logs are intermixed and hard to read. Correct use is as follows:
// discard e.printStackTrace(); Log. error(" Normal printing of abnormal logs ",e);Copy the code
11.4 What Can I Do if an exception occurs during thread Pool Submission?
public class ThreadExceptionTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); Executorservice.submit (()-> {if (I == 5) {system.out.println (" executorservice.out.println "); throw new RuntimeException("error"); } system.out.println (" thread.currentThread ().getName() "); })); executorService.shutdown(); }}Copy the code
Running results:
Current execution level :pool-1-thread-1 Current execution level: Pool-1-thread-2 Current execution level: Pool-1-thread-3 Current execution level: Pool-1-thread-4 An exception occurs Current execution level :pool-1-thread-7 Current execution level :pool-1-thread-8 Current execution level :pool-1-thread-9 Current execution level :pool-1-thread-10Copy the code
It can be found that if an asynchronous task is submitted to the thread pool using the Submit method, the exception will be swallowed. Therefore, in daily discovery, if there are foreseeable exceptions, we can take the following solutions to deal with:
- 1. Catch the exception in the task code try/catch
- 2. Use the Future object’s GET method to receive the thrown exception and process it
- 3. Set UncaughtExceptionHandler for the worker thread and handle exceptions in uncaughtException
- 4. Override the afterExecute method of ThreadPoolExecutor to handle passed exception references
11.5finally rethrows should also be noted
public void wrong() { try { log.info("try"); Throw new RuntimeException("try"); } finally { log.info("finally"); throw new RuntimeException("finally"); }}Copy the code
A method can never have two exceptions, so finally exceptions will override the try exception. Properly used, the finally code block is responsible for its own exception catching and handling.
public void right() { try { log.info("try"); throw new RuntimeException("try"); } finally { log.info("finally"); try { throw new RuntimeException("finally"); } catch (Exception ex) { log.error("finally", ex); }}}Copy the code
JSON serialization,Long converted to Integer!
public class JSONTest { public static void main(String[] args) { Long idValue = 3000L; Map<String, Object> data = new HashMap<>(2); data.put("id", idValue); Data. put("name", "little boy picking up snails "); Assert.assertEquals(idValue, (Long) data.get("id")); String jsonString = JSON.toJSONString(data); // Long is converted to Integer Map Map = json.parseObject (jsonString, map.class); Object idObj = map.get("id"); System.out.println(" Is deserialization of type Integer: "+(idObj instanceof Integer)); Assert.assertEquals(idValue, (Long) idObj); }}Copy the code
Running results:
The type of the Exception in the thread "main" deserialization whether Integer: true. Java lang. ClassCastException: java.lang.Integer cannot be cast to java.lang.Long at object.JSONTest.main(JSONTest.java:24)Copy the code
Note that the Josn string does not have a Long type when serialized as a Json string. If the number received by Object is less than the maximum value of Interger, it is converted to an Integer.
13. Use Executors to declare an OOM problem for the thread pool, newFixedThreadPool
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executor.execute(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//do nothing
}
});
}
Copy the code
IDE specifies JVM arguments: -xmx8m -xMS8m:
Running results:
NewFixedThreadPool uses an unbounded queue.
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { ... /** * Creates a {@code LinkedBlockingQueue} with a capacity of * {@link Integer#MAX_VALUE}. */ public LinkedBlockingQueue() { this(Integer.MAX_VALUE); }... }Copy the code
The newFixedThreadPool thread pool has a fixed number of core threads and uses a nearly unbounded LinkedBlockingQueue to block the queue. When the core thread is used up, the task will be queued to the blocking queue. If the task is executed for a long time and is not released, more and more tasks will pile up to the blocking queue. Finally, the memory usage of the machine keeps soaring, causing THE JVM OOM.
14. Large files or reading too much data from the database into memory at one time may cause OOM problems
You will get an OOM if you load a large file or database into memory at one time. So, why query DB database, generally recommended batch.
To read Files, which are usually not too large, use files.readalllines (). Why is that? Because it is directly read files into memory, it is estimated that the OOM will not use this, can look at its source:
public static List<String> readAllLines(Path path, Charset cs) throws IOException { try (BufferedReader reader = newBufferedReader(path, cs)) { List<String> result = new ArrayList<>(); for (;;) { String line = reader.readLine(); if (line == null) break; result.add(line); } return result; }}Copy the code
If the file is too large, you can use files.line () to read the file on demand
15. Query and update/delete concurrency consistency issues
In everyday development, this kind of code implementation is often seen: checking to see if there are any remaining tickets available, and then updating the ticket margin.
If (selectIsAvailable(ticketId){1, deleteTicketById(ticketId)}else{return "No cash available"}Copy the code
If it is executed concurrently, it is likely to have a problem, and should take advantage of the atomicity of database updates/deletes, as follows:
If (deleteAvailableTicketById (ticketId) = = 1) {1, to increase operating cash} else {return "no available cash voucher"}Copy the code
16. The database is stored using UTF-8, and the pits with abnormal expressions are inserted
The lower version of MySQL supports UTF8 encoding with a maximum length of 3 bytes. However, it takes 4 bytes to store an emoji. So if you use UTF8 to store an emoji, SQLException: Incorrect String value: ‘\xF0\x9F\x98\x84’ for column, so generally use UTF8MB4 encoding to store emoticons.
17. Pit where spring transactions did not take effect
In daily business development, we often deal with transactions, and transaction failure mainly has the following scenarios:
- The underlying database engine does not support transactions
- Used in methods that are not public
- The rollbackFor property is incorrectly set
- This class method is called directly
- The exception is the try… Catch eats, causing the transaction to fail.
Among them, the most easy pit is the latter two, annotated transaction method to this class method directly call, pseudo-code is as follows:
Public class TransactionTest{public void A(){// Insert A data // call method B(local class call, transaction invalid) B(); } @transactional public void B(){// Insert data}}Copy the code
If an exception is caught, the transaction will fail.
@transactional public void method(){try{// insertA(); UpdateB (); }catch(Exception e){logger.error(" if the Exception is caught, your transaction is invalid ",e); }}Copy the code
18. When reflection encounters a method overload pit
Public class ReflectionTest {private void score(int score) {system.out.println ("int ") grade =" + score); } private void score(Integer score) { System.out.println("Integer grade =" + score); } public static void main(String[] args) throws Exception { ReflectionTest reflectionTest = new ReflectionTest(); reflectionTest.score(100); reflectionTest.score(Integer.valueOf(100)); reflectionTest.getClass().getDeclaredMethod("score", Integer.TYPE).invoke(reflectionTest, Integer.valueOf("60")); reflectionTest.getClass().getDeclaredMethod("score", Integer.class).invoke(reflectionTest, Integer.valueOf("60")); }}Copy the code
Running results:
int grade =100
Integer grade =100
int grade =60
Integer grade =60
Copy the code
If integer.valueof (100) is passed without reflection, it takes an Integer overload. However, reflection does not determine method overloading based on the input parameter type, but on the method name and parameter type passed in when reflection obtains the method.
getClass().getDeclaredMethod("score", Integer.class)
getClass().getDeclaredMethod("score", Integer.TYPE)
Copy the code
Mysql > select * from timestamp
Timestamp may be automatically updated to the current time, see demo
CREATE TABLE `t` (
`a` int(11) DEFAULT NULL,
`b` timestamp NOT NULL,
`c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Copy the code
We can see that column C has CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, so column C is updated to the current time as the record is updated. But column B is also updated to the current time as records are updated to.
You can use datetime instead, assign now() when you need to update to the current time, or change explicit_defaults_for_timestamp in mysql.
20. Time zone pit in mysql8 database
We have updated mysql database earlier, the new version is 8.0.12. Mysql8 now() is 8 hours later than Beijing time, because mysql8 defaults to the TIME in the US
jdbc:mysql://localhost:3306/test? useUnicode=true&characterEncoding=UTF-8& serverTimezone=Asia/ShanghaiCopy the code
21. Linear security problem of SimpleDateFormat
public class SimpleDateFormatTest { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000)); while (true) { threadPoolExecutor.execute(() -> { String dateString = sdf.format(new Date()); try { Date parseDate = sdf.parse(dateString); String dateString2 = sdf.format(parseDate); System.out.println(dateString.equals(dateString2)); } catch (ParseException e) { e.printStackTrace(); }}); }}Copy the code
Running results:
Exception in thread "pool-1-thread-49" java.lang.NumberFormatException: For input string: "5151." at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Long.parseLong(Long.java:589) at java.lang.Long.parseLong(Long.java:631) at java.text.DigitList.getLong(DigitList.java:195) at java.text.DecimalFormat.parse(DecimalFormat.java:2051) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.example.demo.SimpleDateFormatTest.lambda$main$0(SimpleDateFormatTest.java:19) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Exception in thread "pool-1-thread-47" java.lang.NumberFormatException: For input string: "5151." at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Long.parseLong(Long.java:589) at java.lang.Long.parseLong(Long.java:631) at java.text.DigitList.getLong(DigitList.java:195) at java.text.DecimalFormat.parse(DecimalFormat.java:2051) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.example.demo.SimpleDateFormatTest.lambda$main$0(SimpleDateFormatTest.java:19) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)Copy the code
Global variable SimpleDateFormat, in the concurrent case, security issues.
- SimpleDateFormat inherits DateFormat
- A global Calendar variable is maintained in the DateFormat class
- Sdf.parse (dateStr) and sdf.format(date) are both stored by Calendar references.
- If SimpleDateFormat is static and globally shared, Calendar references are also shared.
- And since There is no thread-safe mechanism inside Calendar, the globally shared SimpleDateFormat is not linearly safe.
See and thank you
- 100 Common Java business development errors
The public,
Welcome to the public number: a little boy picking up snails