Java反射

1.Java反射概念以及原理;

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

       Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

       Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。反射是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。所以通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

30b08d195a53425a8dcda4c82b16c9c4.png

我们可以通过上图看到,JVM就像一面镜子,由person对象可以获取Person实体的属性、方法。

这里补充一下,当我们的JVM把Person.class加载到内存的时候,会同步的产生关于这个文件的Class对象,这个对象里面封装的就是Person的信息,而且,不管我们new Person()使用多少次,我们的JVM只会为它创建一个Class对象。

我们反射的本质就是得到Class对象之后,通过调用Class中的方法反向的获取Person对象的各种信息。

类加载阶段:

1、Java虚拟机将.class文件读入内存,并为之创建一个class对象。

2、任何类被使用时系统都会为其创建一个且仅有一个的Class对象。

3、这个Class对象描述了这个类创建出来的对象的所有信息,比如有哪些改造方法,都有那些成员方法,都有那些成员变量等。

2.为什么要用反射;

  • 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
  • 获取任意对象的属性,并且能改变对象的属性
  • 调用任意对象的方法
  • 判断任意一个对象所属的类
  • 实例化任意一个类的对象
  • 通过反射我们可以实现动态装配,降低代码的耦合度,动态代理等。

3.如何通过编程方式实现java反射;

创建一个person类

public class Person {
    private String name; 
    private int age;    
    public int height;  
    public String sex;   

    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 void love(){
        System.out.println("love bts");
    }
    private void miss(){
        System.out.println("miss you");
    }
}

创建对象

Person person = new Person();

可以通过以下三个方法实现Java反射

(1)通过已经知道该类的对象调用getClass方法

Class person = person.getClass();

(2)任何一个类都是Class的实例对象,通过这个类的.class获得该类的Class对象

Class pClass = person.class;

(3)Class.forName()方式:通过指定类的全路径名来获取该类的Class对象

Class pClass = Class.forName("com.Person");

用第三种方法需要抛出一个ClassNotFoundException的异常

常用方法

System.out.println(pClass.getName()); //com.Person
System.out.println(pClass.getSuperclass()); //class java.lang.Object
System.out.println(Arrays.toString(pClass.getMethods())); //获取Person类和父类的所有方法
System.out.println("===================");
System.out.println(Arrays.toString(pClass.getDeclaredMethods()));//获取Person类的所有方法
System.out.println("===================");
System.out.println(Arrays.toString(pClass.getFields()));//获取Person的所有公有属性
System.out.println("===================");
System.out.println(Arrays.toString(pClass.getDeclaredFields()));//获取Person的所有属性,包括私有属性
System.out.println("===================");
System.out.println(pClass.getField("height"));//获取对应名的公有属性信息 需抛出NoSuchFieldException异常
System.out.println("===================");
System.out.println(pClass.getDeclaredField("name"));//获取对应名的公有或私有属性信息 需抛出NoSuchFieldException异常
System.out.println("====================");
//获取对应方法的信息,需要传入方法名和方法参数的类型,空参则传null。需抛出NoSuchMethodException异常
System.out.println(pClass.getMethod("love", null));
输出结果
com.Person
class java.lang.Object
[public void com.Person.love(), 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()]
===================
[public void com.Person.love(), private void com.Person.miss()]
===================
[public int com.Person.height, public java.lang.String com.Person.sex]
===================
[private java.lang.String com.Person.name, private int com.Person.age, public int com.Person.height, public java.lang.String com.Person.sex]
===================
public int com.Person.height
===================
private java.lang.String com.Person.name
====================
public void com.Person.love()

利用反射机制实例化对象

1.对于有空构造函数的类 可以直接用字节码文件获取实例:

Class pClass = Class.forName("com.Person");
//在获取类信息后,可以使用反射机制实例化一个对象
//调用 Class 对象的 newInstance() 方法,该方法会调用默认构造函数来实例化对象,如果类没有默认构造函数或者默认构造函数不可访问,则会抛出 InstantiationException 异常
Person p = (Person) pClass.newInstance();//这里我们知道是Person类,所有直接强转了
//调用say方法
p.love();

2.对应写了带参构造方法的类,没有空的构造函数则需要先获取到他的构造对象 在通过该构造方法类获取实例:

//Person类添加了如下的带参构造方法
public Person(String name, int age, int height, String sex) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
    }
Class pClass = Class.forName("com.Person");
 //调用构造函数进行实例化,通过获取类的构造函数对象(Constructor),然后通过该对象的 newInstance() 方法来实例化对象,可以传入相应的构造函数参数。
Constructor constructor = pClass.getConstructor(String.class, int.class, int.class, String.class);
Person o = (Person) constructor.newInstance("泰泰", 27, 180, "男");
o.love();
System.out.println(o.getName());
System.out.println(o.sex);

4.反射的应用以及优缺点。

反射的应用

        有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架,也是利用CGLIB 反射机制才得以实现,最常见的应用是注册数据库驱动Class.forName("com.mysql.jdbc.Driver");,mybatis、rocketmq等也有用到反射。

优缺点
优点
  1. 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等。
  2. 反射提高了程序的灵活性和扩展性。
  3. 通过反射我们可以实现动态装配,降低代码的耦合度,动态代理,提高自适应能力等。
缺点
(1) 性能问题。

       Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。

(2) 安全限制。

       使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。

(3) 程序健壮性。

        反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值