目录
一.Java反射机制是什么?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射机制的作用:可以动态获取类的信息。
Java反射相关的类主要包括:
• Class 类型
• Constructor 构造方法
• Method 方法
• Field 属性
• 除了Class外,其他类都位于java.lang.reflect包中
● 可见,反射API将类的类型、方法、属性都封装成了类,其中最重要的类是Class,可以说,反射的使用都是从Class开始。
二.Class类
一旦class文件被加载到内存,就会为其创建一个Class对象。任何类被使用时都会创建一个Class对象。
Class类是Java反射机制的基础,通过一个Class类,可以得到一个类的基本信息。
获得Class类的对象有三种方式:
1.知道全类名的情况下,可以通过Class.forName()方法去获得Class类对象:
//假设现在在一个普通的java项目中创建了Person类 Class clazz= Class.forName("com.zyk.javareflact.Person");//类可能不存在,这里需要抛出异常 System.out.println(clazz);
2. 知道类名的情况下:可以通过 类名.class 去创建Class类对象,如果类名一样的话,在底层就只会加载这一个类,如果建立了两个一样的Class类的对象,那么他们的地址必然都是一样的。
Class clazz1 = Person.class; System.out.println(clazz==clazz1);//这个结果显然是true
3.已知类的对象时,可以通过 对象.getClass() 去生成Class类
Person person=new Person(); Class clazz2= person.getClass(); System.out.println(clazz==clazz2);//这个结果也当然是true
三.获得Constructor 构造方法
首先,我们想要得到Constructor 构造方法,那么就需要先将某个类的Class对象导入。(可以通过上述的三种方法,这里就采用第一种方法。)
拿到Class对象后,我们需要去new类的对象。
new对象有下面三种方式:
//1.可以直接采用Class对象去调用newInstance()方法去生成对象
Object object= aclass.newInstance();
//2.第二种可以通过得到构造方法(仅限于public权限的构造方法,是不可以拿到private权限的构造方法的),给构造方法中赋值,就也可以实现new对象
Constructor constructor1= aclass.getConstructor();//拿到无参构造
Constructor constructor2=aclass.getConstructor(String.class,String.class);//拿到有参构造,这里的参数是Class对象
Object object1 = constructor1.newInstance();//通过无参构造new对象
Object object2= constructor2.newInstance("张三","男");//通过有参构造new对象
//3.还有一种方法可以拿到类中的所有构造方法/方法/属性(包括私有权限的)但是一般就不建议使用这些方法了,因为他们打破了 封装性的规则 ,虽然反射机制很强大,可以拿到类中的私有权限的方法,构造方法,属性等。
Constructor declaredConstructor = aclass.getDeclaredConstructor();
Constructor[] declaredConstructors = aclass.getDeclaredConstructors();
四.获得Field 属性
通过反射机制,还可以对类中的成员属性进行操作:
Field[] fields = aClass.getDeclaredFields();
这里拿到的就是属性的一个集合。想一下:属性可以直接用吗?显然当然是不可以的,属性单独拿出来只是一个抽象概念,必须是需要一个对象去对对象中的属性进行操作(赋值等),所以在其中必然是存在一个方法可以将其属性依托于某个对象来使用的。方法如下:
field.set(Object obj,Object value);
//这里第一个变量就是属性所寄存的对象名,第二个变量就是变量的值
下面给出一个具体实例:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
public class Test3 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class aClass= Class.forName("com.zyk.javareflact.Person");
Object object = aClass.newInstance();
//这里用HashMap来模拟数据库传进来的数据
HashMap<String,String> map=new HashMap<>();
map.put("name","zhangsan");
map.put("gender","男");
Field[] fields = aClass.getDeclaredFields();
for(Field field:fields){
//field.setAccessible(true);允许对私有变量进行操作(暂时用这种方式)
field.set(object,map.get(field.getName()));
}
System.out.println(object);
}
}
输出结果如下:
Person 无参构造
Person{name='zhangsan', gender='男'}
五.获得Method 方法
与获得属性一样,在Class对象中有对应的方法,同样可以对类中的方法进行调用:
Method eat = aClass.getMethod("eat",String.class);
上述例子调用Person类中的公共方法,传入方法的名称(有参数就可以往里面传 注意这里的参数是Class对象),要注意java中的方法的重载。下面就是举出的有参数的方法,如果方法没有参数,第二个变量就可以不写。
当然,与类中的属性调用一样,一样是需要依托对象去使用的。这里存在一个方法:
//这里第一个变量为方法寄存的对象,第二个变量就是方法所需要的参数了,这个参数可以有多个,没有就可以不写 eat.invoke(object,"大黄");
下面将步骤合在一起举个例子:
可以切换不同的类,只需要将开始创建Class对象时更换全类名即可(后续代码都是不变的)。
package com.zyk.javareflact;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class aClass = Class.forName("com.zyk.javareflact.Person");
//Class aClass=Class.forName("com.zyk.javareflact.Car");
Object object= aClass.newInstance();
//调用Person中的公共方法,传入方法的名称,有参数就可以往里面传,要注意java中的方法的重载。
Method eat = aClass.getMethod("eat",String.class);
//光拿到方法是不行的,和属性一样必须去通过对象来调用
eat.invoke(object,"大黄");
}
}
六.关于类中的私有属性
对于类中的私有的成员变量,虽然Java反射机制可以直接通过setAccessible(true)方法直接允许对私有成员变量的操作,但是这同样也违背了Java的封装性,是不可取的,想要访问其中的私有属性,给其赋值,就需要使用类中对于私有变量赋值和取值的 set 和 get 方法。
对于每一个私有属性的get和set方法,他们的方法名字必须遵从Java的命名规范。
对于反射机制中方法的调用必须要知道类中方法的名字,因为是对私有属性进行操作,他们的set和get方法的名称都是规范的,eg: setAccount(),所以可以通过下面方法进行获取。
String methodName="set"+field.getName().substring(0,1).toUpperCase()+field.getName().substring(1);
拿到方法名字之后,就可以调用Class类中的getMethod()方法,再通过invoke()方法进行赋值操作。
下面给出一个具体的实例可以参考着学习(上面的注释大家需要可以自行查看,博主个人认为还是挺好理解的):
package com.zyk.javareflact;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class Test5 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class aClass = Class.forName("com.zyk.javareflact.Person");
//Class aClass=Class.forName("com.zyk.javareflact.Car");
Object object = aClass.newInstance();
//模拟数据库传进来的数据
HashMap<String,String> map=new HashMap<>();
map.put("name","大黄");
map.put("gender","女");
//得到类中的所有变量(包括私有的)
Field[] fields = aClass.getDeclaredFields();
//增强dor循环,循环对每个变量进行赋值操作,因为是私有权限的,所以需要进入各自的set方法中进行赋值
for(Field field:fields){
//命名规范的话,私有变量的set方法名字就是下面这个,field.getName()得到变量名 toUpperCase()大写
String methodName="set"+field.getName().substring(0,1).toUpperCase()+field.getName().substring(1);
//找到各自的方法,传入方法名和 Class<?>... parameterTypes 因为可以有多个参数,所以参数类型可以写多个 参数类型的Class类
Method method = aClass.getMethod(methodName,field.getType());
method.invoke(object,map.get(field.getName()));
}
System.out.println(object);
}
}
七.实例中所用到的类
package com.zyk.javareflact;
public class Person {
private String name;
private String gender;
public Person() {
System.out.println("Person 无参构造");
}
public Person( String name, String gender) {
this.name = name;
this.gender = gender;
}
public void eat(){
System.out.println("人吃饭");
}
public void eat(String name){
System.out.println(name+"吃饭");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
package com.zyk.javareflact;
public class Car {
private String name;
private String color;
Car() {
}
public Car(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
'}';
}
}
总结
本篇博客解释了Java中的一个很重要的机制--反射机制,这个机制在Java中的框架都有所使用,可以让大家更好地理解框架的底层是如何实现的,为后面学习框架,了解框架的底层逻辑有很大的帮助,所以反射机制必须要理解掌握。
制作不易,如果对大家的学习有帮助的话,希望大家可以三连多多支持一下!!!