De los fosos que pisé en esos años, ¿cuántos pisaste tú?

¡Trabajar juntos para crear y crecer juntos! Este es el día 30 de mi participación en el "Nuggets Daily New Plan·August Update Challenge", haz clic para ver los detalles del evento

antecedentes

He estado en la industria del software durante muchos años y he pisado pozos relevantes en la producción y el desarrollo. Lo compartiré con ustedes hoy. Espero que puedan acumular experiencia relevante y evitar trampas repetidas.

Autoboxing y unboxing de enteros

por ejemplo

 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);
   }
复制代码

Descripción: con respecto a los problemas del desempaquetado y empaquetado de Integer, solo necesita dominar el rango de caché predeterminado de Integer, luego estas preguntas se pueden responder fácilmente. Integer es una clase contenedora de tipo int. Cuando el valor int se asigna a Integer, utilizará el método valueOf(int i) para encuadrar automáticamente. De manera predeterminada, el rango de caché cache[] es [-128,127],

Problema de == y euals de String

Con respecto a la pregunta de String == e iguales, en la entrevista de prácticas de ese año, la primera pregunta fue esta, y el resultado fue incorrecto, y el entrevistador dijo que saliera y doblara a la derecha. Así que el recuerdo de esto todavía está fresco.

por ejemplo

 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()));
    }
复制代码

Nota: Con respecto a == e igual a String, debe dominar el modelo de memoria de String, para que las preguntas anteriores puedan responderse con precisión y no tenga miedo de pisar el pozo.

problema de perdida de precision

El problema de la pérdida de precisión se refleja principalmente en el tipo de punto flotante y los datos BigDecimal, como el siguiente ejemplo

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

La salida es: 0.100000024, porque la representación binaria de 0.1 es un bucle infinito. Dado que los recursos de la computadora son limitados, no hay forma de representar con precisión 0.1 en binario. Solo se puede representar mediante un "valor aproximado", es decir, en el caso de precisión limitada, maximizar el número binario cercano a 0.1 resultar en una falta de precisión Caso.

Entonces, puede usar el tipo BigDecimal para operar, pero BigDecimal también tiene el problema de la pérdida de precisión.

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

Ejecutar el programa reportará el siguiente error:

foto.png

Esto se debe a que se lanzará una ArithmeticException si el cociente es un decimal infinito (0,333...) durante la operación de división y se espera que el resultado de la operación sea un número exacto, por lo que BigDecimal realiza la multiplicación pertinente y al dividir, se asegúrese de mantener los lugares decimales. El código anterior se puede modificar de la siguiente manera.

   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);
复制代码

paso por valor y paso por referencia

Ejemplo de descripción:

 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";
}
复制代码

Explicación: necesita comprender el paso por valor y el paso por referencia, entonces es fácil generar la respuesta al ejemplo anterior.

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

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

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 también es una pregunta frecuente en las entrevistas, pero debe liberar manualmente el objeto durante el uso; de lo contrario, se informará una pérdida de memoria.

Un ejemplo típico de pérdida de memoria

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);
    }

}
复制代码

ilustrar:

Cuando Tomcat carga y ejecuta la aplicación de la aplicación, cuando la solicitud pasa por UserInfoFilter, se insertará una entrada en el mapa privado del subproceso de ejecución, y esta entrada tiene una fuerte referencia al objeto UserInfo. , aunque Tomcat ha publicado una referencia al objeto servlet/filtro, pero el ThreadLocalMap privado del subproceso todavía tiene una referencia al objeto UserInfo, y se puede acceder al objeto UserInfo, por lo que también se puede acceder a la clase UserInfo. Porque Tomcat ha realizado el aislamiento de clases , la misma clase se seguirá cargando la próxima vez que se cargue la aplicación. Cárguela de nuevo. A medida que la aplicación se carga y descarga una y otra vez en Tomcat, las clases de JVMPermGen se acumulan cada vez más y finalmente provocan java.lang.OutOfMemoryError : espacio PermGen.

Resumir

Este artículo enumera algunos registros comunes de pit-step. Espero que pueda acumular experiencia y evitar repetir pit-step. Si tiene alguna pregunta, no dude en enviarnos sus comentarios.

Supongo que te gusta

Origin juejin.im/post/7136442438389858311
Recomendado
Clasificación