一、什么是反射?
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
二、反射的三种方式
这里需要跟大家说一下,所谓反射其实是获取类的字节码文件,也就是.class文件,那么我们就可以通过Class这个对象进行获取。
1、第一种方式
这个方法其实是Object的一个方法,Class继承了Object,所以我们可以直接使用。
代码语言:javascript复制public class Test02 {
public static void main(String[] args) {
// 创建一个对象
Test02 t = new Test02();
// 获取该对象的Class对象
Class c = t.getClass();
// 获取类名称
System.out.println(c.getName()); // com.ms.Test02
}
}
2、第二种方式
代码语言:javascript复制public class Test02 {
public static void main(String[] args) {
Class c = Test02.class;
// 获取类名称
System.out.println(c.getName()); // com.ms.Test02
}
}
3、第三种
这里需要注意,通过类的全路径名获取Class对象会抛出一个异常,如果根据类路径找不到这个类那么就会抛出这个异常。
代码语言:javascript复制public class Test02 {
public static void main(String[] args) {
try {
// 根据类的全路径名获取
Class c = Class.forName("com.ms.Test02");
// 获取类名称
System.out.println(c.getName()); // com.ms.Test02
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
那么这3中方式我们一般选用哪种方式呢?第一种已经创建了对象,那么这个时候就不需要去进行反射了,显得有点多此一举。第二种需要导入类的包,依赖性太强。所以我们一般选中第三种方式。
三、通过反射获取类的构造方法、方法以及属性
1、获取构造方法
代码语言:javascript复制public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException {
// 加载Class对象
Class c = Class.forName("com.reflect.User");
System.out.println("===========================获取所有公用的构造方法==============================");
// 获取所有公用的构造方法
Constructor[] constructors = c.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=============================获取所有的构造方法============================");
// 获取所有的构造方法
Constructor[] declaredConstructors = c.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("=============================获取公有 & 无参的构造方法============================");
Constructor constructor = c.getConstructor(null);
System.out.println(constructor);
System.out.println("=============================获取公有 & 有参的构造方法============================");
Constructor constructor1 = c.getConstructor(new Class[]{String.class, Integer.class, String.class});
System.out.println(constructor1);
System.out.println("=============================获取私有 & 有参 构造方法============================");
Constructor declaredConstructor1 = c.getDeclaredConstructor(new Class[]{String.class});
System.out.println(declaredConstructor1);
}
}
结果:
2、获取类属性
代码语言:javascript复制public class Test02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 获取Class对象
Class<?> clazz = Class.forName("com.reflect.User");
System.out.println("=====获取所有的公共字段=====");
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=====获取所有的字段(公开的、私有的)=====");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("=====获取公有字段并使用=====");
// 获取指定公有字段
Field field = clazz.getField("name");
// 获取一个公有构造方法,然后实例化
Object obj = clazz.getConstructor().newInstance();
// 为属性设置值
field.set(obj, "张三");
// 测试,看设置的值是否成功
User user = (User) obj;
System.out.println(user.getName());
System.out.println("=====获取私有字段并使用=====");
Field field1 = clazz.getDeclaredField("sex");
// 获取构造函数,实例化对象
Object obj1 = clazz.getConstructor().newInstance();
// 暴力反射
field1.setAccessible(true);
// 给属性设置值
field1.set(obj1, "男");
// 测试
User u = (User) obj1;
System.out.println(u.getSex());
}
}
结果
这里需要注意,在获取私有属性的时候如果没有进行暴力反射,那么会抛出下面这个异常。
3、获取类中的方法
先定义几个方法
代码语言:javascript复制public void method1(String str) {
System.out.println("public 修饰的方法");
}
private void method2() {
System.out.println("private 修饰的方法");
}
String method3(String name, Integer age, String sex) {
System.out.println("默认修饰 " name " " sex " " age "岁");
return name sex age;
}
protected void method4() {
System.out.println("protected 修饰的方法");
}
正题
代码语言:javascript复制public class Test03 {
public static void main(String[] args) throws Exception {
// 获取Class对象
Class<?> clazz = Class.forName("com.reflect.User");
System.err.println("======获取所有的public修饰的方法=====");
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
Thread.sleep(1000);
System.err.println("======获取所有的方法=====");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
Thread.sleep(1000);
System.err.println("======获取特定方法(带参)并使用=====");
Method method1 = clazz.getMethod("method1", String.class);
System.out.println(method1);
Thread.sleep(1000);
System.err.println("======获取特定方法(不带参)并使用=====");
Method method2 = clazz.getDeclaredMethod("method2");
System.out.println(method2);
System.err.println("======获取特定方法(多个参数)并使用=====");
Method method3 = clazz.getDeclaredMethod("method3", String.class, Integer.class, String.class);
// 获取构造方法,实例化一个对象
Object obj = clazz.getConstructor().newInstance();
// 给方法传值
Object invoke = method3.invoke(obj, "小涛", 24, "男");
// 测试
System.out.println(invoke);
}
}
结果
这里需要注意的就是当一个方法需要传入多个参数值的时候,一定要注意。踩了一点坑。
四、反射执行main方法
代码语言:javascript复制public class Main {
public static void main(String[] args) {
System.out.println("main方法执行了");
}
}
反射调用
代码语言:javascript复制public class Test04Main {
public static void main(String[] args) {
try {
// 获取Class对象
Class<?> clazz = Class.forName("com.reflect.Main");
// 获取Main方法
Method method = clazz.getMethod("main", java.lang.String[].class);
// 调用
method.invoke(null, (Object) new String[]{"a"});
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里需要告诉大家,在导String包的时候千万要看清楚,我在这填了20多分钟的坑。
五、总结
看到这里你已经对反射有了一个简单的了解,可以使用反射获取一些属性方法,其实我们平时写代码很少用到反射技术,但是在我们使用的一些主流框架中反射技术应用是非常广泛的,所以学好反射也是非常有必要的。
今天就写到这里,下篇给大家分享一下利用反射做一些有应用型的例子。