一起来学习Java的知识:反射
2018-05-27 本文已影响57人
__y
1.Class类的使用
什么是Class类呢?
我们知道在面向对象的思想中,万物都是对象。那么我们写的类就是java.lang.Class类的实例对象;这个对象我们称为该类的类类型
Class表示的方法
package com.test.reflect;
class Foo{
}
public class ClassDemo1 {
public static void main(String[] args) {
//获得Foo的实例
Foo foo1 = new Foo();
//Class类的实例对象的表示方法,有三种该表示方法
//第一种方式
Class c1 = Foo.class;
//第二种方式
Class c2 = foo1.getClass();
//c1 ,c2表示了Foo类的类类型(class Type)
System.out.println(c1 == c2);
//第三种方式
try {
Class c3 = Class.forName("com.test.reflect.Foo");
System.out.println(c2 == c3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//实例化Foo的对象
Foo f2 = (Foo) c1.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
image.png
- 第一种方式:我们知道了每个类都有一个隐式的静态方法
- 第二种方式:我们通过实例对象的getClass可以获得实例对象的类类型
- 第三种方式:要注意的是要写全称,包括包名等
动态加载类
我们可以看到上面第三种方式Class.forName("类的全称");
- 代表了类的类类型,还代表了动态加载类
- 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
我们在写程序的时候用的ide可能不太明显,下面我们用记事本的方式来看看什么是动态什么是静态
public class Office {
public static void main(String[] args) {
if("Word".equals("args[0]")) {
Word word = new Word();
w.start();
}
if("Excel".equals(args[0])) {
Excel excel = new Excel();
e.start();
}
}
}
编译提示:
这个时候我们想一想,我们这时候如果加入一个Word类,他还是会提示Excel不存在;
这就证明了new创建对象是静态加载类实在编译的时候就加载了所需要的类,如果没有的话就会报错,但在实际应用中,有时候我们想的是需要什么就加载什么,这个时候我们就可以通过动态加载的方式解决该问题。
我们接下来改造一下
public class Office {
public static void main(String[] args) {
try {
Class c = Class.forName(args[0]);
OfficeAble officeAble = null;
officeAble = (OfficeAble) c.newInstance();
officeAble.start();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
现在不报错了
image.png
再来改造一下Word类,让他更适用
interface OfficeAble {
public void start();
}
public class Word implements OfficeAble{
public void start() {
System.out.println("Word---start");
}
}
结果:
image.png
定义一个接口的话,可以使代码更具有扩展性,当我们想增加更多地Office的应用的时候比如Excel的时候直接实现接口,实现动态加载。
以后我们实现的时候,功能性的类尽可能使用动态加载
基本数据类型
基本的数据类型有类类型吗?答案是有的
package com.test.reflect;
public class ClassDemo2 {
public static void main(String[] args) {
Class c1 = int.class;
Class c2 = double.class;
Class c3 = void.class;
Class c4 = String.class;
//获得类类型的名字
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c3.getName());
System.out.println(c4.getName());
//简称,不包含包名
System.out.println(c4.getSimpleName());
}
}
image.png
2.方法的反射
创建一个工具类
package com.test.reflect;
import java.lang.reflect.Method;
public class ClassUtil {
/**
* 打印类的信息,包括类的信息,类的成员函数,类的成员变量
* @param object 该类对象的所属类信息
*/
public static void printClassMessage(Object object) {
//获取类的类类型
Class c = object.getClass();//该类的子类的类类型,c就是该子类的类类型
//获取类的名称
System.out.println(c.getName());
/**
* 方法对象
* 获取该类的方法,一个成员方法就是一个Method对象
* getMethods()获取所有的public的函数,包括父类继承而来的
* getDeclaredMethodes()获取的所有该类自己声明的方法,不问访问权限
*/
Method[] methods = c.getMethods();
for (Method method : methods) {
//得到方法的返回值类型的类类型:比如如果返回的是Siring 则返回的是String.class
Class returnType = method.getReturnType();
//得到方法的返回类类型的名字
System.out.print(returnType.getName() + " ");
//得到方法的名字
System.out.print(method.getName() + "(");
//获取参数类型,得到的是参数列表的类型的类类型比如 如果参数类型是Int 则得到的Int.class
Class[] parameterTypes = method.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.print(parameterType.getName() + ",");
}
System.out.println(")");
}
}
}
测试类
package com.test.reflect;
public class TestClient {
public static void main(String[] args) {
String str = "Hello";
ClassUtil.printClassMessage(str);
}
}
结果:
image.png
可以到结果我们可以获取的方法的类型,名字,参数列表等信息
3.成员变量的反射
在ClassUtil类中加一个方法
public static void getFieldMessage(Object object) {
Class c = object.getClass();
Field[] fs = c.getDeclaredFields();
for (Field field : fs) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
//得到成员变量类型的名字
String typeName = fieldType.getTypeName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}
package com.test.reflect;
public class TestClient {
public static void main(String[] args) {
String str = "Hello";
//ClassUtil.printClassMessage(str);
ClassUtil.getFieldMessage(str);
}
}
结果:
image.png
4.构造函数的反射
public static void getConMessage(Object object) {
Class c = object.getClass();
Constructor[] constructors= c.getDeclaredConstructors();
for (Constructor cs: constructors ) {
//获得构造方法的名字
System.out.println(cs.getName() + "(");
//获取构造方法的参数列表
Class[] parameterTypes = cs.getParameterTypes();
for (Class parameterType:
parameterTypes) {
System.out.print(parameterType.getName()+ ",");
}
System.out.println(")");
}
}
package com.test.reflect;
public class TestClient {
public static void main(String[] args) {
String str = "Hello";
//ClassUtil.printClassMessage(str);
//ClassUtil.getFieldMessage(str);
ClassUtil.getConMessage(str);
}
}
结果:
image.png
5.方法的反射
如何获取某个方法呢?我们需要方法的名称和方法的参数列表才能知道是哪个方法
package com.test.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class A {
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());
}
}
public class MethodDemo {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
A a1 = new A();
Class c = a1.getClass();
//获取方法
try {
Method m = c.getMethod("print",int.class,int.class);
//方法的反射操作
//方法如果没有返回值则返回null,没有则是Null
m.invoke(a1,new Object[]{10,20});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
image.png
6.总结
我们可以看到利用反射的机制可以写出很多通用性的代码,在运行的时候获取变量,构造方法,调用对象的方法等。这种对于编写框架等是非常有用的,但是对于写应用程序是很脆弱的。因为编译器很难帮我们发现程序中的错误,因此只有在运行的时候才发现错误并导致异常。