出品|CSDN博客

java@符号_java 除法符号_java符号用法

我的真实经历

题目是我2019年6月28日在深圳一家500强公司面试时面试官告诉我的,现在想起来都觉得惭愧不已。 由于我的愚蠢、懒惰和傲慢,我在深圳的第一份工作面试就跑偏了。

我相信我这辈子都不会忘记那个炎热的早晨。 第一天就收到了K公司的面试通知,这是我在深圳的第一份面试邀请。 收到信息后激动不已,仿佛收到了K公司的offer。 上网查了一下K公司的面试经历,发现很多人都说很看重阅读源码的能力,几乎每次都会问一些关于源码的问题。 经典问题,于是去网上找了几篇关于String,HashMap等的文章,学习了很多Java源码。 看完后,我信心十足,心想明天就能答对所有的题目了,心满意足的去睡觉了。

面试当天上午9点下楼到K公司,然后打电话给联系人接我,在等候室等面试。 9:30左右,前台小姐姐叫了我的名字,我跟着她一起进了一个小房间,里面有两个人,两个人好像都是技术型的(因为有点秃头) ,一开始一切顺利,然后问了一个问题“你的简历说你熟悉Java源代码,那怎么样?” 请问大家一个问题,String类可以继承吗?当然不能继承,文章里面写到String是final修饰的,不能继承,然后说一些面试题内容,面试官然后又问了一个问题:

《请简单说说substring的实现过程》

是的,我没看过这个题,平时用的时候也不看这个方法的源码。 我犹豫着没法回答,只觉得脸有些发烫。 他好像看出了我的尴尬,于是接着说,“你真的看过源码吗?Substring是一个很简单的方法,如果你真的看过,不可能不知道。” 说到这里,我不得不承认,我没有读过源码,是的,我什至不知道如何实现一个简单的子字符串,甚至找不到String类的源码。

面试官说了标题上的那句话,然后我面试失败了。

感谢这次失败的经历,为我打开了新的天地。 我开始尝试看源码,从jdk源码到Spring,再到SpringBoot源码。 越看越佩服那些写出这个优秀框架的大佬们。 ,他们的想法、代码逻辑、设计模式都是那么的优秀和贴切。 不仅如此,我也开始逐渐尝试自己写一些框架。 第一个实践框架是《手写精简Spring框架–YzSpring》,用了一周的时间,每天晚上下班在家敲一两个小时。 ,写完YzSpring,感觉自己真正了解了Spring。 之前在网上看资料的时候,总觉得只是皮毛。 只有自己写,才能明白Spring的工作原理。

后来我的“IPayment”项目的小伙伴一直在抱怨我们的接口反馈速度慢。 我开始优化代码,在 Redis 中缓存一些数据。 两三行代码用于匹配,缓存数据少一点无所谓,但是随着需要写入缓存的数据越来越多,代码变得异常臃肿。 某天看到@Autowired的注入功能,突然想到,为什么不能自己写一个实用的框架,把需要缓存的数据标注出来,然后用框架来处理呢? 干就干,加班一周。 我完成了《基于Redis的快速数据缓存组件》。 引入项目后,需要缓存的数据只需要用@BFastCache修饰即可。 可选操作包括:对数据进行操作、选择数据源、更新数据源、设置/修改Key等,大大提高工作效率。 第一次自己写轮子,效果这么好,得到大哥的肯定,真的很开心。

所以现在我想问你三个问题:

你看过源代码了吗?

你能读懂源代码吗?

你从源代码中得到了什么吗?

java@符号_java符号用法_java 除法符号

查看源码可以获得什么

1.快速检查,减少错误

在编码的时候,我们一般找不到RuntimeException,比如String的substring方法,有时候我们传入的endIndex大于字符串的长度,所以运行时会报错:

String index out of range: 100

有的时候一头雾水把代码改对了,但是不知道为什么会出现这个异常java@符号,下次写的时候又出现同样的问题。 我们查看源码就可以知道为什么会出现这个异常:

public String substring(int beginIndex, int endIndex{
        if (beginIndex < 0) {//起始坐标小于0
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {//结束坐标大于字符串长度
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {//起始坐标大于结束坐标
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

源码给出了三种可能抛出上述异常的场景,那么我们可以根据这三种场景来检查我们的代码,以后编码的时候注意这些问题。

2.学习编程习惯

或者上面的子串源码java@符号,请注意他的返回,如果是你,你会怎么写? 如果我没有看过源码,我肯定会这样写:

if ((beginIndex == 0) && (endIndex == value.length)) return this;
   return new String(value, beginIndex, subLen);

虽然功能是一样的,但是使用三元运算符,一行代码就可以解决问题,不用再写if语句了。 现在我迷上了三元运算符,它真的好用。

3.学习设计模式(适合新手)

好的! 我有一个摊牌。 作为一个半路出家的程序员,没有接受过系统的教学,都是自学。 在那之前,我根本不懂设计模式。 我只知道有23种设计模式,最多只知道单例模式。

不会设计模式的主要原因是我当时没有实战经验,写的项目都是比赛项目,根本不需要用到设计模式,基本上只需要能够运行。 第一次接触设计模式是在log4j的工厂模式中。 那时,我根本不知道如何使用工厂模式。 刚刚看了log4j的源码,一步步学习。 然后我开始在做项目时有意无意地使用设计。 模式,下面是我项目中使用单例模式获取配置类的代码:

import java.util.ResourceBundle;

public class Configration {
    private static Object lock              = new Object();
    private static Configration config     = null;
    private static ResourceBundle rb        = null;

    private Configration(String filename{
        rb = ResourceBundle.getBundle(filename);
    }


    public static Configration getInstance(String filename{
        synchronized(lock) {
            if(null == config) {
                config = new Configration(filename);
            }
        }
        return (config);
    }

    public String getValue(String key{
        String ret = "";
        if(rb.containsKey(key))
        {
            ret = rb.getString(key);
        }
        return ret;
    }
}

三、总结

很多人可能觉得上面的东西很简单,请不要被我误导,因为上面是最简单的例子,源码里面有很多值得学习的东西,只有自己看了才能明白你自己。

java符号用法_java 除法符号_java@符号

阅读源代码的正确姿势

这里我们以一个非常流行的 HashMap 类为例。 同时强烈推荐大家使用IDEA来阅读代码。 它自带反编译器,可以让我们快速方便的看到源码,还有很多快捷键。 让我们的操作爽到飞起来。

1.定位源码

其实定位的时候有很多情况:

Ctrl+左键

java 除法符号_java@符号_java符号用法

在这种情况下,如果我们想进入只属于HashMap类的方法,可以直接按Ctrl+左键定位到源码。

Ctrl+Alt+B

java 除法符号_java@符号_java符号用法

HashMap的put方法重写了Map的方法。 如果我们使用Ctrl+左键,会直接跳转到Map接口的put方法。 这不是我们想要的结果。 这时,我们应该把鼠标光标放在放上。 然后按Ctrl+Alt+B,然后有很多类重写了put方法。

找到我们需要查看的类,左键定位到put方法。

2.查看继承关系

一个类的继承关系非常重要,尤其是被继承的抽象类,因为抽象类中的方法可以在子类中使用。

上一步我们已经定位到了HashMap的源码,现在拉到最上面,可以看到在类定义的时候,有如下的继承关系:

public class HashMap<K,Vextends AbstractMap<K,V>
    implements Map<K,V>, CloneableSerializable 

当然,如果你想更直观、更详细,IDEA中有一个功能,提供了继承关系的展示。 可以把鼠标放在要查看的类上,然后Ctrl+Alt+Shift+U,或者右键=》Diagrams=》Show Diagram,我们就可以看到继承关系了:

然后看一下AbstractMap抽象类,因为后面可能会用到。

3.查看类常量

当我们进入到HashMap的构造函数中,发现有如下代码:

public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

我们只知道initialCapacity是我们传入的初始容量,却不知道这个DEFAULT_LOAD_FACTOR是什么,等于多少。 我们可以先粗略的看一下这个类拥有的常量,留下印象,对后面看源码有帮助,Ctrl+左键定位到这个量的位置,然后发现有几个常量,里面有常量的注释,我们来看一下,这有助于我们理解这些常量:

//序列号
    private static final long serialVersionUID = 362498820763181265L;

    /**
     * 初始容量,必须是2的幂数
     * 1 << 4 = 10000 = 16
     */

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4// 初始默认值二进制1左移四位 = 16

    /**
     * 最大容量
     * 必须是2的幂数 <= 1<<30.
     */

    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 加载因子,构造函数中没有指定时会被使用
     */

    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 从链表转到树的时机
     */

    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 从树转到链表的时机
     */

    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * The smallest table capacity for which bins may be treeified.
     * (Otherwise the table is resized if too many nodes in a bin.)
     * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
     * between resizing and treeification thresholds.
     */

    static final int MIN_TREEIFY_CAPACITY = 64;    

这样我们就对HashMap中常量的作用和意义有了一个认识

4.查看构造函数

我们一般在看一个类的时候,首先要看这个类是如何构造的,也就是构造方法的实现:

    /**
     * 构造一个空的,带有初始值和初始加载因子的HashMap
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */

    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

很明显,上面的构造函数指向了另外一个构造函数,我们点进去看看

  /**
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

这里就是我们的构造器实现的地方,我们逐行分析一下:

1、我们的initialCapacity参数是我们一开始传入的16,loadFactor是上一步使用的默认参数0.75f。

2、判断初始容量是否小于0,小于0则抛异常。 如果不小于0,则进行下一步。

3.判断初始容量是否大于最大容量(1