Java反射机制

反射机制

1.反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件。

优点类似于黑客。(可以读和修改字节码文件。)

通过反射机制可以操作代码片段。(class文件。)

2.反射机制的相关类在哪个包下?

java . lang . reflect.* ;

3.反射机制相关的重要的类有哪些?

  • java.lang.Class:代表整个字节码,代表一个类型。代表整个类

  • java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法

  • java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法。

  • java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

//Class 
public class User {    
    int no;//Field    
    //Constructor    
    public User() {}    
    public User(int no) {        
        this.no = no;    
    }             //Method    
    public int getNo() {        
        return no;    
    }    
    public void setNo(int no) {      
        this.no = no;   
    } 
}

4.要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java. lang. Class实例?

三种方式

第一种:Class c = Class .forName(“完整类名带包名”);
第二种:Class c = 对象. getClass();
第三种:Class c = 任何类型.class;

①、Class.forName()

  • 静态方法
  • 方法的参数是一个字符串
  • 字符串需要的是一个完整类名
  • 完整类名必须带有包名。java.lang包也不能省略。

②、java中任何一个对象都有一个方法:getClass()

③、java语言中任何一种类型,包括基本数据类型,它都有.class属性。

获取到Class 能干什么?

通过Class的newInstance()方法来实例化对象。

注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。

5.验证反射机制的灵活性

java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。

非常之灵活。( 符合OCP开闭原则:对扩展开放,对修改关闭。)

后期要学习的是高级框架,而工作过程中,也都是使用高级框架,

包括: Spring、 SpringMVC、 MyBatis 、Struts 、Hibernate、、、、、、

这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的。

学会了反射机制有利于你理解剖析框架底层的源代码。

类加载的过程主要分为三个部分:加载、链接、初始化,这三个阶段

class. forName()发生了什么?

重点: 如果你只是希望一个类的静态代码块执行,其它代码一律不执行,你可以使用:

Class . forName(“完整类名”);

这个方法的执行会导致类加载,类加载时,静态代码块执行。

//获取一个文件的绝对路径
String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
System.out.println(path);
//通过IO流读取classinfo2.properties文件
FileReader reader = new FileReader(path);*/
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");
//创建属性类对象Map
Properties pro = new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value;
String className = pro.getProperty("className");
Class c = Class.forName(className);

6.接下来说一种比较通用的一种路径。即使代码换位置了,这样编写仍然是通用的。

​ 注意:使用以下通用方式的前提是:这个文件必须在类路径下

​ 什么类路径下?凡是在src下的都是类路径下。【记住它】

​ src是类的根路径。

  • ​ Thread.currentThread()当前线程对象

  • ​ getContextClassloader()是线程对象的方法,可以获取到当前线程的类加载器对象。

  • ​ getResource() [获取资源] 这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。

String path = Thread.currentThread().getContextClassLoader().getResource("从src的根路径下作为起点开始").getPath();

7.java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。

使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下,并且在写路径的时候,路径后面的扩展名不能写

ResourceBundle bundle = ResourceBundle.getBundle("classinfo"); 
String className = bundle.getString("className"); System.out.println(className);

8.关于JDK中自带的类加载器

8.1、什么是类加载器?

专门负责加载类的命令/工具。ClassLoader

8.2、JDK中自带了3个类加载器

  • 启动类加载器

  • 扩展类加载器

  • 应用类加载器

8.3、假设有这祥一段代码

String s =" abc" ;

代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载,那么是怎么进行加载的呢?

首先通过"启动类加载器"加载。

注意:启动类加载器专门加载:C:\ProgramFiles\Java\jdk1.8.0_jre\lib\rt.jar

rt.jar中都是JDK最核心的类库

如果通过"启动类加载器"加载不到的时候,会通过"扩展类加载器"加载。

注意:扩展类加载器专门加载: C:\ProgramFiles\Java\jdk1.8.0\jre\lib\ext*.jar

如果"扩展类加载器"没有加载到,那么会通过应用类加载器"加载。

注意:应用类加载器专门加载: classpath中的类。

8.4、java中为了保证类加载的安全,使用了双亲委派机制。

优先从启动类加载器中加载,这个称为“父",

“父"无法加载到,再从扩展类加载器中加载,这个称为"母”。

双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。


9.验证反射

Student类

//反射属性Field
public class Student {
    public int no;
    private String name;
    protected int age;
    boolean sex;
    public static final double PI = 3.1415926;
}

反射Field

 //获取整个类
Class aClass = Class.forName("com.se.reflect.Student");
//获取完整类名
String name = aClass.getName();
System.out.println("完整类名:" + name);
//获取简类名
String simpleName = aClass.getSimpleName();
System.out.println("简类名:" + simpleName);
//获取类中的Field(只有public修饰的才可以获取到)
Field[] fields = aClass.getFields();
System.out.println("数组长度:" + fields.length);//测试数组的长度 1
//取出这个Field
Field f = fields[0];
//取出Field的名字
String fieldName = f.getName();
System.out.println(fieldName);
System.out.println("=======================================================");
//获取所有的Field
Field[] fields1 = aClass.getDeclaredFields();
for (Field field : fields1) {
    //获取属性的修饰符列表
    int a = field.getModifiers();
    //返回的修饰符是一个数字,每个数字是修饰符的代号!!!
    //将这个“代号”转换成字符串
    String modifierName = Modifier.toString(a);
    System.out.println(modifierName);
    //获取属性的类型
    Class fieldType = field.getType();
    System.out.println(fieldType);
    String fName = field.getName();
    System.out.println(fName);
    System.out.println("===========================");
}

结果截图
在这里插入图片描述
通过反射机制反编译一个类的属性Field

//创建这个是为了拼接字符串
StringBuilder stringBuilder = new StringBuilder();
Class studentClass = Class.forName("com.se.reflect.Student");
stringBuilder.append(Modifier.toString(studentClass.getModifiers())+" class " + studentClass.getSimpleName() + "{\n");
Field[] fields = studentClass.getDeclaredFields();
for (Field field: fields) {
    stringBuilder.append("\t");
    stringBuilder.append(Modifier.toString(studentClass.getModifiers()));
    stringBuilder.append(" ");
    stringBuilder.append(field.getType().getSimpleName());
    stringBuilder.append(" ");
    stringBuilder.append(field.getName());
    stringBuilder.append(";\n");
}
stringBuilder.append("}");
System.out.println(stringBuilder);

结果截图
在这里插入图片描述

怎么通过反射机制访问一个java对象的属性?

  • 给属性赋值set

  • 获取属性的值get

        //给对象的属性赋值
        Student student = new Student();
        student.no = 1111;
        //通过反射机制,获取对象
        Class studentClass = Class.forName("com.se.reflect.Student");
        Object obj = studentClass.newInstance();
        //获取no属性
        Field noField = studentClass.getDeclaredField("no");
        //给Object对象(Student)no赋值
        noField.set(obj,2222);
        System.out.println("noField:"+noField);
        //获取no属性的值
        System.out.println("获取no属性的值:"+noField.get(obj));
        //获取name属性
        Field nameField = studentClass.getDeclaredField("name");
        System.out.println("nameField:"+nameField);
        //打破封装,name属性为private修饰
        nameField.setAccessible(true);
        nameField.set(obj,"Mark");
        System.out.println("获取name属性的值:"+nameField.get(obj));

注意:name属性是private修饰的,需要打破封装setAccessible(true);

结果截图
在这里插入图片描述

可变长度参数

int… args这就是可变长度参数

语法是:类型…(注意:一定是3个点。)

1、可变长度参数要求的参数个数是:0~N个。

2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。

3、可变长度参数可以当做一个数组来看待


反射Method

UserService类

public class UserService {
    public boolean login(String name, String password) {
        if ("admin".equals(name) && "123".equals(password)) {
            return true;
        }
        return false;
    }

    public void loginOut() {
        System.out.println("系统已安全退出");
    }
}
//获取类
        Class aClass = Class.forName("com.se.reflect.UserService");
        //获取所有的Method(包括私有的)
        Method[] methods = aClass.getDeclaredMethods();
        //遍历Method
        for (Method method : methods) {
            //获取修饰符列表
            System.out.println("修饰符:"+Modifier.toString(method.getModifiers()));
            //获取方法的返回值类型
            String returnType = method.getReturnType().getSimpleName();
            System.out.println("获取方法返回值类型:"+returnType);
            //获取方法名
            System.out.println("获取方法名:"+method.getName());
            //获取方法的参数列表
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType :
                    parameterTypes) {
                System.out.println("参数列表:"+parameterType.getSimpleName());
            }
            System.out.println("==================================");
        }

结果截图
在这里插入图片描述


重点:必须掌握,通过反射机制怎么调用一个对象的方法?

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

        Class aClass = Class.forName("com.se.reflect.UserService");
        //创建对象
        Object obj = aClass.newInstance();
        //获取Method
        Method loginMethod = aClass.getDeclaredMethod("login", String.class, String.class);
        //四要素:对象,方法名,实参列表,返回值
        //反射机制最重要的一个方法
       /* loginMethod是方法
       * obj是对象
       * "admin","123"是实参列表
       * retValue是返回值*/
       Object retValue =  loginMethod.invoke(obj,"admin","123");
        System.out.println("调用方法返回值:"+retValue);

结果截图
在这里插入图片描述

通过反射机制反编译Method

 Class aClass = Class.forName("com.se.reflect.UserService");
        StringBuilder s = new StringBuilder();
        s.append(Modifier.toString(aClass.getModifiers()) + " class " + aClass.getSimpleName() + "{");
        s.append("\n");
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            s.append("\t");
            s.append(Modifier.toString(aClass.getModifiers()));
            s.append(" ");
            s.append(method.getReturnType().getSimpleName());
            s.append(" ");
            s.append(method.getName());
            s.append("(");
            //参数列表
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            if (parameterTypes.length > 0) {
                s.deleteCharAt(s.length() - 1);
            }
            s.append("){\n\t}\n");
        }
        s.append("}");
        System.out.println(s);

结果截图
在这里插入图片描述


反编译Constructor

Vip类

public class Vip {
    int no;
    String name;
    String birth;
    boolean sex;

    public Vip() {
    }

    public Vip(int no, String name, String birth, boolean sex) {
        this.no = no;
        this.name = name;
        this.birth = birth;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Vip{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", birth='" + birth + '\'' +
                ", sex=" + sex +
                '}';
    }
}
        StringBuilder s = new StringBuilder();
        Class aClass = Class.forName("com.se.reflect.Vip");
        s.append(Modifier.toString(aClass.getModifiers()));
        s.append(" class ");
        s.append(aClass.getSimpleName());
        s.append("{\n");
        //拼接构造方法
        Constructor[] constructors = aClass.getDeclaredConstructors();
        for (Constructor c : constructors) {
            s.append("\t");
            s.append(Modifier.toString(aClass.getModifiers()));
            s.append(" ");
            s.append(aClass.getSimpleName());
            s.append("(");
//public Vip(int no, String name, String birth, boolean sex) {
            //拼接参数
            Class[] parameterTypes = c.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                s.append(parameterType.getSimpleName());
                s.append(" ,");

            }
            if (parameterTypes.length > 0) {
                s.deleteCharAt(s.length() - 1);
            }
            s.append("){\n\t}\n");

        }
        s.append("}");
        System.out.println(s);

结果截图
在这里插入图片描述


通过反射机制调用构造方法实例化对象

        //使用反射机制创建对象
        Class aClass = Class.forName("com.se.reflect.Vip");
        //调用无参构造方法
        System.out.println("调用无参构造方法");
        Object obj = aClass.newInstance();
        System.out.println(obj);
        System.out.println("=======================================");
        System.out.println("先获取有参数构造方法,再调用构造方法new对象");
        //先获取有参数构造方法
        Constructor con = aClass.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);
        //调用构造方法new对象
        Object obj2 = con.newInstance(12, "zhangsan", "20-3-7", true);
        System.out.println(obj2);

结果截图
在这里插入图片描述


给你一个类,怎么获取这个类的父类,已经实现了哪些接口?

//获取父类
Class superClass = aClass.getSuperclass();
System.out.println(superClass.getName());
//获取类所实现的所有接口(一个类可以实现多个接口)
Class[] interfaces = aClass.getInterfaces();
for(Class c :interfaces){
    System.out.println(c.getName());
}

结果截图
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值