概念
Reflection(反射) 是被视为动态语言的关键,反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部消息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
正常方式
引入需要的“包类”名称----->通过new实例化----->取得实例化对象
反射方式
实例化对象----->getClass()方法----->取得完整的“包类”名称
功能
在运行时判断任意一个对象所属的类
在运行时判断任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
反射相关的主要API
java.lang.Class 代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
反射初体验
public class Person {
private String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("你好,我是一个人");
}
private String showNation(String nation){
System.out.println("我的国籍是"+nation);
return nation;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
这里创建一个Person,里面包含共有构造器和私有构造器还有一些共有方法和私有方法。方便后面进行反射的测试。
在使用反射之前,我们对Person对象的操作权限
// 反射之前,对于Person的操作
@Test
public void test(){
Person p1 = new Person("tom",12);
p1.age = 10;
System.out.println(p1.toString());
p1.show();
// 在person类外部,不可以通过Person类的对象调用其内部私有结构
// 比如:name,showNation()以及私有构造器
}
Person{name='tom', age=10}
你好,我是一个人
使用反射之后创建对象
// 反射之后,对于Person的操作
@Test
public void test1() throws Exception {
Class clazz = Person.class;
// 1、通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class, int.class);
Object tom = cons.newInstance("tom", 12);
Person p = (Person) tom;
System.out.println(p.toString());
// 2、通过反射,调用对象指定的属性、方法
// 调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p.toString());
// 调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
System.out.println("********************************");
// 通过反射调用Person类的私有结构
// 调用私有构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");
System.out.println(p1);
// 调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
// 相当于 p1.setName("")
name.set(p1,"Hanmeimei");
System.out.println(p1);
// 调用私有方法
Method m2 = clazz.getDeclaredMethod("showNation",String.class);
m2.setAccessible(true);
// 相当于p1.showNation();
m2.invoke(p1,"中国");
}
Person{name='tom', age=12}
Person{name='tom', age=10}
你好,我是一个人
********************************
Person{name='Jerry', age=0}
Person{name='Hanmeimei', age=0}
这里需要注意调用私有的属性、方法、构造器,都需要加上setAccessible(true).以上为反射常用的方法。后面会逐一讲解。
关于java.lang.Class的理解
1、类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释和运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
2、换句话说,Class实例就对应着一个运行时类。
3、加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
4、获取Class实例的方式
/**
* 获取Class的实例的方式
*/
@Test
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类的属性: .class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
// 方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.company.base.reflect.Person");
System.out.println(clazz3);
// 方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.company.base.reflect.Person");
System.out.println(clazz4);
System.out.println(clazz1==clazz2);
System.out.println(clazz2==clazz3);
System.out.println(clazz3==clazz4);
}
class com.company.base.reflect.Person
class com.company.base.reflect.Person
class com.company.base.reflect.Person
class com.company.base.reflect.Person
true
true
true
创建运行时类的对象
以前在创建POJO对象的时候不明白为什么要给一个空参构造器,其一原因是方便new出对象,个人觉得更为重要的原因在于反射的newInstance方法会用到这个空参构造器。当然,空参构造器权限必须比private更大。
/**
* newInstance:调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Test
public void test1() throws IllegalAccessException, InstantiationException {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
System.out.println(person);
}
Person{name='null', age=0}
反射的动态性
在运行时代码可以根据某些条件改变自身的结构。简单的说,就是一般我们都是通过new的方式来创建对象的,这样的方式决定了我们要创建的对象是什么,而动态性在于创建对象的时候,我们还不知道要创建的是什么,根据自身条件去决定的。
1、首先写一个反射创建对象方法
/**
* 创建一个指定类的对象
* @param classPath:指定类的全类名
* @return
* @throws Exception
*/
public Object getInstance(String classPath) throws Exception {
Class clazz = Class.forName(classPath);
Object instance = clazz.newInstance();
return instance;
}
2、动态生成对象
@Test
public void test2() throws Exception {
int num = new Random().nextInt(3);
String classPath="";
switch (num){
case 0:
classPath="java.util.Date";
break;
case 1:
classPath="java.lang.Object";
break;
case 2:
classPath="com.company.base.reflect.Person";
break;
}
System.out.println(getInstance(classPath));
}
Mon May 17 15:19:39 CST 2021
从结果可以看出此处生成的对象为日期对象。就是说只有在运行之后才知道造的是哪个对象(动态性)。这个模式熟悉不?对,就是简单工厂模式。
获取运行时类的完整结构
为了体会反射强大的功能。我们重新创建一个person类,使它尽可能包含更多的信息
1、创建person的父类creature
public class Creature<T> implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println("生物呼吸");
}
private void eat(){
System.out.println("生物吃东西");
}
}
2、自定义接口
public interface Myinterface {
void info();
}
3、自定义注解
这里需要注意Retention必须为RUNTIME否则反射无法获取.
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
4、创建person类
@MyAnnotation("Hi")
public class Person extends Creature<String> implements Comparable<String>,Myinterface{
private String name;
int age;
public int id;
public Person(){}
@MyAnnotation("ABC")
private Person(String name){
this.name = name;
}
Person(String name,int age){
this.name = name;
this.age = age;
}
@MyAnnotation
public String show(String nation){
System.out.println("我的国籍是"+nation);
return nation;
}
public String display(String interests){
return interests;
}
@Override
public void info() {
System.out.println("我是一个人");
}
@Override
public int compareTo(String o) {
return 0;
}
}
1、获取运行类的属性结构
target:
父类中: weight(public),gender(private)
自身:name(private),age(默认),id(public)
java反射包提供了两个获取属性的方法:getFields(),getDeclaredFields()
@Test
public void test1(){
Class clazz = Person.class;
// 获取属性结构
// getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for(Field f: fields){
System.out.println(f);
}
System.out.println();
//getDeclaredFields():获取当前运行时类中的所有属性(不包含父类中的属性)
Field[] fields1 = clazz.getDeclaredFields();
for(Field f: fields1){
System.out.println(f);
}
}
public int com.company.base.reflect.Person.id
public double com.company.base.reflect.Creature.weight
private java.lang.String com.company.base.reflect.Person.name
int com.company.base.reflect.Person.age
public int com.company.base.reflect.Person.id
区别如注释中所说:
getFields():获取当前运行时类及其父类中声明为public访问权限的属性
getDeclaredFields():获取当前运行时类中的所有属性(不包含父类中的属性)
除了属性本身还可以获取属性的权限修饰符、数据类型 、变量名
@Test
public void test2(){
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f: fields){
//1.权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier)+" ");
//2.数据类型
Class type = f.getType();
System.out.print(type.getName()+"\t");
//3.变量名
String fName = f.getName();
System.out.print(fName);
System.out.println();
}
}
private java.lang.String name
int age
public int id
结果与我们定义的完全相同
2、获取运行类的方法结构
java反射包提供了两种方法:
getMethods(),getDeclaredMethods()
@Test
public void test1(){
Class clazz = Person.class;
// getMethods():获取当前运行时类及其所有父类中声明为public的权限方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println();
// getDeclaredMethods():获取当前运行时类中的所有方法(不包含父类)
Method[] methods1 = clazz.getDeclaredMethods();
for(Method m: methods1){
System.out.println(m);
}
}
public int com.company.base.reflect.Person.compareTo(java.lang.String)
public int com.company.base.reflect.Person.compareTo(java.lang.Object)
public void com.company.base.reflect.Person.info()
public java.lang.String com.company.base.reflect.Person.show(java.lang.String)
public java.lang.String com.company.base.reflect.Person.display(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public int com.company.base.reflect.Person.compareTo(java.lang.String)
public int com.company.base.reflect.Person.compareTo(java.lang.Object)
public void com.company.base.reflect.Person.info()
public java.lang.String com.company.base.reflect.Person.show(java.lang.String)
public java.lang.String com.company.base.reflect.Person.display(java.lang.String)
区别
getMethods():获取当前运行时类及其所有父类中声明为public的权限方法
getDeclaredMethods():获取当前运行时类中的所有方法(不包含父类)
首先修改Person类,给Person类中中的方法手动抛异常
public String display(String interests) throws RuntimeException, ParseException{
return interests;
}
获取方法详细信息
@Test
public void test2(){
Class clazz = Person.class;
Method[] methods1 = clazz.getDeclaredMethods();
for(Method m: methods1){
//1、获取方法声明的注解
Annotation[] annos = m.getAnnotations();
for(Annotation a: annos){
System.out.print(a);
}
//2、获取权限修饰符
System.out.print(Modifier.toString(m.getModifiers())+"\t");
//3、返回值类型
System.out.print(m.getReturnType().getName()+"\t");
//4、方法名
System.out.print(m.getName());
//5、形参列表
System.out.print("(");
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length==0)){
for (int i=0;i<parameterTypes.length;i++){
if(i == parameterTypes.length-1){
System.out.print(parameterTypes[i].getName()+" args_"+i);
}else {
System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
}
}
}
System.out.print(")");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(exceptionTypes!=null && exceptionTypes.length!=0){
System.out.print(" throws");
for(int i=0;i<exceptionTypes.length;i++){
if(i == exceptionTypes.length-1){
System.out.print(exceptionTypes[i].getName());
}else {
System.out.print(exceptionTypes[i].getName() + ",");
}
}
}
System.out.println();
}
}
public int compareTo(java.lang.String args_0)
public volatile int compareTo(java.lang.Object args_0)
public void info()
public java.lang.String display(java.lang.String args_0) throwsjava.lang.RuntimeException,java.text.ParseException
@com.company.base.reflect.MyAnnotation(value=hello)public java.lang.String show(java.lang.String args_0)
3、获取运行类的构造器结构
@Test
public void test1(){
Class clazz = Person.class;
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
for(Constructor c : constructors){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():获取当前运行时类中所有的构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor c: declaredConstructors){
System.out.println(c);
}
}
public com.company.base.reflect.Person()
com.company.base.reflect.Person(java.lang.String,int)
private com.company.base.reflect.Person(java.lang.String)
public com.company.base.reflect.Person()
4、获取运行类的父类
@Test
public void test2(){
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
}
class com.company.base.reflect.Creature
此时获取的父类是不带泛型的,获取带泛型父类的执行如下方法
@Test
public void test3(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
com.company.base.reflect.Creature<java.lang.String>
如果想只获取父类泛型,执行如下方法
@Test
public void test4(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramtype = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] arguments = paramtype.getActualTypeArguments();
System.out.println(arguments[0].getTypeName());
}
java.lang.String
5、获取运行类的接口
@Test
public void test5(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class p: interfaces){
System.out.println(p);
}
System.out.println();
//获取运行时类父类的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class p: interfaces1){
System.out.println(p);
}
}
interface java.lang.Comparable
interface com.company.base.reflect.Myinterface
interface java.io.Serializable
6、获取运行类指定东西
① 获取指定属性
@Test
public void test6() throws Exception {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
Field id = clazz.getField("id");
id.set(p,15);
int id2 = (int) id.get(p);
System.out.println(id2);
}
15
getField方法只能获取到public权限的字段,一般在实际代码中都是使用如下方法
@Test
public void test6() throws Exception {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
// getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
// 保证当前属性是可访问的
name.setAccessible(true);
name.set(p,"tom");
String pName = (String) name.get(p);
System.out.println(pName);
}
tom
使用getDeclaredField和setAccessible可访问当前类的任意属性
②获取指定构造器
@Test
public void test() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Person.class;
/**
* private Person(String name)
* 1、获取指定的构造器
* getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
// 2、保证此构造器是可访问的
constructor.setAccessible(true);
// 3、调用此构造器创建运行时类的对象
Object tom = constructor.newInstance("tom");
System.out.println(tom);
}
com.company.base.reflect.Person@5d6f64b1
一般情况下,创建运行时类对象是直接用clazz.newInstance(使用空参构造器),特殊情况下,可能会使用到指定参数的构造器
网友评论