一、引言:
首先,要理解一句话“万事万物皆对象。”
比如一个类Foo:(1)它的实例对象,new Foo()出来就可以;(2)那么这个Foo是不是对象呢?是的,Foo这个类是java.lang.Class的实例对象。那么它如何表示呢?new Class()就可以吗?
Class源码:
构造函数是私有的。只有JVM才能创建。
那么Class的实例对象Foo如何(用Class)表示呢?
二、创建Foo类的类类型
Class的实例对象Foo如何(用Class)表示方法:
(1)
Class c1=Foo.class;
(2)已经知道该类的对象通过getClass方法
Class c2 = foo1.getClass();
(3)
Class.forName()
注:
c1,c2,c3表示Foo类的类类型(class type)
也就是说:Foo类的实例对象表示Foo类的实例对象
Foo类的类类型表示Class类的实例对象。
三、通过类的类类型创建类的实例对象
即通过c1 or c2 or c3创建Foo的实例对象:
c1.newInstance(); //前提:Foo有无参构造方法
四、区分编译和运行时期加载类
我们说编译时刻加载类为静态加载类,而运行时刻加载类是动态加载类。
我们看一段代码:
在编译时期,new要加载需要的类,也就是在编译时期,Word和Excel都要加载。
那么存在一个问题,如果Word存在,Excel不存在,我只要用Word,是不可以的,因为编译时期Excel不过。我希望的是,Excel不要捣乱,用到你时你再加载,也就是用哪个,哪个加载,不用就不加载,也就是我们所说的动态加载。
我们写一个能动态加载类类型的类:
获得类类型,并获得实例对象。但是获得谁的呢?
Word w = (Word)c.newInstance();
Excel e = (Excel)c.newInstance();
都不行。我们可以统一一下,比如实例一个他们俩都可以用的实例对象。
那么OfficeAble就是一个interface。让Word和Excel实现它就可以了。
五、万事万物皆对象之所以再提这个是因为,通过上面我们知道,类的实例对象是对象,类本身也是对象。其实还有更广泛的:
void也有类类型。
也就是说只要在类里,基本类型、关键字==>都有类类型。
那么获取它们的类类型可以做什么呢?就要看Class类的API操作了。
六、Class类的基本API操作
package com.ali.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassUtil {
public static void printClassMethodMessage(Object obj){
//要获取类的信息 首先要获取类的类类型
Class c = obj.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
//获取类的名称
System.out.println("类的名称是:"+c.getName());
/*
* 方法也是对象,是Method对象
* getMethods()方法获取的是所有的public的函数,包括父类继承而来的
* getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
*/
Method[] ms = c.getMethods();
for(int i = 0; i < ms.length;i++){
//得到方法的返回值类型的类类型
//如果返回值是String,那么 ms[i].getReturnType()得到的就是String.class,
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName()+" ");
//得到方法的名称
System.out.print(ms[i].getName()+"(");
//获取参数类型--->得到的是参数列表的类型的类类型(int.class,String.class.....)
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
/**
* 获取成员变量的信息
*/
public static void printFieldMessage(Object obj) {
Class c = obj.getClass();
/*
* 成员变量也是对象,是Field对象
* getFields()方法获取的是所有的public的成员变量的信息
* getDeclaredFields获取的是该类自己声明的成员变量的信息
*/
Field[] fs = c.getDeclaredFields();
for (Field field : fs) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}
}
/**
* 打印对象的构造函数的信息
*/
public static void printConMessage(Object obj){
Class c = obj.getClass();
/*
* 构造函数也是对象,是Constructor对象
* getConstructors获取所有的public的构造函数
* getDeclaredConstructors得到所有的构造函数
*/
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表--->得到的是参数列表的类类型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}
通过上述代码可以知道,方法、成员变量、构造函数都是对象,有自己的类类型。
七、反射
那么我们就知道什么是反射操作了:
比如a1.print(10);这个方法是a1操作print(),那么反射就是print()这个方法对象来操作a1。
A a1 = new A();
Class c = a1.getClass();//获取Class类的实例对象
Method m = c.getMethod("print", int.class);//获取Method类的实例对象
Object o = m.invoke(a1, 10);//Method类的实例对象 操作 Class类的实例对象
八、扩展:通过反射了解集合泛型的本质
package com.ali.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class MethodDemo4 {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList<String>();
list2.add("hello");
//list2.add(20);错误的
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2);//返回true
}
}
list2.add(20)在编译期就会报错,但是当我们运行期加载c1 c2时,发现c1==c2为true,也就是说,编译之后,集合是去泛型的。
我们来验证一下,我们用通过方法反射来操作list2.add(20),使之绕过编译:
package com.ali.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class MethodDemo4 {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList<String>();
list2.add("hello");
//list2.add(20);错误的
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2);//返回true
try {
Method m = c2.getMethod("add", Object.class);
m.invoke(list2, 20);//绕过编译操作就绕过了泛型
System.out.println(list2.size());//打印出来是2,也就是说,list2加上20这个int类型了。
} catch (Exception e) {
e.printStackTrace();
}
}
}