Java基础知识 32(反射机制,反射构造方法,反射成员方法,反射成员变量,通过反射越过泛型检测)

Java基础知识 32

在写反射这篇文章之前,我们先思考一个问题,java中如何创建一个对象,有哪几种方式?

java中创建对象大概有这几种方式:
(1)使用new关键字:这是我们最常见的也是最简单的创建对象的方式。
(2)使用clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去。
(3)使用反序列化:当我们序列化和反序列化一个对象时,JVM会给我们创建一个单独的对象。
上述是Java中常见的创建对象的三种方式,还有另外一种方式,就是我们下面要讨论的反射

反射概述:

(1)什么是反射?
反射就是把Java类中的各个部分,映射成一个个的Java对象,拿到这些对象后我们可以做一些事情。
通过反射机制,我们来剖析一个类的构成:
一个类的构成包括:构造方法,成员变量,成员方法。
通过反射来剖析这个类的三个构成部分,那么这三个构成,也会被看作是一种类型。
构造方法:constructor 成员变量:field 成员方法:Method
(2)反射能干什么?
一般来说反射是用来做框架的,或者说可以做一些抽象都比较高的底层代码,反射在日常的开发者用到的不多,但是必须得懂它,搞懂反射以后,可以帮助我们理解框架的一些原理。有一句很经典的话:反射是框架设计的灵魂

1.反射构造方法

反射机制:Java反射机制是在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取类的信息以及动态调用对象的方法的功能被称为Java语言的反射机制。要想解剖一个类。必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的class类型的对象。
我们要使用反射机制,那么就先要获取一个类的字节码文件对象(class字节码文件类型)
(1)获取一个类的字节码文件对象class对象的三种方式
着重理解理解记忆forName(String className)

//方式1:就是通过Object类中的getClass()方法
Student student = new Student(); //Student.class------->字节码文件对象
Class<? extends Student> aClass = student.getClass();
Class<? extends Student> aClass1 = student.getClass();
System.out.println(aClass == aClass1);
System.out.println("---------------------------");
//方式2:通过Class中的静态方法 forName()方法来获取一个类的字节码文件对象
//权限定类名:包名+类名 就能确定一个类的唯一性
//使用反射方式来强制创建某个类或接口对应的java.long.Class对象
Class<?> aClass2 = Class.forName("org.westos.demo.Student");
Class<?> aClass3 = Class.forName("org.westos.demo.Student");
System.out.println(aClass2 == aClass3);
Class<?> aClass4 = Class.forName("org.westos.demo.Student");
System.out.println(aClass2 == aClass4);
System.out.println("---------------------------");
//方式3:针对每个类,都有一个静态的.class属性 来获取一个类的字节码文件对象
Class<Student> studentClass = Student.class;
Class<Object> objectClass = Object.class;
Class<String> stringClass1 = String.class;
Class<String> stringClass2 = String.class;
System.out.println(stringClass1 == stringClass2);

(2)通过反射获取私有和非私有的无参构造方法
获取到所有的构造方法,私有的构造方法获取不到:getConstructors()
获取到该类中所有的构造方法 ,包括私有的构造方法:getDeclaredConstructors()
获取到单个(空参的)的构造方法对象,getConstructor()

//通过反射,来调用构造方法执行,创建出该类对象
//1.先获取该类的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo2.Student");//Student.class
//2.可以获取Student类的构造方法对象
//获取该类中所有的构造方法对象,私有的构造方法对象获取不到
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);
}
System.out.println("------------------------");
//获取单个的构造方法对象
//获取空参的构造方法对象
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);
System.out.println("------------------------");
//获取一个参数的构造方法对象,参数就是你构造方法的参数类型字节码类型
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
System.out.println("------------------------");
//获取私有的构造方法对象 getDeclaredConstructor()
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);

(3)通过反射获取私有和非私有的有参构造方法
获取到一个参数的构造方法对象,参数就是你构造方法的参数类型的字节码类型:getConstructor(String.class)
获取到私有的构造方法对象:getDeclaredConstructor(String.class,int.class)

//获取一个参数的构造方法对象,参数就是你构造方法的参数类型字节码类型
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
System.out.println("--------------------------------");
//获取私有的构造方法对象 getDeclaredConstructor()
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);
public class Mytest2 {
    public static void main(String[] args) throws Exception {
        //要创建一个类的对象,肯定要借助构造方法
        //我们通过反射的方式来创造一个类的对象。
        Class<?> aClass = Class.forName("org.westos.demo2.Student");
        //获取空参的构造方法对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        //通过构造方法对象中的方法,创建出Student类对象
        Student o = (Student) declaredConstructor.newInstance();
        System.out.println(o);
        System.out.println("----------------------");
        //使用反射的方式,通过有参构造来创建对象
        Constructor<?> constructor = aClass.getConstructor(String.class);
        //调整构造方法对象中的方法来创建student类的对象
        Object obj = constructor.newInstance("李四");
        System.out.println(obj);
        System.out.println("----------------------");
        //反射这种方式,即便你是私有构造,也能创建出该类的对象
        //获取私有的构造方法对象
        Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
        //取消语法检查
        declaredConstructor1.setAccessible(true);
        Object obj1 = declaredConstructor1.newInstance("赵六", 26);
        Student stu= (Student) obj1;
        System.out.println(stu);
    }
}

注意:在反射私有的构造函数时,用普通的aClass.getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法aClass.getDeclaredConstructor(int.class)//读取私有的构造函数,用这个方法读取完还需要设置一下取消语法检测(也叫暴力反射)c.setAccessible(true)//暴力反射。

//反射这种方式,即便你是私有构造,也能创建出该类的对象
//获取私有的构造方法对象
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
//取消语法检查
declaredConstructor1.setAccessible(true);//暴力反射
Object obj1 = declaredConstructor1.newInstance("赵六", 26);
Student stu= (Student) obj1;
System.out.println(stu);
public class Mytest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.我们使用反射的方式,调用空参构造来创建对象的方式
        Class<?> aClass = Class.forName("org.westos.demo3.Teacher");
        //2.获取空参构造方法对象
        Constructor<?> constructor = aClass.getConstructor();
        //3.调用构造方法对象中的方法来创建该对象
        Teacher teacher = (Teacher) constructor.newInstance();
        System.out.println(teacher);
        System.out.println("----------------------");
        //方式2:如果只是想通过空参构造来创建对象,可以使用Class类里面的newInstance()方法直接创建对象
        Teacher teacher1 = (Teacher) aClass.newInstance();
    }
}
2.反射成员变量

如果通过反射来剖析一个类的构成,对于类中的成员变量,使用Field类型来描述成员变量。
获取类中的所有字段对象,但是私有的字段对象获取不到:getFields()
获取类中的所有字段对象,包括私有的字段对象:getDeclaredFields()
获取单个的非私有的字段对象,要获取哪个字段对象,可以将名字传入:getField()
获取单个的私有或非私有的字段对象,getDeclaredField()

public class Mytest {
    public static void main(String[] args) throws Exception {
        //如果通过反射来剖析一个类的构成,对于类中的成员变量,使用Field类型来描述成员变量
        //1.要使用反射,第一步得先获得它的字节码文件
        Class<?> aClass = Class.forName("org.westos.demo4.Dog");
        //2.获取Dog类的所有字段对象,但是私有的字段对象获取不到
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("--------------------------");
        //获取Dog类中的所有字段对象,包括私有的字段对象
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        System.out.println("--------------------------");
        //获取单个的非私有的字段对象,你要获取哪个字段对象,你可以将名字传入
        Field Filename = aClass.getField("name");
        System.out.println(Filename);
        Field Fileage = aClass.getField("age");
        System.out.println(Fileage);
        System.out.println("--------------------------");
        //获取单个的私有或者非私有的字段对象
        Field Filesex = aClass.getDeclaredField("sex");
        System.out.println(Filesex);
    }
}

通过反射的方式,给类中的成员变量设置值以及获取值。

public class Mytest2 {
    public static void main(String[] args) throws Exception {
        //我们通过反射的方式,给类中的成员变量设置值,以及获取值
        //1.要使用反射,得先获取该类的的字节码文件对象
        Class<?> aClass = Class.forName("org.westos.demo4.Dog");
        //2.给name字段设置,我们就要获取name字段对象
        Field fieldname = aClass.getField("name");
        //调用field类中的方法来设置值
        //通过反射,来创建一个类的对象getDeclaredConstructor()
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj1 = declaredConstructor.newInstance();
        //set()方法要两个参数,第一个参数就是你该类的对象,第二个就是你要设置的具体的值
        fieldname.set(obj1,"小黑");
        //获取name字段的值
        Object obj2 = fieldname.get(obj1);
        System.out.println(obj2);
        System.out.println("-------------------------------");
        Field fieldage = aClass.getField("age");
        Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor();
        declaredConstructor1.setAccessible(true);
        Object obj3 = declaredConstructor1.newInstance();
        fieldage.set(obj3,8);
        //获取age字段的值
        Object obj4 = fieldage.getInt(obj3);
        System.out.println(obj4);
        System.out.println("-------------------------------");
        //给私有字段设置值
        Field fieldsex = aClass.getDeclaredField("sex");
        //对于私有的,我们可以取消语法检查
        fieldsex.setAccessible(true);
        fieldsex.setChar(obj1,'男');
        //获取字段的值
        char aChar = fieldsex.getChar(obj1);
        System.out.println(aChar);
    }
}
3.反射成员方法

对应类中的成员方法Java给我们提供了Method这个类来描述。
获取到类中的所有的非私有的成员方法,包括从父类继承下来的成员方法:getMethods()
获取到类中的所有的成员方法,包括私有的成员方法,但不包括继承父类的方法:getDeclaredMethods()
获取到单个的非私有方法对象:getMethod() 方法的参数:参数1:方法名,参数2:方法上形参数据类型.class
获取到单个的私有方法对象:getDeclaredMethod()

public class Mytest {
    public static void main(String[] args) throws Exception {
        //1.获取该类的字节码文件对象 
        Class<?> aClass = Class.forName("org.westos.demo.Student");
        //2.获取到类中所有的成员方法对象,获取到非私有的方法对象,包括从父类继承下来的方法对象也能获得到
        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("----------------------");
        //获取单个的非私有方法对象
        Method heheMethod = aClass.getMethod("hehe");
        System.out.println(heheMethod);
        System.out.println("----------------------");
        //获取有参的方法对象, getMethod()方法的参数1:方法名 参数2:方法上形参数据类型的.class
        Method testMethod = aClass.getMethod("test", String.class, int.class);
        System.out.println(testMethod);
        System.out.println("----------------------");
        //获取单个的私有方法对象
        Method showMethod = aClass.getDeclaredMethod("show", String.class, int.class);
        System.out.println(showMethod);
    }
}

invoke(Object obj,Object…args):对带有指定参数的指定对象调用由此Method对象表示的底层方法。

public class Mytest2 {
    public static void main(String[] args) throws Exception {
        //我们用反射的方式,调用方法执行
        Class<?> aClass = Class.forName("org.westos.demo.Student");
        Method heheMethod = aClass.getMethod("hehe");
        //Object invoke(Object obj, Object... args)
        //对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();
        //invoke(obj);  //参数1 就是该类的对象
        Object invoke = heheMethod.invoke(obj);
        System.out.println("---------------------------");
        //调用有参方法执行
        Method hahaMethod = aClass.getMethod("haha", String.class);
        hahaMethod.invoke(obj,"老师");
        System.out.println("--------------------------");
        Method testMethod = aClass.getMethod("test", String.class, int.class);
        testMethod.invoke(obj,"张三",23);
        System.out.println("--------------------------");
        Method test2Method = aClass.getMethod("test2", String.class, int.class);
        Object invoke1 = test2Method.invoke(obj, "李四", 24);
        System.out.println(invoke1);
        System.out.println("--------------------------");
        //调用私有的方法执行
        Method showMethod = aClass.getDeclaredMethod("show", String.class, int.class);
        //取消私有的检查
        showMethod.setAccessible(true);
        showMethod.invoke(obj,"王五",25);
    }
}
通过反射越过泛型检查
public class Mytest {
    public static void main(String[] args) throws Exception {
        //泛型,只在编译期有效,运行期就擦除了
        ArrayList<String> list = new ArrayList<>();
        list.add("AAA");
        list.add("BBB");
        list.add("CCC");
        //list.add(123);
        //我们可以通过反射机制,越过泛型检测
        Class<? extends ArrayList> aClass = list.getClass();
        //获取add方法对象
        Method addMethod = aClass.getDeclaredMethod("add", Object.class);
        //调用add方法执行
        addMethod.invoke(list,"DDD");
        addMethod.invoke(list,123);
        addMethod.invoke(list,true);

        System.out.println(list);
    }
}
通过反射写一个通用的设置某个对象的某个属性为指定的值
测试类:
public class Mytest {
    public static void main(String[] args) throws Exception {
        //通过反射机制写一个通用的设置某个对象的某个属性为指定的值
        Constructor<?> declaredConstructor = Class.forName("org.westos.demo4.Teacher").getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();
        MyUtils.setProperty(obj,"name","张三");

        Object name = MyUtils.getProperty(obj, "name");
        System.out.println(name);
        System.out.println("----------------------");
        MyUtils.setProperty(obj,"age",30);
        Object age = MyUtils.getProperty(obj, "age");
        Integer num= (Integer) age;
        System.out.println(num);
    }
}
--------------------------------------------
工具包:
public class MyUtils {
    public static void setProperty(Object obj,String propertyName,Object value) throws Exception {
        //获取字节码对象
        Class<?> aClass = obj.getClass();
        Field declaredField = aClass.getDeclaredField(propertyName);
        declaredField.setAccessible(true);
        declaredField.set(obj,value);
    }
    public static Object getProperty(Object obj,String propertyName) throws Exception {
        //获取字节码对象
        Class<?> aClass = obj.getClass();
        Field declaredField = aClass.getDeclaredField(propertyName);
        declaredField.setAccessible(true);
        Object value = declaredField.get(obj);
        return value;
    }
}
---------------------------------------------
具体类:
public class Teacher {
    private Teacher(){

    }
    private String name;
    private int age;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值