反射机制
通过Java中的反射机制可以操作字节码文件。
可以在类加载(classloader )的时候动态获取类的信息(字节码)
java.lang.Class:代表整个字节码,代表一个类型,代表整个类。
java.lang.reflect. Method:代表字节码中的方法字节码,代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码,代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码,代表类中的成员变量(静态变量+实例变量)。
反射的好处:可以通过操作字节码的方式侧面的去操作类,会使得程序的灵活性和可扩展性增强!!
- 双亲委派机制(可能被面试官问到):在类加载的时候, 实际上jvm调用类加载器classloader去加载类
- 双亲:jdk提供的包 rat, reft
- 类加载的时候,首先会去这两个包下面去找对应的字节码文件,如果双亲包找不到就去classpath下面找。
反射的用途:最重要的用途就是开发各种框架,比如在Spring中,我们将所有的类Bean 交给Spring容器管理,无论是XML配置Bean还是注入配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,Spring根据这些信息,需要创建那些Bean,Spring就动态的创建这些类。还有在Struts2的struts.xml中配置action,也是通过反射调用的action。
一、获取Class(字节码)的三种方式
1)Class c = Class.forName(“完整类名”);
2)Class c = 对象.getClass();
3)Class c = String.class;(String指的是类型,可以是int.class,double.class等等)
PS:Java中所以的属性类型都有一个隐式属性class。
/**
* 获取字节码的三种方式
* 1、Class.forName("全类名")
* 2、对象.getClass();
* 3、类型.class
*
* 反射机制的概念:在类加载的时候动态获取类的信息,也可以通过反射去操作类的方法、属性等。
* 还可以通过反射去创建对象
*
*/
public class Reflect01 {
public static void main(String[] args) {
try {
//获取字节码文件(加载字节码或者说使得类加载)
Class threadClass1 = Class.forName("java.lang.Thread");
//对象.getClass();
Thread thread = new Thread();
Class threadClass2 = thread.getClass();
//类型.class
Class threadClass3 = Thread.class;
System.out.println("获取的字节码对象为"+threadClass1);
System.out.println("获取的字节码对象为"+threadClass2);
System.out.println("获取的字节码对象为"+threadClass3);
System.out.println(threadClass1 == threadClass2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
二、具体操作
1、通过反射机制访问对象的某个属性。
public class Reflect02 {
public static void main(String[] args) {
try {
//通过反射获取user类的字节码
Class userClass = Class.forName("com.jy.pojo.User");
//通过反射创建user对象 newInstance() 这个方法在底层会调用无参构造
//强转后就可以操作属性
User o = (User)userClass.newInstance();
System.out.println(o);
//通过字节码直接获取属性 这个方法只能获取公共的属性
Field sex = userClass.getField("sex");
Field id1 = userClass.getDeclaredField("id");
//获取属性的类型
System.out.println(sex.getType());
//获取属性的访问权限修饰符 sex.getModifiers() 返回的是当前属性所对应的修饰符代号
int modifiers = sex.getModifiers();
//将修饰符代号转换成修饰符
String s = Modifier.toString(modifiers);
System.out.println(s);
//getDeclaredField() 可以获取所有属性
Field id = userClass.getDeclaredField("id");
System.out.println(id.getType());
//获取所有属性
Field[] declaredFields = userClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.print("属性名称为"+declaredField.getName());
System.out.print("属性类型为"+declaredField.getType());
System.out.println("属性修饰符为"+Modifier.toString(declaredField.getModifiers()));
}
} catch (ClassNotFoundException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}
2、通过反射机制调用对象的某个方法。
/**
* 反射获取方法
*/
public class Reflect03 {
public static void main(String[] args) {
//获取字节码对象
Class studentClass = Student.class;
//获取方法
try {
//不传参数,即无参方法
Method study1 = studentClass.getDeclaredMethod("study");
System.out.println(study1.getName());
//获取所有类型的方法 getDeclaredMethod()方法后面的是参数类型.class,是可变长参数
Method study = studentClass.getDeclaredMethod("study",int.class);
//获取方法的返回值类型
Class returnType = study.getReturnType();
//returnType.getName() 获得的是全类名 returnType.getSimpleName() 获得简类名
System.out.println("方法的返回值类型为"+returnType.getSimpleName());
//获取方法的修饰符
int modifiers = study.getModifiers();
//将代号转为修饰符
String s = Modifier.toString(modifiers);
System.out.println("方法的修饰符为"+s);
//获取方法所有形参的类型
Class<?>[] parameterTypes = study.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
PS:可变长参数
/**
* 可变长参数 ...只能是三个点,实质上传的是数组
* 只能出现在参数列表的末尾,可传可不传
*/
public class Test01 {
public static void main(String[] args) {
Test01 test01 = new Test01();
test01.study("a","b","c");
}
public void study(String...arg){
for (String s : arg) {
System.out.println(s);
}
}
}
3、通过反射机制获取某个构造方法实例化对象。
/**
* 通过反射获取一个类的构造
*/
public class Reflect04 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//获取字节码对象
Class aClass = Class.forName("com.jy.pojo.Student");
//获取公共的空参构造
//Constructor constructor = aClass.getConstructor();
//获取公共的有参构造
//Constructor constructor = aClass.getConstructor(int.class, String.class, int.class);
//获取所有构造方法
//Constructor[] constructors = aClass.getConstructors();
//获取空参构造
Constructor declaredConstructor1 = aClass.getDeclaredConstructor();
//获取有参构造
Constructor declaredConstructor2 = aClass.getDeclaredConstructor(int.class, String.class, int.class);
//获取参数列表
Class[] parameterTypes = declaredConstructor2.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(parameterType);
}
//获取修饰符
System.out.println(Modifier.toString(declaredConstructor2.getModifiers()));
}
}
4、如何通过反射给属性赋值
思考:如果通过反射给私有属性赋值的话,需要调用set方法吗?
答:不需要,直接打破封装!!这个算是反射的缺点。
package com.jy.reflect;
import java.lang.reflect.Field;
/**
* 如何通过反射给属性赋值
* 1、如果通过反射给私有属性赋值的话,需要调用set方法吗?
* 不需要,直接打破封装!!这个算是反射的缺点
*/
public class Reflect06 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//获取字节码对象
Class aClass = Class.forName("com.jy.pojo.Student");
//必须通过反射先创建对象
Object obj = aClass.newInstance();
//先要获取被赋值的属性
Field sex = aClass.getDeclaredField("sex");
//给属性赋值 obj 赋值属性所属对象 value 具体要赋的值
sex.set(obj,true);
//获取属性值
Object o = sex.get(obj);
System.out.println(o);
/**
* 操作私有属性
*/
Field name = aClass.getDeclaredField("name");
/*如果需要给私有属性赋值,需要打破封装,setAccessible(true)是用于打破封装的方法*/
name.setAccessible(true);
name.set(obj,"韩信");
Object o1 = name.get(obj);
System.out.println(o1);
}
}
5、如何通过反射调用方法(比较重要)
/**
* 如何通过反射调用方法(比较重要 动态代理)
*/
public class Reflect07 {
public static void main(String[] args) {
try {
//获取字节码对象
Class userClass = User.class;
//创建对象
Object o = userClass.newInstance();
//获取需要被调用的方法
Method login = userClass.getDeclaredMethod("login", String.class, String.class);
//打破封装后,可以调用私有方法
login.setAccessible(true);
//调用方法 login.invoke()通过反射调用普通方法的方法
//o 方法所属对象
//args 需要传的参数
//返回值指的就是方法的返回值
Object shizi = login.invoke(o, "柿子", "123456");
System.out.println(shizi);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6、通过反射调用构造方法及获取一个类的父类和接口
/**
* 通过反射调用构造方法
*/
public class Reflect08 {
public static void main(String[] args) {
try {
//获取字节码对象
Class aClass = Class.forName("com.jy.pojo.User");
//这种写法也是调用空参构造,如果没有无参构造就会报错
//Object o1 = aClass.newInstance();
//调用空参构造
Constructor declaredConstructor = aClass.getDeclaredConstructor();
Object o = declaredConstructor.newInstance();
Constructor declaredConstructor1 = aClass.getDeclaredConstructor(int.class, String.class, boolean.class, int.class);
Object o1 = declaredConstructor1.newInstance(1, "asf", true, 2);
if(o1 instanceof User){
User user = (User)o1;
System.out.println(user);
}
//获取一个类的父类
Class superclass = aClass.getSuperclass();
System.out.println(superclass);
//获取一个类的所有接口
Class[] interfaces = aClass.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println(anInterface);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}