美文网首页
Java 反射常用的知识点。

Java 反射常用的知识点。

作者: xqiiitan | 来源:发表于2024-07-11 17:41 被阅读0次

反射的常用知识点。

概念

(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)


1.获取各个字节码对应的实例对象

方法一 : 类名:class
Class cls1 = Person.class; // 一般在我们知道有这个类的时候去使用
方法二 : 对象:getClass
Class cls2 = p1.getClass(); // 前提是对象已经被实例化出来了
方法三 : forName
Class cls3 = Class.forName("java.lang.String"); // 写需要反射的类名,一般是以包名.类名
方法四 : getClassLoader().getClass
Class clazz = getClassLoader().getClass("java.lang.String");

.class()、getClass()、Class.forName()、getClassLoader()的区别
  • .class是获取声明时的类。
  • getClass()是获取运行时的类。
  • Class.forName()是通过类名来获得类。
  • getClassLoader()是获得类的加载器。

若字节码曾经被加载过,已经待在Java虚拟机里面了,可以直接返回,如方法一和方法二
若Java虚拟机中还没有这份字节码,则用类加载器去加载,把加载的字节码缓存到虚拟机中,
以后,使用这份字节码就不需要加载了,如方法三

预定义对象

有9个预定义对象,8个基本类型和void

Class class1 = boolean.class;
Class class2 = int.class;
//.......
Class class3 = Void.class;

int.class.isPrimitive   // 用isPrimitive判断是否是基本类型
int[].class.isArray     // 用isArray判断是否是数组
getclass().getName();   // 获得类型

2.反射

反射就是把Java类中的各种成分映射成相应的Java类

  • Field:成员变量
  • Method:方法
  • Constructor:构造方法
  • Package:包

2.1 Constructor 构造方法

Constructor[] constructors = clz.getDeclaredConstructors();     //获取所有的构造函数
Constructor constructor = getDeclaredConstructor(参数类型);  //获取一个所有的构造函数
Constructor[] constructors1 = getConstructors();         //获取所有公开的构造函数
Constructor constructor = getConstructor(参数类型);      //获取单个公开的构造函数

Class.forName("java.lang.String").getConstructors(); // 得到某类所有的构造方法
Class.forName("java.lang.String").getConstructor(StringBuffer.class); // 根据getConstructor中的参数返回相应的构造方法

getModifiers();  //获取所有修饰符

返回类型:整数类型,如果有两个修饰符,则返回两个修饰符之和,例如public static void getAll(){ }
返回值会是public和static之和
整数定义:
0–默认不写
1–public
2–private
4–protected
8–static
16–final
32–synchronized
64–volatile
128–transient
256–native
512–interface
1024–abstract

获取类名字

String, 可以反射类名,方法名,构造函数名等等
getName();    //获取全名 例如:com.bean.Book
getSimpleName()  //获取类名 例如:Book

getPackage() 获取包,返回类型package。
Package aPackage = clz.getPackage();
getInterfaces() 返回接口列表
Class[] interfaces = clz.getInterfaces();
getSuperclass() 获取父类/超类
Class superclass = clz.getSuperclass();

Constructor可以干什么

获取实例对象(一个constructor可以new出很无数个实例对象)
(强制转换)constructor1.newInstance(参数要和getConstructor中的一致);

通过默认构造方法创建实例(内部会缓存 构造方法,第二次获取就不会再创建,直接获取)
String obj = (String) Class.frName("java.lang.String").newInstance();

原版反射的步骤是 Class -> Constructor -> newInstance 用Class.newInstance()直接在内部Constructor,
即直接Class -> Class.newInstance()即可获得默认构造方法的实例

2.2 Method 类的成员方法

Object result = method.invoke(owner, args);

  • owner: 执行这个方法的对象,args:参数。 可以这么理解:owner对象中带有参数args的method方法。

  • 返回值是Object,也既是该方法的返回值。
    Method[] methods = clz.getMethods()  //获取所有公开的方法
    Method[] method = getMethod("printAll")  //获取单个公开的方法,参数是可以指定方法名
    method.invoke(clz.newInstance, "message");

    getDeclaredMethods()   //获取所有的方法. 注意:它不会获取系统自带的方法
    getDeclaredMethod(String name)    //获取单个的所有方法 参数是可指定方法名
    Method method1 = clz.getDeclaredMethod("printOne"); //获取单个所有的方法

String s = "abc";  
Method methodCharAt = String.class.getMethod("charAt", int.class);
String newString = methodCharAt.invoke(s, 1);//invoke是方法身上的方法,s是执行该方法的对象(必须是实例化的对象)
System.out.println(newString); 
// 调用静态方法
methodCharAt.invoke(null, args); // 静态方法,owner传入null。

2.3 Field 类中的一个成员变量

public class Person {
    public String name;
    private int age;
    public int getAge() { return age;}
    public void setAge(int age) { this.age = age; }
}
  • getFields()   // 获取所有的公开字段
  • getField(String name)  //参数可以指定字段  获取单个public字段
  • getDeclaredFields()  //获取所有的字段
  • getDeclaredField(String name)  //获取单个字段 参数可以指定字段
 Class clz = Book.class;
 //Public
 Field[] fields = clz.getFields();   //所有公开字段
 Field id = clz.getField("age"); //age字段
 //所有
 Field[] declaredFields = clz.getDeclaredFields();   //所有字段
 clz.getDeclaredField("name");   //name字段

2.3.0 实例化对象

newInstance(Object initargs)
第一种方式

Class clz = Book.class;
Object obj =  clz.newInstance();        //将创建一个无参book对象

第二种方式

Class clz = Book.class;
Constructor constructor = clz.getDeclaredConstructor();   //获得无参构造 
Object obj = constructor.newInstance();    //实例化book对象

设置访问属性

 clz.setAccessible(true)  //可访问
 clz.setAccessible(false)  //不可访问
 //默认是false
 Field id = clz.getField("age"); //age字段  
 id.setAccessible(true); //设为可访问
 id.setAccessible(false);    //设为不可访问

使用方法
method.invoke(Object obj,Object… args)
obj:如果是实例方法,则放个该方法的类对象给它
obj:静态方法,写null
args:方法的参数值,没有写null,或不写都行

Method method = clz.getMethod("printAll");        //获取单个的公开方法
method.invoke(clz.newInstance(),null);    //使用方法

2.3.1 通过反射获取成员变量

    Person person = new Person();
    person.setAge(20);
    person.name = "Heiko";
    // Field fieldName = person.getClass().getField("name"); // 方式一
    Field fieldName = Class.forName("com.Person").getField("name"); // 方式二
    // fieldName的值为多少?不是Heiko,因为fieldName不是对象身上的变量,而是类上,要通过field.get("实例")或getDeclaredField来获取该实例上的值
    String name = (String) fieldName.get(person); // 获取对象的属性。
    System.out.println("name:" + name);
    
    //对于私有变量,需暴力破解
    Field fieldAge = person.getClass().getDeclaredField("age"); //getDeclaredField 不管是私有还是public的,都可以获取
    fieldAge.setAccessible(true); //设置可以访问私有属性
    int age = (int) fieldAge.get(person); // 获取person的age属性。
    System.out.println("age:" + age);  

2.3.2 获取静态变量

Field filed = obj.getClass().getField(cmdFieldName);
Integer cmdValue = (Integer) filed.get(null); // 获取静态变量,传入null
获得所有的成员变量

obj.getClass().getFields();

getFields和getDeclaredFields的区别
  • getFields(): 只能访问类中声明为公有的字段,私有的字段它无法访问,能访问从其它类继承来的公有方法;
  • getDeclaredFields(): 能访问类中所有的字段,与public,private,protect无关,不能访问从其它类继承来的方法;

比较成员变量类型: if(field.getType() == String.class){}
获取对象obj的某属性的值: String oldValue = (String)field.get(obj);
给对象的属性设置值: field.set(object, 新的值);

最后的例子

public class Test{
    public static void main(String[] args) throws Exception {
        //获得Dog类的Class对象
        Class<?> classType = Class.forName("Dog");
        //生成对象的实例
        Object obj = classType.newInstance();
        
        //取得dogName属性
        Field dogName = classType.getDeclaredField("dogName");
        //禁止Field的访问控制检查
        dogName.setAccessible(true);
        //将Field的值设为“Xiao Qiang”
        dogName.set(obj, "Xiao Qiang");
        
        //取得say()方法
        Method say = classType.getDeclaredMethod("say", new Class[]{});
        //禁止say方法的访问控制检查
        say.setAccessible(true);
        //调用obj对象的 say方法
        say.invoke(obj, new Object[]{});
    }
}
 
class Dog {
    //私有的属性, 方法
    private String dogName = "Wang Cai";
    private void say() {
        System.out.println(dogName + ": Wang Wang");
    }
}

相关文章

网友评论

      本文标题:Java 反射常用的知识点。

      本文链接:https://www.haomeiwen.com/subject/ukfxhjtx.html