在学习Java反射机制,首先应该了解:
1、什么是反射?
2、为什么会有反射的存在?
3、用到反射有哪些需要了解?
4、使用反射有哪些方式?
5、常见的API有哪些?
1、什么是反射?
百度百科的解释:JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
看到这里应该有点了解,它是一个工具,可以动态获取对象的方法或者属性,不管是类的私有方法还是类的共有方法,都可以在程序运行时动态的获取到。
2、为什么会有反射的存在?
一句话概括就是使用反射可以赋予jvm 动态编译的能力,否则类的元数据信息只能用静态编译的方式实现。
Java中编译类型有两种:
反射可以在运行时加载、探知、使用编译时时期完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、调用它的methods。 反射允许静态语言在运行时(runtime)检查、修改程序的结构与行为。在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时期都保存了class文件中,这样运行时才能保证准确无误。换句话说,程序在运行时的行为都是固定的,如果想在运行时改变,就需要反射这个东西。
3、用到反射有哪些需要了解?
实现Java反射机制的类都位于Java.lang.reflect包中:
可以通过动态的方式获取上面的各种参数,如:私有/公有的字段、私有/共有的方法、构造器等。
4、使用反射有哪些方式?
使用反射主要有3种方式:
方式一:
package FanSheTest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/*
* 测试类的反射
* */
class A {
private String name ;
public int age ;
public String home ;
private void say() {
System.out.println("私有方法say()成功调用!");
}
public void read() {
System.out.println("公有方法read()成功调用!");
}
public A() {
System.out.println("构造器执行了!");
}
}
public class Test {
public static void main(String[] args) {
try {
// 方式一:很普通的使用new进行对象的创建
/*
* A a = new A();
* Class clazz = a.getClass();
*/
// 方式二:
/*
Class clazz = A.class;
*/
// 方式三:使用Class.forName("类的全命名")
Class clazz = Class.forName("FanSheTest.A");
// 获取该类的所有字段,包括私有的字段
Field[] f = clazz.getDeclaredFields();
//获取该类的所有方法,包含私有的方法
Method[] method = clazz.getDeclaredMethods();
// 获取所有的public字段
for (Field field : f) {
System.out.println("含有字段:" + field.getName());
}
//获取所有的方法
for(Method m:method) {
System.out.println(m);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
含有字段:name
含有字段:age
含有字段:home
public void FanSheTest.A.read()
private void FanSheTest.A.say()
使用反射的方法主要有上面3种方式,常用的就是第三种,通过Class.forName(“类全名”)获取对象。
5、常见的API有哪些?
常见的反射获取对象的API主要有以下几类:
获取字段:
//获取所有的public字段
Field[] field = clazz.getFields();
//获取所有的字段,包含private修饰的字段
Filed[] field = clazz.getDeclaredFields();
获取方法:
//获取所有的public方法
Method[] field = clazz.getMethods();
//获取所有的字段,包含private修饰的方法
Method[] field = clazz.getDeclaredMethods();
单独获取某个方法的调用:
//调用read方法,如果该方法有参数,则在后面继续添加方法的类型//clazz.getDeclaredMethod("read",String.class,....)
Method m1 = clazz.getDeclaredMethod("read");
//调用invoke方法执行
m1.invoke(clazz.newInstance());
获取构造器:
//获取包括有参构造和无参构造
Constructor[] con = clazz.getConstructors();
for(Constructor c:con) {
System.out.println(c);
}
使用反射可以跳过泛型的检查
package FanSheTest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/*
* 通过反射了解泛型的本质
* */
public class Test1 {
public static void main(String[] args) throws InstantiationException {
List list1 = new ArrayList<>();
List<String> list2 = new ArrayList<String>();
list1.add("张三");
list1.add("李四");
System.out.println(list1.size());
list2.add("abc");
//list2.add(20);直接会在检查时报错
System.out.println(list2.size());
Class clazz = list2.getClass();
Method m2;
try {
m2 = clazz.getMethod("add", Object.class);
m2.invoke(list2, 20);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("--------------使用反射添加-------------------");
System.out.println("集合大小:"+list2.size());
}
}
运行结果:
2
1
--------------使用反射添加-------------------
集合大小:2
虽然在定义list2的时候已经规定了泛型参数类型为:String,但是在正常使用添加元素的时候会在检查阶段报错,如果使用反射方式,拿到add方法时,向其中添加int类型的元素时,集合的长度是2,表明已经添加成功;当时当遍历这个集合时,会报java.lang.ClassCastException
,表明泛型只是在编译时期有效,在运行时期是失效的。
总结:
1、学习反射可以实现对类对象的动态访问,而不只是在静态编译时期
2、反射可以访问对象的私有成员变量和方法,并可以对其进行值的修改操作。
3、反射技术主要是用在框架中,平时开发时很少用到反射技术,所以学习好反射技术是很重要的。