闲聊
一时无聊,在简书开一个文集,专门记录一些技术上的偏门知识,少用但有用的知识。也不知道该取什么名字,就叫转角遇到爱吧。
有丰富开发经验的人一定都有体会,真正在项目开发中,实现功能的时间其实并不长,大量的时间是浪费在了解决一些稀奇古怪的问题上,有很多冷门、少见的技术我们不知道,知道后才发现这么神奇,就拿Android开发来说,你用300行代码实现一个效果,可能一个神奇的属性就搞定了。而这些冷门的知识我们不常用,用过一次之后很快又忘记了,下次遇到同样的问题又要Google很久。这就是我决定写这个系列的原因,把一些冷门的知识汇集起来,方便自己和大家查询。
1、魔鬼死循环
boolean flag = false;
for (int i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) {
flag = !flag;
}
System.out.println(flag);
大多数人第一反应就是:flag初始为false,执行奇数次就是true,偶数次就是false,所以问题就是for循环执行奇数次还是偶数次。
如果你是这么想的,那你就上当了,这段代码的执行结果是死循环。因为Integer.MAX_VALUE + 1 = Integer.MIN_VALUE。Java中Integer类型值域范围一旦超过就会回头,变成最小值,是不是恍然大悟。
System.out.println(Integer.MIN_VALUE);
System.out.println(Integer.MAX_VALUE + 1);
// 执行结果
-2147483648
-2147483648
2、Java7.0的try-with-resources
在 Java7之前,可以使用 finally 块来确保资源被关闭,不管 try 语句正常地完成或是发生意外。
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
Java7之后,使用try-with-resources语句,当try退出时,会自动调用res.close()方法。不管代码块如何退出,只要之前已经被创建出来,它们都会被关闭。那什么样的资源会被关闭呢?任何实现了 java.lang.AutoCloseable的对象, 包括所有实现了 java.io.Closeable的对象, 都可以用作一个资源。
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
可以在一个 try-with-resources 语句中声明一个或多个资源。资源的 close 方法调用顺序与它们的创建顺序相反。
try (
File file = new File("");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
) {
// do someting
}
3、数字下划线分割
我们在显示大数金额时,通常会用逗号分隔符,便于清晰读出数值。Java7之后,对于数字也支持下划线分割。
long one_million = 1_000_000_000;
System.out.println(one_million);
// 输出结果
1000000000
4、Math.abs()绝对值
你猜下面的代码打印结果是什么?
int i = Math.abs(Integer.MIN_VALUE);
System.out.println(i)
结果还是Integer.MIN_VALUE,吃惊吧!
Math.abs()并不是一定会给你返回正数,原因很简单,就是Integer的值域问题,Integer的最大值是2147483647,最小值是-2147483648,绝对值就是2147483648,还记得前面的那个魔鬼死循环吗?
Integer.MAX_VALUE + 1 = Integer.MIN_VALUE;就是这么无语。
5、ArrayList扩容
正常创建一个ArrayList你会这么写:
ArrayList a = new ArrayList();
对应ArrayList的无参构造函数:
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
这里有一个细节,ArrayList 底层采用 Object 类型的数组实现,当使用无参构造方法时,ArrayList 底层会生成一个长度为 10 的 Object 类型数组,当向 ArrayList 添加对象时,计数加 1,并计算容量是否适当,当存储的元素个数超过容量时,就会新建一个数组,新数组的长度是原来的1.5倍,然后把原来数组的内容拷贝大新数组。
注意,这个复制操作是非常伤性能的,如果 ArrayList 很大,执行数百次扩容,那么就会进行更多次数的新数组分配操作,以及更多次数的旧数组回收操作。于是你就会发现性能越来越差,但是又不知道为什么。
正因为如此,所以才有了第二个构造函数,传入一个指定值,作为初始数组的大小。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];
}
比如,你预期你的ArrayList至少会保存100个对象,那么你就使用第二个构造函数,传入100。这样,前100次添加都不会有数组的拷贝操作。
ArrayList a = new ArrayList(100);
总结:如果你能预期ArrayList会保存大量的数据,那么请使用第二个构造函数,传入一个合适的值作为初始容量,尽可能避免大量的性能消耗。
6、单例模式
说起单例模式,相信大多数人都能徒手写出来,毕竟是最简单的设计模式,所以我主要讲一下4种单例写法的递进关系。
1、基础形式
这是最简单的形式,申明静态实例的时候直接创建对象。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
这中写法的缺点很明显,在初始化类的时候就创建了对象,如果我们没有用到这个单例,那就是一种浪费。所以我们需要改进。
2、懒惰形式
比起基础形式,这种形式的好处就是可以在需要的时候初始化实例。
public class Singleton {
private Singleton(){}
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
虽然解决了延迟初始化的问题,但是还有个明显的问题就是线程不安全,所以还需要继续优化。
3、方法锁形式
在方法上使用synchronized关键字就可以处理多个线程同时访问的问题。每个类实例对应一个线程锁, synchronized 修饰的方法必须获得调用该方法的类实例的锁方能执行, 否则所属线程阻塞。方法一旦执行, 就独占该锁,直到从该方法返回时才将锁释放。此后被阻塞的线程方能获得该锁, 重新进入可执行状态。
public class Singleton {
private Singleton(){}
private static Singleton instance = null;
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
4、Class锁形式
上面的写法虽然是线程安全的,但是每次调用 getInstance() 方法都需要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能下降。事实上,不仅效率很低,99%情况下不需要线程锁定判断。这个时候,我们可以通过双重校验锁的方式进行处理。换句话说,利用双重校验锁,第一次检查是否实例已经创建,如果还没创建,再进行同步的方式创建单例对象。
public class Singleton {
private Singleton(){}
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
网友评论