java中反射的使用

  • 1. 概念
  • 2. 为什么需要反射
  • 3. 反射Class类对象
  • 4. 常用方法
  • 5. 反射的应用
  • 5.1 获取类对象
  • 5.2 获取构造方法
  • 5.3 获取成员方法
  • 5.4 获取成员变量
  • 5.5 创建对象
  • 5.6案例
  • 6. 暴力反射
  • 6.1案例


1. 概念

Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,private的只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。

反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

2. 为什么需要反射

好好的我们new User(); 不是很好,为什么要去通过反射创建对象呢?

那我要问你个问题了,你为什么要去餐馆吃饭呢?

例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。

好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好?

那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢?

我们直接拿过来吃就好了。

再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。

在开发的世界里,spring就是专业的组织,它来帮我们创建对象,管理对象。我们不在new对象,而直接从spring提供的容器中beans获取即可。Beans底层其实就是一个Map<String,Object>,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。

总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。
作用:

  • 拿到.class文件里的数据进行解析.
  • 获取文件里的所有数据(公开的.私有的.构造.属性.方法)
  • 并把所有数据封装在Class对象身上.

3. 反射Class类对象

  • Class.forName(“类的全路径”);
  • 类名.class
  • 对象.getClass();

4. 常用方法

获得包名、类名

  • clazz.getPackage().getName()//包名
  • clazz.getSimpleName()//类名
  • clazz.getName()//完整类名

成员变量定义信息

  • getFields()//获得所有公开的成员变量,包括继承的变量
  • getDeclaredFields()//获得本类定义的成员变量,包括私有,不包括继承的变量
  • getField(变量名)
  • getDeclaredField(变量名)

构造方法定义信息

  • getConstructor(参数类型列表)//获得公开的构造方法
  • getConstructors()//获得所有公开的构造方法
  • getDeclaredConstructors()//获得所有构造方法,包括私有
  • getDeclaredConstructor(int.class, String.class)

方法定义信息

  • getMethods()//获得所有可见的方法,包括继承的方法
  • getMethod(方法名,参数类型列表)
  • getDeclaredMethods()//获得本类定义的方法,包括私有,不包括继承的方法
  • getDeclaredMethod(方法名, int.class, String.class)

反射新建实例

  • c.newInstance();//执行无参构造
  • c.newInstance(6, “abc”);//执行有参构造
  • c.getConstructor(int.class, String.class); //执行含参构造,获取构造方法

反射调用成员变量

  • c.getDeclaredField(变量名); //获取变量
  • c.setAccessible(true); //使私有成员允许访问
  • f.set(实例, 值); //为指定实例的变量赋值,静态变量,第一参数给 null
  • f.get(实例); //访问指定实例的变量的值,静态变量,第一参数给 null

反射调用成员方法

获取方法

Method m = c.getDeclaredMethod(方法名, 参数类型列表);

m.setAccessible(true) ;//使私有方法允许被调用

m.invoke(实例, 参数数据) ;//让指定的实例来执行该方法

5. 反射的应用

5.1 获取类对象

private static void method() throws Exception {
       Class clazz = Student.class;
       Class<?> clazz2 = Class.forName("seday15.Student");
       Class clazz3 = new Student().getClass();
      
       System.out.println(clazz.getName());
       System.out.println(clazz2.getName());
       System.out.println(clazz3.getName());
    }

5.2 获取构造方法

private static void method3(Class clazz) {
       Constructor[] cs = clazz.getDeclaredConstructors();
       for (Constructor c : cs) {
           String name = clazz.getSimpleName();
           System.out.println(name);
          
           Class[] cs2 = c.getParameterTypes();//参数
           System.out.println(Arrays.toString(cs2));
          
       }
      
    }

5.3 获取成员方法

private static void method4(Class clazz) {
     Method[] ms = clazz.getMethods();
       for (Method m : ms) {
           String name = m.getName();
           System.out.println(name);
          
           Class<?>[] cs = m.getParameterTypes();
           System.out.println(Arrays.toString(cs));
       }
    }

5.4 获取成员变量

private static void method2(Class clazz) {
       Field[] fs = clazz.getFields();//获取public的属性
       for (Field f : fs) {
           String name = f.getName();
           String tname = f.getType().getSimpleName();
           System.out.println(name);
           System.out.println(tname);
       }
    }

5.5 创建对象

import java.lang.reflect.Constructor;
import java.util.Scanner;
 
//反射新建两个对象
public class Test3 {
    public static void main(String[] args) throws Exception {
       String s =  new Scanner(System.in).nextLine();
       Class<?> clazz = Class.forName(s);
      
       Object o1 = clazz.newInstance();//用无参构造
       System.out.println(o1);
 
			Constructor<?> c = clazz.getConstructor(String.class);//用含参构造
       Object o2 = c.newInstance("jack");
       System.out.println(o2);
      
    }
}

5.6案例

创建Student类:

//配合 测试 反射
public class Student {
    //构造方法-generate-constructor-多选ctrl/单选-ok
    public Student() {
    }
    public Student(String name) {
        this.name = name;
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    String  name ;
    int age ;
    double score ;

    public void study(){
        System.out.println("study()");
    }
    public void coding(){
        System.out.println("coding()");
    }

}

测试:

import java.lang.reflect.Constructor;
		import java.lang.reflect.Field;
		import java.lang.reflect.Method;
		import java.util.Arrays;

		//测试 反射 获取Class对象
		public class Test2_ReflectStudent {
		    public static void main(String[] args) throws Exception {
		//        method();//获取Class对象
		//        method2();//获取构造方法
		//        method3();//获取成员方法
		//        method4();//获取成员变量
		        method5();//通过反射的技术创建对象
		    }
		    //创建对象
		    public static void method5() throws Exception {
		        //1,获取Class对象
		        Class<Student> clazz = Student.class ;
		        //2,调用实例化方法 --也要触发构造方法,而且触发的是无参构造
		        //newInstance()必须提供无参构造,否则[抛出异常:java.lang.InstantiationException: cn.tedu.reflect.Student
		        Student s = clazz.newInstance();
		        System.out.println("s = " + s);
		        //重写toString()前:s = cn.tedu.reflect.Student@1b6d3586
		       //重写toString()后:s = Student{name='null', age=0, score=0.0}

		        //怎么触发含参构造--触发public Student(String name)
		        //getConstructor(m);--m参数要 匹配构造方法里 参数的类型 的Class对象
		        Constructor<Student> c = clazz.getConstructor(String.class);//指定
		        //newInstance(x)--x是你创建对象时具体的参数
		        Student s2 = c.newInstance("jack");
		        System.out.println("s2 = " + s2);

		        //TODO 触发public Student(String name, int age)
		        Constructor<Student> c2 = clazz.getConstructor(String.class,int.class);
		        Student s3 = c2.newInstance("xiongda",20) ;
		        System.out.println("s3 = " + s3);
		    }
		    //获取成员变量
		    public static void method4() {
		        //1,获取Class对象
		        Class clazz = Student.class ;
		        //2,获取所有成员变量 -- 只能获取公开的变量
		        Field[] fs = clazz.getFields();
		        //3,遍历数组,得到每个变量f
		        for (Field f : fs) {
		            //4,获取变量名称
		            System.out.println(f.getName());
		            //5,获取变量类型
		            System.out.println(f.getType().getName() );
		        }
		    }
		    //获取成员方法
		    public static void method3() {
		        //1,获取Class对象
		        Class<Student> clazz = Student.class ;
		        //2,获取所有成员方法们 -- 包括自己的和父类的 的公开的方法们
		        Method[] ms = clazz.getMethods() ;
		        //3,遍历数组,得到每个方法m
		        for (Method m : ms) {
		            //4,获取方法名称
		            System.out.println(m.getName());
		            //5,获取方法的参数的类型
		            Class<?>[] cs = m.getParameterTypes();
		            System.out.println(Arrays.toString(cs));
		        }
		    }
		    //获取构造方法
		    public static void method2() {
		        //1,获取Class对象 --封装了.class文件里的所有数据
		        Class clazz = Student.class ;
		        //2,获取所有构造方法们  , 并存入数组 -- 只能反射 公开的 资源
		        Constructor[] cs = clazz.getConstructors();
		        //3,获取每个构造方法c
		        for (Constructor c : cs) {
		            //4,获取构造方法的名字
		            System.out.println( c.getName() );
		            //5,获取构造方法的参数类型 们
		            Class[] css = c.getParameterTypes();
		            System.out.println( Arrays.toString(css) );
		        }
		    }
		    //获取Class对象
		    public static void method() throws ClassNotFoundException {
		        Class c = Class.forName("cn.tedu.reflect.Student");//参数是类的全路径
		        Class c2 = Student.class ;
		//        Class c3 = new Student().getClass() ;//使用了父类Object提供的getClass()
		//        System.out.println("c3 = " + c3);
		//        System.out.println("c2 = " + c2);
		//        System.out.println("c = " + c);
		    }
		}

6. 暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:

什么时候需要使用反射 java java为什么要使用反射_构造方法

6.1案例

创建Person类

class Person{
   
    private String name="jack";
    private int age = 30;
   
    private void show(int[] a) {
       System.out.println("show()..."+Arrays.toString(a));
    }
    private void test() {
       System.out.println("test()...");
    }
}

测试

1、 获取私有属性值并修改

2、 获取私有方法并执行

import java.lang.reflect.Field;
import java.lang.reflect.Method;
//测试 暴力反射:
//条件 : 使用getDeclaredXxx()  + 开启私有可见的权限
public class Test3_ReflectPerson {
    public static void main(String[] args) throws Exception {
       Class<?> clazz = Class.forName("seday16new.Person");
//     method(clazz);//暴力反射隐私属性
       method2(clazz);//暴力反射执行方法
    }
 
    private static void method2(Class<?> clazz) throws Exception {
     	//暴力反射 某个方法
		  //getDeclaredMethod(1,2)--获取指定的方法
		  //--1是指方法名--2是指方法需要的参数类型的Class对象
       Method m = clazz.getDeclaredMethod("show", int[].class);
       Object obj = clazz.newInstance();
       m.setAccessible(true);//暴力反射:除了用对API,另外还需要开启访问权限
       m.invoke(obj, new int[]{1,2,3});//执行方法--invoke(1,2)--1是指对象名称--2是方法需要传入的参数
    }
 
    private static void method(Class clazz) throws Exception {
    	 //暴力反射
       Field f = clazz.getDeclaredField("name");
       //获取变量类型
       System.out.println(f.getType().getName());
       f.setAccessible(true);//属性隐私可见,开启 访问权限,否则访问私有抛出异常:IllegalAccessException:
       Object obj = clazz.newInstance();
       //设置值--set(1,2)--1是指对象名称--2是要设置的具体值
//     f.set(obj, "rose");//设置值
       //获取值--get(1)--1是指对象名称
       System.out.println(f.get(obj));//获取值
   
      
       //---所有属性
       Field[] fs = clazz.getDeclaredFields();
       for (Field ff : fs) {
           System.out.println(ff);
           ff.setAccessible(true);//暴力反射
           System.out.println(ff.get(obj));
       }
      
    }
   
}