点击上方Java后端,选择“设为星标”
我认为,代码优化的最重要的作用应该是:避免未知的错误。在代码上线运行的过程中,往往会出现很多我们意想不到的错误,因为线上环境和开发环境是非常不同的,错误定位到最后往往是一个非常小的原因。
然而为了解决这个错误,我们需要先自验证、再打包出待替换的class文件、暂停业务并重启,对于一个成熟的项目而言,最后一条其实影响是非常大的,这意味着这段时间用户无法访问应用。因此,在写代码的时候,从源头开始注意各种细节,权衡并使用最优的选择,将会很大程度上避免出现未知的错误,从长远看也极大的降低了工作量。
代码优化的目标是:
-
减小代码的体积
-
提高代码运行的效率
代码优化细节
for (int i = 0; i < list.size(); i++)
{...}
for (int i = 0, length = list.size(); i < length; i++)
{...}
String str = "aaa";
if (i == 1)
{
list.add(str);
}
if (i == 1)
{
String str = "aaa";
list.add(str);
}
fillInStackTrace()
的本地同步方法,
fillInStackTrace()
方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。
HashMap
、HashSet等等,以StringBuilder为例:
StringBuilder() // 默认分配16个字符的空间
StringBuilder(int size) // 默认分配size个字符的空间
StringBuilder(String str) // 默认分配16个字符+str.length()个字符空间
-
在4096 的基础上,再申请8194个大小的字符数组,加起来相当于一次申请了12290个大小的字符数组,如果一开始能指定5000个大小的字符数组,就节省了一倍以上的空间
-
把原来的4096个字符拷贝到新的的字符数组中去
new HashMap(128)
、
new HashMap(256)
都可以。
System.arraycopy()
命令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;
}
for (int i = 1; i <= count; i++)
{
Object obj = new Object();
}
Object obj = null;
for (int i = 0; i <= count; i++)
{
obj = new Object();
}
new Object()
的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了
内存空间
了。
static final
,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变
单例
,简单来说,单例主要适用于以下三个方面:
-
控制资源的使用,通过线程同步来控制资源的并发访问
-
控制实例的产生,以达到节约资源的目的
-
控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信
public class A
{
private static B b = new B();
}
大规模集群
中,对对象进行序列化的代价是很昂贵的。因此,当会话不再需要时,应当及时调用HttpSession的
invalidate()
方法清除会话。
RandomAccess
接口的解释是:实现
RandomAccess
接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。
if (list instanceof RandomAccess)
{
for (int i = 0; i < list.size(); i++){}
}
else
{
Iteratorlist.iterable(); > iterator =
while (iterator.hasNext()){iterator.next()}
}
http://www.cnblogs.com/xrq730/p/4868465.html
http://www.cnblogs.com/xrq730/p/4851530.html
The value of the local variable i is not used"、"The import java.util is never used
,那么请删除这些无用的内容
http://www.cnblogs.com/xrq730/p/4862111.html
ArrayList
和LinkedList的原理就知道了
-
违反了面向对象的编程思想,Java讲求一切都是对象,太多的形参,和面向对象的编程思想并不契合
-
参数太多势必导致方法调用的出错概率增加
String str = "123";
if (str.equals("123"))
{
...
}
String str = "123";
if ("123".equals(str))
{
...
}
if (i == 1)
和if (1 == i)
是没有区别的,但从阅读习惯上讲,建议使用前者if (i == 1)
和
if (1== i)
有没有区别,这就要从C/C++讲起。
if (i == 1)
判断条件成立,是以0与非0为基准的,0表示false,非0表示true,如果有这么一段代码:
int i = 2;
if (i == 1)
{
...
}
else
{
...
}
i==1
不成立,所以以0表示,即false。但是如果:
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语句写为:
int i = 2;
if (1 == i)
{
...
}
else
{
...
}
1 = i
,C/C++编译器也可以第一时间检查出来,因为我们可以对一个变量赋值i为1,但是不能对一个常量赋值1为i。
if (i = 1)
的语法是不可能出现的,因为一旦写了这种语法,Java就会编译报错
Type mismatch: cannot convert from int to boolean
。但是,尽管Java的
if (i == 1)
和
if (1 == i)
在语义上没有任何区别,从阅读习惯上讲,建议使用前者会更好些。
toString()
方法toString()
打印出来的是什么:
public static void main(String[] args)
{
int[] is = new int[]{1, 2, 3};
System.out.println(is.toString());
}
[I@18a992f
toString()
没有意义,但是对集合
toString()
是可以打印出集合里面的内容的,因为集合的父类
AbstractCollections<E>
重写了Object的
toString()
方法。
public static void main(String[] args)
{
long l = 12345678901234L;
int i = (int)l;
System.out.println(i);
}
1942892530
0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010
0111 0011 1100 1110 0010 1111 1111 0010
-
整型默认的数据类型是
int,long l = 12345678901234L
,这个数字已经超出了int的范围了,所以最后有一个L,表示这是一个long型数。顺便,浮点型的默认类型是double,所以定义float的时候要写成
float f = 3.5f
-
接下来再写一句
int ii = l + i;
会报错,因为long + int是一个long,不能赋值给int
i.toString()
、
String.valueOf(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");
}
String.valueOf():11ms
Integer.toString():5ms
i + "":25ms
toString()
方法。至于为什么,很简单:
-
String.valueOf()
方法底层调用了
Integer.toString()
方法,但是会在调用前做空判断
-
Integer.toString()
方法就不说了,直接调用了
-
i + ""
底层使用了StringBuilder实现,先用append方法拼接,再用
toString()
方法获取字符串
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());
}
}
Set<String> keySet = hm.keySet();
会比较合适一些
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都会被close掉
ThreadLocal.ThreadLocalMap
的引用:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap
中的数据依然存在,那么在下一条线程重用这个Thread的时候,很可能get到的是上条线程set的数据而不是自己想要的内容。
扎实的基础
非常难发现这个问题,因此在写代码的时候就要注意这一点,这将给你后续减少很多的工作量。
@Override
注解-
清楚地可以知道这个方法由父类继承而来
-
getObject()
和
get0bject()
方法,前者第四个字母是"O",后者第四个子母是"0",加了
@Override
注解可以马上判断是否重写成功
-
在抽象类中对方法签名进行修改,实现类会马上报出编译错误
a.equals(b)
,有空指针异常的风险public String appendStr(String oriStr, String... appendStrs) {
if (appendStrs == null || appendStrs.length == 0) {
return oriStr;
}
for (String appendStr : appendStrs) {
oriStr += appendStr;
}
return oriStr;
}
toString()
方法转换字符串赋值给oriStr对象,即循环多少次,就会new出多少个
StringBuilder()
来,这对于内存是一种浪费。
-
ArithmeticException可以通过判断除数是否为空来规避
-
NullPointerException可以通过判断对象是否为空来规避
-
IndexOutOfBoundsException可以通过判断数组/字符串长度来规避
-
ClassCastException可以通过instanceof关键字来规避
-
ConcurrentModificationException可以使用迭代器来规避
nextInt()
方法实现:
public int nextInt() {
return next(32);
}
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;
荐
阅
读
欢
文
章
,
点
个
在看
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
相关文章
暂无评论...