资讯专栏INFORMATION COLUMN

Java反射机制

galaxy_robot / 1363人阅读

摘要:反射机制相关类介绍类的实例表示正在运行的应用程序中的类和接口。包括基本数据类型没有公共构造方法。越过泛型检查遍历集合输出结果越过泛型检查通过反射机制获得数组信息并修改数组的大小和值通过反射机制分别修改和类型的数组的大小并修改数组的第一个值。

什么是Java的反射机制?

Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

简单来说,反射就是可以在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成的对象。

Java反射机制相关API Class类介绍

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。JVM 中有 N 多的实例,每个类的实例都有 Class 对象。(包括基本数据类型)

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM 已经帮我们创建好了。

Java 类的加载过程:

在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步:

装载:查找和导入类或接口的二进制数据;

链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;

校验:检查导入类或接口的二进制数据的正确性;

准备:给类的静态变量分配并初始化存储空间;

解析:将符号引用转成直接引用;

初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。

如果知道一个实例,那么可以通过实例的getClass()方法获得运行实例的 Class(该类型的字节码文件对象),如果你知道一个类型,那么你也可以使用.class的方法获得运行实例的 Class。

部分相关api如下所示

//返回与给定字符串名称的类或接口相关联的类对象。
public static Class <?> forName(String className)throws ClassNotFoundException

//返回类表示此所表示的实体(类,接口,基本类型或void)的超类类 。
public Class<? super T> getSuperclass()

//确定由该对象表示的类或接口实现的接口。 
public Class< ? >[] getInterfaces()

//创建此 Class 对象所表示的类的一个新实例
public T newInstance() throws InstantiationException, IllegalAccessException

//返回表示此类公共构造方法的 Constructor 对象数组
public Constructor< ? >[] getConstructors() throws SecurityException

//返回 Constructor 对象的一个数组
public Constructor[] getDeclaredConstructors() throws SecurityException

//返回表示公共字段的 Field 对象的数组
public Field[] getFields()  throws SecurityException

//返回表示此类所有已声明字段的 Field 对象的数组
public Field[] getDeclaredFields() throws SecurityException

//表示此类中公共方法的 Method 对象的数组
public Method[] getMethods() throws SecurityException

//表示此类所有声明方法的 Method 对象的数组
public Method[] getDeclaredMethods()  throws SecurityException

//返回该类的类加载器。
public ClassLoader getClassLoader()

//返回:使用参数 args 在 obj 上指派该对象所表示方法的结果
public Object invoke(Object obj, Object... args)  throws IllegalAccessException, IllegalArgumentException,  InvocationTargetException

//查找具有给定名称的资源。
public InputStream getResourceAsStream(String name)

//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException

具体相关方法及其使用方法可以查看API文档

Java 反射机制举例

以下例子基于Person类

package DateTest;

public class Person implements Comparable{
    public String name;
    private int age;
    public int id;
    protected String phone;
    public Person() {
        System.out.println("默认的无参构造方法执行了");
    }
    
    public Person(String name) {
        System.out.println("姓名:"+name);
    }
    
    public Person(String name,int age) {
        System.out.println("姓名:"+name+"年龄:"+age);
    }
    
    //受保护的构造方法
    protected Person(boolean b) {
        System.out.println("这是一个受保护的构造方法:b="+b);
    }
    
    //私有构造方法
    private Person(int age) {
        System.out.println("这是一个私有构造方法,年龄="+age);
    }

    @Override
    public int compareTo(Object arg0) {
        // TODO Auto-generated method stub
        return 0;
    }
    
    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;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
通过一个对象获取某个类的完整包名和类名

通过实例的 getClass() 方法获得运行实例的字节码文件对象,然后通过 getName() 方法获得类的完整包名和类名。

public void test1() {
        Person person = new Person();
        System.out.println(person.getClass().getName());
}
//输出结果:
默认的无参构造方法执行了
DateTest.Person
获取 Class 对象

方式1:通过 Class 类的静态方法获取 Class 类对象

方式2:因为所有类都继承 Object 类。因而可通过调用 Object 类中的 getClass 方法来获取

方式3:任何数据类型(包括基本数据类型)都有一个“静态”的 class 属性

public void test2() throws ClassNotFoundException {
        Class class1 = null;
        Class class2 = null;
        Class class3 = null;
        class1 = Class.forName("DateTest.Person");
        class2 = new Person().getClass();
        class3 = Person.class;
        System.out.println("类名称1:"+class1.getName());
        System.out.println("类名称2:"+class2.getName());
        System.out.println("类名称3:"+class3.getName());
}
//输出结果
默认的无参构造方法执行了
类名称1:DateTest.Person
类名称2:DateTest.Person
类名称3:DateTest.Person
获取一个对象的父类和实现的接口

为了测试getInterfaces()方法,此处将Person类继承Comparable接口

public void test3() throws ClassNotFoundException {
        Class clazz = Class.forName("DateTest.Person");
        //获取父类
        Class parentClass = clazz.getSuperclass();
        System.out.println("父类为:"+parentClass.getName());
        //获取所有接口
        Class interf[] = clazz.getInterfaces();
        System.out.println("实现的接口有:");
        for(int i = 0; i < interf.length; i++) {
            System.out.println((i+1)+":"+interf[i].getName());
        }
}
//输出结果
父类为:java.lang.Object
实现的接口有:
1:java.lang.Comparable
获取某个类的全部构造函数并调用私有构造方法

获取 Student 类的全部构造函数,并使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类 Person 的实例来调用私有构造方法。

public void test4() throws Exception {
        //1.加载Class对象
        Class clazz = Class.forName("DateTest.Person");
        //2.获取所有公有构造方法
        System.out.println("所有公有构造方法");
        Constructor[] conArray = clazz.getConstructors();
        for(Constructor c : conArray) {
            System.out.println(c);
        }
        
        System.out.println("----------------------------");
        System.out.println("所有构造方法");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray) {
            System.out.println(c);
        }
        
        System.out.println("----------------------------");
        System.out.println("公有、无参的构造方法");
        Constructor con = clazz.getConstructor(null);
        System.out.println("con = " + con);
        //调用构造方法  
        Object obj = con.newInstance();
        System.out.println("obj = " + obj);
        
        System.out.println("----------------------------");
        System.out.println("获取私有的构造方法,并调用");
        con = clazz.getDeclaredConstructor(int.class);
        System.out.println(con);
        //调用构造方法  
        con.setAccessible(true);
        obj = con.newInstance(20);
    }
//输出结果
所有公有构造方法
public DateTest.Person(java.lang.String,int)
public DateTest.Person(java.lang.String)
public DateTest.Person()
----------------------------
所有构造方法
private DateTest.Person(int)
protected DateTest.Person(boolean)
public DateTest.Person(java.lang.String,int)
public DateTest.Person(java.lang.String)
public DateTest.Person()
----------------------------
公有、无参的构造方法
con = public DateTest.Person()
默认的无参构造方法执行了
obj = DateTest.Person@7a4f0f29
----------------------------
获取私有的构造方法,并调用
private DateTest.Person(int)
这是一个私有构造方法,年龄=20
获取某个类的全部属性

获取 Person类的全部属性并调用:

public void test5() throws Exception {
        //1.获取Class对象
        Class pclass = getClass().forName("DateTest.Person");
        //2.获取字段
        System.out.println("获取所有公有字段");
        Field[] fieldArray = pclass.getFields();
        for(Field f : fieldArray) {
            System.out.println(f);
        }
        System.out.println("----------------------------");
        System.out.println("获取所有字段");
        fieldArray = pclass.getDeclaredFields();
        for(Field f : fieldArray) {
            System.out.println(f);
        }
        System.out.println("----------------------------");
        Field f = pclass.getField("name");
        System.out.println(f);
        //获取一个对象
        Object obj = pclass.getConstructor().newInstance();
        f.set(obj,"张三");
        //验证
        Person p = (Person)obj;
        System.out.println("验证的姓名:"+p.getName());
        System.out.println("----------------------------");
        f = pclass.getDeclaredField("age");
        System.out.println(f);
        f.setAccessible(true);
        f.set(obj, 21);
        System.out.println("验证年龄:"+p);
    }
//输出结果
获取所有公有字段
public java.lang.String DateTest.Person.name
public int DateTest.Person.id
----------------------------
获取所有字段
public java.lang.String DateTest.Person.name
private int DateTest.Person.age
public int DateTest.Person.id
protected java.lang.String DateTest.Person.phone
----------------------------
public java.lang.String DateTest.Person.name
默认的无参构造方法执行了
验证的姓名:张三
----------------------------
private int DateTest.Person.age
验证年龄:Person [name=张三, age=21]
获取某个类的全部方法
public void test6() throws Exception {
        //1.获取Class对象
        Class pClass = Class.forName("DateTest.Person");
        //2.获取所有公有方法
        System.out.println("获取所有的公有方法*");
        pClass.getMethods();
        Method[] methodArray = pClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("----------------------------");
        System.out.println("获取所有的方法");
        methodArray = pClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("获取公有的test1()方法");
        Method m = pClass.getMethod("test1", String.class);
        System.out.println(m);
        //实例化一个Student对象
        Object obj = pClass.getConstructor().newInstance();
        m.invoke(obj, "lisi");

        System.out.println("获取私有的test4()方法*");
        m = pClass.getDeclaredMethod("test4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
        System.out.println("返回值:" + result);
}
//输出结果
获取所有的公有方法*
public java.lang.String DateTest.Person.toString()
public int DateTest.Person.compareTo(java.lang.Object)
public java.lang.String DateTest.Person.getName()
public int DateTest.Person.getId()
public void DateTest.Person.setName(java.lang.String)
public void DateTest.Person.test1(java.lang.String)
public int DateTest.Person.getAge()
public java.lang.String DateTest.Person.getPhone()
public void DateTest.Person.setAge(int)
public void DateTest.Person.setPhone(java.lang.String)
public void DateTest.Person.setId(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
----------------------------
获取所有的方法
public java.lang.String DateTest.Person.toString()
public int DateTest.Person.compareTo(java.lang.Object)
public java.lang.String DateTest.Person.getName()
public int DateTest.Person.getId()
public void DateTest.Person.setName(java.lang.String)
public void DateTest.Person.test1(java.lang.String)
protected void DateTest.Person.test2()
private java.lang.String DateTest.Person.test4(int)
void DateTest.Person.test3()
public int DateTest.Person.getAge()
public java.lang.String DateTest.Person.getPhone()
public void DateTest.Person.setAge(int)
public void DateTest.Person.setPhone(java.lang.String)
public void DateTest.Person.setId(int)
获取公有的test1()方法
public void DateTest.Person.test1(java.lang.String)
默认的无参构造方法执行了
调用了:公有的,String参数的test1(): name = lisi
获取私有的test4()方法*
private java.lang.String DateTest.Person.test4(int)
调用了,私有的,并且有返回值的,int参数的test4(): age = 20
返回值:abcd
调用某个类的方法

Java 反射获取 Class 对象并使用invoke()方法调用方法 reflect1() 和 reflect2():

public void test7() throws Exception{
        Class clazz = Class.forName("DateTest.ReflectTest");
        Method method = clazz.getMethod("show1");
        method.invoke(clazz.newInstance());
        method = clazz.getMethod("show2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "张三");
    }

    public void show1() {
        System.out.println("调用show1()");
    }
    public void show2(int age, String name) {
        System.out.println("调用show2()");
        System.out.println("age: " + age + "name: " + name);
    }
//输出结果
调用show1()
调用show2()
age: 20name: 张三
反射机制的动态代理

首先需要定义一个 InvocationHandler 接口的子类,完成代理的具体操作

package DateTest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler{
     private Object obj = null;

     public Object bind(Object obj) {
         this.obj = obj;
         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
     }

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         Object temp = method.invoke(this.obj, args);
         return temp;
     }
}
package DateTest;

public interface Animal {
    public void eat(String name);
}
package DateTest;

public class Dog implements Animal{
    public void eat(String name) {
        System.out.println(name+"eat!");
    }
}

新建类实现接口Animal,使用 MyInvocationHandler.bind() 方法获取即可实现调用。

public void test8(){
        MyInvocationHandler invo = new MyInvocationHandler();
        Animal sub = (Animal) invo.bind(new Dog());
        sub.eat("小狗");
    }
//输出结果
小狗eat!
通过反射越过泛型检查

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

 public void test9() throws Exception{
        ArrayList list = new ArrayList();
        list.add(111);
        list.add(222);
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "越过泛型检查");
        //遍历集合  
        for(Object obj : list){  
            System.out.println(obj);  
        }
    }
//输出结果
111
222
越过泛型检查
通过反射机制获得数组信息并修改数组的大小和值

通过反射机制分别修改int和String类型的数组的大小并修改int数组的第一个值。

 public static void test10() throws Exception{
         int[] temp = { 12,45,65,5,1,32,4,56,12};
         int[] newTemp = (int[]) arrayInc(temp, 15);
         print(newTemp);
         Array.set(newTemp, 0, 100);
         System.out.println("修改之后数组第一个元素为: " + Array.get(newTemp, 0));
         print(newTemp);
         String[] atr = { "a", "b", "c" };
         String[] str1 = (String[]) arrayInc(atr, 8);
         print(str1);
    }
    // 修改数组大小
    public static Object arrayInc(Object obj, int len) {
        Class arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    // 打印
    public static void print(Object obj) {
        Class c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        Class arr = obj.getClass().getComponentType();
        System.out.println("数组类型: " + arr.getName());
        System.out.println("数组长度为: " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }
//输出结果
数组类型: int
数组长度为: 15
12 45 65 5 1 32 4 56 12 0 0 0 0 0 0 
修改之后数组第一个元素为: 100
数组类型: int
数组长度为: 15
100 45 65 5 1 32 4 56 12 0 0 0 0 0 0 
数组类型: java.lang.String
数组长度为: 8
a b c null null null null null 
将反射机制应用于工厂模式

对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。

package DateTest;

public interface Animal {
    public abstract void sleep();
}
package DateTest;

public class Dog implements Animal{
    @Override
    public void sleep() {
        System.out.println("dog");
    }
}
package DateTest;

public class Cat implements Animal{
    @Override
    public void sleep() {
        System.out.println("cat");
    }
}
package DateTest;

public class Factory {
    public static Animal getInstance(String ClassName) {
        Animal a = null;
        try {
            a = (Animal) Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return a;
    }
}
public void test11()throws Exception{
        Animal a = Factory.getInstance("DateTest.Dog");
        if (a != null) {
            a.sleep();
        }
    }
//输出结果
dog
加载配置文件
//配置文件内容为
//className = DateTest.ReflectTest
public void test13() throws IOException {
         Properties pro = new Properties();//获取配置文件的对象  
         InputStream in=new Person().getClass().getResourceAsStream("/pro.txt");
         pro.load(in);//将流加载到配置文件对象中  
         in.close();  
         System.out.println(pro.getProperty("className"));
    }
//输出结果
默认的无参构造方法执行了
DateTest.ReflectTest
获取 ClassLoader 类加载器

public void test12() throws ClassNotFoundException {  
        //1、获取一个系统的类加载器  
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();  
        System.out.println(classLoader);  

        //2、获取系统类加载器的父类加载器  
        classLoader = classLoader.getParent();  
        System.out.println(classLoader);  

        //3、获取扩展类加载器的父类加载器  
        //输出为Null,引导类加载器无法被Java程序直接引用  
        classLoader = classLoader.getParent();  
        System.out.println(classLoader);  

        //4、测试当前类由哪个类加载器进行加载 ,结果是系统的类加载器  
        classLoader = Class.forName("DateTest.ReflectTest").getClassLoader();  
        System.out.println(classLoader);  

        //5、测试JDK提供的Object类由哪个类加载器负责加载的  
        classLoader = Class.forName("java.lang.Object").getClassLoader();  
        System.out.println(classLoader);  
    }
//输出结果
sun.misc.Launcher$AppClassLoader@6bc7c054
sun.misc.Launcher$ExtClassLoader@2ef1e4fa
null
sun.misc.Launcher$AppClassLoader@6bc7c054
null
总结

反射机制的应用虽然有很多优点,但是反射会额外消耗一定的系统资源,因此,反射操作的效率要比那些非反射操作低得多。另外,反射允许代码执行一般情况下不被允许的操作,所以一般能用其它方式实现的就尽量不去用反射。

参考文章

https://blog.csdn.net/sinat_3...
https://www.cnblogs.com/tech-...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74833.html

相关文章

  • Reflection:Java反射机制的应用场景

    近期在维护公司项目的时候遇到一个问题,因为实体类中的 set 方法涉及到了业务逻辑,因此在给对象赋值的过程中不能够使用 set 方法,为了实现功能,所以采用了反射的机制给对象属性赋值,借此机会也了解了反射的一些具体用法和使用场景,分以下两点对反射进行分析: 反射的优势和劣势 反射的应用场景 反射的优势和劣势   个人理解,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式...

    浠ラ箍 评论0 收藏0
  • Java反射机制详解

    摘要:反射机制的应用实例在泛型为的中存放一个类型的对象。工厂模式可以参考现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。 学习交流群:669823128java 反射 定义 功能 示例概要:Java反射机制详解| |目录 1反射机制是什么 2反射机制能做什么 3反射机制的相关API 通过一个对象获得完整的包名和类名 实例化Class类对象 获取一个对象的父类与...

    paraller 评论0 收藏0
  • Reflection:Java反射机制基础

    摘要:反射机制是什么反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意一个方法和属性这种动态获取的信息以及动态调用对象的方法的功能称为语言的反射机制反射机制能做什么反射机制主要提供了以下功 反射机制是什么 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种...

    hizengzeng 评论0 收藏0
  • Java笔记-反射机制(一)

    摘要:反射机制一结合官方通过编写的反射教程,复习一下反射的知识。反射的概念反射是一种在运行时获取以及修改应用行为的一种工具。因为反射需要动态的解析类的信息,相比于非反射使用的方式要慢。反射需要获取一定的运行时权限,在特定的安全环境下不一定存在。 Java反射机制(一) 结合Oracle官方通过JDK8编写的反射教程,复习一下反射的知识。结尾篇补一个小例子。 主要内容 这次博客的主要内容就是简...

    AWang 评论0 收藏0
  • 最最最常见的Java面试题总结——第二周

    摘要:与都继承自类,在中也是使用字符数组保存字符串,,这两种对象都是可变的。采用字节码的好处语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。 String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的? String和StringBuffer、StringBuilder的区别 可变性...

    yearsj 评论0 收藏0
  • 反射机制与原理笔记

    反射机制与原理笔记 声明 文章均为本人技术笔记,转载请注明出处https://segmentfault.com/u/yzwall 反射机制 反射:当程序无法获知对象类型时,在运行期间动态获取类的所有属性和方法,这种动态获取类信息和动态调用对象方法的功能称为反射机制;反射机制实现:Class类与java.lang.reflect类库一起实现机制,java.lang.reflect类库包含Field...

    fobnn 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<