Class的本质也是一个类,只不过它是将咱们定义类的共同的部分进行抽象,好比咱们常定义的类都含有构造方法,类变量,函数,而Class这个类就是来操做这些属性和方法的。固然咱们常定义的类包含的类型均可以经过Class间接的来操做。而类的类型包含通常的类,接口,枚举类型,注解类型等等。这么说可能有点太理论,咱们看下面这个例子:html
咱们将生活中的一类事物抽象为一个类的时候,每每是由于他们具备相同的共性和不一样的个性。定义一个类的做用就是将相同的共性抽离出来。通常的类都包含属性和方法(行为),下面咱们定义水果和汽车这两个大类:java
代码以下:apache
汽车类:api
class Car{ // 定义属性 private String name; private String color; /** * 定义两个构造方法 */ public Car(){ } public Car(String name,String color){ this.name = name; this.color = color; } /** * 定义两个普通方法(行为) */ public void use(){ } public void run(){ } /** * 属性的get和set方法 * @return */ public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
水果类:数组
class Fruit{ // 定义属性 private String name; private int size; /** * 定义两个构造方法 */ public Fruit(){ } public Fruit(String name,int size){ this.name = name; this.size =size; } /** * 定义两个方法(行为) */ public void use(){ } public void doFruit(){ } /** * 属性的get和set方法 * @return */ public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } }
能够看到水果和汽车这两个类都有共同的部分,也就是一个类共同的部分,那就是属性和方法,而Class就是来操做咱们定义类的属性和方法。安全
小试牛刀:经过Class这个类来获取Fruit这个类中定义的方法;oracle
public static void main(String[] args) { Fruit fruit = new Fruit(); Class fruitClass = fruit.getClass(); Method[] fruitMethods = fruitClass.getMethods(); System.out.println("方法个数:" + fruitMethods.length); for (Method method : fruitMethods) { //获得返回类型 System.out.print("方法名称和参数:" + method.getName() + "("); //取得某个方法对应的参数类型数组 Class[] paramsType = method.getParameterTypes(); for (Class paramType : paramsType) { System.out.print(paramType.getTypeName() + " "); } System.out.print(")"); Class returnType = method.getReturnType(); System.out.println("返回类型:" + returnType.getTypeName()); } }
运行结果:框架
方法个数:15 方法名称和参数:getName()返回类型:java.lang.String 方法名称和参数:setName(java.lang.String )返回类型:void 方法名称和参数:getSize()返回类型:int 方法名称和参数:setSize(int )返回类型:void 方法名称和参数:use()返回类型:void 方法名称和参数:doFruit()返回类型:void 方法名称和参数:wait()返回类型:void 方法名称和参数:wait(long int )返回类型:void 方法名称和参数:wait(long )返回类型:void 方法名称和参数:equals(java.lang.Object )返回类型:boolean 方法名称和参数:toString()返回类型:java.lang.String 方法名称和参数:hashCode()返回类型:int 方法名称和参数:getClass()返回类型:java.lang.Class 方法名称和参数:notify()返回类型:void 方法名称和参数:notifyAll()返回类型:void
这里可能有人疑惑了,Fruit类并无定义的方法为何会出现,如wait(),equals()方法等。这里就有必要说一下java的继承和反射机制。在继承时,java规定每一个类默认继承Object这个类,上述这些并无在Fruit中定义的方法,都是Object中的方法,咱们看一下Object这个类的源码就会一清二楚:jvm
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public final native void wait(long timeout) throws InterruptedException; public final void wait() throws InterruptedException { wait(0); }
而Class类中的getMethods()方法默认会获取父类中的公有方法,也就是public修饰的方法。因此Object中的公共方法也出现了。ide
注: 要想得到父类的全部方法(public、protected、default、private),可使用apache commons包下的FieldUtils.getAllFields()能够获取类和父类的全部(public、protected、default、private)属性。
是否是感受很是的强大 ,固然,使用Class来获取一些类的方法和属性的核心思想就是利用了Java反射特性。万物皆反射,可见反射的强大之处,至于反射的原理,期待个人下一个博客。
源码以下:
private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
能够看到Class类只有一个构造函数,而且是私有的。也就是说不能经过new来建立这个类的实例。官方文档的解释:私有构造函数,仅Java虚拟机建立Class对象。我想可能就是为了安全,具体缘由不是很了解。若是有了解的话,能够在评论区内共同的交流。
那么既然这个class构造器私有化,那咱们该如何去构造一个class实例呢,通常采用下面三种方式:
1.运用.class的方式来获取Class实例。对于基本数据类型的封装类,还能够采用.TYPE来获取相对应的基本数据类型的Class实例,以下的示例。
// 普通类获取Class的实例。接口,枚举,注解,均可以经过这样的方式进行得到Class实例 Class fruitClass = Fruit.class; // 基本类型和封装类型得到Class实例的方式,二者等效的 Class intClass = int.class; Class intClass1 = Integer.TYPE;
下面的表格两边等价:
boolean.class | Boolean.TYPE |
---|---|
char.class | Character.TYPE |
byte.class | Byte.TYPE |
short.class | Short.TYPE |
int.class | Integer.TYPE |
long.class | Long.TYPE |
float.class | Float.TYPE |
double.class | Double.TYPE |
void.class | Void.TYPE |
可是这种方式有一个不足就是对于未知的类,或者说不可见的类是不能获取到其Class对象的。
2.利用对象.getClass()方法获取该对象的Class实例;
这是利用了Object提供的一个方法getClass() 来获取当着实例的Class对象,这种方式是开发中用的最多的方式,一样,它也不能获取到未知的类,好比说某个接口的实现类的Class对象。
Object类中的getClass()的源码以下:
public final native Class<?> getClass();
源码说明:
能够看到,这是一个native方法(一个Native Method就是一个java调用非java代码的接口),而且不容许子类重写,因此理论上全部类型的实例都具备同一个 getClass 方法。
使用:
Fruit fruit = new Fruit(); Class fruitClass = fruit.getClass();
3.使用Class类的静态方法forName(),用类的名字获取一个Class实例(static Class forName(String className) ),这种方式灵活性最高,根据类的字符串全名便可获取Class实例,能够动态加载类,框架设计常常用到;
源码以下:
/* 因为方法区 Class 类型信息由类加载器和类全限定名惟一肯定,因此参数name必须是全限定名, 参数说明 name:class名,initialize是否加载static块,loader 类加载器 */ public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { Class<?> caller = null; // 1.进行安全检查 SecurityManager sm = System.getSecurityManager(); if (sm != null) { .... } } // 2.调用本地的方法 return forName0(name, initialize, loader, caller); } // 3.核心的方法 private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class<?> caller) throws ClassNotFoundException; /* 这个 forName是上述方法的重载,平时通常都使用这个 方法默认使用调用者的类加载器,将类的.class文件加载 到 jvm中 这里传入的initialize为true,会去执行类中的static块 */ public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }
源码说明已在注释中说明,有些人会疑惑, static native Class<?> forName0()这个方法的实现。
这就要说到java的不完美的地方了,Java的不足除了体如今运行速度上要比传统的C++慢许多以外,Java没法直接访问到操做系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。
基本使用:
Class fruitClass = Class.forName("cn.chen.test.util.lang.Fruit");
注: 这种方式必须使用类的全限定名,,这是由于因为方法区 Class 类型信息由类加载器和类全限定名惟一肯定,不然会抛出ClassNotFoundException的异常。
Class类的通常的方法总共有六十多种,其实看到这么多方法咱也不要怂,这里面还有不少重载的方法,根据二八原则,咱们平时用的也就那么几个方法,因此这里只对如下几个方法的使用和实现进行交流,其余的方法能够移步Java官方文档:
这个方法主要是用来了解一个类的构造方法有哪些,包含那些参数,特别是在单例的模式下。通常包含的方法以下:
public Constructor[] getConstructors() :获取类对象的全部可见的构造函数
public Constructor[] getDeclaredConstructors():获取类对象的全部的构造函数
public Constructor getConstructor(Class... parameterTypes): 获取指定的可见的构造函数,参数为:指定构造函数的参数类型数组,若是该构造函数不可见或不存在,会抛出 NoSuchMethodException 异常
基本使用:
Constructor[] constructors = fruitClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println("得到共有的构造方法:"+constructor); }
输出结果:
得到共有的构造方法:public cn.chen.test.util.lang.Fruit() 得到共有的构造方法:public cn.chen.test.util.lang.Fruit(java.lang.String,int)
能够看到咱们前面定义的来个构造方法,都被打印出来了。注意getConstructors()只能得到被public修饰的构造方法,若是要得到被(protected,default,private)修饰的构造方法,就要使用的getDeclaredConstructors()这个方法了。接下来,修改Fruit中的一个构造方法为private:
private Fruit(String name,int size){ this.name = name; this.size =size; }
使用getConstructors()和getDeclaredConstructors()着两个方法进行测试:
Class fruitClass = Fruit.class; Constructor[] constructors = fruitClass.getConstructors(); Constructor[] constructors1 = fruitClass.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println("得到共有的构造方法:"+constructor); } System.out.println("================================================="); for (Constructor constructor : constructors1) { System.out.println("得到全部的构造方法:"+constructor); }
输出结果:
得到共有的构造方法:public cn.chen.test.util.lang.Fruit() ===================分隔线============================= 得到全部的构造方法:public cn.chen.test.util.lang.Fruit() 得到全部的构造方法:private cn.chen.test.util.lang.Fruit(java.lang.String,int)
能够看到二者的区别。因此,反射在必定程度上破坏了java的封装特性。毕竟人无完人,语言亦是同样。
getConstructors()的源码分析:
public Constructor<?>[] getConstructors() throws SecurityException { // 1.检查是否容许访问。若是访问被拒绝,则抛出SecurityException。 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyConstructors(privateGetDeclaredConstructors(true)); } private static <U> Constructor<U>[] copyConstructors(Constructor<U>[] arg) { // 2.使用克隆,获得当前类的全部构造函数 Constructor<U>[] out = arg.clone(); // 3.使用ReflectionFactory构造一个对象,也是不使用构造方法构造对象的一种方式。 ReflectionFactory fact = getReflectionFactory(); // 4.遍历,将构造函数进行拷贝返回,注意在调用fact.copyConstructor(out[i])这个方法的时候,还会进行安全检查,用的就是下面的LangReflectAccess() 这个方法。 for (int i = 0; i < out.length; i++) { out[i] = fact.copyConstructor(out[i]); } return out; } private static LangReflectAccess langReflectAccess() { if (langReflectAccess == null) { Modifier.isPublic(1); } return langReflectAccess; }
经过打断点调试,能够看到下面的信息:
代码的调用逻辑在注释里已进行说明。
主要获取类的属性字段,了解这个类声明了那些字段。
通常有四个方法:
基本使用:
首先咱们在Fruit的类中加入一个public修饰的属性:
public double weight;
Class fruitClass = Fruit.class; Field[] field2 = fruitClass.getFields(); for (Field field : field2) { System.out.println("定义的公有属性:"+field); } Field[] fields = fruitClass.getDeclaredFields(); for (Field field : fields) { System.out.println("定义的全部属性:"+field); }
输出结果:
定义的公有属性:public double cn.chen.test.util.lang.Fruit.weight ========================分隔线============================ 定义的全部属性:private java.lang.String cn.chen.test.util.lang.Fruit.name 定义的全部属性:private int cn.chen.test.util.lang.Fruit.size 定义的全部属性:public double cn.chen.test.util.lang.Fruit.weight
源码分析,就以getFileds()这个方法为例,涉及如下几个方法:
public Field[] getFields() throws SecurityException { // 1.检查是否容许访问。若是访问被拒绝,则抛出SecurityException。 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyFields(privateGetPublicFields(null)); } private static Field[] copyFields(Field[] arg) { // 2\. 声明一个Filed的数组,用来存储类的字段 Field[] out = new Field[arg.length]; // 3.使用ReflectionFactory构造一个对象,也是不使用构造方法构造对象的一种方式。 ReflectionFactory fact = getReflectionFactory(); // 4.遍历,将字段复制后返回。 for (int i = 0; i < arg.length; i++) { out[i] = fact.copyField(arg[i]); } return out; } public Field copyField(Field var1) { return langReflectAccess().copyField(var1); } // 再次检查属性的访问权限 private static LangReflectAccess langReflectAccess() { if (langReflectAccess == null) { Modifier.isPublic(1); } return langReflectAccess; }
就是获取一个类中的方法,通常有如下几个方法:
public Method[] getMethods(): 获取全部可见的方法
public Method[] getDeclaredMethods() :获取全部的方法,不管是否可见
public Method getMethod(String name, Class... parameterTypes)
参数说明:
null
做为参数类型数组,或者不传值)null
做为参数类型数组,或者不传值)基本使用:
//在fruit中定义一个这样的方法 private void eat(String describe){ System.out.println("经过getMethod()方法调用了eat()方法: "+describe); }
调用这个方法:
Class fruitClass = Fruit.class; Method method = fruitClass.getDeclaredMethod("eat",String.class); method.setAccessible(true); method.invoke(fruitClass.newInstance(),"我是该方法的参数值");
输出结果:
经过getMethod()方法调用了eat()方法:我是该方法的参数值
分析getDeclaredMethod()涉及的源码:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { // 1.检查方法的修饰符 checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); // 2.searchMethods()方法的第一个参数肯定这个方法是否是私有方法,第二个参数咱们定义的方法名,第三个参数就是传入的方法的参数类型 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method; } // 这个方法就是经过传入的方法名找到咱们定义的方法,而后使用了Method的copy()方法返回一个Method的实例,咱们经过操做mehtod这个实例就能够操做咱们定义的方法。 private static Method searchMethods(Method[] methods, String name, Class<?>[] parameterTypes) { Method res = null; String internedName = name.intern(); for (int i = 0; i < methods.length; i++) { Method m = methods[i]; if (m.getName() == internedName && arrayContentsEq(parameterTypes, m.getParameterTypes()) && (res == null || res.getReturnType().isAssignableFrom(m.getReturnType()))) res = m; } return (res == null ? res : getReflectionFactory().copyMethod(res)); } public Method copyMethod(Method var1) { return langReflectAccess().copyMethod(var1); } // 检查属性的访问权限 private static LangReflectAccess langReflectAccess() { if (langReflectAccess == null) { Modifier.isPublic(1); } return langReflectAccess; }
这类型的方法顾名思义,就是来判断这个类是什么类型,是接口,注解,枚举,仍是通常的类等等。部分方法以下表
boolean |
isAnnotation() 判断是否是注解 |
---|---|
boolean |
isArray() 判断是否为数组 |
boolean |
isEnum() 判断是否为枚举类型 |
boolean |
isInterface() 是否为接口类型 |
boolean |
isMemberClass() 当且仅当基础类是成员类时,返回“true” |
boolean |
isPrimitive() 肯定指定的“类”对象是否表示原始类型。 |
boolean |
isSynthetic() 若是这个类是合成类,则返回' true ';不然返回“false”。 |
基本用法:
// 定义一个接口: interface Animal{ public void run(); }
判断是否是一个接口:
Class AnimalClass = Animal.class; boolean flag = AnimalClass.isInterface(); System.out.println(flag);
输出结果:
true
源码分析isInterface():
public native boolean isInterface();
这是一个native方法,你们都知道native方法是非Java语言实现的代码,供Java程序调用的,由于Java程序是运行在JVM虚拟机上面的,要想访问到比较底层的与操做系统相关的就没办法了,只能由靠近操做系统的语言来实现。
将对象转换为字符串。字符串表示形式是字符串“类”或“接口”,后跟一个空格,而后是该类的全限定名。
基本使用:
// 这是前面定义的两个类Fruit和Car,Car是一个接口 Class fruitClass = Fruit.class; Class AnimalClass = Animal.class; System.out.println(AnimalClass.toString()); System.out.println(fruitClass.toString());
输出结果:
// 格式 字符串“类”或“接口”,后跟一个空格,而后是该类的全限定名 interface cn.chen.test.util.lang.Animal class cn.chen.test.util.lang.Fruit
源码以下:
public String toString() { // 先是判断是接口或者类,而后调用getName输出类的全限定名 return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); } public native boolean isInterface(); public native boolean isPrimitive();
追本溯源,方能阔步前行。