Maison >Java >javaDidacticiel >Comment optimiser le code Java et quels conseils pratiques sont disponibles au travail ?

Comment optimiser le code Java et quels conseils pratiques sont disponibles au travail ?

王林
王林avant
2023-05-09 08:16:071263parcourir

1. Réduisez la visibilité des membres de la classe et des méthodes

Exemple : S'il s'agit d'une méthode privée, supprimez-la si vous souhaitez la supprimer

S'il s'agit d'un public. >service ou une variable membre publique, supprimez-la sans trop réfléchir. publicservice方法,或者一个public的成员变量,删除一下,不得思考很多。

2.使用位移操作替代乘除法

计算机是使用二进制表示的,位移操作会极大地提高性能。

> 右移相当于除以 2;

>>> 无符号右移相当于除以 2,但它会忽略符号位,空位都以 0 补齐。

a = val << 3;
b = val >> 1;

3.尽量减少对变量的重复计算

我们知道对方法的调用是有消耗的,包括创建栈帧、调用方法时保护现场,恢复现场等。

//反例
for (int i = 0; i < list.size(); i++) {
  System.out.println("result");
}
//正例
for (int i = 0, length = list.size(); i < length; i++) {
  System.out.println("result");
}

list.size()很大的时候,就减少了很多的消耗。

4.不要捕捉RuntimeException

RuntimeException 不应该通过 catch 语句去捕捉,而应该使用编码手段进行规避。

如下面的代码,list 可能会出现数组越界异常。

是否越界是可以通过代码提前判断的,而不是等到发生异常时去捕捉。

提前判断这种方式,代码会更优雅,效率也更高。

public String test1(List<String> list, int index) {
    try {
        return list.get(index);
    } catch (IndexOutOfBoundsException ex) {
        return null;
    }
}
//正例
public String test2(List<String> list, int index) {
    if (index >= list.size() || index < 0) {
        return null;
    }
    return list.get(index);
}

5.使用局部变量可避免在堆上分配

由于堆资源是多线程共享的,是垃圾回收器工作的主要区域,过多的对象会造成 GC 压力,可以通过局部变量的方式,将变量在栈上分配。这种方式变量会随着方法执行的完毕而销毁,能够减轻 GC 的压力。

6.减少变量的作用范围

注意变量的作用范围,尽量减少对象的创建。

如下面的代码,变量 s 每次进入方法都会创建,可以将它移动到 if 语句内部。

public void test(String str) {
    final int s = 100;
    if (!StringUtils.isEmpty(str)) {
        int result = s * s;
    }
}

7.懒加载策略

尽量采用懒加载的策略,在需要的时候才创建

String str = "月伴飞鱼";
if (name == "公众号") {
  list.add(str);
}
if (name == "公众号") {
  String str = "月伴飞鱼";
  list.add(str);
}

8.访问静态变量直接使用类名

使用对象访问静态变量,这种方式多了一步寻址操作,需要先找到变量对应的类,再找到类对应的变量。

 // 反例
int i = objectA.staticMethod();
 // 正例
int i = ClassA.staticMethod();

9.字符串拼接使用StringBuilder

字符串拼接,使用 StringBuilder 或者 StringBuffer,不要使用 + 号。

//反例
public class StringTest {
    @Test
    public void testStringPlus() {
        String str = "111";
        str += "222";
        str += "333";
        System.out.println(str);
    }
}
//正例
public class TestMain {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("111");
        sb.append("222");
        sb.append(333);
        System.out.println(sb.toString());
    }
}

10.重写对象的HashCode

重写对象的HashCode,不要简单地返回固定值

有同学在开发重写 HashCode 和 Equals 方法时,会把 HashCode 的值返回固定的 0,而这样做是不恰当的

当这些对象存入 HashMap 时,性能就会非常低,因为 HashMap 是通过 HashCode 定位到 Hash 槽,有冲突的时候,才会使用链表或者红黑树组织节点,固定地返回 0,相当于把 Hash 寻址功能无效了。

11.HashMap等集合初始化

HashMap等集合初始化的时候,指定初始值大小

这样的对象有很多,比如 ArrayList,StringBuilder 等,通过指定初始值大小可减少扩容造成的性能损耗。

初始值大小计算:

Comment optimiser le code Java et quels conseils pratiques sont disponibles au travail ?

12.循环内创建对象引用

循环内不要不断创建对象引用

//反例
for (int i = 1; i <= size; i++) {
    Object obj = new Object();    
}
//正例
Object obj = null;
for (int i = 0; i <= size; i++) {
    obj = new Object();
}

第一种会导致内存中有size个Object对象引用存在,size很大的话,就耗费内存了

13.遍历Map 使用 EntrySet 方法

使用 EntrySet 方法,可以直接返回 set 对象,直接拿来用即可;而使用 KeySet 方法,获得的是key 的集合,需要再进行一次 get 操作,多了一个操作步骤,所以更推荐使用 EntrySet 方式遍历 Map。

Set<Map.Entry<String, String>> entryseSet = nmap.entrySet();
for (Map.Entry<String, String> entry : entryseSet) {
    System.out.println(entry.getKey()+","+entry.getValue());
}

14.不要在多线程下使用同一个 Random

Random 类的 seed 会在并发访问的情况下发生竞争,造成性能降低,建议在多线程环境下使用 ThreadLocalRandom 类。

 public static void main(String[] args) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        Thread thread1 = new Thread(()->{
            for (int i=0;i<10;i++){
                System.out.println("Thread1:"+threadLocalRandom.nextInt(10));
            }
        });
        Thread thread2 = new Thread(()->{
            for (int i=0;i<10;i++){
                System.out.println("Thread2:"+threadLocalRandom.nextInt(10));
            }
        });
        thread1.start();
        thread2.start();
    }

15.自增推荐使用LongAddr

自增运算可以通过 synchronized volatile 的组合来控制线程安全,或者也可以使用原子类(比如 AtomicLong)。

后者的速度比前者要高一些,AtomicLong 使用 CAS 进行比较替换,在线程多的情况下会造成过多无效自旋,可以使用 LongAdder 替换 AtomicLong 进行进一步的性能提升。

public class Test {
    public int longAdderTest(Blackhole blackhole) throws InterruptedException {
        LongAdder longAdder = new LongAdder();
        for (int i = 0; i < 1024; i++) {
            longAdder.add(1);
        }
        return longAdder.intValue();
    }
}

16.程序中要少用反射

反射的功能很强大,但它是通过解析字节码实现的,性能就不是很理想。

现实中有很多对反射的优化方法,比如把反射执行的过程(比如 Method)缓存起来,使用复用来加快反射速度。

Java 7.0 之后,加入了新的包java.lang.invoke

2. Utilisez les opérations de déplacement au lieu de la multiplication et de la division🎜🎜Les ordinateurs utilisent la représentation binaire et les opérations de déplacement amélioreront considérablement les performances. 🎜🎜> Le décalage à droite équivaut à une division par 2 ; 🎜🎜>>> ; bit de signe, les postes vacants sont remplis de 0. 🎜rrreee🎜3. Minimisez les calculs répétés de variables🎜🎜Nous savons que l'appel de méthodes est coûteux, notamment la création de cadres de pile, la protection de la scène lors de l'appel de méthodes, la restauration de la scène, etc. 🎜rrreee🎜Lorsque list.size() est très volumineux, cela réduit beaucoup la consommation. 🎜🎜4. N'attrapez pas RuntimeException🎜🎜RuntimeException ne doit pas être intercepté via des instructions catch, mais doit être évité en utilisant des méthodes de codage. 🎜🎜Comme le montre le code ci-dessous, une exception hors limites du tableau peut se produire dans la liste. 🎜🎜Le fait qu'il soit hors limites peut être jugé à l'avance via le code, au lieu d'attendre d'attraper une exception lorsqu'elle se produit. 🎜🎜Si vous jugez cette méthode à l'avance, le code sera plus élégant et efficace. 🎜rrreee🎜5. Utilisez des variables locales pour éviter l'allocation sur le tas🎜🎜Étant donné que les ressources du tas sont partagées par plusieurs threads et constituent la zone principale dans laquelle fonctionne le garbage collector, trop d'objets entraîneront une pression GC. Vous pouvez utiliser des variables locales pour allouer. variables sur la pile. De cette façon, les variables seront détruites une fois l’exécution de la méthode terminée, ce qui peut réduire la pression sur le GC. 🎜🎜6. Réduisez la portée des variables🎜🎜Faites attention à la portée des variables et minimisez la création d'objets. 🎜🎜Comme le montre le code ci-dessous, la variable s sera créée à chaque fois que la méthode est saisie et elle peut être déplacée dans l'instruction if. 🎜rrreee🎜7. Stratégie de chargement paresseux🎜🎜Essayez d'utiliser une stratégie de chargement paresseux et ne la créez que lorsque cela est nécessaire🎜rrreee🎜8 Pour accéder aux variables statiques, utilisez directement le nom de la classe🎜🎜Utilisez des objets pour accéder aux variables statiques. étape supplémentaire. Pour les opérations d'adresse, vous devez d'abord trouver la classe correspondant à la variable, puis trouver la variable correspondant à la classe. 🎜rrreee🎜9. Utilisez StringBuilder pour l'épissage de chaînes🎜🎜Pour l'épissage de chaînes, utilisez StringBuilder ou StringBuffer, n'utilisez pas le signe +. 🎜rrreee🎜10. Réécrivez le HashCode de l'objet🎜🎜Réécrivez le HashCode de l'objet, ne renvoyez pas simplement une valeur fixe🎜🎜Certains étudiants renverront la valeur HashCode à une valeur fixe de 0 lors du développement des méthodes de remplacement HashCode et Equals. , et c'est inapproprié de le faire🎜🎜Lorsque ces objets sont stockés dans HashMap, les performances seront très faibles, car HashMap localise l'emplacement de hachage via HashCode. En cas de conflit, il utilisera une liste chaînée ou un. arbre rouge-noir pour organiser les nœuds et leur renvoyer fixement 0, ce qui équivaut à invalider la fonction d'adressage Hash. 🎜🎜11. Initialisation de collections telles que HashMap🎜🎜Lors de l'initialisation de collections telles que HashMap, spécifiez la taille de la valeur initiale🎜🎜Il existe de nombreux objets de ce type, tels que ArrayList, StringBuilder, etc. En spécifiant la taille de la valeur initiale, la perte de performances causée par l’expansion peut être réduite. 🎜🎜🎜Calcul de la taille de la valeur initiale : 🎜🎜🎜Optimisation pratique du code dans le travail Java Quoi sont les techniques🎜🎜12. Créer des références d'objet dans une boucle🎜
🎜Ne créez pas continuellement des références d'objet dans une boucle🎜
rrreee🎜La première méthode fera exister des références d'objet de taille dans le mémoire, taille S'il est trop grand, il consommera de la mémoire🎜🎜13. Utilisez la méthode EntrySet pour parcourir la Map🎜🎜En utilisant la méthode EntrySet, vous pouvez directement renvoyer l'objet set et l'utiliser directement tout en utilisant la méthode KeySet, vous obtenez une collection de clés, qui doivent être exécutées. L'exécution d'une opération d'obtention nécessite une étape d'opération supplémentaire, il est donc plus recommandé d'utiliser la méthode EntrySet pour parcourir la carte. 🎜rrreee🎜14. N'utilisez pas le même Random dans plusieurs threads🎜🎜La graine de la classe Random sera en compétition lors d'un accès simultané, ce qui entraînera une réduction des performances. Il est recommandé d'utiliser la classe ThreadLocalRandom dans un environnement multithread. 🎜rrreee🎜15. Il est recommandé d'utiliser LongAddr pour l'auto-incrémentation🎜🎜L'opération d'auto-incrémentation peut contrôler la sécurité des threads grâce à la combinaison de synchronisé et volatile , ou vous peut également utiliser des classes atomiques (telles que AtomicLong ). 🎜🎜Ce dernier est plus rapide que le premier. AtomicLong utilise CAS pour la comparaison et le remplacement. Lorsqu'il y a de nombreux threads, cela entraînera trop de tours invalides. Vous pouvez utiliser LongAdder pour remplacer AtomicLong pour améliorer davantage les performances. . 🎜rrreee🎜16. Utilisez moins de réflexion dans votre programme🎜🎜La réflexion est très puissante, mais elle est implémentée en analysant le bytecode, donc les performances ne sont pas très idéales. 🎜🎜En réalité, il existe de nombreuses méthodes d'optimisation de la réflexion, telles que la mise en cache du processus d'exécution de la réflexion (comme la méthode) et l'utilisation du multiplexage pour accélérer la réflexion. 🎜🎜Après Java 7.0, un nouveau package java.lang.invoke a été ajouté et une nouvelle instruction de bytecode JVM Invocationdynamic a été ajoutée pour prendre en charge l'accès direct aux méthodes cibles via des chaînes du niveau JVM. Effectuez l'appel. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer