反射详细讲解
反射机制
- 反射机制允许程序在执行期借助于Reflection
API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层经常用到。 - 加载完类之后,在堆中就产生了一个class类型的对象(一个类只有一个class对象),此对象包含了类的完整信息,通过这个对象得到类的结构。
通过反射创建对象
- 方式一:通过调用类中的public修饰的无参构造器
- 方式二:调用类中的指定结构
- Class类相关方法
1. newlnstance:调用类中的无参构造器,获取对应类的对象
2. getConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
3. getDecalareadConstructor(Class…clazz):根据参数列表,获取对应的构造器对象 - Constructor类相关方法
1. setAccessible:爆破
2. newInstance(Object…obj):调用构造器
package com.NXY.reflection.question;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author 聂星宇
* 演示通过反射机制创建实例
*/
public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.先获取到User类的Class对象
Class<?> userClass = Class.forName("com.NXY.reflection.question.User");
//2.通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
//3.通过public的有参构造器创建实例
/*
constructor对象就是
public User(String name){
this.name = name;
}
*/
//(1).先得到对应的构造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//(2).创建实例 并传入实参
Object t = constructor.newInstance("汤亚波");
System.out.println(t);
//4.通过非public的有参构造器创建实例
/*
constructor
private User(int age,String name){
this.age = age;
this.name = name;
}
*/
//(1).先得到private的构造器对象
Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
//(2).
constructor1.setAccessible(true);//爆破 使用反射可以访问private构造器
Object user2 = constructor1.newInstance(100, "张军统");
//5.
}
}
class User{
private int age;
private String name = "汤亚波";
public User(){
}
public User(String name){
this.name = name;
}
private User(int age,String name){
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
反射访问类中的成员
- 根据属性名获取Field对像
Field f = clazz对象.getDeclaredField(对象名) - 爆破:f.setAccessible(true);// f 是Field
- 访问
f.set(o,值);o为反射创建的对象
syso(f.get(o)) - 如果是静态属性 则set和get中的参数o 可以写成null
package com.NXY.reflection.question;
import javax.accessibility.Accessible;
import java.lang.reflect.Field;
/**
* @author 聂星宇
* 演示反射操作属性
*/
public class ReflecAccessProperty {
public static void main(String[] args) throws Exception{
// 1.得到Student类对应的Class对象
Class<?> stuClass = Class.forName("com.NXY.reflection.question.Student");
//2.创建对象
Object o = stuClass.newInstance(); //运行类型是 Student
System.out.println(o.getClass());
//3.使用反射得到属性
Field age = stuClass.getField("age");
age.set(o,88);//通过反射来操作属性
System.out.println(o);
//4.使用反射操作name
Field name = stuClass.getDeclaredField("name");
//对name进行爆破 可以对private属性操作
name.setAccessible(true);
name.set(0,"汤亚波");
System.out.println(o);
System.out.println(name.get(o));
System.out.println(name.get(null));//要求name是static
}
}
class Student{
public int age;
private static String name;
public Student(){
}
@Override
public String toString() {
return "Student{" +
"age=" + age +"name=" + name+
'}' ;
}
}
反射访问类中的方法
- 根据方法名和参数列表获取Method方法对象:
Method m = clazz.getDeclaredMethod(方法名,xx.class); - 获取对象:Object o = clazz.newInstance();
- 爆破:m.setAccessible(true);
- 访问:Object returnValue = m.invole(o,实参列表);
- 注意 如果是静态方法,则invoke的参数o 可以写成null。
package com.NXY.reflection.question;
import java.lang.reflect.Method;
/**
* @author 聂星宇
* 演示通过反射调用方法
*/
public class ReflecAccessMethod {
public static void main(String[] args) throws Exception{
//1.得到Boss类对应的Class对象
Class<?> bosClass = Class.forName("com.NXY.reflection.question.Boss");
//2.创建对象
Object o = bosClass.newInstance();
//3.调用public的方法
//hi为方法名
Method hi = bosClass.getMethod("hi",String.class);
Method his = bosClass.getDeclaredMethod("hi",String.class);
hi.invoke(o,"汤亚波");
//调用private static 方法
Method say = bosClass.getDeclaredMethod("say", int.class,String.class, char.class);
say.setAccessible(true);
say.invoke(0,100,"汤亚波",'男');
}
}
class Boss{
public int age;
private static String name;
public Boss(){
}
private static String say(int n,String s,char c){
return n + " " + s + " " + c;
}
public void hi(String s){
System.out.println("hi " + s);
}
}
反射的主要类
- java.lang.Class;代表一个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method;代表类的方法,Method对象表示某个方法
- java.lang.reflect.Field: 达标类的成员变量,Field对象表示某个类的成员变量
- java.lang.reflect.Constructor;代表类的构造方法,Constructor对象表示
package com.NXY;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
/**
1. @author 聂星宇
*/
public class Reflection01 {
public static void main(String[] args) throws Exception {
//1. 使用Properties 类, 可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classFullPath").toString();//"com.hspedu.Cat"
String methodName = properties.get("method").toString();//"hi"
//2. 使用反射机制解决
//(1) 加载类, 返回Class类型的对象cls
Class cls = Class.forName(classfullpath);
//(2) 通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例
Object o = cls.newInstance();
System.out.println("o的运行类型=" + o.getClass()); //运行类型
//(3) 通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法对象
// 即:在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
//(4) 通过method1 调用方法: 即通过方法对象来实现调用方法
System.out.println("=============================");
method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)
//java.lang.reflect.Field: 代表类的成员变量, Field对象表示某个类的成员变量
//得到name字段
//getField不能得到私有的属性
Field nameField = cls.getField("age"); //
System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)
//java.lang.reflect.Constructor: 代表类的构造方法, Constructor对象表示构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
System.out.println(constructor);//Cat()
Constructor constructor2 = cls.getConstructor(String.class); //这里老师传入的 String.class 就是String类的Class对象
System.out.println(constructor2);//Cat(String name)
}
}
获取类的结构信息
- getName:获取全名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类中的所有属性包含私有属性
- getMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类中的所有方法
- getConstructors:获取本类所有public修饰的构造器
- getDeclaredConstructors:获取本类中所有构造器
- getPackage:以Package形式返回包信息
- getSuperClass:以class形式返回父类信息
- getInterfaces: 以class[]形式返回接口信息
- getAnnotations:以Annotation[] 形式返回注解信息
package com.NXY.reflection.question;
import org.junit.jupiter.api.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author 聂星宇
* 演示如何通过反射获取类的结构信息
*/
@SuppressWarnings({"all"})
public class ReflectionUtils {
public static void main(String[] args) {
}
@Test
public void api_02() throws ClassNotFoundException, NoSuchMethodException {
//得到Class对象
Class<?> personCls = Class.forName("com.NXY.reflection.question.Person");
//getDeclaredFields:获取本类中所有属性
//规定 说明: 默认修饰符 是0 , public 是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类中所有属性=" + declaredField.getName()
+ " 该属性的修饰符值=" + declaredField.getModifiers()
+ " 该属性的类型=" + declaredField.getType());
}
//getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类中所有方法=" + declaredMethod.getName()
+ " 该方法的访问修饰符值=" + declaredMethod.getModifiers()
+ " 该方法返回类型" + declaredMethod.getReturnType());
//输出当前这个方法的形参数组情况
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该方法的形参类型=" + parameterType);
}
}
//getDeclaredConstructors:获取本类中所有构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("====================");
System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里老师只是输出名
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该构造器的形参类型=" + parameterType);
}
}
}
//第一组方法API
@Test
public void api_01() throws ClassNotFoundException, NoSuchMethodException {
//得到Class对象
Class<?> personCls = Class.forName("com.NXY.reflection.question.Person");
//getName:获取全类名
System.out.println(personCls.getName());//com.hspedu.reflection.Person
//getSimpleName:获取简单类名
System.out.println(personCls.getSimpleName());//Person
//getFields:获取所有public修饰的属性,包含本类以及父类的
Field[] fields = personCls.getFields();
for (Field field : fields) {//增强for
System.out.println("本类以及父类的属性=" + field.getName());
}
//getDeclaredFields:获取本类中所有属性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类中所有属性=" + declaredField.getName());
}
//getMethods:获取所有public修饰的方法,包含本类以及父类的
Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println("本类以及父类的方法=" + method.getName());
}
//getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类中所有方法=" + declaredMethod.getName());
}
//getConstructors: 获取所有public修饰的构造器,包含本类
Constructor<?>[] constructors = personCls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("本类的构造器=" + constructor.getName());
}
//getDeclaredConstructors:获取本类中所有构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里老师只是输出名
}
//getPackage:以Package形式返回 包信息
System.out.println(personCls.getPackage());//com.hspedu.reflection
//getSuperClass:以Class形式返回父类信息
Class<?> superclass = personCls.getSuperclass();
System.out.println("父类的class对象=" + superclass);//
//getInterfaces:以Class[]形式返回接口信息
Class<?>[] interfaces = personCls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("接口信息=" + anInterface);
}
//getAnnotations:以Annotation[] 形式返回注解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解信息=" + annotation);//注解
}
}
}
class A {
public String hobby;
public void hi() {
}
public A() {
}
public A(String name) {
}
}
interface IA {
}
interface IB {
}
@Deprecated
class Person extends A implements IA, IB {
//属性
public String name;
protected static int age; // 4 + 8 = 12
String job;
private double sal;
//构造器
public Person() {
}
public Person(String name) {
}
//私有的
private Person(String name, int age) {
}
//方法
public void m1(String name, int age, double sal) {
}
protected String m2() {
return null;
}
void m3() {
}
private void m4() {
}
}
类加载
反射机制是JAVA实现动态语言的关键,也就是通过反射实现类动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性强
- 动态加载:运行时加载需要的类,如果运行时不用该类,在不报错,降低依赖性
类加载时机
- 当创建对象时(new) //静态加载
- 当被子类加载时 ,父类也加载 //静态加载
- 调用类中的静态成员时 //静态加载
- 通过反射 //动态加载
package com.NXY.Classload_;
/**
* @author 聂星宇
*/
public class ClassLoad03 {
public static void main(String[] args) {
/*
1.加载B类 并生成B的class对象
2.链接 num = 0;
3.初始化阶段:
依次自动收集类中的所有静态变量的赋值动作 和 静态代码块中的语句
clinit(){
System.out.println("B 静态代码块被执行");
num = 300;
num = 100;
}
合并 num = 100;
*/
new B();//没有赋值对象 不会有构造器
System.out.println(B.num);
}
}
class B{
static{
System.out.println("B 静态代码块被执行");
num = 300;
}
static int num = 100;
public B(){
System.out.println("B() 构造器被执行");
}
}