Java反射机制 知识点总结 想学的莫错过!

一、反射机制的作用

  反射机制是指动态获取对象信息和调用对象方法的功能。Java 反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。
  反射可以通过操作字节码文件(.class)实例化对象、调用方法、获取和设置变量值,还可以在运行时检查类,接口,方法和变量。比如当我们不知道一个类中是否拥有某个方法时,我们就可以使用反射来检查是否拥有这个方法。

二、获取Class的三种方式

Class 类表示正在运行的 Java 程序中的类和接口。

  1. 调用java.lang.Class<T> 中的static 类<?> forName(String className) 方法,该方法导致类加载,执行类中的静态代码块,其中作为参数的字符串需要的是一个完整的类名,不能省略类的包名
  2. 调用任何一个对象的类<?> getClass() 方法;
  3. 调用类型的class属性。

示例如下:

    public static void main(String[] args) {
        //方法1
        Class string1 = null;
        try {
            string1 = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //方法2
        String s = "xzy";
        Class string2 = s.getClass();
        //方法3
        Class string3 = String.class;
        //其中,string1==string2==string3,说明它们均指向String.class字节码文件
    }

注释
字节码文件装载到JVM中的时候只有一份。

三、实例化对象

一般的实例化类对象方法都是通过new,反射机制可以获取字节码文件之后通过调用newInstance() 方法,该方法实际调用的是对应类的无参构造方法,必须保证无参构造存在才可以。后者看似复杂,实则非常的灵活!

src/class.properties文件信息如下:
classname=java.util.Date

	public static void main(String[] args) throws Exception {
        //新建文件输入流对象
        FileReader reader=new FileReader("src/class.properties");
        //新建属性类对象
        Properties ppt=new Properties();
        //加载配置信息到Map集合中
        ppt.load(reader);
        reader.close();
        //通过key获取value
        String classname=ppt.getProperty("classname");
        //利用Class的静态方法
        Class c=Class.forName(classname);
        Object obj=c.newInstance();
    }

本例创建了一个Date对象,在不更改代码的前提下修改配置文件就可以创建其它的对象。(符合OCP开闭原则:对扩展开发,对修改关闭)
注释

  1. 更加通用的获取类路径(src路径)下文件的绝对路径的方法如下,好处在于具备了可移植性。
//getContextClassLoader()获取到当前线程的类加载器对象
//getResource()默认从类的根路径下加载资源
String filepath=Thread.currentThread().getContextClassLoader().getResource("class.properties").getPath();
//如果乱码,往往是路径上出现了空格、中文等字符,解决方法如下:
filepath = java.net.URLDecoder.decode(filepath, "utf-8");
  1. 还可以通过引流的方法获取输入流
 InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("class.properties");
  1. 通过资源绑定器获取类路径下的属性配置文件
	ResourceBundle pptresource=ResourceBundle.getBundle("class");
    String classname=pptresource.getString("classname");

三、获取和设置Field

1、获取Field

方法作用
ClassField[] getDeclaredFields()获取指定类的所有实例域
ClassString getSimpleName()获取源代码中给出的基础类的简单名称
Fieldint getModifiers()获取由该 Field对象表示的字段的Java语言修饰符对应的访问修饰符标志
FieldString getName()获取由此 Field对象表示的字段的名称
FieldClass<?> getType()获取实例域中变量类型所对应的Class
Modifierstatic String toString(int mod)获取描述指定修饰符中的访问修饰符标志的字符串

示例:通过反射机制反编译一个类的属性

    public static void main(String[] args) throws ClassNotFoundException {
        StringBuilder message=new StringBuilder();
        Class stringClass=Class.forName("java.lang.String");
        message.append(Modifier.toString(stringClass.getModifiers())+" class "+stringClass.getSimpleName()+" {\n");
        Field[] stringField=stringClass.getDeclaredFields();
        for(Field field:stringField){
            message.append("\t"+Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");
        }
        message.append("}");
        System.out.println(message);
    }

输出结果如下:

public final class String {
	private final char[] value;
	private int hash;
	private static final long serialVersionUID;
	private static final ObjectStreamField[] serialPersistentFields;
	public static final Comparator CASE_INSENSITIVE_ORDER;
}

2、设置Field

方法作用
ClassField getDeclaredField(String name)根据属性的名字获取到对应的Field对象
FieldObject get(Object obj)获取指定对象对应属性的指
Fieldvoid set(Object obj, Object value)设置指定对象对应属性的值
Fieldvoid setAccessible(boolean flag)打破封装,使私有域可访问

示例

//package From0To100.Reflect.Field下的学生类
public class Student {
    public static final String schoolname="xx小学";
    public boolean gender;
    protected String name;
    private int age;
}
//package From0To100.Reflect.Field下的测试类
public class TestField {
    public static void main(String[] args) throws Exception {
        Class studentClass=Class.forName("From0To100.Reflect.Field.Student");
        Object student=studentClass.newInstance();
        Field genderField=studentClass.getDeclaredField("age");
        //反射机制的缺点:打破封装
        genderField.setAccessible(true);
        //设置属性,需要用到对象、属性、值
        genderField.set(student,18);
        System.out.println(genderField.get(student));
        //最终输出18
	}
}

四、获取和调用Method

1、获取Method

方法作用
ClassMethod[] getDeclaredMethods()获取对应类中的方法
Methodint getModifiers()获取对应方法修饰符的访问修饰符标志
MethodClass<?> getReturnType()获取方法返回类型对应的Class对象
MethodString getName()获取方法名称
MethodClass<?>[] getParameterTypes()获取方法的参数列表

示例:通过反射机制反编译一个类的方法

	public static void main(String[] args) throws Exception {
        StringBuilder message=new StringBuilder();
        Class stringClass = Class.forName("java.lang.String");
        message.append(Modifier.toString(stringClass.getModifiers())+" class "+stringClass.getSimpleName()+" {\n");
        Method[] stringMethods=stringClass.getDeclaredMethods();
        for(Method method:stringMethods){
        	//获取方法体
            message.append("\t"+Modifier.toString(method.getModifiers())+" "+method.getReturnType().getSimpleName()+" "+method.getName()+"(");
            //获取参数
            Class[] parametersType=method.getParameterTypes();
            for(Class parameter:parametersType){
                message.append(parameter.getSimpleName()+",");
            }
            //考虑无参的方法
            if(parametersType.length!=0)
                message.deleteCharAt(message.length()-1);
            message.append("){}\n");
        }
        message.append("}");
        System.out.println(message);
	}

2、调用Method

方法作用
ClassMethod getDeclaredMethod(String name, Class<?>... parameterTypes)根据方法名和参数获取对应的方法
MethodObject invoke(Object obj, Object... args)根据参数和对象调用指定的方法

注释
   Class<?>... parameterTypes和Object... args都是可变长度参数,可以当成数组来对待,每个方法的参数列表中最多有一个可变长度参数,且必须要放在最后的位置上!
示例

//package From0To100.Reflect.Method下的用户类
public class User {
    /**
     * @param name 用户名
     * @param password 密码
     * @return 登陆成功返回true,否则返回false
     */
    public boolean login(String name,String password) {
        if ("xzy".equals(name) && "001".equals(password)) {
            return true;
        } else
            return false;
    }
}
//package From0To100.Reflect.Method下的测试类
public class TestMethod {
    public static void main(String[] args) throws Exception {
        Class userClass=Class.forName("From0To100.Reflect.Method.User");
        Object user=userClass.newInstance();
        Method loginMethod=userClass.getDeclaredMethod("login",String.class,String.class);
        //调用方法,需要用到对象、方法、参数、返回值
        Object result=loginMethod.invoke(user,"xzy","001");
        System.out.println(result);
        //结果为true
}

五、获取和调用构造方法

1、获取Constructor

方法作用
ClassConstructor<?>[] getDeclaredConstructors()获取对应类的构造器
Constructorint getModifiers()获取对应构造器修饰符的访问修饰符标志
ConstructorClass<?>[] getParameterTypes()获取构造方法的参数列表

示例:通过反射机制反编译一个类的构造方法

    public static void main(String[] args) throws Exception {
        StringBuilder message=new StringBuilder();
        Class stringClass = Class.forName("java.lang.String");
        Constructor[] stringConstructors=stringClass.getDeclaredConstructors();
        message.append(Modifier.toString(stringClass.getModifiers())+" class "+stringClass.getSimpleName()+" {\n");
        for(Constructor constructor:stringConstructors){
            message.append("\t"+Modifier.toString(constructor.getModifiers())+" "+stringClass.getSimpleName()+"(");
            //获取参数
            Class[] parametersType=constructor.getParameterTypes();
            for(Class parameterType:parametersType){
                message.append(parameterType.getSimpleName()+",");
            }
            //删除没用的,
            if(parametersType.length!=0)
                message.deleteCharAt(message.length()-1);
            message.append("){}\n");
        }
        message.append("}");
        System.out.println(message);
    }

2、调用构造方法创建类对象

方法作用
ClassConstructor<T> getDeclaredConstructor(Class<?>... parameterTypes)根据参数对应的Class获取相应的构造器
ConstructorObject newInstance(Object... initargs)根据构造器传入参数创建类对象

示例:反射机制创建String对象

    public static void main(String[] args) throws Exception {
        Class stringClass=Class.forName("java.lang.String");
        //无参构造方法
        //获取到无参的构造器
        Constructor constructor1=stringClass.getDeclaredConstructor();
        Object string1=constructor1.newInstance();
        System.out.println(string1);
        //有参构造方法
        char[] chars={'x','z','y'};
        //获取到有参的构造器
        Constructor constructor2=stringClass.getDeclaredConstructor(char[].class);
        Object string2=constructor2.newInstance(chars);
        System.out.println(string2);
    }

六、获取父类和接口

方法作用
ClassClass<? super T> getSuperclass()获取父类
ClassClass<?>[] getInterfaces()获取实现的接口

示例:获取String的父类和接口

	public static void main(String[] args) throws Exception {
        Class stringClass=Class.forName("java.lang.String");
        //获取父类
        Class superClass=stringClass.getSuperclass();
        System.out.println("父类:"+superClass.getName());
        //获取接口
        Class[] interfaces=stringClass.getInterfaces();
        for(Class itf:interfaces)
            System.out.println("接口:"+itf.getName());
	}

输出结果如下:

父类:java.lang.Object
接口:java.io.Serializable
接口:java.lang.Comparable
接口:java.lang.CharSequence

欢迎评论区留言交流👍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值