Java中的反射

反射

什么是反射?

正常情况下我们编写程序都是在编译前就已经知道要调用哪个类的哪个方法,如果在编译期间不确定或不知道要调用哪个类的哪个方法,只在运行时才知道,那这种调用方式就叫反射。

反射也被称为Java动态调用技术。

Class类

Class类是什么?

第一个类加载到内存后都会被创建出一个Class对象,该对象中存储了该类有哪些属性、哪些方法,所以,Class对象就是用来描述类的对象。

Class对象的三种获取方式
1.对象名.getClass();
2.类名.class
3.Class.forName("类的全路径");动态获取

 反射获取及调用构造方法

1.直接调用无参构造方法
class.newInstance();

2.获取指定的构造方法(公共的)
Constructor constructor = class.getConstructor(数据类型.class);

3.获取所有的构造方法(公共的)
Constructor[] constructors class.getConstructors();

4.Constructor类提供的方法
    1.获取构造方法的形参个数
        constructor.getParameterCount()
    2.获取构造方法的形参数据类型
        constructor.getParameterTypes()

5.获取指定的非公共的构造方法
class.getDeclaredConstructor(数据类型.class);

6.获取所有的非公共的构造方法
class.getDeclaredConstructors();

反射获取类中的属性及调用

1.获取指定成员变量(公共的)
class.getField(String name);

2.获取所有的成员变量(非公共的)
class.getDeclaredField(String name);

3.获取指定的成员变量(公共的)
class.getFields(String name);

4.获取所有的成员变量(非公共的)
class.getDeclaredFields();

5.取值
field.get(obj);

6.赋值
field.set(obj, value);

反射获取类中的方法及调用

1.获取类中指定的方法(所有的)
class.getDeclaredMethod(String methodName, 类... 参数类型);

面试题:

创建对象有几种方式?

1.new

2.clone

3.反序列化

4.反射

概述

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

        这里的反理解为反转,控制反转。假如我们需要零花钱就找老婆要,这是“正”。但是现在反过来是老婆主动给我们,这是“反”。虽然最后的结果都一样,都是老婆给我们零花钱,但是方式不一样。程序中以前创建对象是通过new来创建对象,自上而下。但是现在我们可以使用反射技术反过来自下而上的提供访问。反射相关的类都在java.lang.reflect包下。

  • 优点 :在程序运行时,对类和对象的信息进行解析、调用方法,极大地提高了灵活性。
  • 缺点 :由于需要动态解析类的信息,降低了程序性能。

Class类

        面向对象的过程中,把对象的相同特征跟行为能力提取出来成为类,比如人类、桌子类、猫类、狗类……那这些类又具有什么相同的特征跟行为能力?是否可以继续往上抽取出来呢?答案是肯定的。所有的类可以继续抽象出Class类、所有的方法可以抽取为Method类、所有的字段抽取为Filed类等等。

        通过Class、Method、 Filed等类的API间接创建、操作对象的方式称为反射机制。

        使用反射机制首先要获取对应类的Class对象。获取Class对象有3种方式

方式1—通过类名

Class clazz = Person.class ;

方式2—通过对象

Class  clazz = new Person().getClass();

方式3—类路径的字符串

Class  clazz = Class.forName("com.blb.Person");

代码演示

class Person {
​
}
​
public class Demo01Reflect {
​
    public static void main(String[] args) throws ClassNotFoundException {
//      第1种方式:
        Class clazz = Person.class ;
//      第2种方式:
//      Person p  = new Person();
//      Class clazz = p.getClass();
//      第3种方式:
//      Class  clazz = Class.forName("com.blb.Person");
    }
}

创建对象

无参构造

方式1

可以通过Class对象的newInstance方法直接通过无参的构造方法创造对象。这种方式只能针对无参的构造方法使用。

代码演示

Person  person = (Person) clazz.newInstance();

方式2

所有的构造方法也同样的映射成Constructor类型的对象,所以也可以使用Constructor对象的方式构建对象。

  • getConstructor:获取所有的public类型构造器
  • getDeclaredConstructor : 获取自身定义的所有构造器,包括private的构造器

代码演示

//Constructor c = clazz.getConstructor();
Constructor c = clazz.getDeclaredConstructor();
// 通过newInstance方法创建构造器对应的对象
Person  person = (Person) c.newInstance();

有参构造

带参数的构造器只能通过Constructor方式,由于构造器的参数个数跟类型都不是固定的,所以getConstructor跟getDeclaredConstructor方法的参数列表设计成可变参数类型。

代码演示

class  User{
​
    private String name;
​
    private int age ;
    
// 构造方法是私有的
    private User(String name , int age ){
        this.name = name ;
        this.age = age ;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
​
public class Demo02Reflect {
​
    public static void main(String[] args) throws Exception {
//      获取对应的Class对象
        Class clazz = User.class ;
//      获取想要的构造器,这里的参数列表的类型要跟定义的构造方法一致
        Constructor c = clazz.getDeclaredConstructor(String.class, int.class);
//       由于构造方式是私有的,需要设置权限
        c.setAccessible(true);
//        通过对应的构造器创建对应的对象,这里的参数列表的值要跟构造方法的类型一致
        User user = (User) c.newInstance("blb", 18);
        System.out.println(user);
    }
}

属性

我们也可以通过API获取对应的属性,常用API如下:

  • Field[] getDeclaredFields(): 获取当前类声明的所有属性,包括private的属性。
  • Field[] getFields(): 获取当前类声明的所有的公共(public)属性,包括继承过来的。
  • Field getDeclaredField(String name): 获取当前类声明的某个属性,包括private的属性 。
  • Field getField(String name): 获取当前类声明的某个公共(public)属性,包括继承过来的。

代码演示

class  Student{
​
    private String name;
​
    private int age ;
​
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
​
public class Demo03Reflect {
​
    public static void main(String[] args) throws Exception {
//      获取对应的Class对象
        Class clazz = Student.class ;
//      获取对象
        Student s = (Student) clazz.newInstance();
//       获取当前类定义的所有的属性
        Field[] fields = clazz.getDeclaredFields();
//        遍历所有的属性
        for (Field f: fields) {
//          打印字段名称
            System.out.println(f.getName());
        }
//      获取属性名为name的属性
        Field nameField = clazz.getDeclaredField("name");
//      私有的属性需要打开访问权限
        nameField.setAccessible(true);
//      给对象s的name属性赋值为blb
        nameField.set(s,"blb");
​
        System.out.println(s);
​
    }
}

tips:

  • 属性的赋值依赖于某个对象,表示给哪个对象的属性赋值。
  • 操作私有的属性需要设置权限setAccessible

方法

我们也可以通过API获取对应的成员方法,常用API如下:

  • Method[] getDeclaredMethods(): 获取当前类声明的所有方法,包括private的方法。
  • Method[] getMethods(): 获取当前类声明的所有的公共(public)方法,包括继承过来的。
  • Method getDeclaredMethod(String name, Class... parameterTypes): 获取当前类声明的某个方法,包括private的方法 。
  • Method getMethod(String name, Class... parameterTypes): 获取当前类声明的某个公共(public)方法,包括继承过来的 。

代码演示

class  Student{
​
    public void study(){
        System.out.println("好好学习,天天向上。");
    }
​
    private int exam(String name){
        System.out.println(name+"同学,认真考试");
        int score = 90 ;
        return score ;
    }
​
}
​
public class Demo04Reflect {
​
    public static void main(String[] args) throws Exception {
//      获取对应的Class对象
        Class clazz = Student.class ;
//      获取对象
        Student s = (Student) clazz.newInstance();
//       获取当前类定义的所有的属性
        Method[] methods = clazz.getDeclaredMethods();
​
//        遍历所有的方法
        for (Method m: methods) {
//          打印方法名称
            System.out.println(m.getName());
        }
​
//      获取方法名为study的方法
        Method study = clazz.getDeclaredMethod("study");
//      通过invoke方法调用这个s对象的study方法
        study.invoke(s);
​
//      获取方法名为exam的方法,参数类型要一致
        Method exam = clazz.getDeclaredMethod("exam",String.class);
//      私有方法,必须要设置访问权限
        exam.setAccessible(true);
//      通过invoke方法调用这个s对象的exam方法,接收方法调用的结果
        Integer result = (Integer) exam.invoke(s,"blb");
        System.out.println("考试分数为: "+result);
​
    }
}

tips:

  • 方法的调用依赖于某个对象,表示调用哪个对象方法。
  • 调用私有的方法需要设置权限setAccessible
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习Java的鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值