Java基础——对反射机制的初步理解,通过反射调用类的私有结构的方法

JAVA反射

  1. Java Reflection
  • 反射是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflaction API取得任何类的内部信息,并且能直接操作任意对象的内部属性和方法。
  • 动态语言:是一类在运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进,已有函数可以被删除或是其他结构上的改变,在运行时代码可以根据某些条件改变自身结构。除了静态语言。Object C php 等。
  • 静态语言,运行时候不能改变结构的原因,JAVA C++
  • 但是JAVA提供了反射机制。
  1. 反射概述
  • 加载完一个类之后,在堆内存的方法区中就擦汗恒了一个Class类型的对象,一个类只能有一个Class对象,这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的内部结构,这个对象就像是一面镜子,透过这个镜子可以看到类的内部结构,所以我们形象的称之为反射。
  • 利用反射可以做的事情
    在这里插入图片描述
  • Class类:类的类…
package com.ntt.sts;

public class Person {

    private String name;
    public int age;

    public Person() {
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }


    private void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //私有构造器
    private Person(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("这是person类的show方法");
    }

    private void say(String name){
        System.out.println(name + "说我是" + name);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.ntt.sts;

import org.junit.Test;

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

public class ReflectionTest {

    @Test
    public void method() throws Exception{
    	//反射的源头Class
    	//Person本身成为Class类的对象,类本身成为了对象。类本身也是对象。(万物皆对象)
        Class clazz = Person.class;
        //通过反射创建对象
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Tom",12);
        //向下转型
        Person p = (Person)obj;
        System.out.println(p.toString());
        //通过反射,调用对象的指定属性
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        //通过反射,调用对象的指定方法(第一个参数是方法的名称)
        Method show =	clazz.getDeclaredMethod("show");
        //调用了show()方法
        show.invoke(p);

        //通过反射可以调用类的私有结构,比如:私有构造器、私有的属性、私有的方法
        //调用非私有的构造器
        Constructor cos1 = clazz.getDeclaredConstructor(String.class);
        cos1.setAccessible(true);
        //一个参数的构造器被定义为私有,但是通过反射就可以调用。
        Person p1 =(Person)cos1.newInstance("Jerry");
        System.out.println(p1);

        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"suitianshuang");
        System.out.println(p1);

        //调用私有的方法
        Method showNation = clazz.getDeclaredMethod("say",String.class);
        showNation.setAccessible(true);
        //以下相当于p1.showNation();
        String nation = (String)showNation.invoke(p1,"suitianshuang");
        System.out.println(nation);
    }
}

  1. 通过直接new的方式或者反射的方式都可以调用公共的结构,开发中到底应该用哪个?

    • 编译的时候无法确定要实例化哪一个对象。这个时候使用反射。
      根据外部的传入条件不同,在运行的时候创建不同的对象
  2. 什么时候会使用反射的方式,反射的特性:动态性

  3. 反射机制与面向对象中的封装性是否矛盾,如何看待两个技术?

    • 不矛盾。???
  4. java.long.Class类的理解

  • 类的加载过程,程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,我们就称之为运行时类,此时运行时类就作为Class的一个实例。换句话说Class的实例就对应着一个运行时类。
获取Class实例
@Test
    public void method1() throws ClassNotFoundException {
        //方式一:通过调用运行时类的属性
        Class clazz = Person.class;
        System.out.println(clazz);
        //方式二:通过运行时类的对象调用getClass()
        Person person = new Person();
        Class clazz1 = person.getClass();
        System.out.println(clazz1);
        //方式三:调用Class的静态方法:forName(String classPath);(使用频率最高)
        //编译的时候没有写死,运行的时候再创建对象,体现了运行时的动态性
        Class clazz3 = Class.forName("com.ntt.sts.Person");
        System.out.println(clazz3);
        //方式四使用类的加载器ClassLoder(了解)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.ntt.sts.Person");
        System.out.println(clazz4);
        System.out.println(clazz3 == clazz4);//true
    }
创建运行时类的对象
package cn.stians.demo;

public class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void say(String name){
        System.out.println(name+"say i am" + name);
    }

    public Person() {
    }

    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 +
                '}';
    }
}

    @Test
    public void test() throws IllegalAccessException, InstantiationException {

        Class<Person> personClass = Person.class;

        /**
         * newInstance()方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器。
         *
         * 要想此方法正常的创建运行时类的对象,要求:
         * 1.运行时类必须提供空参构造器
         * 2.空参构造器的访问权限得够,通常,设置为public
         *
         * 在javabean中要求提供一个public的空参构造器,原因是:
         * 1.便于通过反射,创建运行时的对象
         * 2.便于子类继承此运行时类时,默认调用super()时,保证父类有次构造器。
         */

        Person person = personClass.newInstance();
        System.out.println(person);
    }
//根据传入的类路径创建对象
 public Object getInstance(String classPath) throws Exception{
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }
//初步理解反射的动态性
for (int i = 0; i < 100; i++) {
            int num = new Random().nextInt(2);
            String classPath = "";
            switch (num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                default:
                    System.out.println("error");
            }
            try {
                Object instance = getInstance(classPath);
                System.out.println(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
  • 以上是反射动态性的简单模拟,在运行时创建对象,是框架实现自动化最核心的功能。
调用运行时类的指定结构
  1. 以下时通过反射获取运行时类的全部属性(仅列出部分)
@Test
    public void test2(){
        Class clazz = Person.class;

        //getFields();获取Person(当前运行时类)及其父类中所有public访问权限的属性
        Field[] fields = clazz.getFields();
        for (Object i : fields){
            System.out.println(i);
        }

        //getDeclaredFields();获取当前运行时类全部的属性
        Field[] declaredFields = clazz.getDeclaredFields();
        System.out.println(declaredFields);
        for (Field f: declaredFields) {
            //获取运行时类的权限修饰符
            //public default private producted 默认返回0 - 3 使用Modifier.toString(modifiers)还原。
            int modifiers = f.getModifiers();
            System.out.println(Modifier.toString(modifiers));

            //获取数据类型
            Class type = f.getType();
            System.out.println(type.getName());

            //获取变量名
            String name = f.getName();
            System.out.println(name);
        }

    }

在这里插入图片描述

  • 可以看到每个属性的权限修饰符、数据类型、变量名全部被获取到。
  1. 获取运行时类的方法
//getMethods()获取被public修饰的方法
Method[] methods = clazz.getMethods();
//getDeclaredMethods()获取当前运行时类的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
  1. 获取方法声明的注解
 //getDeclaredMethods()获取当前运行时类的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
	for (Method m: declaredMethods) {
     	Annotation[] anotation = m.getAnnotations();
        for(Annotation a : anotation){
            System.out.println(a);
        }
    }
  • 需要注意的是获取的注解的生命周期必须是运行时的。
4. 此外还可以获取方法的权限修饰符、返回值类型、方法名、形参列表、方法抛出的异常、构造器、运行时类的父类、 运行时类父类的注解、父类的泛型、运行时类实现的接口、运行时类所在的包、运行类声明的注解。
  • 框架实际上就是:注解 + 反射 + 设计模式,可以看出反射的重要程度

  • 如何操作运行时类指定的属性(需要掌握)

@Test
    public void test3() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
        //创建运行时类的对象
        Person p =(Person)clazz.newInstance();
        //获得运行时类中指定变量名的属性
        Field name = clazz.getDeclaredField("name");
        
        //必须调用此方式,才能修改private修饰的属性,否则只能修改public修饰的属性
        name.setAccessible(true);
        //修改、设置指定对象的属性值
        name.set(p,"suitianshuang");
        System.out.println(name.get(p));
    }
  • 获取运行时类的方法(需要掌握)
@Test
    public void test4() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class clazz = Person.class;
        Person p =(Person)clazz.newInstance();
        //参数一是方法名,参数二是方法参数的类型
        Method say = clazz.getDeclaredMethod("say", String.class);

        say.setAccessible(true);
		//参数1是调用者(当前类),参数二是想要设置的实参
        Object returnValue = say.invoke(p, "suitianshuang");
        System.out.println(returnValue);
		/**
		* 调用静态方法,唯一不同是不需要指定invoke方法的调用者。
		*/
    }
  • 调用运行时类的构造器(不常用)
  • 一般是调用空参数的构造器。比较通用。
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        Object suitianshuang = declaredConstructor.newInstance("suitianshuang", 25);
        System.out.println(suitianshuang);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值