全网资源采集网站搭建

电脑端+手机端+微信端=数据同步管理

免费咨询热线:13114099110

当前位置: 主页 > 新闻资讯

网站代码优化-(dedecms网站优化公司/seo优化企业模板)

发布时间:2023-01-18 10:09   浏览次数:次   作者:派坤优化

点击上方并选择“设为星标”

优秀的文章,及时交付

作者| 五月仓颉链接| //p/.html推荐|

在我看来,代码优化最重要的作用应该是:避免未知错误。 在线运行代码的过程中,经常会出现很多意想不到的错误,因为在线环境和开发环境差别很大,错误定位到最后往往是一个很小的原因。

但是,为了解决这个错误,我们需要先验证自己,然后打包要替换的class文件,暂停业务重启。 对于一个成熟的项目来说,最后一项其实影响是非常大的,也就是说在这段时间用户无法访问应用。 所以,在写代码的时候,从源头上注意各种细节,权衡使用最优方案,从长远来看会很大程度上避免未知错误,大大减少工作量。

代码优化的目标是:

本文内容部分来源于网络,部分来源于日常工作学习。 当然,这并不重要。 重要的是这些代码优化的细节是否真的有用。 那么这篇文章会长期保持更新。 只要有值得分享的代码优化细节,本文都会不定期更新。 代码优化细节 1.尽量指定类和方法的final修饰符。 无法派生具有 final 修饰符的类。 在Java核心API中,有很多应用final的例子,比如java.lang。 整个班级是最终的。 为类指定final修饰符可以防止类被继承,为方法指定final修饰符可以防止方法被重写。 如果一个类被指定为 final,则该类的所有方法都是 final。 Java 编译器会寻找机会内联所有 final 方法。 内联对于提高Java运行的效率有着重要的作用。 有关详细信息,请参阅 Java 运行时优化。 这可以将性能平均提高 50%。 2.尽可能重复使用对象,尤其是对象的使用。 当发生字符串连接时,应该使用/替换它。 由于Java虚拟机不仅需要时间来生成对象,以后还可能需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象会对程序的性能造成很大的影响。 3.尽量使用局部变量。 调用方法时传递的参数和调用过程中创建的临时变量存储在栈中,速度更快。 其他的变量,比如静态变量和实例变量,都是在堆中创建的,速度更快。 慢的。 另外,栈中创建的变量会随着方法结束而消失,不需要额外的垃圾回收。 4、及时关闭流 在Java编程过程中,进行数据库连接和I/O流操作时要小心。 使用后及时关闭,释放资源。 因为对这些大对象的操作会造成很大的系统开销,稍有不慎就会导致严重的后果。 5. 尽量减少变量的重复计算,弄清一个概念。 调用一个方法,即使方法中只有一条语句,也是有消耗的,包括创建栈帧,保护调用方法时的场景,恢复调用方法时的场景。 因此,例如以下操作:

for (int i = 0; i < list.size(); i++){...}

建议替换为:

for (int i = 0, length = list.size(); i < length; i++){...}

这样在list.size()很大的时候,减少了很多消耗。 6、尽量采用懒加载策略,即只在需要的时候创建。 例如:

String str = "aaa";if (i == 1){  list.add(str);}

建议替换为:

if (i == 1){  String str = "aaa";  list.add(str);}

7. 谨慎使用异常异常不利于性能。 抛出异常首先创建一个新对象。 接口的构造函数调用一个名为()的本地同步方法,()方法检查堆栈并收集调用跟踪信息。 每当抛出异常时,Java 虚拟机都必须调整调用堆栈,因为在处理过程中会创建一个新对象。 异常应该只用于错误处理,不应用于控制程序流程。 8、循环中不要用try...catch...,应该放在最外层。 根据网友提出的意见,我觉得这一点值得商榷,欢迎大家发表意见! 9. 如果可以预估添加内容的长度,则以数组的形式指定底层集合和工具类实现的初始长度,如 ,,,,, 等,例如:

StringBuilder()      // 默认分配16个字符的空间StringBuilder(int size)  // 默认分配size个字符的空间StringBuilder(String str) // 默认分配16个字符+str.length()个字符空间

可以通过类的构造函数来设置它的初始化容量(这里指的不仅仅是上面的),这样可以显着提高性能。 例如表示当前可以保留的字符数。 因为当它达到最大容量时,它会将自己的容量增加到当前容量的2倍加2,每当它达到最大容量时,它就不得不创建一个新的字符数组,然后将旧的字符数组内容复制到一个新字符数组——这是一个非常耗费性能的操作。 试想一下,如果在不指定长度的情况下,可以预估字符数组会存储大约5000个字符,最接近2的5000次方为4096,每次展开加2都没有关系,那么:这将不仅浪费内存空间,而且降低代码运行效率。 因此,为数组实现的底层集合和工具类设置一个合理的初始化容量是没有错的,会带来立竿见影的效果。 不过要注意,像这种数组+链表实现的集合,不要把初始大小设置成你估计的大小,因为一张表上只连接一个对象的可能性几乎为零。 建议将初始大小设置为2的N次方,如果可以估计有2000个元素,可以设置为new(128)或new(256)。 10. 复制大量数据时,使用.() 命令 11. 乘法和除法使用移位操作如:

for (val = 0; val < 100000; val += 5){  a = val * 8;  b = val / 2;}

使用移位操作可以大大提高性能,因为在计算机底层,位的操作是最方便快捷的,所以建议修改为:

for (val = 0; val < 100000; val += 5){  a = val << 3;  b = val >> 1;}

移位操作虽然很快,但可能会使代码难以理解网站代码优化,所以最好加上相应的注释。 12. 不要一直在循环中创建对象引用。 例如:

for (int i = 1; i <= count; i++){    Object obj = new Object();    }

这种做法会导致count对象引用存在于内存中。 如果计数太大,会消耗内存。 建议改成:

Object obj = null;for (int i = 0; i <= count; i++){    obj = new Object();}

这样的话,内存中只有一个对象引用,每次new()时,对象引用指向不同的一个,但内存中只有一份,省事不少。 13、基于效率和类型检查的考虑,尽量使用数组。 仅在无法确定数组大小时才使用它。 尽量使用,,,除非线程安全有要求,否则不建议使用,,。 后三种是使用同步机制造成的 15. 不要把数组声明为final,因为没有意义。 这只是把引用定义为final,数组的内容还是可以随意改变的。 将数组声明为安全漏洞更多的是安全漏洞,这意味着数组可以被外部类更改 16.在适当的场合尽量使用单例。 使用单例可以减轻加载负担,缩短加载时间,提高加载效率,但并不是所有地方都适用。 简单来说,单例主要适用于以下三个方面: 17、尽量避免随意使用静态变量。 要知道当一个对象被定义为变量的变量引用时,那么gc通常不会回收这个对象占用的堆内存,比如:

public class A{    private static B b = new B();  }

此时静态变量b的生命周期与类A相同,如果类A不卸载,引用B指向的B对象将一直保留在内存中,直到程序终止。 ,很多应用服务器都有一个默认的超时时间,一般是30分钟。 当应用服务器需要保存更多的时,如果内存不足,操作系统会将一些数据转移到磁盘中,应用服务器也可能会根据MRU(most used)算法将一些不活跃的转储到磁盘中. 它甚至可能抛出内存不足的异常。 如果要将会话转储到磁盘,则必须首先对其进行序列化,而序列化对象在 Java 中的开销很大。 因此,当不再需要时,应该及时调用()方法清除。 19. 实现接口的集合 比如最常见的for循环,而不是循环遍历。 这是 JDK 向用户推荐的。 JDK API对接口的解释是:接口的实现用于表明它支持快速随机访问。 该接口的主要目的是允许通用算法改变其行为,以便在应用于随机或连续访问列表时能够提供良好的性能。 实践经验表明,如果随机访问实现接口的类实例,使用普通for循环的效率会高于使用循环; 反之,如果是顺序访问,则使用效率会更高。 可以使用类似下面的代码进行判断:

if (list instanceof RandomAccess){    for (int i = 0; i < list.size(); i++){}}else{    Iterator> iterator = list.iterable();    while (iterator.hasNext()){iterator.next()}}

循环的底层实现原理是迭代器,参见Java语法糖1:变长参数与循环原理。 所以后半句“反过来说,如果是顺序访问的话,使用效率会更高”的意思是那些顺序访问的类实例,用一个循环来遍历。 20.使用同步代码块代替同步方法。 这一点在多线程模块中的Lock 一文中已经解释的很清楚了。 除非你能确定整个方法都需要同步,否则尽量使用同步代码块,避免不需要同步的代码也同步,影响代码执行效率。 21、将常量声明为final,并以大写命名,这样编译时就可以将这些内容放入常量池,避免在运行时计算生成的常量的值。 另外,用大写命名常量也可以很容易区分常量和变量 22.不要创建一些不用的对象,不要导入一些不用的类,这是没有意义的,如果局部i的值出现在代码中not used, “爪哇。 util 从来没有用过,那么请删除这些无用的内容 23. 程序运行时避免使用反射 详见。 反射是Java提供给用户的一个非常强大的功能,强大的功能往往意味着低效。

不建议在程序运行过程中特别频繁地使用反射机制。 如果确实需要,建议的做法是在项目启动时通过反射实例化需要加载的类。 将一个对象放入内存----用户只关心在与对端交互时获得最快的响应速度,并不关心对端的项目需要多长时间才能启动。 24.使用数据库连接池和线程池。 两个池都用于重用对象。 前者可以避免频繁打开和关闭连接,后者可以避免频繁创建和销毁线程。 25. Use input and 用缓冲的输入输出流进行IO操作,也就是,,,,可以大大提高IO效率 原理都知道 27. 不要让方法有太多形参网站代码优化,也就是,对外提供的方法。 如果给这些方法过多的形参,主要有两个缺点:至于“太多”指的是多少,3个还是4个。比如我们用JDBC写一个方法,有10个学生信息字段要插入表,这10个参数可以封装在一个实体类中,作为方法参数时 28、字符串变量和字符串常量,先写字符串常量是比较常用的trick,如果有以下代码:

String str = "123";if (str.equals("123")){    ...}

建议修改为:

String str = "123";if ("123".equals(str)){    ...}

这主要是为了避免空指针异常 29.请知道java中if(i == 1)和if(1 == i)没有区别,但是从阅读习惯的角度,建议使用前者,if(i == 1)和if(1 == i)有什么区别吗,都是C/C++开头的。 在C/C++中,if(i == 1)判断条件成立,根据0和非零,0表示假,非0表示真,如果有这样一段代码:

int i = 2;if (i == 1){    ...}else{    ...}

C/C++判断i==1不为真,所以用0表示,为假。 但如果:

int i = 2;if (i = 1){    ...}else{    ...}

万一程序员不小心把if (i == 1)写成if (i = 1),就会出问题。 在if内部给ia赋值1,if判断里面的内容不为0,返回值为true,但显然i为2,比较的值为1,应该返回false。 这种情况在C/C++的开发中很可能会出现,会导致一些无法理解的错误。 因此,为了避免开发者对if语句进行错误的赋值操作,建议将if语句写成:

int i = 2;if (1 == i){    ...}else{    ...}

这样即使开发者不小心写了1 = i,C/C++编译器也能马上查出来,因为我们可以把i赋给1给一个变量,但是我们不能把1赋给i给常量。 但是在Java中,C/C++的if(i = 1)语法是不可能的,因为一旦写了这个语法,Java就会编译报错Type: from int to。 不过,虽然Java的if(i == 1)和if(1 == i)没有语义上的区别,但从阅读习惯上来说,还是推荐前者为佳。 30. 不要在数组上使用() 方法查看在数组上使用() 打印的内容:

public static void main(String[] args){    int[] is = new int[]{1, 2, 3};    System.out.println(is.toString());}

结果是: [I@ 打算打印出数组的内容,但是可能会因为数组引用为空而导致空指针异常。 不过,虽然对于array()来说没有意义,但是对于()来说是可以打印出集合的内容的,因为集合的父类重写了()方法。 31. 不要向下转换超出范围的原始数据类型。 这永远不会得到想要的结果:

public static void main(String[] args){    long l = 12345678901234L;    int i = (int)l;    System.out.println(i);}

我们可能已经预料到了其中的一些,但结果是:解释。 在Java中long是8字节64位,所以234在计算机中的表示应该是:0 0 0 0 0 0010。一个int类型的数据是4字节32位。 从低位取出上面这串二进制数据 前32位是: 0 1 1111 0010 这串二进制表示是十进制的,所以就是我们上面控制台输出的。 从这个例子中,我们可以顺便得出两个结论: 32. 公共集合类中未使用的数据必须及时丢弃。 如果一个集合类是公共的(也就是说,它不是方法中的一个属性),那么集合元素中的数据不会被自动释放,因为总是有对它们的引用。 因此,如果公共集合中的一些数据没有被使用,没有被移除,就会导致公共集合不断增长,导致系统存在内存泄漏的隐患。 33、基本数据类型转字符串,基本数据类型.()是最快的方式,其次是.(data),data+""是基本数据类型转通用最慢的方式。 有3种方式, I 有一种数据类型i,可以用i.(), .(i), i+""三种方式,三种方式效率如何,看一个测试:

public static void main(String[] args){    int loopTime = 50000;    Integer i = 0;    long startTime = System.currentTimeMillis();    for (int j = 0; j < loopTime; j++)    {        String str = String.valueOf(i);    }        System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms");    startTime = System.currentTimeMillis();    for (int j = 0; j < loopTime; j++)    {        String str = i.toString();    }        System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms");    startTime = System.currentTimeMillis();    for (int j = 0; j < loopTime; j++)    {        String str = i + "";    }        System.out.println("i + \"\":" + (System.currentTimeMillis() - startTime) + "ms");}

运行结果为:.(): 11ms

.(): 5 毫秒

i + "": 25ms 所以遇到将基本数据类型转换为 时,优先使用 () 方法。 至于为什么,很简单:三者比较后,很明显2最快,1次之,3最慢。 34.使用最高效的方式遍历Map。 Map中的Key和Value,那么推荐最高效的方式是:

public static void main(String[] args){    HashMap<String, String> hm = new HashMap<String, String>();    hm.put("111", "222");
Set<Map.Entry<String, String>> entrySet = hm.entrySet(); Iterator<Map.Entry<String, String>> iter = entrySet.iterator(); while (iter.hasNext()) { Map.Entry<String, String> entry = iter.next(); System.out.println(entry.getKey() + "\t" + entry.getValue()); }}

如果只想遍历这个Map的key值,那就用Set = hm.(); 它会更合适。 35、建议单独操作资源close()。

try{    XXX.close();    YYY.close();}catch (Exception e){    ...}

建议修改为:

try{    XXX.close();}catch (Exception e){    ...}try{    YYY.close();}catch (Exception e){    ...}

虽然麻烦点,但是可以避免资源泄露。 我们认为如果没有修改过的代码,如果XXX.close()抛出异常,那么就会进入catch块,YYY.close()不会执行,YYY资源也不会被回收。 占用,如果此类代码过多,可能会造成资源句柄泄漏。 改成如下写法后,保证无论线程数多少,XXX和YYY都关闭,线程可以复用。 但是,如果你在项目中使用它,你一定要记得在使用之前或之后使用它。 这是因为上面说的线程池技术是一种线程复用,也就是说在代码运行的时候,一个线程用完了,不会销毁而是等待下一次使用。 让我们看一下类中持有的引用:

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

不销毁线程意味着上一个线程的集合中的数据仍然存在,所以下一个线程重用这个的时候,很可能拿到的是上一个线程的集合的数据,而不是你想要的内容。 这个问题很晦涩。 一旦出现这种原因导致的错误,没有相关经验或者没有相关经验是很难发现这个问题的。 所以大家在写代码的时候要注意这一点,这样会减少很多后续的工作量。 37.记得用常量定义来代替恶魔号。 恶魔号的存在会大大降低代码的可读性。 字符串常量是否由常量定义,视情况而定。 38. long 或 Long 初始赋值时,使用大写的 L 而不是小写的 l,因为字母 l 很容易和数字 1 混淆,这一点很详细,值得注意 39,所有重写的方法必须保留 @注解三个原因: 40、建议使用JDK7 Class中新引入的工具比较对象,直接a.(b),有空指针异常的风险 41、字符串拼接不要使用“+”循环体,而是直接使用不使用“+”进行字符串拼接的原因,如果我有一个方法:

public String appendStr(String oriStr, String... appendStrs) {    if (appendStrs == null || appendStrs.length == 0) {        return oriStr;    }
for (String appendStr : appendStrs) { oriStr += appendStr; }
return oriStr;}

编译这段代码后使用javap -c反编译.class文件,截取关键部分:意思是虚拟机每次遇到“+”运算符拼接字符串,都会出现new一个,然后调用方法,和最后调用()方法将字符串转换并赋值给对象,即循环多少次,就会有多少次new(),很浪费内存。 42、不捕获Java类库中定义的继承的运行时异常类。 异常处理效率低。 大多数运行时异常类是程序员可以避免的,例如: 43. 避免实例被多个线程使用,虽然共享实例是线程安全的,但会因为竞争同一个种子而导致性能下降。 JDK7以后可以用它来获取随机数。 解释竞争同一种子导致性能下降的原因。 比如看类的()方法实现:

public int nextInt() {    return next(32);}

调用 next(int bits) 方法,这是一个受保护的方法:

protected int next(int bits) {    long oldseed, nextseed;    AtomicLong seed = this.seed;    do {        oldseed = seed.get();        nextseed = (oldseed * multiplier + addend) & mask;    } while (!seed.compareAndSet(oldseed, nextseed));    return (int)(nextseed >>> (48 - bits));}

这里的种子是一个全局变量:

/** * The internal state associated with this pseudorandom number generator. * (The specs for the methods in this class describe the ongoing * computation of this value.) */private final AtomicLong seed;

当多个线程同时获取随机数时,它们会竞争同一个种子,导致效率下降。 44. 静态类、单例类和工厂类将它们的构造函数设置为this。 这是因为静态类、单例类、工厂类不需要从外部new,后面设置构造函数。 这些类保证不会生成实例对象。 -END- 如果您看到这里,表示您喜欢这篇文章,请转发,谢谢。 微信搜索“”,关注并回复“进群”即可进入无广告交流群。 ↓扫描二维码进群↓

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。