java反射 【详细版】

Java反射

一、理解Class类

1.1 Class类的介绍

Class类是一个对象照镜子后的结果。对象照镜子后可以得到的信息;某个类的属性、方法、构造器、实现的接口等。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。

  • class对象只能由系统建立对象(在类加载阶段建立了类对象,不是new出来,而是系统创建)
  • 对于某个类的Class对象,在内存中只有一份,因为类只加载一次。
  • 每个类的实例都会记得自己是由那个Class实例所生成。
  • Class也是类,因此也继承Object类
  • 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
  • Class对象是存放在堆的
  • 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据。

通俗的来说,Class类是一个对象的基本信息的描述,对象通过照镜子,看到自己的全部特征。而class类就是描述这些全部特征的一个类对象。

1.2 Class类常用方法

在这里插入图片描述

二、java反射机制

在这里插入图片描述

2.1 反射机制的作用

通过这种反射机制:

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

2.2 反射相关的主要类

java.lang.Class 
Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method
代表类的方法,Method对象表示某个类的方法
java.lang.reflect.Filed
代表类的成员变量,Field对象表示某个类的成员变量
java.lang.reflect.Constructor
代表类的构造方法,Constructor对象表示构造器

2.3 利用反射机制读取配置文件实例化对象

通过读取配置文件,而不改变原来程序就能实例化对象!Spring框架的核心功能的底层实现就是依赖这种反射机制

public static void main(String[] args) throws Exception {
//1. 使用 Properties 类, 可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
String methodName = properties.get("method").toString();//"hi"
//2. 使用反射机制解决
//(1) 加载类, 返回 Class 类型的对象 cls
Class cls = Class.forName(classfullpath);
//(2) 通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例
Object o = cls.newInstance();
System.out.println("o 的运行类型=" + o.getClass()); //运行类型
//(3) 通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法对象
// 即: 在反射中, 可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
//(4) 通过 method1 调用方法: 即通过方法对象来实现调用方法
System.out.println("=============================");
method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)
//java.lang.reflect.Field: 代表类的成员变量, Field 对象表示某个类的成员变量
//得到 name 字段
//getField 不能得到私有的属性
Field nameField = cls.getField("age"); //
System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)
//java.lang.reflect.Constructor: 代表类的构造方法, Constructor 对象表示构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
System.out.println(constructor);//Cat()
Constructor constructor2 = cls.getConstructor(String.class); //这里传入的 String.class 就是 String 类的
Class 对象
System.out.println(constructor2);//Cat(String name)
}

2.4反射机制的优缺点

  • 优点
    反射可以在不知道会运行哪一类的情况下,获取到类的信息,创建对象以及操作对象。方便于拓展,所以反射是框架设计的灵魂,因为框架在设计的时候,为了降低耦合度,肯定是需要考虑扩展功能的,不能将类型写死。降低耦合度,变得更灵活,在运行时去确定类型,绑定对象,体现了多态功能。
  • 缺点
    反射是需要动态类型的,JVM没有办法优化这部分代码,执行效率相对直接初始化对象较低。一般业务代码不介意使用。反射可以修改权限,这是会破坏封装性,存在安全隐患。

可以通过setAccessible来关闭安全检查来提高一下执行效率
在这里插入图片描述

三、有关反射的具体操作

3.1 获取对象的包名以及类名

package invocation;
public class MyInvocation {
    public static void main(String[] args) {
        getClassNameTest();
    }
    
    public static void getClassNameTest(){
        MyInvocation myInvocation = new MyInvocation();
        System.out.println("class: " + myInvocation.getClass());
        System.out.println("simpleName: " + myInvocation.getClass().getSimpleName());
        System.out.println("name: " + myInvocation.getClass().getName());
        System.out.println("package: " +
                "" + myInvocation.getClass().getPackage());
    }
}

在这里插入图片描述

1.getClass():打印会带着class+全类名
2.getClass().getSimpleName():只会打印出类名
3.getName():会打印全类名
4.getClass().getPackage():打印出package+包名
getClass()获取到的是一个对象,getPackage()也是。

3.2 获取Class对象

在java中,一切皆对象。java中可以分为两种对象,实例对象和Class对象。这里我们说的获取Class对象,其实就是第二种,Class对象代表的是每个类在运行时的类型信息,指和类相关的信息。比如有一个Student类,我们用Student student = new Student()new一个对象出来,这个时候Student这个类的信息其实就是存放在一个对象中,这个对象就是Class类的对象,而student这个实例对象也会和Class对象关联起来。我们有三种方式可以获取一个类在运行时的Class对象,分别是

Class.forName(“com.Student”)
student.getClass()
Student.class

package invocation;

public class MyInvocation {
    public static void main(String[] args) {
        getClassTest();
    }
    public static void getClassTest(){
        Class<?> invocation1 = null;
        Class<?> invocation2 = null;
        Class<?> invocation3 = null;
        try {
            // 最常用的方法
            invocation1 = Class.forName("invocation.MyInvocation");
        }catch (Exception ex){
            ex.printStackTrace();
        }
        invocation2 = new MyInvocation().getClass();
        invocation3 = MyInvocation.class;
        System.out.println(invocation1);
        System.out.println(invocation2);
        System.out.println(invocation3);
    }
}

在这里插入图片描述

3.3 newIntence; newInstance()是一个无参构造方法

class Student{
    private int age;

    private String name;

    public Student() {
    }
    public Student(int age) {
        this.age = age;
    }

    public Student(String name) {
        this.name = name;
    }
    
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
public static void getInstanceTest() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Student student = (Student) stduentInvocation.newInstance();
            student.setAge(9);
            student.setName("Hahs");
            System.out.println(student);

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    
    
输出结果如下:
Student{age=9, name='Hahs'}

3.4 通过构造函数对象实例化对象

可以先获取一个类的所有的构造方法,然后遍历输出:

 public static void testConstruct(){
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            for(int i=0;i<cons.length;i++){
                System.out.println(cons[i]);
            }

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

在这里插入图片描述
不同构造函数创建对象

public static void constructGetInstanceTest() {
        try {
            Class<?> stduentInvocation = Class.forName("invocation.Student");
            Constructor<?> cons[] = stduentInvocation.getConstructors();
            // 一共定义了4个构造器
            Student student1 = (Student) cons[0].newInstance(9,"Sam");
            Student student2 = (Student) cons[1].newInstance("Sam");
            Student student3 = (Student) cons[2].newInstance(9);
            Student student4 = (Student) cons[3].newInstance();
            System.out.println(student1);
            System.out.println(student2);
            System.out.println(student3);
            System.out.println(student4);

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

在这里插入图片描述

四、通过反射获取类的结构信息

获取类中的结构信息时,当要通过反射设置私有属性时,系统会报错!此时我们可以使用对象.setAccessible(true);来进行爆破。与含有Declared中的方法搭配使用。

4.1 第一组: java.lang.Class 类

在这里插入图片描述

4.2 第二组: java.lang.reflect.Field 类

在这里插入图片描述

4.3 第三组: java.lang.reflect.Method 类

在这里插入图片描述

4.4 第四组: java.lang.reflect.Constructor 类

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值