前言
身为一个Java开发者,你可能还在初级阶段,或者正在往更高的阶段努力着。不管你的编码能力如何,阅读源码的能力是你最基本的技能也是最容易潜移默化的提升各方面能力的一部分。为什么这么说呢?Java从1995年问世直到今天,它积累了太多前人的智慧结晶,每一行代码都是教科书级别的诠释。阅读它并在项目中学着应用其精华,想必对你的成长速度是最快的。从这篇文章开始,我将写下阅读JDK源码后的过程、困难和心得,让我们一起学习进步吧。
首先需要大概讲一下如何查看JDK的源码。如果你使用的是IDEA工具,那么当你双击Shift键后输入一个类名进入后,可以点击Download Source进行源码的下载。如果你还在用Eclipse工具,那么很不幸,你需要将JDK安装目录下的src.zip文件加压后才能得到JDK源码。所以这里建议决定走Java开发的用IDEA来开发更符合时代的潮流(因为真的方便很多)。
今天要讲的是Java反射包中的Modifier.java
放心即使你目前没学过反射也不影响对这份代码的理解,因为我们还没有要真正进入反射机制的探索。
Modifier.java
是一个修饰符类,提供许多用于标识类、方法和变量访问修饰符的常量。
标识类和变量访问修饰符的常量(十六进制)
public static final int PUBLIC = 0x00000001; // public
public static final int PRIVATE = 0x00000002; // private
public static final int PROTECTED = 0x00000004; // protected
public static final int ABSTRACT = 0x00000400; // abstract
public static final int STATIC = 0x00000008; // static
public static final int FINAL = 0x00000010; // final
public static final int SYNCHRONIZED = 0x00000020; // synchronized(修饰方法的同步锁)
public static final int VOLATILE = 0x00000040; // volatile(修饰变量的同步锁)
public static final int TRANSIENT = 0x00000080; // transient(修饰不参与序列化的变量)
public static final int NATIVE = 0x00000100; // native(修饰本地方法)
public static final int STRICT = 0x00000800; // strictfp(修饰类、接口、方法,使其所有的float、double类型都以精确浮点IEEE-754规范进行计算)
public static final int INTERFACE = 0x00000200; // interface
从上面可以看到用整型数定义了访问一个类、方法和变量的所有修饰符标识,仔细观察就可以发现他们有一个共同的特点:整数标识转化成二进制后,进制位为1的位置都是不一样的。
public static final int PUBLIC = 0x00000001; // 000000000001
public static final int PRIVATE = 0x00000002; // 000000000010
public static final int PROTECTED = 0x00000004; // 000000000100
public static final int STATIC = 0x00000008; // 000000001000
public static final int FINAL = 0x00000010; // 000000010000
public static final int SYNCHRONIZED = 0x00000020; // 000000100000
public static final int VOLATILE = 0x00000040; // 000001000000
public static final int TRANSIENT = 0x00000080; // 000010000000
public static final int NATIVE = 0x00000100; // 000100000000
public static final int INTERFACE = 0x00000200; // 001000000000
public static final int ABSTRACT = 0x00000400; // 010000000000
public static final int STRICT = 0x00000800; // 100000000000
这样的设计是相当巧妙的,因为接下来就可以通过这个特点来做运算得到一些信息了。
判断一个类、方法或变量是否具有某属性
public static boolean isPublic(int mod) { /* mod就是当前类、方法或属性具有的所有属性值的和 */
return (mod & PUBLIC) != 0;
}
(mod & PUBLIC) != 0
这里巧妙的使用了二进制的’与运算’来达到识别一个数是否是构成一个十六进制数的必要条件。
- 假设当前属性值的和
mod = PUBLIC + STATIC
,则有以下运算:
mod = 0x00000001 + 0x00000008 = 0x00000009 = 000000001001
mod & PUBLIC = 000000001001 & 000000000001 = 000000000001 = 1 // 说明具有PUBLIC属性
- 假设当前属性值的和
mod = PRIVATE+ STATIC
,则有以下运算:
mod = 0x00000002 + 0x00000008 = 0x00000010 = 000000010000
mod & PUBLIC = 000000010000 & 000000000001 = 000000000000 = 0 // 说明没有有PUBLIC属性
- 当然JDK还提供了许多这样的方法:
public static boolean isPublic(int mod) {
return (mod & PUBLIC) != 0;
}
public static boolean isPrivate(int mod) {
return (mod & PRIVATE) != 0;
}
public static boolean isProtected(int mod) {
return (mod & PROTECTED) != 0;
}
public static boolean isStatic(int mod) {
return (mod & STATIC) != 0;
}
public static boolean isFinal(int mod) {
return (mod & FINAL) != 0;
}
public static boolean isSynchronized(int mod) {
return (mod & SYNCHRONIZED) != 0;
}
public static boolean isVolatile(int mod) {
return (mod & VOLATILE) != 0;
}
public static boolean isTransient(int mod) {
return (mod & TRANSIENT) != 0;
}
public static boolean isNative(int mod) {
return (mod & NATIVE) != 0;
}
public static boolean isInterface(int mod) {
return (mod & INTERFACE) != 0;
}
public static boolean isAbstract(int mod) {
return (mod & ABSTRACT) != 0;
}
public static boolean isStrict(int mod) {
return (mod & STRICT) != 0;
}
获取一个类、方法或变量的所有属性
public static String toString(int mod) { /* mod就是说有属性对应整型标志相加后的结果 */
StringBuilder sb = new StringBuilder();
int len;
if ((mod & PUBLIC) != 0) sb.append("public ");
if ((mod & PROTECTED) != 0) sb.append("protected ");
if ((mod & PRIVATE) != 0) sb.append("private ");
if ((mod & ABSTRACT) != 0) sb.append("abstract ");
if ((mod & STATIC) != 0) sb.append("static ");
if ((mod & FINAL) != 0) sb.append("final ");
if ((mod & TRANSIENT) != 0) sb.append("transient ");
if ((mod & VOLATILE) != 0) sb.append("volatile ");
if ((mod & SYNCHRONIZED) != 0) sb.append("synchronized ");
if ((mod & NATIVE) != 0) sb.append("native ");
if ((mod & STRICT) != 0) sb.append("strictfp ");
if ((mod & INTERFACE) != 0) sb.append("interface ");
if ((len = sb.length()) > 0) /* 去除最后一个空格 */
return sb.toString().substring(0, len-1);
return "";
}
(mod & PUBLIC) != 0
这里巧妙的使用了二进制的’与运算’来达到识别一个数是否是构成一个十六进制数(mod)的必要条件,这里我们可以来模拟一下就清楚为什么了:
public static final String TAG = "";
根据上面的修饰符对应的十六进制做以下计算得到mod:
int mod = Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL;
// mod = 0x00000001 + 0x00000008 + 0x00000010 = 0x00000019 = 000000011001
通过计算得到mod十进制为19,二进制为000000011001
。通过与运算:
mod & PUBLIC mod & STATIC mod & FINAL
= 000000011001 = 000000011001 = 000000011001
& 000000000001 & 000000001000 & 000000010000
= 000000000001 = 000000001000 = 000000010000
= 1 = 8 = 16
通过打印可以看到 : public static final
综合以上的知识应用到自己的项目中
比如你要做一个链接地址分类功能,那么你可以定义一个类LinkType.java
public class LinkType {
// 科技
public static final int SCIENCE = 0x00000001;
// 计算机
public static final int COMPUTER = 0x00000002;
// 艺术
public static final int ART = 0x00000004;
// 新闻
public static final int NEW = 0x00000008;
public static String toString(int type) { /* type:链接属于的所有类型值得和 */
StringBuilder sb = new StringBuilder();
if ((type & SCIENCE) != 0) sb.append("科技 ");
if ((type & COMPUTER) != 0) sb.append("计算机 ");
if ((type & ART) != 0) sb.append("艺术 ");
if ((type & NEW) != 0) sb.append("新闻 ");
int len = sb.length();
if (len > 0) /* 去掉最后一个空格 */
return sb.toString().substring(0, len-1);
return "";
}
}
相信通过这个讲解已经对Modifer.java源码得思想有了一定得思考。其实这种采用二进制的方式来判断一个十六进制得数由哪些数(标志)构成在真实项目中经常会被用到,而且套路上都是一样的。
如果不是通过阅读源码,我想就算看再多的书籍都不可能学习到这样一种编码方式,而且是最为科学的了。所以说只要敢于挑战源码,那么就一定会有所收获!