反射(Reflect)

本文介绍了Java反射机制,包括反射的概念、应用场景、如何获取Class实例、如何通过反射实例化对象、访问Field和Method。反射提供了操作字节码的能力,增强了代码的通用性和灵活性,允许在运行时动态创建对象并调用方法。

反射(Reflect)

1· 什么是是反射

1.1 反射的介绍

​ 不是正常编程中出现的机制

​ 框架底层源码会经常使用

​ 可以解决代码通用性问题
​ 学习要求:
​ 1.什么是反射,为什么要有反射,它能解决的问题
​ 2.反射中有常用的类,类中的方法的使用
​ 3.使用反射封装一些通用性比较强的代码

​ 反射机制:
通过java语言占用的反射机制,可以操作字节码文件
有点类似于黑客的技术(读写字节码文件)
通过反射机制可以操作代码片段(class文件)

1.2 相关包

​ 反射机制位于java.lang.reflect.*包下

  反射机制相关的类有哪些?
  java.lang.Class:
    	代表整个字节码文件,代表一个类型,代表整个类
  java.lang.reflect.Method:
        代表字节码中的方法字节码,代表类中的方法
    java.lang.reflect.Constructor:
       代表字节码中的构造方法字节码,代表类中的构造方法
   java.lang.reflect.Field:
     代表字节码中的属性字节码,代表类中的成员变量(静态变量+实例变量)

​ 以一个bean类为例介绍:

public class User {
    // Field
    int no;

    // Constructor
    public User() {
    }
    public User (int no) {
        this.no = no;
    }

    // Method
    public void setNo(int no) {
        this.no = no;
        public int getNo () {
            return no ;
        }
    }

2. Class

2.1 获取实例

​ 要操作一个类的字节码,需要首先获取到这个类的字节码
​ 怎么获取java.lang.Class实例?
​ 三种方式
​ 1.Class c = Class.forName(“完整类名包名”);
​ 2.Class c = 对象.getClass()
​ 3.Class c = 任何类型.class;

/*
Class.forName()
    1.静态方法
    2.方法的参数是一个字符串
    3.字符串需要的是一个完整类名
    4.完整类名必须带有包名,java.lang包也不能省略
*/

//方式1
Class c1 = null;
Class c2 = null;
try {
    c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说c1代表String类
    c2 = Class.forName("java.util.Date");//以此类推
    Class c3 = Class.forName("java.lang.Integer");
    Class c4 = Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

//方式2
//java中任何一个对象都有一个方法:getClass()
String s = "abc";
Class x = s.getClass();
System.out.println(c1 == x);//true
//字节码文件装载到jvm中的时候只装载一份,c1和x都装载这个文件

Date date = new Date();
Class y = date.getClass();
System.out.println(c2 == y);

//方式3:java语言中任何一种类型,包括基本数据类型,他都有.class属性
Class z = String.class;
Class m = int.class;
Class n = Date.class;
System.out.println(z == x);
2.2 使用实例化对象

​ 通过反射机制,获取Class,通过Class实例化对象
​ 获取到Class能干什么
​ 通过Class的newInstance()方法来实例化对象
​ 注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造才可以

try {
    //通过反射机制,获取Class,通过Class实例化对象
    Class c = Class.forName("Reflect.Bean.User");//c代表user类型
    //newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建
    //重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的
    Object o = c.newInstance();
    System.out.println(o);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
} 

3. Field

3.1 反射Student类当中所有的Field
public static void main(String[] args) throws Exception {
    //获取整个类
    Class s = Class.forName("java.lang.String");
    String sd = s.getName();
    System.out.println("完整类名"+sd);
    String simpleName = s.getSimpleName();
    System.out.println("简类名"+simpleName);

    //获取类中所有的public修饰的Field
    Field[] fields = s.getFields();
    System.out.println(fields.length);//测试数组中只有一个元素
    //取出这个Field
    Field f = fields[0];
    //取出这个Field它的名字
    String name = f.getName();
    System.out.println(name);

    //获取所有的Field
    Field[] fs = s.getDeclaredFields();
    for(Field field :fs){
        //获取属性的修饰符列表
        int modifiers = field.getModifiers();
        String ms = Modifier.toString(modifiers);//将数字转化为类型字符串
        System.out.println(modifiers+":"+ms);
        //获取属性类型
        Class type = field.getType();
        String n = type.getSimpleName();
        System.out.print(n+":");
        //获取属性名字
        System.out.println(field.getName());
    }
}
3.2 通过反射机制,反编译一个类的属性Field
public static void main(String[] args) throws Exception {
    //拼接字符串
    StringBuilder s = new StringBuilder();

    Class studentClass = Class.forName("java.lang.Integer");
    s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");
    Field[] fields = studentClass.getDeclaredFields();
    for (Field field:fields){
        s.append("\t");
        s.append(Modifier.toString(field.getModifiers()));
        s.append(" ");
        s.append(field.getType().getSimpleName());
        s.append(" ");
        s.append(field.getName());
        s.append(";\n");
    }
    s.append("}");
    System.out.println(s);
}
3.3 通过反射机制访问对象属性(必须掌握)
public static void main(String[] args) throws Exception {
    //使用反射机制,怎么去访问一个对象的属性(set get)
    Class studentClass = Class.forName("Reflect.Bean.Student");
    Object obj = studentClass.newInstance();//obj就是Student对象(底层调用无参构造方法)

    //获取a属性的值(根据属性名称获取Field)
    Field a = studentClass.getDeclaredField("a");

    //给obj对象(Student对象)的a赋值
    a.set(obj,111);
    /*
        赋值三要素:对象,属性,值
        反射机制让代码复杂了,但是更灵活了
    */

    //读取a属性的值
    //两个要素:获取obj对象no的值
    System.out.println(a.get(obj));

    //可以访问私有的属性吗?
    studentClass.getDeclaredField("a");
    //打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会)
    //这样设置完之后,在外部也是可以访问private
    a.setAccessible(true);
    //给a属性赋值
    a.set(obj,1321);
    //获取a属性的值
    System.out.println(a.get(obj));
}

4. Method

4.1 反射Method(了解内容)
public static void main(String[] args) throws Exception{
    //获取类
    Class userClass = Class.forName("Reflect.Bean.UserService");
    //获取所有的Method(包括私有的)
    Method[] method = userClass.getDeclaredMethods();
    System.out.println(method.length);

    for (Method m : method){
        //获取修饰符列表
        System.out.print(Modifier.toString(m.getModifiers())+" ");
        //获取方法的返回值类型
        System.out.print(m.getReturnType().getSimpleName()+ " ");
        //获取方法名
        System.out.print(m.getName());
        //方法的修饰符列表
        Class[] parameterTypes = m.getParameterTypes();
        System.out.print("(");
        for(Class p : parameterTypes){
            System.out.print(p.getSimpleName()+" ");
        }
        System.out.println(")");
    }
}
4.2 反编译一个类的方法(了解内容)
public static void main(String[] args) throws Exception{
    StringBuilder s = new StringBuilder();
    Class userClass = Class.forName("Reflect.Bean.UserService");

    s.append(Modifier.toString(userClass.getModifiers())+" class "+userClass.getSimpleName()+"{\n");
    Method[] methods = userClass.getDeclaredMethods();
    for (Method method:methods){
        s.append("\t");
        s.append(Modifier.toString(method.getModifiers()));
        s.append(" ");
        s.append(method.getReturnType().getSimpleName());
        s.append(" ");
        s.append(method.getName());
        s.append("(");
        Class[] parameterTypes = method.getParameterTypes();
        for (Class p : parameterTypes){
            s.append(p.getSimpleName());
            s.append(",");
        }
        //删除指定下标位置上的字符
        if(parameterTypes.length > 0){
            s.deleteCharAt(s.length() - 1);
        }
        s.append(")");
        s.append("{}\n");
    }

    s.append("}");
    System.out.println(s);
}
4.3 通过反射机制如何去调用一个对象的方法(必须掌握)
public static void main(String[] args) throws Exception{
    //通过反射机制如何调用一个对象的方法
    Class userClass = Class.forName("Reflect.Bean.UserService");
    //创建对象
    Object obj = userClass.newInstance();
    //获取Method
    Method method = userClass.getDeclaredMethod("login", String.class, String.class);
    //调用方法
    /*
    调用方法有几个要素?
        对象,方法名,实参列表,返回值
    */
    //反射机制中最重要的一个方法,必须记住
    Object retValue = method.invoke(obj, "admin", "123");
    System.out.println(retValue);
}

5. 小结

反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。这就是反射的魅力所在之处。

以上均为上课时当堂笔记和当堂所写代码,记录下来以助理解记忆

### Java 反射 Reflect 使用教程 #### 获取 `Class` 对象 在Java中,反射的核心是`Class`对象。可以通过三种方式获得: - **通过类名**:使用`.class`语法。 ```java Class<?> clazz = String.class; ``` - **通过实例**:调用对象的`.getClass()`方法。 ```java Object obj = new Integer(0); Class<?> clazz = obj.getClass(); ``` - **通过全限定名**:利用`Class.forName(String className)`静态方法。 ```java try { Class<?> clazz = Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { // Handle exception } ``` 上述每种方法都可用于获取指定类型的`Class`对象[^1]。 #### 访问字段 (`Field`) 一旦有了`Class`对象,就可以访问其声明的成员变量(即字段)。这包括私有字段在内的所有字段。 ```java // 假设有一个Person类 public class Person { private String name; public void setName(String n){ this.name=n; } @Override public String toString(){ return "Name:"+this.name; } } try { Class<Person> personClass = Person.class; // 获取名为"name"的私有字段 Field field = personClass.getDeclaredField("name"); // 设置可访问标志位为true以便能够读写该字段 field.setAccessible(true); Person p = new Person(); // 向p设置值 field.set(p,"John Doe"); System.out.println(p.toString()); // 输出 Name:John Doe } catch (NoSuchFieldException | IllegalAccessException e) { // 处理异常... } ``` 这段代码展示了如何创建一个新的人(`Person`)对象并为其内部不可见的名字(`name`)属性赋值。 #### 调用方法 (`Method`) 除了可以操作字段外,还可以通过反射来执行某个特定的方法。 ```java try { Class<Person> personClass = Person.class; Method method = personClass.getMethod("setName",String.class); Person p = new Person(); // 执行setName()方法并将参数传递给它 method.invoke(p, "Jane Doe"); System.out.println(p.toString()); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { // 处理异常... } ``` 这里说明了怎样找到公共方法`setName`并通过传入的对象实例去调用这个方法。 #### 构造器 (`Constructor`) 最后,也可以借助于反射机制来进行对象的构建工作。 ```java try { Class<Person> personClass = Person.class; Constructor<Person> constructor = personClass.getConstructor(); Person p = constructor.newInstance(); System.out.println(p.toString()); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { // 处理异常... } ``` 此片段显示了一个无参构造函数是如何被用来生成新的`Person`实体的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值