Java学习--反射

Posted whiperhong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java学习--反射相关的知识,希望对你有一定的参考价值。

1.反射的定义

反射:(reflection):在运行时期,动态地去获取类中的信息(类的信息,方法信息,构造器信息,字段等信息进行操作)。

2.获取类的Class实例的三种方式

1. 类名.class 

2. 类的对象.getClass()

3. Class.forName(“类的全限定名”)      全限定名 = 包名 + 类名

注意 :同一个类在JVM的字节码实例只有一份。

public class User {
    @Test
    public void testName() throws Exception {
        //1.使用类名.class 获取类的字节码实例
        Class<User>  clz1 = User.class;
        System.out.println(clz1.toString());
        
        //2.对象.getClass()
        User user = new User();
        Class<?> clz2 =  user.getClass();
        System.out.println(clz2);
        
        //3.Class.forName("全限定名"):用的最多
        Class<?> clz3 = Class.forName("cn.sxt.reflect.User");
        System.out.println(clz3);
    }    
}

3.获取九大内置类的字节码实例

对于对象来说,可以直接使用对象.getClass()或者Class.forName(className); 类名.class都可以获取Class实例.

但是我们的基本数据类型,就没有类的权限定名,也没有getClass方法.

问题: 那么如何使用Class类来表示基本数据类型的Class实例?

八大基本数据类型和 void关键字都是有 字节码实例的

byte,short,int,long,char,float,double,boolean ,void关键字

答 : 数据类型/void.class 即可

每个基本数据类型都是包装类型 如 :int ----Integer包装类型

注意: 基本数据类型和包装数据类型底层的字节码实例是不相同

//获取8大基本数据类型和void的字节码实例
//byte,short,int,long,char,float,double,boolean ,void关键字
public class BaiscDataTypeClassTest {
    @Test
    public void testName() throws Exception {
        //1.获取byte的字节码实例
        Class<?> byteClz = byte.class;
        System.out.println(byteClz);
        //....
        //获取void关键字的字节码实例
        Class<?> voidClz =void.class;
        System.out.println(voidClz);
        
        
        //所有的基本数据类型都有包装类型
        //int---Integer 
        //int 和Integer 的字节码是绝对不相等的
        Class<?> intClz1 = int.class;
        Class<?> intClz2 = Integer.class;
        System.out.println(intClz1);
        System.out.println(intClz2);
        System.out.println(intClz1 == intClz2);    
    }
}

4.获取数组类型的字节码实例

表示数组的Class实例:

   String[] sArr1 = {"A","C"};

   Class clz = String[].class;//此时clz表示就是一个String类型的一位数组类型

所有具有相同元素类型和维数的数组才共享同一份字节码(Class对象);

   注意:和数组中的元素没有一点关系.

package cn.sxt.reflect._01.getclass;

import static org.junit.Assert.*;

import org.junit.Test;

//获取数组类型的字节码实例
public class ArrayClassTest {
    @Test
    public void testName() throws Exception {
        //定义数组
        int[] arr = {1,2,3};
        Class<int[]> clz1 = (Class<int[]>) arr.getClass();
        System.out.println(clz1);
        Class<int[]>  clz2= int[].class;
        System.out.println(clz2);
        
        
        int[] arr2 = {2,3,4};
        Class<int[]> clz3 = (Class<int[]>) arr2.getClass();
        System.out.println(clz3);
        
        
        System.out.println(clz1 == clz2);//true
        System.out.println(clz1 == clz3);//true
    }
}

5.构造函数-Construstor

5.1获取构造函数

类的构函数有 有参数构造函数,无参构造函数,公共构造函数,非公共构造函数,根据不同的构造函数 Class提供了几种获取不同构造函数的方法。

技术图片

//通过字节码实例获取构造器
public class ConstructorTest {
    @Test
    public void testName() throws Exception {
        
        //1.获取Student的字节码实例
        Class<?>  stuClz = Student.class;
        
        //2.获取所有的公共构造函数
        Constructor<?>[] cts1 = stuClz.getConstructors();
        for (Constructor<?> ct : cts1) {
            System.out.println(ct);
        }
        System.out.println("----------------------");
        //3.获取所有的构造函数包括私有的
        Constructor<?>[] cts2 = stuClz.getDeclaredConstructors();
        for (Constructor<?> ct : cts2) {
            System.out.println(ct);
        }
        System.out.println("----------------------");
        
        //4.获取指定的构造函数(clz.getConstructor(...))只能获取公共的构造函数
        Constructor<?> ct1 = stuClz.getConstructor();
        System.out.println(ct1);
        
        Constructor<?> ct2 =stuClz.getConstructor(String.class);
        System.out.println(ct2);
        //4.获取指定的构造函数(clz.getDeclaredConstructor(...))获取的构造函数和权限没有关系
        Constructor<?> ct3=stuClz.getDeclaredConstructor(String.class,int.class);
        System.out.println(ct3);
    }
}

 5.2调用构造函数创建对象

Constructor<T>类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器

常用方法:

public T newInstance(Object... initargs):如调用带参数的构造器,只能使用该方式.  参数:initargs:表示调用构造器的实际参数

  返回:返回创建的实例,T表示Class所表示类的类型

如果:一个类中的构造器可以直接访问,同时没有参数.,那么可以直接使用Class类中的newInstance方法创建对象.

 public Object newInstance():相当于new 类名();

调用私有的构造器:

//使用构造器创建对象
public class NewInstanceTest {
    @Test
    public void testName() throws Exception {
        
        //1.获取Student的字节码实例
        Class<?> clz = Class.forName("cn.sxt.reflect.Student");
        
        //1.1如果类有无参数公共构造函数,直接可以使用类的字节码实例就创建对象
        Student stu0 = (Student) clz.newInstance();
        
        
        //2.获取一个参数的构造函数
        Constructor<Student> ct1 = (Constructor<Student>) clz.getConstructor(String.class);
        //2.1.创建对象
        Student stu1 = ct1.newInstance("东方不败");
        
        //3.获取私有构造函数并创建对象
        Constructor<Student> ct2 =  (Constructor<Student>) clz.getDeclaredConstructor(String.class,int.class);
        //3.1设置权限可以创建对象
        ct2.setAccessible(true);
        //3.2创建对象
        Student stu2 = ct2.newInstance("西门吹雪",50);
    }
}

6.操作方法-Method

一个类创建对象以后,一般就要执行对象的方法等等,使用反射操作对象

首先要获取方法,再去执行.。

技术图片

package cn.sxt.reflect._03method;

import java.util.Arrays;

public class Person {
    
    public void hell1() {
        System.out.println("我是无参数无返回值方法");
    }
    
    public String hello2(String name) {

        return "你好 :"+name;
    }
    
    private String hello3(String name,int age) {
        return "我是 :"+name+",今年 :"+age;
    }
    
    
    
    public static void  staticMethod(String name) {
        System.out.println("我是静态方法 :"+name);
    }
    
    
    
    public static void method1(int[] intArr) {
        System.out.println(Arrays.toString(intArr));
    }
    
    public static void method2(String[] strArr) {
        System.out.println(Arrays.toString(strArr));
    }
    
}
package cn.sxt.reflect._03method;

import static org.junit.Assert.*;

import java.lang.reflect.Method;

import org.junit.Test;

//获取Person类的方法
public class GetMethodTest {

    @Test
    public void testName() throws Exception {
        // 1.获取Person字节码实例
        Class<Person> clz = Person.class;
        // 2.创建对象
        Person p = clz.newInstance();

        // 3.获取方法(使用反射),获取所有公共方法,包含父类的公共方法
        Method[] methods1 = clz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }
        System.out.println("------------------------------");
        // 4.获取自己类中的所有方法(包括私有)
        Method[] methods2 = clz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }
        System.out.println("------------------------------");
        // 4.获取单个指定名称的方法
        Method method = clz.getMethod("hello2", String.class);
        System.out.println(method);

        // 4.1执行方法
        Object res = method.invoke(p, "陆小凤");
        System.out.println(res);

        // 5.获取私有的方法
        Method hello3 = clz.getDeclaredMethod("hello3", String.class, int.class);
        System.out.println(hello3);

        // 5.1设置忽略访问权限
        hello3.setAccessible(true);

        Object res1 = hello3.invoke(p, "叶孤城", 30);
        System.out.println(res1);

        // 6.获取静态方法
        Method staticMethod = clz.getMethod("staticMethod", String.class);

        // 6.1执行静态方法
        staticMethod.invoke(null, "花满楼");

        // 7.获取有整数数组参数的方法
        Method method1 = clz.getMethod("method1", int[].class);
        method1.invoke(null, new Object[] {new int[] { 1, 2, 3, 4 }});

        // 8.获取有整数数组参数的方法
        /*
         * 如果反射传递的参数是引用类型,底层有一个拆箱的功能,会将数组的元素拆成一个个参数传递过来
         * 解决方案: 将数组外面在包装一层数组,如果拆箱一次,得到还是一个数组
         */
        Method method2 = clz.getMethod("method2", String[].class);
        method2.invoke(null,new Object[] {new String[] {"AA","BB","CC"}});
    }
}
package cn.sxt.reflect._03method;

import java.util.Arrays;

public class Person {
    
    public static void method1(int... intArr) {
        System.out.println(Arrays.toString(intArr));
    }
    
    public static void method2(String...strArr) {
        System.out.println(Arrays.toString(strArr));
    }
}

        // 7.获取有整数数组参数的方法
        Method method1 = clz.getMethod("method1", int[].class);
        method1.invoke(null, new Object[] {new int[] { 1, 2, 3, 4 }});

        // 8.获取有整数数组参数的方法
        /*
         * 如果反射传递的参数是引用类型,底层有一个拆箱的功能,会将数组的元素拆成一个个参数传递过来
         * 解决方案: 将数组外面在包装一层数组,如果拆箱一次,得到还是一个数组
         */
        Method method2 = clz.getMethod("method2", String[].class);
        method2.invoke(null,new Object[] {new String[] {"AA","BB","CC"}});

7.操作字段(成员变量)--Field

类中的字段有各种数据类型和各种访问权限,针对这些情况,反射操作有对应的方法来获取和处理

技术图片

package cn.sxt.reflect._04Field;

import static org.junit.Assert.*;

import java.lang.reflect.Field;

import org.junit.Test;

public class FieldTest {
    
    @Test
    public void testName() throws Exception {
        //1.获取People字节码
        Class<People> clz = People.class;
        
        People p = clz.newInstance();
        
        //2.获取所有公共字段
        Field[] fields1 = clz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
        System.out.println("---------------------");
        //3.获取所有字段,和访问权限无关
        Field[] fields2 = clz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }
        System.out.println("---------------------");
        //3.获取指定的公共字段
        Field emialField = clz.getField("emial");
        System.out.println(emialField);
        
        //为字段设置值
        emialField.set(p, "[email protected]");
        System.out.println(p);
        //4.获取指定所有的字段,和访问权限无关
        Field nameFiled = clz.getDeclaredField("name");
        System.out.println(nameFiled);
        //设置忽略访问权限
        nameFiled.setAccessible(true);
        nameFiled.set(p, "张三");
        System.out.println(p);
        
        
        //5 获取age字段
        Field ageFile = clz.getDeclaredField("age");
        ageFile.setAccessible(true);
        //设置忽略访问权限
        ageFile.setInt(p, 18);
        System.out.println(p);
        
    }
}

8.Class的其他常用API方法

//Class字节码的其他api
    @Test
    public void testName() throws Exception {
        
        //1.获取UserDaoImpl的字节码实例
        Class<?> clz = Class.forName("cn.sxt.reflect._05otherapi.UserDaoImpl");
        
        //2.获取所有的接口
        Class<?>[] interfaces = clz.getInterfaces();
        for (Class<?> intface : interfaces) {
            System.out.println(intface);
        }
        
        //3.获取全限定名
        System.out.println(clz.getName());
        
        //4.获取简单类名
        System.out.println(clz.getSimpleName());
        //5.获取包
        System.out.println(clz.getPackage().getName());
    }

 

以上是关于Java学习--反射的主要内容,如果未能解决你的问题,请参考以下文章

java SpringRetry学习的代码片段

反射机制入门

反射机制入门

Java-反射机制学习

JAVA 学习笔记 - 反射机制

反射机制入门