反射学习总结

原文链接:https://blog.csdn.net/sinat_38259539/article/details/71799078
一、反射的概述
java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能成为java的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。
反射就是把java类中的各个成分映射成一个个的java对象
例如:一个类有:成员方法、构造方法、成员变量、包等信息。利用反射技术可以对一个类进行解剖,把一个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
反射学习总结
二、查看Class类在java中的api详解(1.7的API)
反射学习总结
Class类的实例表示正在运行的Java应用程序中的类和接口,也就是JVM中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class没有公共构造方法。Class对象实在加载类时由java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了
在这里插入图片描述

三、反射的使用
先写个Student类
1、获取Class对象的三种方式
1.1 Object -> getClass()
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
1.3 通过Class类的静态方法:forName(String className) 常用
其中1.1是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取
反射学习总结

public class Main {
    public static void main(String[] args) {
        // 获取Class对象的三种方式
        // 1、 Object -> getClass()
        Student student = new Student();
        Class c1 = student.getClass();
        System.out.println(c1.getName());

        // 2、 任何数据类型(包括基本数据类型)都有一个“静态"的class属性
        Class c2 = Student.class;
        System.out.println(c1 == c2);

        // 3、通过Class类的静态方法:forName(String className) 常用
        try {
            Class c3 = Class.forName("reflect.Student");
            System.out.println(c2 == c3);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都已经有了,无需再反射。第二种,需要导入类的包,依赖太强,不导包就抛出编译错误。一般都第三种,一个字符串可以传入也可卸载配置文件中等多种方法。

2、通过反射获取构造方法并使用:

package reflect;

public class   Student {
    public String name;
    private Integer age;
    char sex;
    protected String mobliePhone;
    // 默认构造方法
    Student(String str){
        System.out.println("默认构造方法: " + str);
    }
    // 无参构造方法
    public Student(){
        System.out.println("调用了公有的无参构造方法");
    }
    // 有一个参数的构造方法
    public Student(char name){
        System.out.println("名字:" + name);
    }
    // 多个参数的构造方法
    public Student(String name, Integer age){
        this.name = name;
        this.age = age;
        System.out.println("名字:" + this.name + "年龄:" + this.age);
    }

}

测试通过反射获取构造方法

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ConstructorTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 1、加载Class对象
        Class aClass = Class.forName("reflect.Student");

        // 获取所有公有的构造方法
        System.out.println("---------所有公有的构造方法----------");
        Constructor[] constructors = aClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        // 获取所有的构造方法
        System.out.println("--------------所有的构造方法-------------");
        Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        // 获取指定公有构造方法需要传参数类型,公有无参的构造方法的参数可以传null或者不传
        System.out.println("-----获取指定的公有的构造方法------");
        Constructor constructor = aClass.getDeclaredConstructor(String.class, Integer.class);
        System.out.println(constructor);
        // 调用构造方法,只能调用当前获取到的构造方法。
        Object o = constructor.newInstance("陈陶然", 12);
        System.out.println(o.toString());

        /**
        Constructor constructor1 = aClass.getConstructor();
        Object o1 = constructor1.newInstance("陈陶然", "22"); // java.lang.IllegalArgumentException: 参数数目错误
        System.out.println(o1.toString());
         */

        System.out.println("----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------");
        Constructor declaredConstructor = aClass.getDeclaredConstructor(Integer.class);
        System.out.println(declaredConstructor);
        declaredConstructor.setAccessible(true); // 开启暴力反射
        o = declaredConstructor.newInstance(22);
        System.out.println(o.toString());

    }
}

后台输出:

---------所有公有的构造方法----------
public reflect.Student(java.lang.String,java.lang.Integer)
public reflect.Student()
public reflect.Student(char)
--------------所有的构造方法-------------
private reflect.Student(java.lang.Integer)
protected reflect.Student(boolean)
public reflect.Student(java.lang.String,java.lang.Integer)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
-----获取指定的公有的构造方法------
public reflect.Student(java.lang.String,java.lang.Integer)
名字:陈陶然年龄:12
Student{name='陈陶然', age=12, sex= , mobliePhone='null'}
----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------
private reflect.Student(java.lang.Integer)
私有构造方法, 年龄:22
Student{name='null', age=22, sex= , mobliePhone='null'}

调用方法:
1.获取构造方法:
1).批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

调用构造方法:
Constructor–>newInstance(Object… initargs)

2、 newInstance是 Constructor类的方法(管理构造函数的类)
api的解释为:
newInstance(Object… initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

3、获取成员变量并调用
Student类:

package reflect;

public class   Student {
    public String name;
    private Integer age;
    char sex;
    protected String mobliePhone;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", mobliePhone='" + mobliePhone + '\'' +
                '}';
    }

}

测试通过反射获取Studnet的成员变量

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Fields {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        // 1、获取Class对象
        Class aClass = Class.forName("reflect.Student");
        // 2、获取字段
        System.out.println("----------获取所有公有的字段----------");
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("-----------获取所有的字段-------------");
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        System.out.println("------------获取指定的公有字段----------------");
        Field fieldname = aClass.getField("name");
        System.out.println(fieldname);
        // 获取一个对象
        Object o = aClass.getConstructor().newInstance();
        fieldname.set(o, "陈陶然");  //为Student对象中的name属性赋值-》student.name = "陈陶然"
        Student student = (Student) o;
        System.out.println("验证名字: " + student.name);


        System.out.println("--------------获取指定私有字段--------------");
        Field age = aClass.getDeclaredField("age");
        System.out.println(age);
        Object o1 = aClass.getDeclaredConstructor(String.class, Integer.class).newInstance("陈陶然", 22);;
        age.setAccessible(true);// 暴力反射,解除限定
        age.set(o1,18);
        System.out.println("验证年龄: " + o1);
    }
}

结果为:

---------所有公有的构造方法----------
public reflect.Student()
public reflect.Student(char)
public reflect.Student(java.lang.String,java.lang.Integer)
--------------所有的构造方法-------------
private reflect.Student(java.lang.Integer)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
public reflect.Student(java.lang.String,java.lang.Integer)
protected reflect.Student(boolean)
-----获取指定的公有的构造方法------
public reflect.Student(java.lang.String,java.lang.Integer)
名字:陈陶然年龄:12
Student{name='陈陶然', age=12, sex= , mobliePhone='null'}
----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------
private reflect.Student(java.lang.Integer)
私有构造方法, 年龄:22
Student{name='null', age=22, sex= , mobliePhone='null'}

Process finished with exit code 0

//为字段设置值 :fieldname.set(o, “陈陶然”); //为Student对象中的name属性赋值-》student.name = “陈陶然”

4、获取成员方法并调用
Student:

package reflect;

public class   Student {
    public String name;
    private Integer age;
    char sex;
    protected String mobliePhone;
    // 默认构造方法
    Student(String str){
        System.out.println("默认构造方法: " + str);
    }
    // 无参构造方法
    public Student(){
        System.out.println("调用了公有的无参构造方法");
    }
    // 有一个参数的构造方法
    public Student(char name){
        System.out.println("名字:" + name);
    }
    // 多个参数的构造方法
    public Student(String name, Integer age){
        this.name = name;
        this.age = age;
        System.out.println("名字:" + this.name + "年龄:" + this.age);
    }
    // 受保护的构造方法
    protected Student(boolean n){
        System.out.println("受保护的方法: " + n);
    }
    // 私有构造方法
    private Student(Integer age){
        this.age = age;
        System.out.println("私有构造方法, 年龄:" + this.age);
    }


    public void show1(String s1){
        System.out.println("调用了公有的成员方法show1,参数为: " + s1);
    }
    protected void show2(){
        System.out.println("调用了受保护的无参方法show2()");
    }
    void show3(){
        System.out.println("调用了:默认的无参方法show3()");
    }
    private String show4(String s2){
        System.out.println("调用了私有方法show4(),参数为:" + s2);
        return s2;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", mobliePhone='" + mobliePhone + '\'' +
                '}';
    }

    public static void main(String[] args) {
        System.out.println("Student的Main方法执行了....");
    }
}

测试通过反射获取Student的成员方法。

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MethodTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        Class aClass = Class.forName("reflect.Student");
        // 获取所有公有方法
        System.out.println("---------获取所有公有方法-----------");
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("---------获取所有的方法,包括私有方法------------");
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        System.out.println("-------------获取指定公有方法---------");
        // 不能获取公有方法以外的方法,执意获取报NoSuchMethodException
        Method show1 = aClass.getMethod("show1", String.class);
        System.out.println(show1);
        // 实例化一个Student对象
        Object o = aClass.getConstructor().newInstance();
        show1.invoke(o, "陈陶然");

        System.out.println("----------获取指定私有方法-----------");
        Method show2 = aClass.getDeclaredMethod("show4", String.class);
        show2.setAccessible(true);
        Object res = show2.invoke(o, "陈陶然123");
        System.out.println(res);

    }

}

由此可见:
Method show2 = aClass.getDeclaredMethod(“show4”, String.class);//调用指定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
show2.setAccessible(true);;//解除私有限定
Object res = show2.invoke(o, “陈陶然123”);//需要两个参数,一个是要调用的对象(反射获取),一个是实参
System.out.println(res);

结果为:

---------所有公有的构造方法----------
public reflect.Student(java.lang.String,java.lang.Integer)
public reflect.Student()
public reflect.Student(char)
--------------所有的构造方法-------------
private reflect.Student(java.lang.Integer)
protected reflect.Student(boolean)
public reflect.Student(java.lang.String,java.lang.Integer)
reflect.Student(java.lang.String)
public reflect.Student()
public reflect.Student(char)
-----获取指定的公有的构造方法------
public reflect.Student(java.lang.String,java.lang.Integer)
名字:陈陶然年龄:12
Student{name='陈陶然', age=12, sex= , mobliePhone='null'}
----------获取指定任意修饰符构造方法,并调用;调用私有修饰符的构造方法需要setAccessible(true)--------------------
private reflect.Student(java.lang.Integer)
私有构造方法, 年龄:22
Student{name='null', age=22, sex= , mobliePhone='null'}

5、反射main方法
Student类:

package reflect;

public class   Student {
    public static void main(String[] args) {
        System.out.println("Student的Main方法执行了....");
    }
}

测试通过反射获取main方法

public class Main {
    public static void main(String[] args) {
        // 3、通过Class类的静态方法:forName(String className) 常用
        try {
            Class c3 = Class.forName("reflect.Student");
            Method main = c3.getMethod("main", String[].class);
            //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
            //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
             main.invoke(null, (Object)new String[]{"a", "b", "c"}); // 方式一
            // main.invoke(null, new Object[]{new String[]{"a", "b", "c"}}); // 方式二
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台输出:Student的Main方法执行了…

6、反射方法的其它使用之—通过反射运行配置文件内容
Student类:

package reflect;

public class   Student {
    public String name;
    private Integer age;
    char sex;
    protected String mobliePhone;
    // 默认构造方法
    Student(String str){
        System.out.println("默认构造方法: " + str);
    }
    // 无参构造方法
    public Student(){
        System.out.println("调用了公有的无参构造方法");
    }
    // 有一个参数的构造方法
    public Student(char name){
        System.out.println("名字:" + name);
    }
    // 多个参数的构造方法
    public Student(String name, Integer age){
        this.name = name;
        this.age = age;
        System.out.println("名字:" + this.name + "年龄:" + this.age);
    }
    // 受保护的构造方法
    protected Student(boolean n){
        System.out.println("受保护的方法: " + n);
    }
    // 私有构造方法
    private Student(Integer age){
        this.age = age;
        System.out.println("私有构造方法, 年龄:" + this.age);
    }


    public void show1(String s1){
        System.out.println("调用了公有的成员方法show1,参数为: " + s1);
    }
    protected void show2(){
        System.out.println("调用了受保护的无参方法show2()");
    }
    void show3(){
        System.out.println("调用了:默认的无参方法show3()");
    }
    private String show4(String s2){
        System.out.println("调用了私有方法show4(),参数为:" + s2);
        return s2;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", mobliePhone='" + mobliePhone + '\'' +
                '}';
    }

    public static void main(String[] args) {
        System.out.println("Student的Main方法执行了....");
    }
}

创建配置文件ctr.txt内容:
className = reflect.Student
methodName = show2

测试:

package reflect;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo {
    public static String getValue(String key) throws IOException{
        Properties properties = new Properties(); // 获取配置文件对象
        FileReader fileReader = new FileReader("D:\\study\\reflect\\src\\ctr.txt"); // 绝对路径,获取输入流
        properties.load(fileReader); // 将流加载到配置文件对象中
        fileReader.close();
        return properties.getProperty(key);

    }
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class className = Class.forName(getValue("className"));
        Method methodName = className.getDeclaredMethod(getValue("methodName"));
        methodName.invoke(className.getConstructor().newInstance());

    }
}

结果为:
反射学习总结
7、通过反射越过泛型检查
泛型用在编译期,编译过后泛型擦除,所以是可以通过反射越过泛型检查

package reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class Demo2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<String> strList = new ArrayList<>();
        strList.add("abc");
        strList.add("efg");
        // strList.add(100);
        Class aClass = strList.getClass(); // 得到strList对象的字节码对象
        // 获取add()方法
        Method m = aClass.getMethod("add", Object.class);
        // 调用add()方法
        m.invoke(strList, 100);
        for (Object o : strList) {
            System.out.println(o);
        }

    }
}

控制台输出:
abc
efg
100
上一篇:iOS 根据图片URL从本地相册获取图片


下一篇:Reflect对象一共哟13个静态方法