java高级之反射

java高级之反射

反射的概念:

反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造 方法)。

反射的使用前提:

要获得该类字节码文件对象,就是Class对象

获取Class对象的三种方式:

  • 方式1:通过类名.class获得
  • 方式2:通过对象名.getClass()方法获得
  • 方式3:通过Class类的静态方法获得: static Class forName(“类全名”)
    注意: 每一个类的Class对象都只有一个。

代码演示: 这里的Person类就不在提供了,就是一个普通的类。

		//获取Person类的Class对象
        Class c1 = Person.class;

        //根据对象名获取Person类的Class对象
        Person p = new Person();
        Class c2 = p.getClass();

        //根据Class类的静态方法获取 static Class forName(全限定类名)
        Class c3 = Class.forName("cn.it.FanShe.Person");

Class类中常用的方法:

  • String getSimpleName(); 获取Class对象关联对象的 (类名)
  • String getName(); 获取Class对象关联对象的 (包名+类名)
  • T newInstance() ; 创建Class对象关联类的对象

代码演示:

		//获取Person类的Class对象
        Class c1 = Person.class;
	
		//获取类名
        System.out.println("类名为:" + c1.getSimpleName());

        //获取全限定类名
        System.out.println("全限定类名为:" + c1.getName());

        //通过反射创建Person对象 只能获取public修饰的空参构造
        Person person = (Person) c1.newInstance();
        person.setName("李四");
        person.setAge(20);
        System.out.println(person);


结果:
	类名为:Person
	全限定类名为:cn.it.FanShe.Person
	Person{name='李四', age=20}

操作构造方法:

概述:反射构造方法的目的是为了获取Constructor对象来创建类的对象, 每一个构造方法都是一个Constructor类的对象。

获取Constructor对象的方法:

  • Constructor getConstructor(Class… parameterTypes)根据指定的参数获取指定的构造器,只能获取public的构造器
  • Constructor getDeclaredConstructor(Class… parameterTypes) 根据指定的参数获取指定的构造器,可以获取任意权限修饰符的构造器
  • Constructor[] getConstructors() 获得类中所有public修饰的构造器
  • Constructor[] getDeclaredConstructors() 获得类中所有的构造器,可以是任意权限修饰符的构造器

其他方法:

  • T newInstance(Object… initargs)" 根据指定的参数创建对象
  • void setAccessible(true) 设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消
    **作用:当操作的不是public修饰的对象时,在操作对象前要进行暴力反射。
    **

代码演示:

		//Person类
		public class Person extends ApeMan {
		    public String name;
		    private int age;
		
		    public Person() {
		    }
		
		    private Person(String name) {
		        this.name = name;
		    }
		
		    public Person(String name, int age) {
		        this.name = name;
		        this.age = age;
		    }
		
		    public void eat(){
		        System.out.println("人要吃饭!");
		    }
		
		    private void work(){
		        System.out.println("工人要工作!");
		    }
		
		    private void game(String name){
		        System.out.println(name + "会玩游戏!");
		    }
		
		    public String getName() {
		        return name;
		    }
		
		    public void setName(String name) {
		        this.name = name;
		    }
		
		    public int getAge() {
		        return age;
		    }
		
		    public void setAge(int age) {
		        this.age = age;
		    }
		
		    @Override
		    public String toString() {
		        return "Person{" +
		                "name='" + name + '\'' +
		                ", age=" + age +
		                '}';
		    }
		}

//----------------------------------演示-------------------------------------------

		//获取Person类的Class对象
        Class c1 = Person.class;

		//获取无参 public 修饰的构造器
        Constructor con1 = c1.getConstructor();
        
        //获取有参 public 修饰的构造器
        Constructor con2 = c1.getConstructor(String.class, int.class);
        
        //获取有参 任意修饰符的构造器
        Constructor con3 = c1.getDeclaredConstructor(String.class);
        //暴力反射
        con3.setAccessible(true);
        //创建对象 在操作获取的构造器前必须进行暴力反射,因为该构造器是私有的
        //根据有参构造创建Person对象
        Person p1 = (Person) con3.newInstance("A");


        //获取所有public修饰的构造器
        Constructor[] cons1 = c1.getConstructors();
        for (Constructor c : cons1) {
            System.out.println(c);
        }
        //结果:public cn.it.FanShe.Person(java.lang.String,java.lang.Integer)
        //     public cn.it.FanShe.Person()

        //获取任意权限修饰符的所有构造器
        Constructor[] cons2 = c1.getDeclaredConstructors();
        for (Constructor c : cons2) {
            System.out.println(c);
        }
        //结果: public cn.it.FanShe.Person(java.lang.String,java.lang.Integer)
        //      private cn.it.FanShe.Person(java.lang.String)
        //      public cn.it.FanShe.Person()


        //根据构造器创建Person对象
        Person person1 = (Person) con1.newInstance();
        person1.setName("王五");
        person1.setAge(20);
        System.out.println(person1);
        //结果:Person{name='王五', age=20}

操作成员方法:

概述:通过操作Method对象来调用成员方法,每一个成员方法都是一个Method类的对象。

获取Method对象的方法:

  • Method getMethod(String name,Class…args); 根据方法名和参数类型获得指定方法,只能获得public的
  • Method getDeclaredMethod(String name,Class…args); 根据方法名和参数类型获得指定方法,可以获取任意权限修饰符的方法
  • Method[] getMethods(); 获取类中所有的成员方法对象,只能是public修饰的,(包含父类的方法)
  • Method[] getDeclaredMethods(); 获取类中所有的成员方法对象,可以获取任意权限修饰符的方法,(不包含父类的方法)

其他方法

  • Object invoke(Object obj, Object… args) obj指的是当前方法的对象名 args调用方法传递的参数
  • void setAccessible(true) 设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
    作用:当操作不是public修饰的构造器,方法,变量是要在使用前暴力反射一下,不然会报异常

代码演示:

		public class ApeMan {
		    public void cry(){
		        System.out.println("猿人会叫!");
		    }
		}


		public class Person extends ApeMan {
		    private String name;
		    private int age;
		
		    public Person() {
		    }
		
		    private Person(String name) {
		        this.name = name;
		    }
		
		    public Person(String name, int age) {
		        this.name = name;
		        this.age = age;
		    }
		
		    public void eat(){
		        System.out.println("人要吃饭!");
		    }
		
		    private void work(){
		        System.out.println("工人要工作!");
		    }
		
		    private void game(String name){
		        System.out.println(name + "会玩游戏!");
		    }
		
		    public String getName() {
		        return name;
		    }
		
		    public void setName(String name) {
		        this.name = name;
		    }
		
		    public int getAge() {
		        return age;
		    }
		
		    public void setAge(int age) {
		        this.age = age;
		    }
		
		    @Override
		    public String toString() {
		        return "Person{" +
		                "name='" + name + '\'' +
		                ", age=" + age +
		                '}';
		    }
		}

//-------------------------------------------演示---------------------------------------------------

		//获取Person类的Class对象
        Class c = Person.class;
        
        //获取Person对象
        Person p = (Person) c.newInstance();
        //获取public修饰的指定方法
        Method method = c.getMethod("eat");
        //执行invoke方法必须传入相应的对象,有参数后面还要写参数
        //如果方法有返回值,可以用参数接收
        method.invoke(p);
        //结果:人要吃饭!

        //获取私有方法
        Method work = c.getDeclaredMethod("work");
        //开启暴力反射
        work.setAccessible(true);
        //执行invoke方法必须传入相应的对象,有参数后面还要写参数
        //如果方法有返回值,可以用参数接收
        work.invoke(p);
        //结果:工人要工作!

        //获取所有的public修饰的方法(包含父类中public修饰的方法)
        Method[] methods = c.getMethods();
        for (Method m : methods) {
            //展示方法的名称
            System.out.println(m.getName());
        }
        /*
        因为Object是顶级父类,所以也获取了Object中的public修饰的方法,但是这里面的 eat cry 是我们自定义的public方法,其中cry是父类的public方法。
        结果:toString getName setName getAge eat setAge cry wait wait wait equals hashCode getClass notify notifyAll
         */

        //获取所有权限修饰符修饰的方法(不包含父类的)
        Method[] methods1 = c.getDeclaredMethods();
        for (Method m : methods1) {
            //展示方法的名称
            System.out.println(m.getName());
        }
        //因为Object是顶级父类,所以也获取了Object中的方法,但是我们自定义的私有的方法game work public的方法eat都出来了,但是父类的cry没有显示。
        //结果:toString getName setName game work getAge eat setAge      

操作成员变量:

概述:通过Field对象给对应的成员变量赋值和取值,每一个成员变量都是一个Field类的对象。

获取Field对象的方法:(了解即可)

  • Field getField(String name); 根据成员变量名获得对应Field对象,只能获得public修饰
  • Field getDeclaredField(String name); 根据成员变量名获得对应Field对象,可以获取任意权限修饰符的
  • Field[] getFields(); 获得所有的成员变量对应的Field对象,只能获得public的
  • Field[] getDeclaredFields(); 获得所有的成员变量对应的Field对象,可以获取任意权限修饰符的

其他方法:

  • void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
  • Class getType(); 获取属性的类型,返回Class对象。

操作Field对象的方法:

设置:

  • void set(Object obj, Object value)
  • void setInt(Object obj, int i)
  • void setLong(Object obj, long l)
  • void setBoolean(Object obj, boolean z)
  • void setDouble(Object obj, double d)

获取:

  • Object get(Object obj)
  • int getInt(Object obj)
  • long getLong(Object obj)
  • boolean getBoolean(Object ob)
  • double getDouble(Object obj)

代码演示:

		//获取Person的Class对象
        Class c = Class.forName("cn.it.FanShe.Person");

        //获取Person对象
        Person p = (Person) c.newInstance();
        //获取公有的name
        Field name1 = c.getField("name");
        //给name设置值
        name1.set(p,"张三");
        //输出name,如果单一输出name是一个字符串值,所以要使用gei方法
        System.out.println(name1.get(p));
        //结果:张三

        //获取私有的age
        Field age = c.getDeclaredField("age");
        //暴力反射,因为age是私有的
        age.setAccessible(true);
        //输出变量 int类型默认值是0
        System.out.println(age.getInt(p));
        //结果:0

使用反射的好处是什么?(使用场景之一)

我们通过一个简单的例子来演示一下:

需求:在不改动源码的情况下,执行不同类中不同的方法。

分析:我们可以通过配置properties文件的方式,指定所要加载的类路径和要执行的方法的名称,然后通过反射创建该类的对象,并且获取该类的方法并执行,这样就可以做到不改动源码的情况下,运行不同类中的不同方法了。

这是测试中使用到的两个实体类

public class Person {
    public void sleep(){
        System.out.println("睡觉!");
    }

    public void eat(){
        System.out.println("吃饭!");
    }
}

public class Student {
    public void study(){
        System.out.println("学生要学习!");
    }
}

这是主代码:

public class TestDemo {
    public static void main(String[] args)throws Exception {
        //读取配置文件
        InputStream in = TestDemo.class.getClassLoader().getResourceAsStream("pro.properties");
        //创建Properties对象
        Properties pro = new Properties();
        //把读取到的内容存储到Properties集合中
        pro.load(in);

        //获取配置文件中的value
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //通过反射获取Person的Class对象
        Class c = Class.forName(className);
        //通过Class对象获取Person中的方法对象
        Method m = c.getMethod(methodName);
        //创建对象反射的对象
        Object o = c.newInstance();
        //执行该方法
        m.invoke(o);
    }
}

当properties文件中配置的是这些时:结果就是运行的Person类中的eat方法,如果想运行其他方法,直接改properties文件中的value值就行,不需要动源代码了,这样就减少了该动源代码的频率。

className=cn.it.Demo.Person
methodName=eat

有不对的地方请各位指点!觉得还行的话就给个支持吧!

发布了41 篇原创文章 · 获赞 48 · 访问量 4916

猜你喜欢

转载自blog.csdn.net/weixin_45216092/article/details/105272511