Java反射
1. Claass类的使用
1)在面向对象的世界里,万事万物皆对象
Java语言中,静态的成员、普通数据类型(但是他有包装类)不是对象。类也是对象,类是java.lang.Class类的实例对象。”There is a class named Class”——有一个类,名字就叫Class,他的实例就是我们每一个对象所属的类。
2)这个对象如何表示?
package com.cie.reflect;
public class ClassDemo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Foo foo1 = new Foo(); //foo1就表示出来了
//Foo这个类,也是一个实例对象,Class类的实例对象
//任何一个类都是Class的实例对象,这个实例对象有三种表示方式
//第一种表示方式-->实际在告诉我们,任何一个类都有一个隐含的静态成员变量class.
Class c1 = Foo.class;
//第二种表示方式-->已经知道该类的对象通过getClass方法
Class c2 = foo1.getClass();
/*官网,c1,c2表示了Foo类的类类型(class type)
* 万事万物皆对象,
* 类也是对象,是class类的实例对象
* 这个对象我们成为该类的类类型
*/
//不管c1或者c2,都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象
System.out.println(c1==c2); //true
//第三种表示方式
Class c3 = null;
try {
c3 = Class.forName("com.cie.reflect.Foo");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(c2==c3); //true
//我们完全可以通过类的类类型创建该类的对象实例--> 通过c1或c2或c3创建Foo的实例
try {
Foo foo = (Foo) c1.newInstance(); //需要有无参数的构造方法
foo.print();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Foo{
void print(){
System.out.println("foo");
}
}
2. Java动态加载类
Class.forName(“类的全称”)
不仅表示了类的类类型,还代表了动态加载类
区分编译、运行
编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
public class office{
public static void main(String[] args){
//new创建对象是静态加载类,在编译时刻就需要加载所有的可能使用到的类
//通过动态加载类可以解决该问题(用哪个加载哪个,不用不加载,即在运行时刻加载)
if("Word".equals(args[0])){
Word w = new Word();
w.start();
}
if("Excel".equals(args[0])){
Excel e = new Excel();
e.start();
}
}
}
可以做如下修改。
public class Word implements OfiiceAble {
@Override
public void start() {
System.out.println("Word...");
}
}
public interface OfiiceAble {
public void start();
}
public class OfficeBetter {
public static void main(String[] args) {
try {
//动态加载类,在运行时刻加载
Class c = Class.forName(args[0]); //参数传入类名
//通过类类型,创建该类的对象
//Word w = (Word)c.newInstance();
//Excel e = (Excel)c.newInstace(); //这两种方式都不合适,因为不确定用到的类具体是哪一个
OfiiceAble oa = (OfiiceAble)c.newInstance();
oa.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这就是通过动态的方式加载类。
3. 获取方法信息
- 基本的数据类型、void关键字,都存在类类型。
public class ClassDemo2 {
public static void main(String[] args) {
Class c1 = int.class; //int的类类型
Class c2 = String.class; //String类的类类型 (String类字节码)
Class c3 = double.class; //double这个数据类型的字节码表示方式
Class c4 = Double.class; //Double这个类的类类型字节码表示方式
Class c5 = void.class;
System.out.println(c1.getName()); //int
System.out.println(c2.getName()); //java.lang.String
//不包含包名的类的名称
System.out.println(c2.getSimpleName()); //String
System.out.println(c5.getName()); //void
}
}
public class ClassUtil {
/**
* @return void
* @param obj 该对象所属类的信息
* 打印类的信息——类的成员函数
*/
public static void printMethodsMessage(Object obj){
//要获取类的信息,首先要获取类的类类型
Class c = obj.getClass(); //传递的是哪个子类的对象,c就是该子类的类类型
//获取类的名称
System.out.println("类的名称是:"+c.getName());
/*
* Method类,方法对象
* 一个成员方法就是一个Method对象
* getMethods()方法获取的是所有的public的函数,包括父类继承而来的
* getDeclaredMethods()获取的是所有该类自己声明的方法(继承来的就没有了,只是自己的),不论访问权限
*/
Method[] ms = c.getMethods(); //c.getDeclaredMethods()
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();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
/**
* @return void
* @param obj 该对象所属类的信息
* 打印类的成员变量信息
*/
private static void printFieldMessage(Object obj) {
Class c = obj.getClass();
/*
* 成员变量也是对象
* java.lang.reflect.Field
* Field类封装了关于成员变量的操作
* getFields()方法获取的是所有的public的成员变量的信息
* getDeclaredFields()获取的是该类自己声明的成员变量的信息
*/
//Fields[] fs = c.getFields();
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 class ClassDemo3 {
public static void main(String[] args) {
String s = "hello";
ClassUtil.printMethodsMessage(s);
Integer n1 = 1;
ClassUtil.printFieldMessage(n1);
ClassUtil.printConMessage(n1);
}
}
4. 获取成员变量构造函数信息
/**
* @return void
* @param obj
* 打印对象的构造函数的信息
*/
public static void printConMessage(Object obj){
Class c = obj.getClass();
/*
* 构造函数也是对象
* java.lang.Constructor中封装了构造函数的信息
* getConstructors获取所有的public的构造函数
* getDeclaredConstructors得到所有的构造函数
*/
//Constructor[] cs = c.getConstructors();
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(")");
}
}
/*
* 测试
*/
public class ClassDemo3 {
public static void main(String[] args) {
Integer n1 = 1;
ClassUtil.printConMessage(n1);
}
}
5. 方法反射的基本操作
如何获取某个方法,方法的名称和方法的参数列表才能唯一决定某个方法
首先要获取类的类型——getClass()方法,然后获取方法——getMethod(名称,参数列表)
方法反射的操作
method.invoke(对象,参数列表)
package com.cie.reflect;
import java.lang.reflect.Method;
public class MethodDemo1 {
public static void main(String[] args) {
// 要获取print(int ,int)方法
// 1.要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型
A a1 = new A();
Class c = a1.getClass();
/*
* 2.获取方法,名称和参数列表来决定 getMethod获取的是public方法 getDeclaredMethod自己声明的方法
*/
try {
// 处理异常是为了防止该方法不存在
// Method m = c.getMethod("print", new
// Class[]{int.class,int.class});
// getMethod的第二个参数为可变参数。所以也可以写成
Method m = c.getMethod("print", int.class, int.class);
// 方法调用: 传统方法a1.print(10, 20);
/*
* 方法的反射操作是用m对象来进行方法调用,和a1.print调用的效果相同。
* 可以理解为print是个方法对象,m 即为这个对象,原本是用a1对象操作print方法
* 现在用print所代表的的m对象反过来操作a1,即用invoke()操作
* 方法如果没有返回值,返回null,有返回值,返回具体的返回值(原本是Object,所以要做强制类型转换)
*/
Object o = m.invoke(a1, new Object[]{10,20});
//Object o = m.invoke(a1, 10,20);
System.out.println("================分割线================");
//获取print(String,String)方法
Method m1 = c.getMethod("print", String.class, String.class);
//用方法进行反射操作
//a1.print("hello", "world");
o = m1.invoke(a1, "hello", "world");
System.out.println("================分割线================");
//获取print()方法
//Method m2 = c.getMethod("print",new Class[]{});
Method m2 = c.getMethod("print");
//m2.invoke(a1, new Object[]{});
m2.invoke(a1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class A {
public void print() {
System.out.println("Hello!");
}
public void print(int a, int b) {
System.out.println(a + b);
}
public void print(String a, String b) {
System.out.println(a.toUpperCase() + "," + b.toLowerCase());
}
}
6. 通过反射了解集合泛型的本质
- 反射的操作、Class的操作、Method的操作、Field的操作都是绕过编译,都是在运行时刻来执行的
package com.cie.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class MethodDemo2 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
ArrayList<String> list1 = new ArrayList<String>();
list1.add("String");
//list1.add(20); //赋值错误,编译不通过
Class c1 = list.getClass();
Class c2 = list1.getClass();
System.out.println("c1=c2?"+(c1==c2)); //true
//反射的操作都是编译之后的操作(即运行时)
/*
* c1==c2结果返回true,说明编译之后集合的泛型是去泛型化的
* Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了。
* 验证:我们可以通过方法的反射来操作,绕过编译
*/
try {
Method m = c2.getMethod("add", Object.class);
m.invoke(list1, 20); //绕过变异操作就绕过了泛型
System.out.println("list1.size="+list1.size()); //2
System.out.println(list1); //[String, 20]
/*for (String string : list1) { //遍历会出错,因为int值不能转为String
System.out.println(string);
}*/ //现在不能这样遍历,会有类型转换错误
} catch (Exception e) {
e.printStackTrace();
}
}
}