前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >转角遇到爱_1,你不知道的Java

转角遇到爱_1,你不知道的Java

作者头像
大公爵
发布2019-06-15 15:06:41
4840
发布2019-06-15 15:06:41
举报
文章被收录于专栏:葬爱家族

闲聊

一时无聊,在简书开一个文集,专门记录一些技术上的偏门知识,少用但有用的知识。也不知道该取什么名字,就叫转角遇到爱吧。

有丰富开发经验的人一定都有体会,真正在项目开发中,实现功能的时间其实并不长,大量的时间是浪费在了解决一些稀奇古怪的问题上,有很多冷门、少见的技术我们不知道,知道后才发现这么神奇,就拿Android开发来说,你用300行代码实现一个效果,可能一个神奇的属性就搞定了。而这些冷门的知识我们不常用,用过一次之后很快又忘记了,下次遇到同样的问题又要Google很久。这就是我决定写这个系列的原因,把一些冷门的知识汇集起来,方便自己和大家查询。

1、魔鬼死循环

代码语言:javascript
复制
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类型值域范围一旦超过就会回头,变成最小值,是不是恍然大悟。

代码语言:javascript
复制
System.out.println(Integer.MIN_VALUE);
System.out.println(Integer.MAX_VALUE + 1);

// 执行结果
-2147483648
-2147483648

2、Java7.0的try-with-resources

在 Java7之前,可以使用 finally 块来确保资源被关闭,不管 try 语句正常地完成或是发生意外。

代码语言:javascript
复制
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的对象, 都可以用作一个资源。

代码语言:javascript
复制
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

可以在一个 try-with-resources 语句中声明一个或多个资源。资源的 close 方法调用顺序与它们的创建顺序相反。

代码语言:javascript
复制
try (
  File file = new File("");
  FileReader fr = new FileReader(file);
  BufferedReader br = new BufferedReader(fr);
) {
    // do someting
}

3、数字下划线分割

我们在显示大数金额时,通常会用逗号分隔符,便于清晰读出数值。Java7之后,对于数字也支持下划线分割。

代码语言:javascript
复制
long one_million = 1_000_000_000;
System.out.println(one_million);

// 输出结果
1000000000

4、Math.abs()绝对值

你猜下面的代码打印结果是什么?

代码语言:javascript
复制
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你会这么写:

代码语言:javascript
复制
ArrayList a = new ArrayList();

对应ArrayList的无参构造函数:

代码语言:javascript
复制
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;
}

这里有一个细节,ArrayList 底层采用 Object 类型的数组实现,当使用无参构造方法时,ArrayList 底层会生成一个长度为 10 的 Object 类型数组,当向 ArrayList 添加对象时,计数加 1,并计算容量是否适当,当存储的元素个数超过容量时,就会新建一个数组,新数组的长度是原来的1.5倍,然后把原来数组的内容拷贝大新数组。

注意,这个复制操作是非常伤性能的,如果 ArrayList 很大,执行数百次扩容,那么就会进行更多次数的新数组分配操作,以及更多次数的旧数组回收操作。于是你就会发现性能越来越差,但是又不知道为什么。

正因为如此,所以才有了第二个构造函数,传入一个指定值,作为初始数组的大小。

代码语言:javascript
复制
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    this.elementData = new Object[initialCapacity];
}

比如,你预期你的ArrayList至少会保存100个对象,那么你就使用第二个构造函数,传入100。这样,前100次添加都不会有数组的拷贝操作。

代码语言:javascript
复制
ArrayList a = new ArrayList(100);

总结:如果你能预期ArrayList会保存大量的数据,那么请使用第二个构造函数,传入一个合适的值作为初始容量,尽可能避免大量的性能消耗。

6、单例模式

说起单例模式,相信大多数人都能徒手写出来,毕竟是最简单的设计模式,所以我主要讲一下4种单例写法的递进关系。

1、基础形式 这是最简单的形式,申明静态实例的时候直接创建对象。

代码语言:javascript
复制
public class Singleton {
 
    private static Singleton instance = new Singleton();
 
    private Singleton(){}
 
    public static Singleton getInstance(){
        return instance;
    }
}

这中写法的缺点很明显,在初始化类的时候就创建了对象,如果我们没有用到这个单例,那就是一种浪费。所以我们需要改进。

2、懒惰形式 比起基础形式,这种形式的好处就是可以在需要的时候初始化实例。

代码语言:javascript
复制
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 修饰的方法必须获得调用该方法的类实例的锁方能执行, 否则所属线程阻塞。方法一旦执行, 就独占该锁,直到从该方法返回时才将锁释放。此后被阻塞的线程方能获得该锁, 重新进入可执行状态。

代码语言:javascript
复制
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%情况下不需要线程锁定判断。这个时候,我们可以通过双重校验锁的方式进行处理。换句话说,利用双重校验锁,第一次检查是否实例已经创建,如果还没创建,再进行同步的方式创建单例对象。

代码语言:javascript
复制
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;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.06.12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 闲聊
    • 1、魔鬼死循环
      • 2、Java7.0的try-with-resources
        • 3、数字下划线分割
          • 4、Math.abs()绝对值
            • 5、ArrayList扩容
              • 6、单例模式
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档