Java反射知识点笔记

1. Class对象

类是程序的一部分,每个类都有一个Class对象,而反射所依赖的就是这个Class对象。

在JVM中,类都是动态加载的,当程序创建第一个对类的静态成员的引用时,就会加载这个类,这个可以证明构造函数也是类的静态方法,即使构造方法没有使用static关键字。

类加载器首先检查在这个类的Class对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找 .class 文件,这个类的所有对象都是依据这个类的 .class对象 来创建的。下面举个例子:

package com.ga;
class Candy{
    static{
        System.out.println("Loading Candy");
    }
}
class Gum{
    static{
        System.out.println("Loading Gum");
    }
}
class Cookie{
    static{
        System.out.println("Loading Cookie");
    }
}
public class SweetShop {
    public static void main(String[] args) {
        System.out.println("inside main");
        new Candy();
        System.out.println("After creating Candy");
        try{
            Class.forName("Gum");           //获取Class对象的引用的一种方法
        }catch (ClassNotFoundException e){
            System.out.println("找不到Gum");;
        }
        System.out.println("After Class.forName(\"Gum\")");
        new Cookie();
        System.out.println("After creating Cookie");
    }
}
输出结果:
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

上面就用了两种方式来加载类,第一种直接 new 某个类,第二种就是调用静态函数 forName() ,根据类名进行加载。

常用的获取Class对象的方法有:

Class.forName();         //这里调用的是Class类的静态函数
class.getClass();        //这里是调用对象的 getClass() 方法
Class.getInterfaces();   //查询接口,第一个 Class 是某个类的类名
Class.getSuperclass();   //查询基类,第一个 Class 是某个类的类名
Class.class;             //类字面常量,第一个 Class 是某个类的类名

注:推荐使用类字面常量。如果使用 forName() 或者 getClass() ,有可能会因为没找到类(类名打错)而在运行时报错,使用类字面常量的话,他在编写代码时就受到检查,如果没有导入这个类,就会提示错误。

2. 反射

反射就是在程序运行时,获取某个类的Class对象,并根据这个Class对象新建一个实例对象,或者获得这个类所拥有的方法,属性,来实现一些通用的方法。大部分框架的实现就是使用了反射。

举个例子,获取User的属性和方法:

class User{
    private String name;
    private String password;

	public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public User(String name){
        this.name = name;
    }

    public User() {
    }
    //get/set省略

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    
    public static void main(String[] args) {
        Field[] fields = User.class.getDeclaredFields();       //获取所有字段
        Method[] methods = User.class.getDeclaredMethods();    //获取所有方法
        for (Field field: fields) {
            System.out.println(field.toString());
        }
        for (Method method: methods) {
            System.out.println(method.toString());
        }
    }
}
输出结果:
private java.lang.String com.qianfeng.User.name
private java.lang.String com.qianfeng.User.password
public java.lang.String com.qianfeng.User.getName()
public void com.qianfeng.User.setName(java.lang.String)
public java.lang.String com.qianfeng.User.getPassword()
public void com.qianfeng.User.setPassword(java.lang.String)

注:getConstructors(),getDeclaredFields(),getDeclaredMethods()等返回数组的方法,数组内容的顺序是随机的。(如要保证顺序,可参照Java反射,顺序输出类中的方法

接下来列举一些常用方法(有 Declared 这个词的方法,获得到任意访问权限的属性或方法,没有的话只能访问公有即 public 的属性或方法):

获取构造器

public static void main(String[] args) {
        try {
        	//获取User类中的一个公有构造方法,为空就是无参构造器
            Constructor constructor1 = User.class.getConstructor();
            //根据参数类型的类对象获取对应的一个构造方法,这个方法就会获取只有一个参数,且参数类型为String类型的构造器
            Constructor constructor2 = User.class.getConstructor(String.class);
            //获取User类中所有的公有构造器
            Constructor[] constructor3 = User.class.getConstructors();
            //获取User类中的任意权限的一个构造器,用法同上面的获取公有构造器的方法
            Constructor constructor4 = User.class.getDeclaredConstructor();
            //获取User类中的所有构造器
            Constructor[] constructor5 = User.class.getDeclaredConstructors();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

获取成员变量

public static void main(String[] args) {
        try {
			//根据属性名获取对应的公有属性
            Field field = User.class.getField("name");    
            //获取所有公有属性   
            Field[] fields = User.class.getFields();
            //根据属性名获取对应的任意权限的属性
            Field field1 = User.class.getDeclaredField("password");
            //获取所有权限的属性
            Field[] fields1 = User.class.getDeclaredFields();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

获取成员方法

先给User添加两个方法

	public User Create(String name, String password){
        return new User(name, password);
    }
    public User Create(String name){
        return new User(name);
    }
public static void main(String[] args) {
        try {
        	//根据函数名和参数类型获取对应的公有成员方法
			Method method1 = User.class.getMethod("Create", String.class);
			//获取所有的公有成员方法
            Method[] method2 = User.class.getMethods();
            //根据函数名和参数类型获取对应的所有权限的成员方法
            Method method3 = User.class.getDeclaredMethod("Create", String.class, String.class);
            //获取所有权限的成员方法
            Method[] method4 = User.class.getDeclaredMethods();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

使用技巧

获取了类的属性和方法后,就可以根据这些来修改该类的实例对象的属性的值,或者调用方法,比如:

public static void main(String[] args) {
        User user = null;
        try {
            //通过反射创建实例,
            user = User.class.newInstance();
            //通过反射获取 name 字段,并将对象 user 中的 name 字段的值设为 “user”
            User.class.getField("name").setAccessible(true);    ////解除私有限定
            User.class.getField("name").set(user, "user");
            //通过反射获取 password 字段,并将对象 user 中的 password 字段的值设为 “password”
            User.class.getField("password").setAccessible(true);
            User.class.getField("password").set(user, "password");
            
            System.out.println(user);
            //通过反射获取只有一个参数,且参数类型为 String 类型的 setName 方法,通过invoke调用,invoke()中,第一个参数为要调用该方法的对象实例,第二个为参数的值
            Object object = user;
            User.class.getDeclaredMethod("setName", String.class).invoke(object, "user1");
            User.class.getDeclaredMethod("setPassword", String.class).invoke(object, "password1");
            System.out.println(user);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
输出结果
User{name='user', password='password'}
User{name='user1', password='password1'}

由于反射类中的 Field , Method 和 Constructor 继承自 AccessibleObject ,因此,通过在这些类上调用 setAccessible() 方法,可以修改访问权限,从而实现对这些字段的操作。

也可以用下面的方式解除操作权限,第一个参数为字段的对象,第二个为权限:

AccessibleObject.setAccessible(fields, true);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值