JAVA14-反射

1.反射的简介

1.1 反射的概念

1.1.1 普通方式获取对象

在说反射之前,先看下面的例子:

Pet p1 = new Pet("猫","花花",2); //直接初始化,[正射] [正向]

p1.eat("鱼骨头");
  • 对于需求而言:

    一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

  • 对于程序而言:

    有两个时期,编译期和运行期;编译期就是编译器帮咱们把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给JVM去执行,

    简单来说:程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性

1.1.2 反射的简介

下面我们来说反射:

        反射(Reflection)是java中的一个强大的特性,它允许程序在运行时查询、访问和修改类、接口、字段和方法的信息反射提供了一种动态地操作类的能力,这在很多框架和库中被广泛使用,例如Spring框架的依赖注入。

Oracle 官方对反射的解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions. The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

        简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

        反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

Java 反射主要提供以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  • 在运行时调用任意一个对象的方法

重点:是运行时而不是编译时

1.1.3 反射方法获取对象

        反射(Reflect),在Java中,可以在程序运行的过程中, 动态的获取类、 获取类中的成员(属性、方法、构造方法), 并进行访问。 这里的动态获取, 指的是可以通过一个字符串进行获取。 例如: 通过一个类名字符串, 可以获取到这个类, 并进行对象的实例化。 通过属性名字字符串, 获取到一个属性, 并可以访问。 通过一个方法的名字字符串, 获取一个方法, 并进行访问。 这样的机制, 叫做反射。

1.2 Class类的简介

1.2.1. Class类的简介

        Class是Java中的一个特殊的类, 用来描述Java中的各种类型。 是对每一个类、 接口、 枚举...编译后生成的.class字节码文件的描述。 这个类也是继承自Object类的, 主要用来描述一个类中有什么成员, 包括: 构造方法、 属性、 方法。

1.2.2. Class对象的获取

1) getClass方法

        在Object类中, 有一个方法, 叫做 getClass()。 可以通过对象调用这个方法, 获取到一个用来描述这个对象所对应的类的Class信息。        

2)class属性

        可以使用类的属性 .class 获取Class信息。

3)Class.forName方法

        由于在反射中,重点强调动态性。 类的获取、 属性的获取、 方法的获取、 构造方法的获取, 都是要通过一个字符串进行获取的, 我们甚至可以将一个需要加载的类名写到一个配置文件中, 在程序中读取这个配置文件中的数据, 加载不同的类。 这样一来, 当需要进行不同的类加载的时候, 直接改这个配置文件即可, 其他程序不用修改。 再例如, 可以通过属性的名字, 用反射获取到对应的属性, 并进行访问; 通过方法的名字, 用反射获取对应的方法, 并进行访问。 因此, 上述的两种Class对象的获取都不够动态, 我们需要用来获取Class信息, 更多的使用的就是这个方法。

        这里会出现异常: ClassNotFoundException, 就是字面意思, 没有找到这个类, 很可能是因为类的名字写错了。 这里, 通过类的名字进行类的获取, 需要写类的全限定名, 即从最外层的包开始, 逐层向内查找, 直到找到这个类。 如果是内部类, 则需要用$进行向内查询。 参考内部类编译后生成的字节码文件的格式信息。

        这个方法, 会将类加载到内存, 如果是第一次加载, 会触发这个类中的静态代码段

 演示代码:

import com.se.day11.bDiyClass.Pet;

import java.lang.reflect.Field;

public class _01GetClassDemo {
    public static void main(String[] args) {
        //获取com.se.day11.bDiyClass.Pet
        //方式一
        Pet pet = new Pet("Cat", "画画", 3);
        Class<? extends Pet> aClass = pet.getClass();
        //方式二
        Class<Pet> aClass1 = Pet.class;
        System.out.println(aClass1==aClass);// 返回true, 表示类的描述对象,是唯一的,是单例的
        //方式三
        try {
            Class<?> aClass2 = Class.forName("com.se.day11.bDiyClass.Pet");
            //获取该对象的所有属性(Field 字段): 调用的方法中带有Declared的
            Field[] declaredFields = aClass2.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                System.out.println(declaredField.getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

2. 反射机制实例化对象

        在反射中的对象实例化, 和之前面向对象部分不太一样。 在反射部分, 我们更多强调的是动态, 动态的进行一个类的对象实例化。 只需要通过一个类名字符串, 即可完成对这个对象的实例化。 很多时候, 我们可以将需要加载的不同的类, 以配置文件的形式写入到一个文件中, 可以使用程序读取这个文件, 从而得到一个类的名字, 通过反射进行不同的对象的实例化。

2.1. newInstance()

        这个方法, 是Class类中的一个非静态的方法需要首先获取到Class对象, 使用这个Class对象进行方法的调用, 完成对象的实例化。

演示:

0.Person类

package com.se.day11.bDiyClass;

/*
    使用Person来描述显示生活中人的特点和行为
 */

public class Person {
    private String idcard;
    private String name;
    private int age;
    private char gender;
    private boolean isMarry;
    public Person(){}
    public Person(String idcard,String name){
        this.idcard = idcard;
        this.name = name;
    }
    private Person(String idcard,String name,int age){
        this.idcard = idcard;
        this.name = name;
        this.age = age;
    }
    public Person(String idcard, String name, int age, char gender, boolean isMarry) {
        this.idcard = idcard;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.isMarry = isMarry;
    }
    public void play(String game){
        System.out.println(name + "在玩" + game);

    }
    public void sport(String game) {
        System.out.println(name + "在打" + game);
    }

    @Override
    public String toString() {
        return "Person{" +
                "idcard='" + idcard + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", isMarry=" + isMarry +
                '}';
    }
}

1.写配置文件 reflection.properties

classname=com.se.day11.bDiyClass.Person

 2. 编写反射工具类ReflectionUtil

import java.io.FileReader;
import java.util.Properties;

public class ReflectionUtil {
    public static String getClassNameString(){
        String className = null;
        try(FileReader fr =new FileReader("./reflection.properties")){
            //创建一个Properties配置文件对象
            Properties prop =new Properties();
            //调用方法读取流里的信息,就会自动将配置文件里的每一个key=value封装到自身里
            prop.load(fr);
            //解析prop,通过key,获取value值
            className = prop.getProperty("classname");
        }catch (Exception e){
            e.printStackTrace();
        }
        return className;
    }

    public static void main(String[] args) {
        System.out.println(ReflectionUtil.getClassNameString());
    }
}

3.调用newInstance方法来动态实例化一个类的具体对象

import com.se.day11.ReflectionUtil;
import com.se.day11.bDiyClass.Person;

public class _01newInstanceDemo {
    public static void main(String[] args) {
        try{
            //第一步:通过字符串获取指定类的描述对象,字符串的获取使用了刚刚封装的工具类
            // 注意: c这个变量里的地址指向的对象是描述Person类型的信息
            Class<?> c = Class.forName(ReflectionUtil.getClassNameString());
            //调用Class类里的非静态方法newInstance方法来实例化对象
            // 注意:本质调用的是类的无参构造器,如果没有提供无参构造器,会报NoSuchMethodException异常
            Object o = c.newInstance();
            Person p = (Person) o;
            p.play("LOL");
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

可能出现的异常

         InstantiationException:实例化异常

        NoSuchMethodException:没有这个方法异常

        IllegalAccessException:非法访问异常:权限不足

2.2 setAccessible方法介绍

        在反射中, 可以通过指定的方法, 获取到一些访问权限不足的成员。 例如, 可以通过getDeclaredConstructor(Class ...)获取私有权限的构造方法。 但是, 即便获取到了访问权限不足的成员, 依然无法直接使用。 如果直接使用会出现 IllegalAccessException 异常, 表示访问权限不足。

此时, 可以使用 setAccessible 方法 设置是否需要跳过权限的校验

setAccessible(boolean flag)

  • 参数true: 代表关闭访问权限校验, 即任意的访问权限都可以访问。

  • 参数false: 代表不关闭访问权限校验, 此时在进行成员访问的时候, 依然需要进行权限的校验。

 代码演示:

调用public修饰的构造器(访问权限足够的构造方法)

/*
    使用反射机制,调用指定的构造器获取某一个类的对象
 */

import com.se.day11.ReflectionUtil;
import com.se.day11.bDiyClass.Person;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class _02OtherConstructorDemo {
    public static void main(String[] args) {
        try {
            Class c = Class.forName(ReflectionUtil.getClassNameString());
            //如何指定想要的构造器: 方法中传入参数类型的描述类对象
            Constructor constructor = c.getConstructor(String.class, String.class);
            //使用构造器的newInstance(Object...parameter)给构造器的参数赋值即可。
            Object xiaoming = constructor.newInstance("235511223566", "小明");
            if(xiaoming instanceof Person){
                ((Person) xiaoming).play("LOL");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

调用private修饰的构造器(访问权限不够的方法)

/*
    使用权限不足的构造器来获取对象
 */

import com.se.day11.ReflectionUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class _03OtherConstructorDemo {
    public static void main(String[] args) {
        try {
            Class c = Class.forName(ReflectionUtil.getClassNameString());
            //如何指定想要的构造器: 方法中传入参数类型的描述类对象
            Constructor constructor = c.getDeclaredConstructor(String.class, String.class,int.class);
            // 如果访问的是私有的,需要关闭权限校验,true表示关闭,false表示开启
            constructor.setAccessible(true);
            //使用构造器的newInstance(Object...parameter)给构造器的参数赋值即可。
            Object xiaoming = constructor.newInstance("235511223566", "小明",66);
            System.out.println(xiaoming);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

 3. 成员的访问

3.1 获取类中所有的构造器

 getDeclaredConstructors()方法。得到构造器数组。

获取修饰词:getModifiers()

获取参数:getParameters() 返回数组。

获取参数类型:要用上面的得到的参数调用 .getType()

获取参数名称:同上 getName

/*
    获取所有构造器,包括私有的。
        getDeclaredConstructors()

    修饰词对应的是常量:
        public ---> 1
        private ---> 2
        protected ---> 4
        static ---> 8
        final ---> 16
        Synchronized ---> 32

 */

import com.se.day12.util.ReflectionUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;


public class _01GetAllConstructorDemo {
    public static void main(String[] args) {
        //先获取描述类的对象,描述Person类型
        try {
            Class<?> aClass = Class.forName(ReflectionUtil.getClassNameString());
            Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
            for (Constructor<?> declaredConstructor : declaredConstructors) {
                // 获取构造器的修饰词
                System.out.println(declaredConstructor.getModifiers());
                // 获取构造器的参数类型
                Parameter[] parameterTypes = declaredConstructor.getParameters();
                for (Parameter parameterType : parameterTypes){
                    System.out.println("参数类型:"+parameterType.getType());
                    System.out.println("参数名字:"+parameterType.getName());
                }
                //获取构造器的名字
                System.out.println(declaredConstructor.getName());

                System.out.println("---------------------");
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

3.2 获得类中所有的方法

getDeclaredMethods() 得到方法数组

获取方法的返回值: getReturnType()

import com.se.day12.util.ReflectionUtil;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/*
    获取一个类中的所有方法,包括私有的
 */
public class _02GetAllMethodDemo {
    public static void main(String[] args) {

        try {
            //获取描述的Person对象
            Class<?> aClass = Class.forName(ReflectionUtil.getClassNameString());
            //获取所有的方法
            Method[] methods = aClass.getDeclaredMethods();
            //遍历
            for (Method m : methods){
                //方法的修饰词
                System.out.println("修饰词: "+m.getModifiers());
                //方法的返回值类型
                System.out.println("返回值类型: "+m.getReturnType());
                //方法名
                System.out.println("方法名: "+m.getName());
                //方法参数
                Parameter[] parameters = m.getParameters();
                for (Parameter p : parameters){
                    System.out.println("参数类型: "+p.getType());
                    System.out.println("参数名: "+p.getName());
                }
                System.out.println("------------------------------");
            }


        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

3.3 获取成员变量

import com.se.day12.util.ReflectionUtil;

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

public class _01GetFiledDemo {
    public static void main(String[] args) {
        try {
            //获取描述类对象
            Class<?> aClass = Class.forName(ReflectionUtil.getClassNameString());
            // 获取所有的属性,包括私有的
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                // 获取修饰词
                System.out.println("修饰词:"+field.getModifiers());
                // 获取属性的类型
                System.out.println("属性类型:"+field.getType());
                // 获取属性的名字
                System.out.println("属性名字:"+field.getName());
                System.out.println("----------------------------------");
            }
            //假如不知道有哪些方法,可以指定一个方法名和参数列表,查看是否返回。
            // 注意,如果获取不到方法,抛异常NoSuchMethodException。
            Method sleep = aClass.getDeclaredMethod("sleep", String.class);

            // 假如不知道有哪些属性,可以指定一个属性,查看是否返回正确。
            // 如果不存在,抛NoSuchFieldException
            Field gender = aClass.getDeclaredField("gender");


        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

3.4 成员的访问

如果访问的是公有的变量或者方法,可以直接赋值或使用。

如果访问的是私有的变量或者方法,那么需要调用setAccessible() 方法,来修改访问权限。setAccessible() 的作用:关闭权限校验,true表示关闭,false表示开启

如果访问的是静态的变量,那么调用set方法时,那么第一个参数可以传入任意值,通常情况是传入null。

如果访问的是静态方法,那么调用invoke方法时,第一个参数通常传入null。

 代码演示:

import com.se.day12.util.ReflectionUtil;

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

/*
    使用反射机制,实例化一个对象
 */
public class _01PersonDemo {
    public static void main(String[] args) {
        try {
            // 获取Person的描述类对象
            Class<?> aClass = Class.forName(ReflectionUtil.getClassNameString());
            // 获取无参构造器,实例化对象
            Object o = aClass.newInstance();
            //实例化之后,属性都是默认值,需要进行初始化覆盖默认值
            Field name = aClass.getDeclaredField("name");
            name.setAccessible(true);
            //将属性与对象关联上并赋值
            name.set(o,"小明");
            Field age = aClass.getDeclaredField("age");
            age.setAccessible(true);
            age.set(o,18);
            Field gender = aClass.getDeclaredField("gender");
            gender.setAccessible(true);
            gender.set(o,'男');

            System.out.println(o);

            //调用setGender方法,将'男'覆盖掉
            Method sg = aClass.getDeclaredMethod("setGender", char.class);
            //方法与对象关联
            sg.invoke(o,'女');
            System.out.println(o);

            Method sleep = aClass.getDeclaredMethod("sleep",String.class);
            sleep.setAccessible(true);
            sleep.invoke(o,"花果山");

        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException |
                 NoSuchMethodException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}
  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值