35 个 Java 代码性能优化总结

本贴最后更新于 2668 天前,其中的信息可能已经物是人非

代码优化的目标是:

1、减小代码的体积

2、提高代码运行的效率

代码优化细节

1、尽量指定类、方法的 final 修饰符

带有 final 修饰符的类是不可派生的。在 Java 核心 API 中,有许多应用 final 的例子,例如 java.lang.String,整个类都是 final 的。为类指定 final 修饰符可以让类不可以被继承,为方法指定 final 修饰符可以让方法不可以被重写。如果指定了一个类为 final,则该 类所有的方法都是 final 的。Java 编译器会寻找机会内联所有的 final 方法,内联对于提升 Java 运行效率作用重大,具体参见 Java 运行期优 化。此举能够使性能平均提高 50%。

2、尽量重用对象

特别是 String 对象的使用,出现字符串连接时应该使用 StringBuilder/StringBuffer 代替。由于 Java 虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。

3、尽可能使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。

4、及时关闭流

Java 编程过程中,进行数据库连接、I/O 流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。

5、尽量减少对变量的重复计算

明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作:

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

  2. {...}

  3. 建议替换为:

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

  5. {...}

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

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

例如:

  1. String str = "aaa";if (i == 1)

  2. {

  3. list.add(str);

  4. }

建议替换为:

  1. if (i == 1)

  2. {

  3. String str = "aaa";

  4. list.add(str);

  5. }

7、慎用异常

异常对性能不利。抛出异常首先要创建一个新的对象,Throwable 接口的构造函数调用名为 fillInStackTrace()的本地同步方 法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java 虚拟机就必须调整调用堆栈,因为在处理过程中创建 了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。

8、不要在循环中使用 try…catch…,应该把其放在最外层

除非不得已。如果毫无理由地这么写了,只要你的领导资深一点、有强迫症一点,八成就要骂你为什么写出这种垃圾代码来了

9、如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度

比如 ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet 等等,以 StringBuilder 为例:

  1. (1)StringBuilder()// 默认分配 16 个字符的空间

  2. (2)StringBuilder(int size)// 默认分配 size 个字符的空间

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

可以通过类(这里指的不仅仅是上面的 StringBuilder)的来设定它的初始化容量,这样可以明显地提升性能。比如 StringBuilder 吧,length 表示当前的 StringBuilder 能保持的字符数量。因为当 StringBuilder 达到最大容量的时 候,它会将自身容量增加到当前的 2 倍再加 2,无论何时只要 StringBuilder 达到它的最大容量,它就不得不创建一个新的字符数组然后将旧的字符数 组内容拷贝到新字符数组中—-这是十分耗费性能的一个操作。试想,如果能预估到字符数组中大概要存放 5000 个字符而不指定长度,最接近 5000 的 2 次幂 是 4096,每次扩容加的 2 不管,那么:

(1)在 4096 的基础上,再申请 8194 个大小的字符数组,加起来相当于一次申请了 12290 个大小的字符数组,如果一开始能指定 5000 个大小的字符数组,就节省了一倍以上的空间

(2)把原来的 4096 个字符拷贝到新的的字符数组中去

这样,既浪费内存空间又降低代码运行效率。所以,给底层以数组实现的集合、工具类设置一个合理的初始化容量是错不了的,这会带来立竿见影的效果。但 是,注意,像 HashMap 这种是以数组 + 链表实现的集合,别把初始大小和你估计的大小设置得一样,因为一个 table 上只连接一个对象的可能性几乎为 0。初始大小建议设置为 2 的 N 次幂,如果能估计到有 2000 个元素,设置成 new HashMap(128)、new HashMap(256)都可以。

10、当复制大量数据时,使用 System.arraycopy()命令

11、乘法和除法使用移位操作

例如:

  1. for (val = 0; val < 100000; val += 5)

  2. {

  3. a = val * 8;

  4. b = val / 2;

  5. }

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

  1. for (val = 0; val < 100000; val += 5)

  2. {

  3. a = val << 3;

  4. b = val >> 1;

  5. }

移位操作虽然快,但是可能会使代码不太好理解,因此最好加上相应的注释。

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

例如:

  1. for (int i = 1; i <= count; i++)

  2. {

  3. Object obj = new Object();

  4. }

这种做法会导致内存中有 count 份 Object 对象引用存在,count 很大的话,就耗费内存了,建议为改为:

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

这样的话,内存中只有一份 Object 对象引用,每次 new Object()的时候,Object 对象引用指向不同的 Object 罢了,但是内存中只有一份,这样就大大节省了内存空间了。

13、基于效率和类型检查的考虑,应该尽可能使用 array,无法确定数组大小时才使用 ArrayList

14、尽量使用 HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用 Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销

15、不要将数组声明为 public static final

因为这毫无意义,这样只是定义了引用为 static final,数组的内容还是可以随意改变的,将数组声明为 public 更是一个安全漏洞,这意味着这个数组可以被外部类所改变

16、尽量在合适的场合使用单例

使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:

(1)控制资源的使用,通过线程同步来控制资源的并发访问

(2)控制实例的产生,以达到节约资源的目的

(3)控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信

17、尽量避免随意使用静态变量

要知道,当某个对象被定义为 static 的变量所引用,那么 gc 通常是不会回收这个对象所占有的堆内存的,如:

  1. public class A

  2. {

  3. private static B b = new B();

  4. }

此时静态变量 b 的生命周期与 A 类相同,如果 A 类不被卸载,那么引用 B 指向的 B 对象会常驻内存,直到程序终止

18、及时清除不再需要的会话

为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为 30 分钟。当应用服务器需要保存更多的会话时,如果内存不足,那么操作系统 会把部分数据转移到磁盘,应用服务器也可能根据 MRU(最近最频繁使用)算法把部分不活跃的会话转储到磁盘,甚至可能抛出内存不足的异常。如果会话要被转 储到磁盘,那么必须要先被序列化,在大规模集群中,对对象进行序列化的代价是很昂贵的。因此,当会话不再需要时,应当及时调用 HttpSession 的 invalidate()方法清除会话。

19、实现 RandomAccess 接口的集合比如 ArrayList,应当使用最普通的 for 循环而不是 foreach 循环来遍历

这是 JDK 推荐给用户的。JDK API 对于 RandomAccess 接口的解释是:实现 RandomAccess 接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改 其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验表明,实现 RandomAccess 接口的类实例,假如是随机访问的,使用普通 for 循环效率将高于使用 foreach 循环;反过来,如果是顺序访问的,则使用 Iterator 会效率更高。可以使用类似如下的代码作判断:

if (list instanceof RandomAccess)

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

}else{

Iterator iterator = list.iterable(); while (iterator.hasNext()){iterator.next()}

}

foreach 循环的底层实现原理就是迭代器 Iterator,参见 Java 语法糖 1:可变长度参数以及 foreach 循环原理。所以后半句”反过来,如果是顺序访问的,则使用 Iterator 会效率更高”的意思就是顺序访问的那些类实例,使用 foreach 循环去遍历。

20、使用同步代码块替代同步方法

这点在多线程模块中的 synchronized 锁方法块一文中已经讲得很清楚了,除非能确定一整个方法都是需要进行同步的,否则尽量使用同步代码块,避免对那些不需要进行同步的代码也进行了同步,影响了代码执行效率。

21、将常量声明为 static final,并以大写命名

这样在编译期间就可以把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字以大写命名也可以方便区分出常量与变量

22、不要创建一些不使用的对象,不要导入一些不使用的类

这毫无意义,如果代码中出现”The value of the local variable i is not used”、”The import java.util is never used”,那么请删除这些无用的内容

23、程序运行过程中避免使用反射

关于,请参见反射。反射是 Java 提供给用户一个很强大的功能,功能强大往往意味着效率不高。不建议在程序运行过程中使用尤其是频繁使用反射机制, 特别是 Method 的 invoke 方法,如果确实有必要,一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入 内存—-用户只关心和对端交互的时候获取最快的响应速度,并不关心对端的项目启动花多久时间。

24、使用数据库连接池和线程池

这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭连接,后者可以避免频繁地创建和销毁线程

25、使用带缓冲的输入输出流进行 IO 操作

带缓冲的输入输出流,即 BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,这可以极大地提升 IO 效率

26、顺序插入和随机访问比较多的场景使用 ArrayList,元素删除和中间插入比较多的场景使用 LinkedList

这个,理解 ArrayList 和 LinkedList 的原理就知道了

27、不要让 public 方法中有太多的形参

public 方法即对外提供的方法,如果给这些方法太多形参的话主要有两点坏处:

1、违反了面向对象的编程思想,Java 讲求一切都是对象,太多的形参,和面向对象的编程思想并不契合

2、参数太多势必导致方法调用的出错概率增加

至于这个”太多”指的是多少个,3、4 个吧。比如我们用 JDBC 写一个 insertStudentInfo 方法,有 10 个学生信息字段要插如 Student 表中,可以把这 10 个参数封装在一个实体类中,作为 insert 方法的形参

28、字符串变量和字符串常量 equals 的时候将字符串常量写在前面

这是一个比较常见的小技巧了,如果有以下代码:

  1. String str = "123";

  2. if (str.equals("123")) {

  3. ...

  4. }

建议修改为:

  1. String str = "123";

  2. if ("123".equals(str))

  3. {

  4. ...

  5. }

这么做主要是可以避免空指针异常

29、请知道,在 java 中 if (i == 1)和 if (1 == i)是没有区别的,但从阅读习惯上讲,建议使用前者

平时有人问,”if (i == 1)”和”if (1== i)”有没有区别,这就要从 C/C++ 讲起。

在 C/C++ 中,”if (i == 1)”判断条件成立,是以 0 与非 0 为基准的,0 表示 false,非 0 表示 true,如果有这么一段代码:

  1. int i = 2;

  2. if (i == 1)

  3. {

  4. ...

  5. }else{

  6. ...

  7. }

  8. C/C++ 判断”i==1″不成立,所以以 0 表示,即 false。但是如果:

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

万一程序员一 个不小心,把”if (i == 1)”写成”if (i = 1)”,这样就有问题了。在 if 之内将 i 赋值为 1,if 判断里面的内容非 0,返回的就是 true 了,但是明明 i 为 2,比较的值是 1,应该返回的 false。这种情况在 C/C++ 的开发中是很可能发生的并且会导致一些难以理解的错误产生,所以,为了避免开发者在 if 语句中不正确的赋值操作,建议将 if 语句写为:

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

这样,即使开发者不小心写成了”1 = i”,C/C++ 编译器也可以第一时间检查出来,因为我们可以对一个变量赋值 i 为 1,但是不能对一个常量赋值 1 为 i。

但是,在 Java 中,C/C++ 这种”if (i = 1)”的语法是不可能出现的,因为一旦写了这种语法,Java 就会编译报错”Type mismatch: cannot convert from int to boolean”。但是,尽管 Java 的”if (i == 1)”和”if (1 == i)”在语义上没有任何区别,但是从阅读习惯上讲,建议使用前者会更好些。

30、不要对数组使用 toString()方法

看一下对数组使用 toString()打印出来的是什么:

  1. public static void main(String[] args)

  2. { int[] is = new int[]{1, 2, 3};

  3. System.out.println(is.toString());

  4. }

结果是:

[I@18a992f

本意是想打印出数组内容,却有可能因为数组引用 is 为空而导致空指针异常。不过虽然对数组 toString()没有意义,但是对集合 toString()是可以打印出集合里面的内容的,因为集合的父类 AbstractCollections 重写了 Object 的 toString()方法。

31、不要对超出范围的基本数据类型做向下强制转型

这绝不会得到想要的结果:

  1. public static void main(String[] args)

  2. {

  3. long l = 12345678901234L;

  4. int i = (int)l;

  5. System.out.println(i);

  6. }

我们可能期望得到其中的某几位,但是结果却是:

1942892530

解释一下。Java 中 long 是 8 个字节 64 位的,所以 12345678901234 在计算机中的表示应该是:

0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010

一个 int 型数据是 4 个字节 32 位的,从低位取出上面这串二进制数据的前 32 位是:

0111 0011 1100 1110 0010 1111 1111 0010

这串二进制表示为十进制 1942892530,所以就是我们上面的控制台上输出的内容。从这个例子上还能顺便得到两个结论:

1、整型默认的数据类型是 int,long l = 12345678901234L,这个数字已经超出了 int 的范围了,所以最后有一个 L,表示这是一个 long 型数。顺便,浮点型的默认类型是 double,所以定义 float 的时候要写成”"float f = 3.5f”

2、接下来再写一句”int ii = l + i;”会报错,因为 long + int 是一个 long,不能赋值给 int

32、公用的集合类中不使用的数据一定要及时 remove 掉

如果一个集合类是公用的(也就是说不是方法里面的属性),那么这个集合里面的元素是不会自动释放的,因为始终有引用指向它们。所以,如果公用集合里面的某些数据不使用而不去 remove 掉它们,那么将会造成这个公用集合不断增大,使得系统有内存泄露的隐患。

33、把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据 +”"最慢

把一个基本数据类型转为一般有三种方式,我有一个 Integer 型数据 i,可以使用 i.toString()、String.valueOf(i)、i+”"三种方式,三种方式的效率如何,看一个测试:

  1. public static void main(String[] args)

  2. {

  3. int loopTime = 50000;

  4. Integer i = 0; long startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++)

  5. {

  6. String str = String.valueOf(i);

  7. }

  8. System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms");

  9. startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++)

  10. {

  11. String str = i.toString();

  12. }

  13. System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms");

  14. startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++)

  15. {

  16. String str = i + "";

  17. }

  18. System.out.println("i + "":" + (System.currentTimeMillis() - startTime) + "ms");

  19. }

运行结果为:

String.valueOf():11ms Integer.toString():5ms i + "":25ms

所以以后遇到把一个基本数据类型转为 String 的时候,优先考虑使用 toString()方法。至于为什么,很简单:

1、String.valueOf()方法底层调用了 Integer.toString()方法,但是会在调用前做空判断

2、Integer.toString()方法就不说了,直接调用了

3、i + “”底层使用了 StringBuilder 实现,先用 append 方法拼接,再用 toString()方法获取字符串

三者对比下来,明显是 2 最快、1 次之、3 最慢

34、使用最有效率的方式去遍历 Map

遍历 Map 的方式有很多,通常场景下我们需要的是遍历 Map 中的 Key 和 Value,那么推荐使用的、效率最高的方式是:

  1. public static void main(String[] args)

  2. {

  3. HashMap hm = new HashMap();

  4. hm.put("111", "222");

  5. Set> entrySet = hm.entrySet();

  6. Iterator> iter = entrySet.iterator(); while (iter.hasNext())

  7. {

  8. Map.Entry entry = iter.next();

  9. System.out.println(entry.getKey() + "\t" + entry.getValue());

  10. }

  11. }

如果你只是想遍历一下这个 Map 的 key 值,那用”Set keySet = hm.keySet();”会比较合适一些

35、对资源的 close()建议分开操作

意思是,比如我有这么一段代码:

  1. try{

  2. XXX.close();

  3. YYY.close();

  4. }catch (Exception e)

  5. {

  6. ...

  7. }

建议修改为:

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

虽然有些麻烦,却能避免资源泄露。我们想,如果没有修改过的代码,万一 XXX.close()抛异常了,那么就进入了 cath 块中 了,YYY.close()不会执行,YYY 这块资源就不会回收了,一直占用着,这样的代码一多,是可能引起资源句柄泄露的。而改为下面的写法之后,就保 证了无论如何 XXX 和 YYY 都会被 close 掉。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8207 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    1 引用 • 11 回帖
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 6 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 88 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    9 引用 • 32 回帖 • 161 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 2 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 394 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 31 关注
  • Flume

    Flume 是一套分布式的、可靠的,可用于有效地收集、聚合和搬运大量日志数据的服务架构。

    9 引用 • 6 回帖 • 596 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    311 引用 • 546 回帖 • 33 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    4 引用 • 7 回帖
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 49 关注
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖 • 218 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    534 引用 • 3528 回帖 • 1 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    5 引用 • 26 回帖 • 495 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 1 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 688 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 740 关注
  • gRpc
    10 引用 • 8 回帖 • 55 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 427 关注
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 341 关注
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    122 引用 • 73 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 293 关注