Java反射

反射及作用

        Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

  • 反射(Reflect)是在运行时动态访问类与对象的技术,即把创建对象的时机从程序编译时延迟到程序运行时。

  • 反射是JDK1.2版本后的高级特性隶属于java.lang.reflect

  • 大多数Java框架都基于反射实现参数配置、动态注入等特性

Class类及对象实例化

反射核心类

◆Constructor构造方法类

◆Class类

◆Method方法类

◆Field成员变量类

Class类

  • Class是JVM中代表"类和接口"的类

  • Class对象具体包含了某个特定类的结构信息。

  • 通过Class对象可获取对应类的构造方法/方法/成员变量

1.关于java. lang. Class类的理解 1.类的加载过程: 程序经过javac.exe命令以后,会生成一个或多个字 节码文件(.cLass结尾)。接着我们使用java. exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

2.换句话说,Class的实例就对应着一个运行时类(例如Person类)。

3.加载到内存中的运行时类,会缓存一 定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

        类可以造对象,类本身还是另外一个类的对象。例如有“概念”,“手机”这两个词,“手机”需要用一些描述性语句去解释,这些描述性语句就相当于”手机“的”概念“了,但是”概念”本身也是一个词,”概念”这个词也得需要一些描述性语句(“概念”)去解释。

Class核心方法

 其中Class.forName()的参数是具体类的完整路径字符串,用来获取指定类的Class对象。

获取Class实例的4种方式

  1. 前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠, 程序性能最高

    实例:Class clazz = String.class;

  2. 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象

    实例:Class clazz = "com.zqf.Person".getClass();

  3. 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方 法forName()获取,可能抛出ClassNotFoundException

    实例:Class clazz = Class.forName(“java.lang.String”);

  4. 其他方式

    ClassLoader cl = this.getClass().getClassLoader(); Class clazz4 = cl.loadClass(“类的全类名”);

//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getCLass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(c1azz2);
//方式三:调用Class 的静态方法: forName(String classPath)
Class clazz3 = Class.forName("com.zqf.java.Person");
System. out. println(clazz3);
//方式四:使用类的加载器: ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.1oadClass("com.zqf.java.Person");
System.out.println(clazz4);

案例:

package com.imooc.reflect.entity;

/**
 * 员工实体类
 */
public class Employee {

    //类的初始化是经过静态块来完成的
    static {
        System.out.println("Employee类已被加载到jvm,并已初始化");
    }
    private Integer eno;
    public String ename;
    private Float salary;
    private String dname;

    public Employee(){
        System.out.println("Employee默认构造方法已被执行");
    }

    public Employee(Integer eno,String ename,Float salary , String dname){
        this.eno = eno;
        this.ename = ename;
        this.salary = salary;
        this.dname = dname;
        System.out.println("Employee带参构造方法已被执行");
    }

    public Integer getEno() {
        return eno;
    }

    public void setEno(Integer eno) {
        this.eno = eno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Float getSalary() {
        return salary;
    }

    public void setSalary(Float salary) {
        this.salary = salary;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "eno=" + eno +
                ", ename='" + ename + '\'' +
                ", salary=" + salary +
                ", dname='" + dname + '\'' +
                '}';
    }

    public Employee updateSalary(Float val){
        this.salary = this.salary + val;
        System.out.println(this.ename + "调薪至" + this.salary + "元");
        return this;
    }
}

Sample:

package com.imooc.reflect;

import com.imooc.reflect.entity.Employee;

public class ClassSample {
    public static void main(String[] args) {
        try {
            //Class.forName()方法将指定的类加载到jvm,并返回对应Class对象
            Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
            //employeeClass包含了Employee中所描述的所有静态成员以及方法和构造函数
            System.out.println("Employee已被加载到jvm");
            //newInstance通过默认构造方法(无参)创建新的对象
            //newInstance()默认返回Object类型,需要进行强制转换
            Employee emp = (Employee)employeeClass.newInstance();
            System.out.println(emp);
        } catch (ClassNotFoundException e) {
            //类名与类路径书写错误是抛出"类无法找到"异常
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            //非法访问异常,当在作用域外访问对象方法或成员变量时抛出
            e.printStackTrace();
        } catch (InstantiationException e) {
            //对象无法被实例化,抛出"实例化异常"
            e.printStackTrace();
        }
    }
}

        通过加载Employee得到类对象之后,利用newInstance()方法调用默认构造方法创建对应的对象。

 Constructor构造方法对象

 Constructor类

  • Constructor类是对Java类中的构造方法的抽象

  • Contructor对象包含了具体类的某个具体构造方法的声明

  • 通过Constructor对象调用带参构造方法创建对象

 Constructor类核心方法

       通过Constructor对象我们可以得到具体构造方法,他有几个参数,参数的类型是什么。同时也可以通过Constructor对象调用带参构造方法创建新的对象。

package com.imooc.reflect;

import com.imooc.reflect.entity.Employee;

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

/**
 * 利用带参构造方法创建对象
 */
public class ConstructorSample {
    public static void main(String[] args) {
        try {
            //获得对应的类对象  使用Class.forName对指定的类对象进行加载
            Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
            //获取指定格式的带参构造方法
            Constructor constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class,String.class,Float.class,String.class
            });
            Employee employee = (Employee) constructor.newInstance(new Object[]{
                    100,"李磊",3000f,"研发部"
            });
            System.out.println(employee);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            //没有找到与之对应格式的方法
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            //当被调用的方法的内部抛出了异常而没有被捕获时
            e.printStackTrace();
        }
    }
}

       通过向getConstructor中传入一个Class类型的数组,在数组中声明参数的类型和数量,即可获取指定格式的带参构造方法。

       在调用newInstance方法创建对象的时候,要传入实际的数据,传入一个Object类型的数组,并进行初始化操作。

Method方法对象

Method方法类

  • Method对象指代某个类中的方法的描述

  • Method对象使用classObj.getMethod()方法获取

  • 通过Method对象调用指定对象的对应方法

Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class,String.class,Float.class,String.class
            });
Employee employee = (Employee)constructor.newInstance(new Object[]{
                    100,"李磊",3000f,"研发部"
            });
Method updateSalaryMethod = employeeClass.getMethod("updateSalary" , new Class[]{
                    Float.class
            });
Employee employee1 = (Employee)updateSalaryMethod.invoke(employee,new Object[]{1000f});
            System.out.println(employee1);

       在getMethod方法里,单有一个方法名称是无法精准定位的,因为在JAVA里可以通过重载,也就是一个方法通过不同的参数可以处理不同的事情,只有方法名是无法确定需要调用哪个方法的,所以在getMethod方法里还需要传入一个参数,代表参数类型是哪些,这里需要传入一个类型数组new Class[],传入我们要调用的方法的参数,得到的返回值是Method的方法对象。

        得到方法对象之后就可以对他进行invoke调用,Invoke有两个参数,第一个参数是要执行哪一个对象,第二个参数则要传入具体的参数数值。

Field成员变量对象

Field成员变量类

  • Field对应某个具体类中的成员变量的声明

  • Field对象使用classObj.getField()方法获取

  • 通过Field对象可为某对象成员变量赋值/取值

Field类核心方法

Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class,String.class,Float.class,String.class
            });
Employee employee = (Employee) constructor.newInstance(new Object[]{
                    100,"李磊",3000f,"研发部"
            });
Field enameField = employeeClass.getField("ename");
enameField.set(employee,"李雷");
String ename = (String)enameField.get(employee);
System.out.println("ename:" + ename);

        getField方法只有一个参数,即成员变量的名称,返回一个Field对象。

获取成员变量值:

        所得到的Field对象有一个get的方法, 要获取哪个对象的成员变量值,就将这个对象作为get方法的参数传入进去,其返回值是Object类型,需要进行强制转换。

设置成员变量值:

        与获取成员变量值类似,都是基于Field成员变量实现,只不过是使用set方法,它包含两个参数,第一个是要为哪个成员变量进行赋值,第二个是要为赋值的参数,且参数类型要与成员变量的声明类型一致。

getDeclared系列方法说明

  • getDeclaredConstructor(s)|Method(s)|Field(s)获取对应对象,可以访问私有对象。

  • getConstructor(s)|Method(s)|Field(s)只能获取public对象

  • 访问非作用域内构造方法、方法、成员变量,会抛出无法访问的异常。

加s是获取所有声明的构造方法,不加s是获取单个的构造方法,Method和Field同理。

Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class, String.class, Float.class, String.class
            });
Employee employee = (Employee) constructor.newInstance(new Object[]{
                    100, "李磊", 3000f, "研发部"
            });
//获取当前类所有成员变量
Field[] fields = employeeClass.getDeclaredFields();
for(Field field : fields){
//       System.out.println(field.getName());
         if(field.getModifiers() == 1){ //pubilc修饰
             Object val = field.get(employee);
             System.out.println(field.getName() + ":" + val);
          else if(field.getModifiers() == 2){ //private修饰
              //根据成员变量名获取getXxx方法名,例如  ename -->  getEname()
              String methodName = "get" + field.getName().substring(0,1).toUpperCase()
                     + field.getName().substring(1);
              //getXxx方法一定是public修饰的,所以使用getMethod方法没有问题
              Method getMethod = employeeClass.getMethod(methodName);
              //getXxx()方法是没有参数的,故只需要传入employee
              Object ret = getMethod.invoke(employee);
              System.out.println(field.getName() + ":" + ret);
            }

        field.getModifiers()方法返回值是一个整数,如果返回1则说明当前成员变量是public修饰,如果返回2则说明当前成员变量是private修饰。

        在Object ret = getMethod.invoke(employee);不需要进行强制类型转换,因为成员变量的类型是不同的。

案例:

I18N接口:


public interface I18N {
    public String say();
}

Zhcn:

public class Zhcn implements I18N {
    @Override
    public String say() {
        return "生命不息奋斗不止";
    }
}

Application:

import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Properties;

public class Application {
    public static void say(){
        Properties properties = new Properties();
        String configPath = Application.class.getResource("/config.properties").getPath();
        try {
            configPath = new URLDecoder().decode(configPath,"UTF-8");
            properties.load(new FileInputStream(configPath));
            String language = properties.getProperty("language");
//            System.out.println(language);
            //创建对象这里  需要动态创建
            I18N i18n = (I18N)Class.forName(language).newInstance();
            System.out.println(i18n.say());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Application.say();
    }
}

config-properties:

language=com.imooc.i18n.Zhcn

        以后如果需要修改成英文,需要创建一个En类,实现I18N接口并重写say方法,并且在配置文件中将Zhcn更改为En即可。在Application中

I18N i18n = (I18N)Class.forName(language).newInstance();

利用反射机制可动态地创建对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值