Parmi les fosses sur lesquelles j'ai marché ces années-là, combien avez-vous marché ?

Travailler ensemble pour créer et grandir ensemble ! C'est le 30ème jour de ma participation au "Nuggets Daily New Plan · August Update Challenge", cliquez pour voir les détails de l'événement

Contexte

Je travaille dans l'industrie du logiciel depuis de nombreuses années et j'ai franchi des étapes importantes dans la production et le développement. Je vais le partager avec vous aujourd'hui. J'espère que vous pourrez accumuler une expérience pertinente et éviter les pièges à répétition.

Boîte automatique et déballage d'entiers

par exemple

 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 : En ce qui concerne les problèmes d'unboxing et de boxing d'Integer, il vous suffit de maîtriser la plage de cache par défaut d'Integer, puis ces questions peuvent être facilement résolues. Integer est une classe wrapper de type int. Lorsque la valeur int est affectée à Integer, elle utilise la méthode valueOf(int i) pour encadrer automatiquement. Par défaut, la plage de cache cache[] est [-128,127],

String's == et problème euals

En ce qui concerne la question de String's == et égal, lors de l'entretien de stage cette année-là, la première question était celle-ci, et le résultat était faux, et l'intervieweur a dit de sortir et de tourner à droite. Donc, le souvenir de cela est encore frais.

par exemple

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

Remarque : En ce qui concerne le == et l'égal de String, vous devez maîtriser le modèle de mémoire de String, afin que les questions ci-dessus puissent recevoir une réponse précise et que vous n'ayez pas peur de marcher sur la fosse.

problème de perte de précision

Le problème de perte de précision se reflète principalement dans les données de type virgule flottante et BigDecimal, comme dans l'exemple suivant

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

La sortie est : 0,100000024, car la représentation binaire de 0,1 est une boucle infinie. Les ressources de l'ordinateur étant limitées, il n'y a aucun moyen de représenter précisément 0,1 en binaire, il ne peut être représenté que par une "valeur approchée", c'est-à-dire qu'en cas de précision limitée, maximiser le nombre binaire proche de 0,1 entraîner un manque de précision.

Vous pouvez donc utiliser le type BigDecimal pour fonctionner, mais BigDecimal a également le problème de la perte de précision

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

L'exécution du programme rapportera l'erreur suivante :

image.png

En effet, une ArithmeticException sera levée si le quotient est un nombre décimal infini (0,333 ...) pendant l'opération de division et que le résultat de l'opération devrait être un nombre exact, donc BigDecimal effectue la multiplication appropriée et Lors de la division, être veillez à conserver les décimales. Le code ci-dessus peut être modifié comme suit.

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

passage par valeur et passage par référence

Exemple de descriptif :

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

Explication : il est nécessaire de comprendre le passage par valeur et le passage par référence, il est alors facile de générer la réponse à l'exemple ci-dessus.

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

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

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 est également une question fréquemment posée dans les interviews, mais vous devez libérer manuellement l'objet pendant l'utilisation, sinon une fuite de mémoire sera signalée.

Un exemple typique de fuite de mémoire

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

}
复制代码

illustrer:

Lorsque l'application App est chargée et exécutée par Tomcat, lorsque la demande passe par le UserInfoFilter, une entrée sera insérée dans la carte privée du thread d'exécution, et cette entrée a une référence forte à l'objet UserInfo. , bien que Tomcat ait publié une référence à l'objet servlet/filtre, mais le ThreadLocalMap privé du thread a toujours une référence à l'objet UserInfo, et l'objet UserInfo est accessible, de sorte que la classe UserInfo est également accessible. , la même classe sera toujours chargée la prochaine fois que l'application sera chargée. Chargez-la à nouveau. Au fur et à mesure que l'application est chargée et déchargée dans Tomcat, les classes de JVMPermGen s'accumulent de plus en plus et provoquent finalement java.lang.OutOfMemoryError : Espace PermGen.

Résumer

Cet article répertorie certains enregistrements de pit-stepping courants. J'espère que vous pourrez accumuler de l'expérience et éviter les pit-stepping répétés. Si vous avez des questions, n'hésitez pas à nous faire part de vos commentaires.

Je suppose que tu aimes

Origine juejin.im/post/7136442438389858311
conseillé
Classement