Java 反射机制--框架设计的灵魂(详解)

目录

一.反射的概述

1.反射是什么

2.反射的原理

3.反射机制有什么用

4.反射机制相关的类

二.反射的使用

1.判断任意一个对象所属的类

(1)哪些类型有Class对象

(2)获取Class对象的三种方式

(3)判断任意一个对象所属的类

2.构造任意一个类的对象

(1)获取构造器

(2)实例化对象

3.得到任意一个类所具有的属性和方法

(1)属性

(2)方法

4.调用任意一个对象的属性和方法

(1)获取修改对象的属性

(2)调用对象的方法

5.生成动态代理

三.反射机制的优缺点和优化

1.反射的优缺点

(1)优点

(2)缺点

(3)反射机制适用场景

2.反射调用优化

(1)反射调用优化前,传统方法和反射调用的对比

(2)反射调用优化-关闭访问检查

(3)反射调用优化后,传统方法和反射调用的对比


一.反射的概述

1.反射是什么

Java是一门面向对象的编程语言,在Java的世界里,一切皆对象。对象是对类的实例化,对象的抽象就是类,Java中一个类通常由物种元素组成,即属性、方法、构造方法、块以及内部类。

那我们是否可以做到:对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性?

答案是可以。怎么获取?Java语言的反射机制。

既然在Java的世界里一切皆对象,那我们类的属性、类的方法、类的构造方法何尝不能视作对象?!而这就是Java的反射机制,反射就是把Java类中的各种元素映射成一个个的Java对象。

拿到映射后的属性,我们就可以改变该属性的值;拿到映射后的方法,我们就可以调用它来执行对应的方法;拿到映射后的构造方法,我们就可以用它来构造对象。

2.反射的原理

我们知道,Java的运行流程如下:

 通过JVM,Java实现了平台无关性,我们编写好的Java源文件,先由Java编译器编译成字节码文件(也就是JVM的“机器语言”),在由JVM负责解释执行Java的字节码文件,以运行程序。我们来看一下类加载的过程:

  • JVM加载我们的class文件
  • JVM去我们本地磁盘中找class文件,并加载到JVM内存中
  • 将class文件读入内存的同时,JVM就为其创建一个Class类型的实例,并关联起来

即一个类只产生一个Class实例,也即一个类的不同对象实例背后,对应的是同一个Class实例。一个类的Class实例,包含了该类的所有完整信息,如果我们获取了某个类或对象的Class实例,就可以通过它获取到对应类的所有信息。

3.反射机制有什么用

反射是框架设计的灵魂。反射可以实现在不修改源代码的情况下(修改配置文件),来控制程序,也符合设计模式的ocp原则(开闭原则)。

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时得到任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

4.反射机制相关的类

含义
java.lang.Class代表整个字节码。代表整个类。
java,lang.reflect.Method代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Field代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。
java.util.Properties读取Java的配置文件(.properties文件)

二.反射的使用

我们现在有两个类Animal.java和Person.java:

package com.senxu;

public class Animal {

    public String name="小动物";
    public String type="小狗";
    private int num=0;

    public Animal(){}

    public Animal(String type){
        this.type=type;
    }

    private Animal(int num){}

    public void hi(){
        //System.out.println("汪汪汪");
    }

    public static void hello(){
        System.out.println("你好");
    }

}
package com.senxu;

public class Person {

    public String name="我是人";
    public String type="中国人";

    public Person(){}

    public Person(String type){
        this.type=type;
    }

    public void hi(){
        //System.out.println("hello");
    }

}

有一个配置文件re.properties:

classpathAnimal=com.senxu.Animal
classpathPerson=com.senxu.Person
method=hi
methodHello=hello
varName=name
varType=type
varNum=num

 

1.判断任意一个对象所属的类

(1)哪些类型有Class对象

  • 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • interf:接口
  • 数组
  • enum:枚举
  • annotation:注解
  • 基本数据类型
  • void

(2)获取Class对象的三种方式

  • Class.forname("类的全类名")(推荐)

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

        应用场景:多用于配置文件,读取类全路径,加载类

  • 类.class

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

        应用场景:多用于参数传递,比如通过反射得到对应构造器对象

  • 对象.getClass()

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

        应用场景:通过创建好的对象,获取Class对象

  • 类加载器得到Class对象
package com.senxu.class_;

import com.senxu.Animal;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class GetClass {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //1.Class.forName()
        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        Class cls1 = Class.forName(classpath);
        System.out.println(cls1);

        //2.类名.class
        Class cls2 = Animal.class;
        System.out.println(cls2);

        //3.对象.getClass()
        Animal animal = new Animal();
        Class cls3 = animal.getClass();
        System.out.println(cls3);

        //4.通过类加载器来获取类的Class对象
        //(1)先得到类加载器
        ClassLoader classLoader = animal.getClass().getClassLoader();
        //(2)通过类加载器得到Class对象
        Class cls4 = classLoader.loadClass(classpath);
        System.out.println(cls4);

    }

}
class com.senxu.Animal
class com.senxu.Animal
class com.senxu.Animal
class com.senxu.Animal

 

(3)判断任意一个对象所属的类

package com.senxu.class_;


import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class Class1 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpathAnimal = properties.get("classpathAnimal").toString();
        String classpathPerson = properties.get("classpathPerson").toString();

        Class cls1 = Class.forName(classpathAnimal);
        Class cls2 = Class.forName(classpathAnimal);
        Class cls3 = Class.forName(classpathPerson);

        if (cls1.equals(cls2)) {
            System.out.println("equals");
            System.out.println(cls1);
        }
        else{
            System.out.println("!equals");
        }

        System.out.println("===================");

        if (cls1.equals(cls3)) {
            System.out.println("equals");
            System.out.println(cls1);
        }
        else{
            System.out.println("!equals");
        }

    }

}
equals
class com.senxu.Animal
===================
!equals

2.构造任意一个类的对象

(1)获取构造器

package com.senxu;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Properties;

public class Reflection3 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        Class cls = Class.forName(classpath);

        //获取该类的所有公共构造器
        Constructor[] constructors = cls.getConstructors();
        for (Constructor constructor:constructors){
            System.out.println(constructor);
        }

        System.out.println("==================");

        //获取该类的所有构造器
        constructors = cls.getDeclaredConstructors();
        for (Constructor constructor:constructors){
            System.out.println(constructor);
        }

        System.out.println("==================");

        //获得指定的构造器
        Constructor constructor = cls.getConstructor(String.class);
        System.out.println(constructor);

        System.out.println("==================");

        //获得非公开权限的构造器
        constructor=cls.getDeclaredConstructor(int.class);
        System.out.println(constructor);

    }

}
public com.senxu.Animal(java.lang.String)
public com.senxu.Animal()
==================
private com.senxu.Animal(int)
public com.senxu.Animal(java.lang.String)
public com.senxu.Animal()
==================
public com.senxu.Animal(java.lang.String)
==================
private com.senxu.Animal(int)

 

(2)实例化对象

根据Class对象,使用newInstance()创建此Class对象所表示的类的一个新实例或者构造器实例化对象。

注意:newInstance()是调用空构造,如果空构造不存在,则抛出异常InstantiationException。

package com.senxu;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

public class Reflection4 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        Class cls = Class.forName(classpath);

        //使用newInstance()创建对象 调用空构造
        Object animal = cls.newInstance();
        System.out.println(animal);

        System.out.println("=================");

        //使用构造器实例化对象
        Constructor constructor = cls.getConstructor(String.class);
        Object dog = constructor.newInstance("小狗");
        System.out.println(dog);

    }

}
com.senxu.Animal@154617c
=================
com.senxu.Animal@a14482

3.得到任意一个类所具有的属性和方法

(1)属性

package com.senxu;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Properties;

public class Reflection5 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();

        Class cls = Class.forName(classpath);
        //获得公共的属性
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            
            System.out.println(field);

        }

        System.out.println("=================");

        //获取全部属性
        fields = cls.getDeclaredFields();
        for (Field field : fields) {
      
            System.out.println(field);

        }

    }

}
public java.lang.String com.senxu.Animal.name
public java.lang.String com.senxu.Animal.type
=================
public java.lang.String com.senxu.Animal.name
public java.lang.String com.senxu.Animal.type
private int com.senxu.Animal.num

 

(2)方法

package com.senxu;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Reflection6 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();

        Class cls = Class.forName(classpath);

        //获取类自身的全部方法
        Method[] methods = cls.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("=====================================================");

        //获得类的所有共有方法,包括自身,从基类继承的,从接口实现的所有public方法
        methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

    }

}
public void com.senxu.Animal.hi()
=====================================================
public void com.senxu.Animal.hi()
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 java.lang.String java.lang.Object.toString()
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()

4.调用任意一个对象的属性和方法

(1)获取修改对象的属性

        获取用get()方法,修改用set()方法。

package com.senxu;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Properties;

public class Reflection7 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        String typeName = properties.get("varType").toString();

        Class cls = Class.forName(classpath);
        Object animal = cls.newInstance();

        //获取修改对象的属性
        Field type = cls.getDeclaredField(typeName);
        Object o = type.get(animal);
        System.out.println(o);
        type.set(animal,"小猫");
        o=type.get(animal);
        System.out.println(o);

    }

}
小狗
小猫

(2)调用对象的方法

  • 调用对象的普通方法

        使用Method.invoke()传入实例和参数调用。

  • 调用对象的静态方法

        由于是静态方法,invoke()无需传入参数,即传入null调用。

package com.senxu;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Reflection8 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        String methodName = properties.get("method").toString();
        String methodHello = properties.get("methodHello").toString();

        Class cls = Class.forName(classpath);
        Object animal = cls.newInstance();
        Method method1 = cls.getMethod(methodName);
        Method method2 = cls.getMethod(methodHello);
        //调用对象的普通方法
        method1.invoke(animal);
        //调用对象的静态方法
        method2.invoke(null);

    }

}
汪汪汪
你好

5.生成动态代理

不需要修改源码,修改配置文件即可实现动态的创建和使用对象。

三.反射机制的优缺点和优化

1.反射的优缺点

(1)优点

        反射提高了程序的灵活性和扩展性,降低了耦合性,提高自适应能力。可以动态的创建和使用对象(也就是框架的底层核心),使用灵活,可以说,没有反射机制,框架技术就失去了底层支撑。

(2)缺点

  • 性能问题:使用反射是解释执行,用于字段和方法时要远慢于直接代码。
  • 使用反射会模糊程序内部逻辑:程序员希望在源代码中看到程序的逻辑,反射绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。

(3)反射机制适用场景

主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。

2.反射调用优化

(1)反射调用优化前,传统方法和反射调用的对比

package com.senxu;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Reflection2 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {

        test1();
        test2();

    }

    public static void test1(){

        Animal animal = new Animal();
        long start = System.currentTimeMillis();
        for (int i=0; i<100000000; i++){
            animal.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法来调用hi耗时=" + (end-start));

    }

    public static void test2() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        String methodName = properties.get("method").toString();

        Class cls = Class.forName(classpath);
        Object animal = cls.newInstance();
        Method method = cls.getMethod(methodName);

        long start = System.currentTimeMillis();
        for (int i=0; i<100000000; i++){
            method.invoke(animal);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射调用hi耗时=" + (end-start));

    }

}
传统方法来调用hi耗时=30
反射调用hi耗时=3092

(2)反射调用优化-关闭访问检查

  • Method和Field、Constructor对象都有setAccessible()方法
  • setAccessible()的作用是启动和禁止访问安全检查的开关
  • 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查

(3)反射调用优化后,传统方法和反射调用的对比

package com.senxu;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Reflection2 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {

        test1();
        test2();
        test3();

    }

    public static void test1(){

        Animal animal = new Animal();
        long start = System.currentTimeMillis();
        for (int i=0; i<100000000; i++){
            animal.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法来调用hi耗时=" + (end-start));

    }

    public static void test2() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        String methodName = properties.get("method").toString();

        Class cls = Class.forName(classpath);
        Object animal = cls.newInstance();
        Method method = cls.getMethod(methodName);

        long start = System.currentTimeMillis();
        for (int i=0; i<100000000; i++){
            method.invoke(animal);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射机制来调用hi耗时=" + (end-start));

    }

    public static void test3() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classpath = properties.get("classpathAnimal").toString();
        String methodName = properties.get("method").toString();

        Class cls = Class.forName(classpath);
        Object animal = cls.newInstance();
        Method method = cls.getMethod(methodName);

        method.setAccessible(true);

        long start = System.currentTimeMillis();
        for (int i=0; i<100000000; i++){
            method.invoke(animal);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射机制优化后来调用hi耗时=" + (end-start));

    }

}
传统方法来调用hi耗时=32
反射机制来调用hi耗时=3114
反射机制优化后来调用hi耗时=753

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java反射机制是指在运行时动态地获取一个类的信息,并可以操作类的属性、方法和构造器等。Java反射机制可以使程序员在运行时动态地调用类的方法和属性,扩展类的功能,并可以实现注解、工厂模式以及框架开发等。 Java反射机制的原理如下:首先,Java编译器将Java源代码编译为字节码文件,字节码文件中包含着类的信息,这些信息包括类的名称、方法、属性和构造器等等。接着,Java虚拟机将字节码文件加载到内存中,然后通过类加载器将类加载到内存中形成一个类对象,这个类对象可以操作字节码文件中的信息。 使用Java反射机制的过程如下:首先获取类对象,通过类对象来获取类的构造器、属性、方法等信息,然后调用构造器来创建对象,通过属性获取和设置类的成员属性,通过方法调用类的方法等。 Java反射机制的优点是可以在运行时动态地得到类的信息,使得程序员在程序运行时能够对类进行更加灵活的操作,并可以使得程序更加通用化,同时也存在着一定的性能问题,因为Java反射机制需要Java虚拟机进行一定的额外处理,所以在程序运行时需要进行额外的时间和资源消耗。 总之,Java反射机制Java语言的一项重要特性,在Java开发中广泛应用,在代码编写、框架开发以及API开发中具有重要作用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值