Of the pits I stepped on in those years, how many did you step on?

Work together to create and grow together! This is the 30th day of my participation in the "Nuggets Daily New Plan·August Update Challenge", click to view the details of the event

background

I have been engaged in the software industry for many years, and I have stepped on relevant pits in production and development. I will share it with you today. I hope you can accumulate relevant experience and avoid repeated pitfalls.

Integer autoboxing and unboxing

for example

 public static void main(String[] args) 
    {
        Integer x1 = 10;
        Integer x2 = 10;
        boolean r = x1==x2;
        System.out.println("x1==x2:" + r);

        Integer y1 = 300;
        Integer y2 = 300;
        boolean r1 = y1==y2;
        System.out.println("y1==y2:" + r1);

        Integer m1 = new Integer(300);
        Integer m2 = new Integer(300);
        boolean r2 = m1==m2;
        System.out.println("m1==m2:" + r2);
        
        Integer n1 = new Integer(20);
        Integer n2 = new Integer(20);
        boolean r3 = n1.intValue()==n2.intValue();
        System.out.println("n1==n2:" + r3);
   }
复制代码

Description: Regarding the problems of Integer unboxing and boxing, you only need to master the default cache range of Integer, then these questions can be easily answered. Integer is a wrapper class of int type. When the int value is assigned to Integer, it will use the valueOf(int i) method to automatically box. By default, the cache[] cache range is [-128,127],

String's == and euals problem

Regarding the question of String's == and equal, in the internship interview that year, the first question was this, and the result was wrong, and the interviewer said to go out and turn right. So the memory of this is still fresh.

for example

 public static void main(String[] args)
    {
        //
        String s1 = "abc";
        String s2 = "abc";
        System.out.println("s1 == s2 : " + (s1 == s2));
        
        String s3 = new String("abc");
        String s4 = new String("abc");
        System.out.println("s3 == s4 : " + (s3 == s4));
        
        String s5 = "ab" + "cd";
        String s6 = "abcd";
        System.out.println("s5 = s5 : " + (s5 == s6));
        
        String str1 = "ab"; 
        String str2 = "cd";
        String str3 = str1 + str2;
        String str4 = "abcd";
        System.out.println("str4 = str3 : " + (str3 == str4));
        
        String str6 = "b";
        String str7 = "a" + str6;
        String str8 = "ab";
        System.out.println("str7 = str8 : " + (str7 == str8));
        
        //常量话
        final String str9 = "b";
        String str10 = "a" + str9;
        String str11 = "ab";
        System.out.println("str9 = str89 : " + (str10 == str11));
        
        String s12="abc";
        String s13 = new String("abc");
        System.out.println("s12 = s13 : " + (s12 == s13.intern()));
    }
复制代码

Note: Regarding the == and equal of String, you need to master the memory model of String, so the above questions can be answered accurately, and you are not afraid of stepping on the pit.

precision loss problem

The problem of loss of precision is mainly reflected in floating-point type and BigDecimal data, such as the following example

public class BigDecimalTest
{
   public static void main(String[] args)
    {
          float a = 1;
          float b = 0.9f;
          System.out.println(a - b);
    }
}
复制代码

The output is: 0.100000024, because the binary representation of 0.1 is an infinite loop. Since the computer's resources are limited, there is no way to accurately represent 0.1 in binary. It can only be represented by an "approximate value", that is, in the case of limited precision, maximizing the binary number close to 0.1 will result in a lack of precision. Case.

So you may use the BigDecimal type to operate, but BigDecimal also has the problem of loss of precision

BigDecimal c = new BigDecimal(1.0);
          BigDecimal d =new BigDecimal(3.0);
          BigDecimal e =c.divide(d);
          System.out.println("e = " + e);
复制代码

Executing the program will report the following error:

picture.png

This is because an ArithmeticException will be thrown if the quotient is an infinite decimal (0.333...) during the division operation and the result of the operation is expected to be an exact number, so BigDecimal performs the relevant multiplication and When dividing, be sure to keep the decimal places. The above code can be modified as follows.

   BigDecimal c = new BigDecimal(1.0);
          BigDecimal d =new BigDecimal(3.0);
          BigDecimal e =c.divide(d,2,RoundingMode.HALF_UP);
          System.out.println("e = " + e);
复制代码

pass-by-value and pass-by-reference

Example description:

 public static void func(int a)
 {
    a=30;
 }
public static void main(String[] args) 
{
  int a=20;
  func(a);
  System.out.println(a);
   
   String c="abc";
   Strinfunc(c);
   System.out.println(c);
}

public static void Strinfunc(String c)
{
   c="c";
}
复制代码

Explanation: Need to understand pass-by-value and pass-by-reference, then it's easy to output the answer to the above example.

值传递:是指在调用函数时,将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,就不会影响到实际参数。

引用传递:是指在调用函数时,将实际参数的地址传递到函数中,那么在函数中对参数进行修改,将会影响到实际参数。

List.subList内存泄露

实例说明:

  public static void main(String[] args)
    {
        List cache = new ArrayList();
        try
        {

            while (true)
            {

                List list = new ArrayList();

                for (int j = 0; j < 100000; j++)
                {
                    list.add(j);
                }

                List sublist = list.subList(0, 1);
                cache.add(sublist);
            }
        }
        finally
        {
            System.out.println("cache size = " + cache.size());

        }
    }
复制代码

说明:这是因为SubList的实例中,保存有原有list对象的强引用,只要sublist没有被jvm回收,那么这个原有list对象就不能gc,即使这个list和其包含的对象已经没有其他任何引用。

for循环中删除元素报错

示例:

  public static void main(String[] args) {
          String test = "1,2,3,4,5,6,7";
          List<String> testList = Arrays.asList(test.split(","));
          for(int i = 0; i < testList.size(); i++){
              String temp = testList.get(i);
                  testList.remove(temp);
          }
      }
复制代码

运行上述的代码报如下错误:

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.remove(AbstractList.java:161)
	at java.util.AbstractList$Itr.remove(AbstractList.java:374)
	at java.util.AbstractCollection.remove(AbstractCollection.java:293)
	at com.skywares.fw.juc.integer.ListTest.main(ListTest.java:14)

复制代码

这是因为fail-fast,即快速失败,它是Java集合的一种错误检测机制。当多个线程对集合(非fail-safe的集合类)进行结构上的改变的操作时,有可能会产生fail-fast机制,这个时候就会抛出ConcurrentModificationException(当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常)。

解决的办法

可以通过迭代器来进行删除、或者采用java8的filter过滤 采用java8的filter来过滤

testList.stream().filter(t -> !t.equals("a")).collect(Collectors.toList());
          System.out.println(testList);
复制代码

SimpleDateformat格式化时间

SimpleDateFormat是大家比较常用的,而且在一般情况下,一个应用中模式都是一样的,所以很多人都喜欢使用如下的方式定义SimpleDateFormat

public class SimpleDateFormatTest
{
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args)
    {
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        System.out.println(simpleDateFormat.format(Calendar.getInstance()
                .getTime()));
    }
}
复制代码

采用这样的定义方式,如果在多线程的情况下,存在很大的安全隐患。

public class SimpleDateFormatTest
{
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");

    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1,
            TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000));
    
    public static void main(String[] args)
    {
        SimpleDateFormatTest simpleDateFormatTest =new SimpleDateFormatTest();
        simpleDateFormatTest.test();
    }

    public void test()
    {
        while (true)
        {
            poolExecutor.execute(new Runnable()
            {
                @Override
                public void run()
                {
                    String dateString = simpleDateFormat.format(new Date());
                    try
                    {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    }
                    catch (ParseException e)
                    {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}
复制代码

运行代码如果出现了false,则说明SimpleDateFormat在多线程不安全,导致结果错误,那么我们如何避免呢?可以采用java8提供的DateTimeFormatter来解决SimpleDateFormat在多线程的不安全问题。

未释放对应的资源,导致内存溢出

示例

ZipFile zipFile = new ZipFile(fileName);
Enumeration<ZipEntry> elments = (Enumeration<ZipEntry>) zipFile.getEntries();
while (elments.hasMoreElements())
{
	System.out.println(elments.nextElement());
}
复制代码

说明:数据库资源,文件资源,Socket资源以及流资源等,这些资源都是有限的,当程序中的代码申请了资源之后,却没有释放,就会导致资源没有被释放,当系统没有充足的资源时,就会导致系统因为资源枯竭而导致服务不可用。

ThreadLocal内存泄漏

ThreadLocal is also a frequently asked question in interviews, but you need to manually release the object during use, otherwise a memory leak will be reported.

A typical memory leak example

static class UserInfoContext{
    public static ThreadLocal<UserInfo> userLocal =new ThreadLocal<>();
}

@WebFilter("/*")
public class UserInfoFilter implements Filter{

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        if(hasLogined(req)){
            UserContext.userLocal.set(extractUserInfo(req));
        }
        chain.doFilter(req, res);
    }

}
复制代码

illustrate:

When the App application is loaded and run by Tomcat, when the request passes through the UserInfoFilter, an Entry will be inserted into the private Map of the execution thread, and this Entry has a strong reference to the UserInfo object. When the App is unloaded, although Tomcat has released A reference to the servlet/filter object, but the thread's private ThreadLocalMap still has a reference to the UserInfo object, and the UserInfo object is reachable, so that the UserInfo class is also reachable. Because Tomcat has done class isolation, the same class will still be loaded the next time the App is loaded. Load it again. As the App is loaded and unloaded again and again in Tomcat, the classes of JVMPermGen accumulate more and more, and finally cause java.lang.OutOfMemoryError: PermGen space.

Summarize

This article lists some common pit-stepping records. I hope you can accumulate experience and avoid repeated pit-stepping. If you have any questions, please feel free to give feedback.

Guess you like

Origin juejin.im/post/7136442438389858311