1、Class类
1)任何一个类都是(java.lang.)Class的实例,这个实例对象有三个表示对象。如下所示:
public class ClassDemo1 {
public static void main(String[] args) {
Foo f = new Foo();
//第一种方式:通过类名获得(由这种方式可得任何一个类都有一个隐含的静态成员变量class)
Class c1=Foo.class;
//第二种方式:通过该类的对象名,用getClass方法获得
Class c2=f.getClass();
//第三种方式:通过 Class.forName("类全称");获得
try {
Class c3 = Class.forName("com.java.demo.Foo");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/**
* c1、c2表示Foo类的类类型(class type)
* 类也是对象,是(java.lang.)Class的实例对象
* 该类的类类型可以创建该类型的对象实例,如通过c1、c2创建Foo的实例
* eg:
*/
try {
Foo foo = (Foo)c2.newInstance();//注意:要用无参数的构造方法才能newInstance()
}catch (Exception e) {
e.printStackTrace();
}
}
}
class Foo{}
2)Class.forName(“类全名”);不仅表示类的类类型,还代表了动态加载。
3)编译时刻加载的类是静态加载类、运行时加载类是动态加载类。new创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。
4)基本的数据类型、void关键字都存在类类型。
eg:
5)Method、Field、Constructor类分别封装了关于方法、成员变量、构造函数的操作。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassUtils {
/*
* 获取类的方法信息
*/
public static void printClassMethod(Object obj) {
System.out.println("-----------打印方法信息------------");
Class c = obj.getClass();// 首先要获取类的类类型
System.out.println("类的名称是:" + c.getName());
/**
* Method类,方法对象 一个成员方法就是一个Method对象
* getMethods()方法就是获取所有的public函数,包括父类继承而来的
* getDeclaredMethods()获取的是自己声明的方法
*/
Method[] ms = c.getMethods();
for (int i = 0; i < ms.length; i++) {
Class returnType = ms[i].getReturnType();// 得到方法的返回值类型的类类型
System.out.print(returnType.getName() + " ");// 打印返回值类型的名称
System.out.print(ms[i].getName());// 得到方法的名称
// 获取参数类型,得到的是参数类表的类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
System.out.print("(");
for (Class class1 : paramTypes) {
System.out.print(class1.getTypeName() + ",");
}
System.out.println(")");
}
}
/*
* 获取类的成员变量信息
*/
public static void printClassMenbers(Object obj) {
System.out.println("-----------打印成员变量信息------------");
Class c = obj.getClass();
/**
* 成员变量也是对象,是java.lang.reflect.Field的对象 Field封装了关于成员变量的操作
* getFields()方法获取的是所有的public的成员信息 getDeclaredFields获取的是该类自己声明的成员变量的信息
*/
Field[] fs = c.getFields(); // c.getDeclaredFields()
for (Field field : fs) {
Class fieldType = field.getType();// 得到成员变量的类型的类类型
String typeName = fieldType.getName();// 得到成员变量的名称
String fieldName = field.getName();// 得到成员变量的名称
System.out.println(typeName + " " + fieldName);
}
}
/**
* 构造函数也是对象,是java.lang.Constructor中封装了构造函数的信息
* getConstructors()方法获取到所有public的构造函数 getDeclaredConstructors()得到所有的构造函数
*/
public static void printClassConstructor(Object obj) {
System.out.println("-----------打印构造函数信息------------");
Class c = obj.getClass();
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.getTypeName() + " "
+ class1.getName());
}
System.out.println(")");
}
}
}
2、方法的反射操作
1)反射调用一般分为3个步骤:
(1)得到要调用类的类类型(可从上面介绍的3种方式获得)。
(2)得到要调用的类中的方法(Method)。
(3)调用Method的invoke()方法。
eg:
A a = new A();//要获取类中的方法,也就是要获取类的信息,先要获取类的类类型
Class c = a.getClass();//获取A类的类类型
Method m = c.getMethod("print", int.class, int.class);//获取类的print(int a, int b);方法
m.invoke(a, new Object[]{1,2});//也可以写成m.invoke(a, 1,2);
说明:当使用非反射方式调用方法时,是a.print(1,2),即通过类对象调用方法;而反射操作:方法的反射操作时用Method实例对象来进行方法调用,这a.print(1,2)的作用是一模一样的;方法如果没用返回值则返回null,有返回值则返回具体的返回值。
完整例子:
public class MethodReflectDemo {
public static void main(String[] args) {
A a = new A();
//要获取类中的方法,也就是要获取类的信息,先要获取类的类类型
Class c = a.getClass();//获取A类的类类型
//这个是Method[] ms = c.getMethods();是获取类的所有的public方法,而getMethod()获取的是某一个方法,具体获取哪个方法看getMethod()中传入了什么参数
try {
//Method m = c.getMethod("print",new Class[]{int.class, int.class});可以写成:
Method m = c.getMethod("print", int.class, int.class);//获取类的print(int a, int b);方法
m.invoke(a, new Object[]{1,2});//也可以写成m.invoke(a, 1,2);
Method m1 = c.getMethod("print", new Class[]{String.class, String.class});//获取类的print(String a, String b)方法
m1.invoke(a, "hi","hello");//反射操作,调用方法
Method m2 = c.getMethod("print");//获取类的print()方法;或者写成:Method m2 = c.getMethod("print",new Class[]{});
m2.invoke(a);//反射操作,调用方法;或者写成:m2.invoke(a, new int[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A {
public void print(){
System.out.println("haha");
}
public void print(int a, int b){
System.out.println(a+b);
}
public void print(String a, String b){
System.out.println(a + "," + b);
}
}
3、通过反射了解泛型的本质
java中集合的泛型,是防止错误输入的,只在编译时有效,绕过编译就无效了。
eg:
public class GenericList {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();//实例一个无泛型的ArrayList对象list1
ArrayList<Integer> list2 = new ArrayList<Integer>();//实例一个Integer型的ArrayList对象list2
list1.add("abc");//在无泛型的list1中添加字符串“abc”
list1.add(123);//在无泛型的list1中添加整型数据123
list2.add(123);//在泛型位Integer的list1中添加整型数据123
//list2.add("abc");//在泛型位Integer的list1中添加字符串数据“abc”,Eclipse提示出错
Class c1 = list1.getClass();//获得list1的类类型
Class c2 = list2.getClass();//获得list2的类类型
System.out.println(c1==c2);//打印“true”,说明编译之后集合的泛型是去泛型化的
/**
* java中集合的泛型,是防止错误输入的,只在编译时有效,绕过编译就无效了
* 下面进行验证:通过方法的反射来操作,绕过编译
*/
try {
Method m = c1.getMethod("add",Object.class);//获得ArrayList中的add()方法
m.invoke(list1,11);//list1,反射调用add()方法,为list1添加整型数据
m.invoke(list2, "dfdf");//list2,反射调用add()方法,为list2添加字符串
m.invoke(list2, "dfdf123");//list2,反射调用add()方法,为list2添加字符串
System.out.println(list2.size());//运行后打印"3",说明字符串"dfdf123"插入、list2成功。
} catch (Exception e) {
e.printStackTrace();
}
}
}
说明:本来list2只能存放int型数据的,但由于这里用的是反射操作,就绕过了编译,就绕过泛型,所以list2也能存String类的数据。也就说明了java中集合的泛型,只在编译时有效,绕过编译就无效了。