java 反射的基本操作

一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.


以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
     (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

基本操作如下:

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

public class ReflectTest {

    public static void main(String[] args) throws Exception {
        //获取Class对象的三种方式
        
        //第一种方式获取Class对象  
        Student stu1  = new Student(); //这一new 产生一个Student对象,一个Class对象
        Class stuClass = stu1.getClass();
        System.out.println(stuClass.getName()); // 输出:com.gdut.reflect.Student
        
        //第二种方式获取Class对象
        Class stuClass2 = Student.class;
        System.out.println(stuClass==stuClass2); // 输出:true
        
        //第三种方式获取Class对象
        try {
            Class stuClass3 = Class.forName("com.gdut.reflect.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(stuClass3 == stuClass2);//输出:true
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        //通过反射获取构造方法并使用
        //1.加载Class对象
        Class clazz = Class.forName("com.gdut.reflect.Student");
        System.out.println("**********所有公有构造方法*************");
        Constructor[] consArrays = clazz.getConstructors(); //就是构造方式被public修饰的
        for(Constructor c : consArrays) {
            System.out.println(c);
        }
        
        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        consArrays = clazz.getDeclaredConstructors(); //就是所有的构造方法,不限制访问权限
        for(Constructor c : consArrays){
            System.out.println(c);
        }
        
        System.out.println("*****************获取公有、无参的构造方法*******************************");
        Constructor con = clazz.getConstructor(null);
        //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
        //2>、返回的是描述这个无参构造函数的类对象。
    
        System.out.println("con = " + con);
        //调用构造方法
        Object obj = con.newInstance();
        System.out.println("obj = " + obj);
        
        System.out.println("******************获取私有构造方法,并调用*******************************");
        con = clazz.getDeclaredConstructor(int.class);
        System.out.println(con);
        //调用构造方法
        con.setAccessible(true);//暴力访问(忽略掉访问修饰符),没有这条,下面就会报错
        obj = con.newInstance(11);
        
        // 获取成员变量并调用
        System.out.println("************获取所有公有的字段********************");
        Field[] fieldArray = clazz.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
        fieldArray = stuClass.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        
        System.out.println("*************获取公有字段**并调用***********************************");
        Field f = clazz.getField("name");
        System.out.println(f);
        //获取一个对象
        obj = clazz.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
        //为字段设置值
        f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
        //验证
        Student stu = (Student)obj;
        System.out.println("验证姓名:" + stu.name);
        
        System.out.println("**************获取私有字段****并调用********************************");
        f = clazz.getDeclaredField("phoneNum");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(obj, "18888889999");
        System.out.println("验证电话:" + stu);

        System.out.println("***************获取所有的”公有“方法*******************");
        Method[] methodArray = clazz.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        
        System.out.println("***************获取所有的方法,包括私有的*******************");
        methodArray = clazz.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        
        System.out.println("***************获取公有的show1()方法*******************");
        Method m = clazz.getMethod("show1", String.class);
        System.out.println(m);
        //实例化一个Student对象
        obj = clazz.getConstructor().newInstance();
        m.invoke(obj, "刘德华");
        
        System.out.println("***************获取私有的show4()方法******************");
        m = clazz.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
        System.out.println("返回值:" + result);

    }

}

class Student {
    
    public String name;
    protected int age;
    char sex;
    private String phoneNum;
    
    Student(String str) {
        System.out.println("defalut (默认)的构造方法 string = " + str);
    }
    
    public Student() {
        System.out.println("public 调用了公有、无参构造方法执行了。。。");
    }
    
    //有一个参数的构造方法
    public Student(char sex){
        System.out.println("public 有一个参数的构造函数 char" + sex);
    }
    
    public Student(String name ,int age){
        System.out.println("public 有多个参数的构造方法 string:"+name+" int:"+ age);
    }
    
    protected Student(boolean n){
        System.out.println("protected 受保护的构造方法 boolean = " + n);
    }
    
    private Student(int age){
        System.out.println("private 私有的构造方法   int:"+ age);
    }

    public void show1(String s){
        System.out.println("public 调用了:公有的,String参数的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("protected 调用了:受保护的,无参的show2()");
    }
    void show3(){
        System.out.println("default 调用了:默认的,无参的show3()");
    }
    private String show4(int age){
        System.out.println("private 调用了,私有的,并且有返回值的,String参数的show4(): age = " + age);
        return "abcd";
    }

    
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex
                + ", phoneNum=" + phoneNum + "]";
    }

}

反射方法的其它使用之---通过反射越过泛型检查

  泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的

ArrayList list1 =newArrayList();
ArrayList<String> list2 =newArrayList<String>();

Class c1 = list1.getClass();
Class c2 = list2.getClass();

System.out.println(c1==c2);//结果为true,为什么??

结果分析:因为反射的操作都是编译之后的操作,也就是运行时的操作,c1==c2返回true,说明编译之后集合的泛型是去泛型化的。

                 那么我们就可以理解为,Java集合中的泛型,是用于防止错误类型元素输入的,比如在list2中我们add一个int,add(10)就会编译报错,那么这个泛型就可以只在编译阶段有效,通过了编译阶段,泛型就不存在了。可以验证,我们绕过编译,用反射动态的在list2中add一个int是可以成功的,只是这时因为list2中存储了多个不同类型的数据(String型,和int型),就不能用for-each来遍历了,会抛出类型转换错误异常ClassCastException
另一个例子:
import java.lang.reflect.Method;
import java.util.ArrayList;
 
/*
 * 通过反射越过泛型检查
 * 
 * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
 */
public class Demo {
    public static void main(String[] args) throws Exception{
        ArrayList<String> strList = new ArrayList<>();
        strList.add("aaa");
        strList.add("bbb");
        
    //    strList.add(100);
        //获取ArrayList的Class对象,反向的调用add()方法,添加数据
        Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
        //获取add()方法
        Method m = listClass.getMethod("add", Object.class);
        //调用add()方法
        m.invoke(strList, 100); //不报错哦
        
        //遍历集合
        for(Object obj : strList){
            System.out.println(obj);
        }
    }
}

console:

aaa
bbb
100

猜你喜欢

转载自www.cnblogs.com/myseries/p/10758051.html