Java学习【反射】

反射

反射机制有什么用?

通过反射机制可以操作字节码文件

相关的类在哪些包中?

java.lang.reflect.*

反射机制相关的重要的类?

java.lang.Class:代表整个字节码,代表一个类型,代表整个类
java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)

Class类的理解

1、类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。

加载到内存中的类,我们就称为运行时类,此运行时类就作为Class的一个实例
2、加载到内存中的运行时类,会缓存一定时间。在此时间之内,我们可以通过不同的方式来获取此运行时类,但此运行时类不会加载第二次(获取的内存地址是一致的)

类加载过程的理解

  1. 加载
    class文件字节码内容加载到虚拟机内存中,并在方法区中生成一个·java.lang.Class对象,作为方法区中类数据的方法入口(即引用地址)
  2. 链接
    2.1 验证:作为链接阶段的第一步,验证的主要作用就是确保被加载的类的正确性 ,说白了也就是我们加载好的.class文件不能对我们的虚拟机有危害,所以先检测验证一下

    2.2 准备:准备阶段主要为类变量分配内存并设置初始值,这里的初始值是指数据类型的默认值。这些变量所使用的内存都将在方法区中进行分配。
public class Vip{
	public static int num = 10;
}

以上代码在准备阶段过后num值是0,值为10时是经过初始化阶段

数据类型的默认值有:
在这里插入图片描述
2.3 解析阶段:将常量池中的符号引用替换为直接引用的过程

  1. 初始化
    执行类构造器< clinit >()方法的过程。类加载机制的最后一步。类构造器是构造类信息的,不是构造该类对象的构造器。
    当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
    在这里插入图片描述

获取Class的四种方法

第一种方式
Class.forName()	将字节码文件加载进内存,返回Class对象
    1.静态方法
    2.方法的参数是一个字符串
    3.字符串需要的是一个完整类名
    4.完整的类名必须带有包名,java.lang包也不能省略

 try {
            Class c1 = Class.forName("java.lang.String");     //c1代表String类型
            Class c2 = Class.forName("java.util.Date"); //c1代表Data类型
            Class c3 = Class.forName("java.lang.Integer");  //c1代表Integer类型
            Class c4 = Class.forName("java.lang.System");   //c1代表System类型
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
String s1 = "abc";
Class x = s1.getClass();        //第二种方式:getclass()获取任何一个运行时类的类对象
Class y = String.class;         //第三种方法:调用运行时类的属性。java语言中任何一种类型,包含数据类型,都有.class属性
ClassLoader ClassLoad = Reflect01.class.getClassLoader();   //第四种方法:类加载器ClassLoader
Class c = ClassLoad.loadClass("java.lang.String");
System.out.println(c);

通过读属性文件实例化对象

package Reflect;

import java.io.FileReader;
import java.util.Properties;

/*
反射机制灵活性的体现。
代码不需要改动,可以修改配置文件,即可创建出不同的实例对象
 */
public class Reflect03 {
    public static void main(String[] args) throws Exception {
        //通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("Reflect\\classinfo");
        //创建属性类对象Map
        Properties pro = new Properties();  //key value都是String
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

        //通过key:className获取value
        String className = pro.getProperty("className");
        System.out.println(className);

        //通过反射机制实例化对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

classinfo.properties文件

className=java.util.Date

Class.Name("")

这个方法的执行会导致类加载,类加载时,静态代码块执行。因此Class.Name("")能只让静态代码块执行

package Reflect;
/*
    Class.forName("完整类名");
    这个方法的执行会导致类加载,类加载时,静态代码块执行
 */
public class Reflect04 {
    public static void main(String[] args) {
        try {
            Class.forName("Reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class MyClass{
    static {
        System.out.println("静态代码块执行");
    }

    public static void main(String[] args) {
        System.out.println("Main方法执行");
    }
}

获取绝对路径

Thread.currentThread().getContextClassLoader().getResource("相对路径的文件名").getPath();

package Reflect;

public class AboutPath {
    /*
        前提:这个文件必须在类路径下(src下,src是类的根路径,是从src目录作为起点开始)

        Thread.currentThread()  当前线程对象
        getContextClassLoader() 线程对象的方法,可以获取到当前线程的类加载器对象
        getResource()   【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
     */
    public static void main(String[] args) throws Exception{
        String path = Thread.currentThread().getContextClassLoader().getResource("Testinfo.properties").getPath();  //Testinfo.properties放在src
        System.out.println(path);//得到绝对路径: /E:/IDEA2020/app/src/out/production/app/Testinfo.properties

        String path2 = Thread.currentThread().getContextClassLoader().getResource("Reflect/classinfo.properties").getPath();//classinfo.properties放在src/Reflect下
        System.out.println(path2);
    }
}

以流的形式直接返回

以后要通过上面的方法来获取文件的绝对路径,而不是自己写一个路径上去了,这样才有更高的耦合性、适用性

package Reflect;
import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;

public class Reflect05 {
    public static void main(String[] args) throws Exception {

        /*方法一:使用getResource().getPath()先获取文件名,再通过FileReader创建流
        
            //获取绝对路径
            String path = Thread.currentThread().getContextClassLoader().getResource("Reflect/classinfo.properties").getPath();
            System.out.println(path);
            //创建读取流
            FileReader reader = new FileReader(path);
        */
        
        /*
        方法二:使用getResourceAsStream直接以流的形式返回
         */
        InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("Reflect/classinfo.properties");

        //创建属性类对象Map
        Properties pro = new Properties();
        //以简单的面向行的格式从输入字符流中读取属性列表(键和元素对)
        pro.load(reader);
        reader.close();

        //在属性列表中搜索className键的属性
        String className = pro.getProperty("className");
        System.out.println(className);
    }
}

ResourceBundle资源绑定器

java.util包下提供一个资源绑定器。便于获取属性配置文件中的内容。属性配置文件xx.properties必须放到类路径下
一:资源绑定器,只能绑定xx.properties文件,并且这个文件必须在类路径下
二:所获取文件的后缀必须是properties。但扩展名不需要写

package Reflect;

import java.util.ResourceBundle;

public class ResourceBundleTest {
    public static void main(String[] args) {
        ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
        String className = bundle.getString("className");
        System.out.println(className);
    }
}

类加载器(ClassLoader)

加载指的是将所需类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

JDK自带了三个类加载器

1. 启动类加载器	启动类加载器专门加载:/lib/rt.jar	rt.jar中都是JDK最核心的类库
2. 扩展类加载器	扩展类加载器专门加载:/lib/ext/*.jar	
3. 应用类加载器	应用类加载器专门加载:专门加载classpath中的类
package Reflect;

public class ClassLoaderTest {
    public static void main(String[] args) {
        //对于自定义类,使用应用类加载器(AppClassLoader)进行加载   
        ClassLoader classload1 = ClassLoaderTest.class.getClassLoader();	//getClassLoader()返回类的类加载器。
        System.out.println(classload1);         //sun.misc.Launcher$AppClassLoader@18b4aac2

        //调用应用类加载器的getParent():获取扩展类加载器(ExtClassLoader)
        ClassLoader classloader2 = classload1.getParent();
        System.out.println(classloader2);       //sun.misc.Launcher$ExtClassLoader@7d4991ad

        //调用扩展类加载器的getParent():无法获取启动类加载器
        //引导类加载器主要负责加载java的核心库,无法加载自定义类
        ClassLoader classloader3 = classloader2.getParent();
        System.out.println(classloader3);       //null
    }
}

双亲委派机制

优先从启动类加载器中加载,这个称为"父"。“父"无法加载到,再从扩展类加载器中加载,这个称为"母”。双亲委派。

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

获取Field(属性)

Field[] getFields() :获取所有public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

Field getField(String name) 获取指定名称的 public修饰的成员变量

Field getDeclaredField(String name) 获取指定的成员变量
  1. 获取Field前是要先获取整个类的:
Class StudentClass = Class.forName("Reflect.Student");
  1. 获取Student类的类名
String ClassName = StudentClass.getName();
System.out.println("完整类名"+ClassName);  //Reflect.Student

String ClassSimpleName = StudentClass.getSimpleName();
System.out.println("简易类名"+ClassSimpleName);    //Student
  1. 输出所有public修饰的字段(Field),返回一个数组对象
Field[] fields = StudentClass.getFields();	表示返回一个数组

fields[0].getName()	表示输出数组中第一个对象的名称
  1. 输出所有的字段。这包括public,protected,默认,private字段,但不包括继承的字段。返回一个数组对象
Field[] fs = StudentClass.getDeclaredFields();
  1. 输出字段的属性类型:
 Class fieldType = i.getType();	返回类对象,该对象标识字段对象表示的字段的声明类型。
 System.out.println("属性的类型:"+fieldType.getSimpleName());	
  1. 输出字段的修饰符:
public int getModifiers()
以整数编码,返回对应修饰符的数字标识。 其中有:publicprotectedprivatefinalstaticabstractinterface 
Modifier.toString()	返回数字标识所对应的字符串。

获取Field案例

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

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Reflect06 {
    public static void main(String[] args) throws Exception{
        //获取整个类
        Class StudentClass = Class.forName("Reflect.Student");


        String ClassName = StudentClass.getName();
        System.out.println("完整类名"+ClassName);  //Reflect.Student

        String ClassSimpleName = StudentClass.getSimpleName();
        System.out.println("简易类名"+ClassSimpleName);    //Student

        //获取类中所有public修饰的Field  getFields()返回一个数组
        Field[] fields = StudentClass.getFields();
        System.out.println(fields.length);  //2

        //获取Filed数组中第一个成员变量的名字  getName()
        Field f = fields[0];
        String fileName = f.getName();
        System.out.println(fileName);   //name

        System.out.println("==============");
        Field[] fs = StudentClass.getDeclaredFields();
        for (Field i: fs ) {
            int num  = i.getModifiers();//getModifiers()返回Filed修饰符的代号,以整数编码
            System.out.println(num);
            String modifierString = Modifier.toString(num);   //将数字代号转成字符串 静态方法:Modifier.toString()
            System.out.println("修饰符:"+modifierString);

            Class fieldType = i.getType();
            System.out.println("属性的类型:"+fieldType.getSimpleName());    //获取属性的类型 getSimpleName()
        }
    }
}

反编译获取Field

package Reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/*
    通过反射机制,反编译一个类的属性Field
 */
public class Reflect07 {
    public static void main(String[] args) throws Exception{
        //用于拼接字符串
        StringBuilder s = new StringBuilder();
        
        Class StudentClass = Class.forName("Reflect.Student");
        //获取Student类的所有属性
        Field[] fs = StudentClass.getDeclaredFields();

        s.append(Modifier.toString(StudentClass.getModifiers()) + " class " + StudentClass.getSimpleName()+"{\n");
        for ( Field f:fs ) {
            s.append("\t");
            s.append(Modifier.toString(f.getModifiers()));      //属性修饰符
            s.append(" ");
            s.append(f.getType().getSimpleName());              //属性type
            s.append(" ");
            s.append(f.getName());                      //属性的名称
            s.append(";\n");
        }
        s.append("\n}");
        System.out.println(s);
    }
}

反射机制访问对象属性

package Reflect;

import java.lang.reflect.Field;

public class Reflect08 {
    public static void main(String[] args) throws Exception{
        //反射机制创建obj对象(Student对象)
        Class Stu = Class.forName("Reflect.Student");
        Object obj = Stu.newInstance();
        
        //获取name属性(public类型),返回Field类型
        Field nameField = Stu.getField("name");
        //set给name属性赋值
        nameField.set(obj,"DMIND");
        
        //读取属性的值
        String name =nameField.get(obj).toString();
        System.out.println(name);
    }
}

可变长度参数

比如:int... args

  1. 可变长度参数要求的参数个数是:0~N个
  2. 可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能出现一个
  3. 可变长度参数可以当作一个数组看待
package Reflect;

public class Reflect09 {
    public static void main(String[] args) {
        m(1,"a","b","c");
        m(2,"你","ok?","?");
    }
    public static void m(int a, String... args){
        for (String s : args) {`            //获取方法名
            System.out.println(method.getName());`
            System.out.println(s);
        }
    }
}

获取Method(方法)

Method getMethod(String name,<?>... parameterTypes) //返回该类所声明的public方法

Method getDeclaredMethod(String name,<?>... parameterTypes) //返回该类所声明的所有方法
															  //第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型

Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法

Method[] getDeclaredMethods() // 获取该类中的所有方法

获取所有类型的方法,返回一个Method数组

Class Stu = Class.forName("Reflect.UserService");
Method[] methods = Stu.getDeclaredMethods();

获取修饰符列表

System.out.println(Modifier.toString(method.getModifiers()));	//public static

获取方法的返回值类型

System.out.println(method.getReturnType().getSimpleName());		//boolean

获取方法名

System.out.println(method.getName());		//login

获取方法的参数修饰符

Class[] parameterType  = method.getParameterTypes();
for (Class parm:parameterType ) {
    System.out.println(parm.getSimpleName());
}

案例:

package Reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Reflect10 {
    public static void main(String[] args) throws Exception{
        Class Stu = Class.forName("Reflect.UserService");
        Method[] methods = Stu.getDeclaredMethods();
        for (Method method:methods ) {
            //获取修饰符列表
            System.out.println(Modifier.toString(method.getModifiers()));
            //获取方法的返回值类型
            System.out.println(method.getReturnType().getSimpleName());
            //获取方法名
            System.out.println(method.getName());
            //获取方法的参数修饰符
            Class[] parameterType  = method.getParameterTypes();
            for (Class parm:parameterType ) {
                System.out.println(parm.getSimpleName());
            }
        }
    }
}

反编译获取Method

package Reflect;

/*
反编译获取Method
 */

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Reflect11 {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        Class UserService = Class.forName("Reflect.UserService");
        s.append(Modifier.toString(UserService.getModifiers())+" class "+UserService.getSimpleName()+" {\n");

        Method[] methods = UserService.getMethods();
        for (Method m:methods ) {
            s.append("\t"+Modifier.toString(m.getModifiers()));
            s.append(" ");

            s.append(m.getReturnType().getSimpleName());
            s.append(" ");
            s.append(m.getName());
            s.append("(");
            Class[] parms = m.getParameterTypes();
            for (int i = 0; i < parms.length; i++) {
                s.append(parms[i].getSimpleName());
                if(i==parms.length-1){
                }else s.append(",");
            }

            s.append("){}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

反射机制调用方法

package Reflect;

import java.lang.reflect.Method;

public class Reflect12 {
    public static void main(String[] args) throws Exception {
        //创建对象
        Class UserService = Class.forName("Reflect.UserService");
        Object obj = UserService.newInstance();
        //获取login方法
        Method loginMethod = UserService.getDeclaredMethod("login", String.class, String.class);

        //使用invoke()调用login方法
        Object retValue = loginMethod.invoke(obj,"admin","123");
        System.out.println(retValue);
    }
}

获取Constructor(构造方法)

Constructor<?>[] getConstructors() :只返回public构造函数

Constructor<?>[] getDeclaredConstructors() :返回所有构造函数

Constructor<> getConstructor(<?>... parameterTypes) : 匹配和参数配型相符的public构造函数

Constructor<> getDeclaredConstructor(<?>... parameterTypes) : 匹配和参数配型相符的构造函数

反编译获取Constructor

package Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

public class Reflect13 {
    public static void main(String[] args) throws Exception{
        StringBuilder s  = new StringBuilder();
        Class vipConstruct = Class.forName("Reflect.Vip");
        //获取头
        s.append(Modifier.toString(vipConstruct.getModifiers())+" class "+vipConstruct.getSimpleName()+"{\n");
        Constructor[] constructors =  vipConstruct.getDeclaredConstructors();
        //获取构造方法
        for (Constructor constructor:constructors ) {
            s.append("\t");
            s.append(Modifier.toString(constructor.getModifiers()));
            s.append(" ");
            s.append(vipConstruct.getSimpleName());
            s.append("(");
            Class[] parameterTypes = constructor.getParameterTypes();
            
            //获取参数列表的类型
            for (int i = 0; i < parameterTypes.length; i++) {
                s.append(parameterTypes[i].getSimpleName());
                if (i < parameterTypes.length-1){
                    s.append(",");
                }
            }

            s.append("){}\n");
        }
        
        s.append("}");
        System.out.println(s);
    }
}

调用 无参/有参构造方法

package Reflect;

import java.lang.reflect.Constructor;

public class Reflect14 {
    public static void main(String[] args) throws Exception {
        
        Class vip = Class.forName("Reflect.Vip");
        //调用无参构造
        Object noarg = vip.newInstance();
        System.out.println(noarg);
        //调用有参构造
        //根据参数类型确定哪个有参构造方法 --> newInstance()创建对象
        Constructor con = vip.getDeclaredConstructor(int.class,String.class);
        Object obj = con.newInstance(10,"DMIND");
        System.out.println(obj);
    }
}

invoke调用方法

public Object invoke(Object obj, Object... args)

第一个参数是类的实例对象,第二个参数为相应函数中的参数

如果调用的这个方法是普通方法,第一个参数就是类对象

如果调用的这个方法是静态方法,第一个参数就是

package MyClassLoader;

import java.lang.reflect.Method;

public class invokeTest {
    public void Hello(){
        System.out.println("DMIND");
    }
    public static void main(String[] args) throws Exception{
        //获取类对象
        Class c = Class.forName("MyClassLoader.invokeTest");
        //获取实例对象
        Object obj = c.newInstance();
        //获取Hello方法
        Method method = c.getMethod("Hello");
        method.invoke(obj);		//输出DMIND
    }
}

setAccessible(true)访问私有属性和函数

在一般情况下,我们使用反射机制不能对类的私有private字段进行操作,绕过私有权限的访问。但一些特殊场景存在例外的时候,比如我们进行序列化操作的时候,需要去访问这些受限的私有字段,这时我们可以通过调用AccessibleObject上的setAccessible()方法来允许访问。

Java.lang.reflect.AccessibleObject类是FieldMethodConstructor类对象的基类,可以提供将反射对象标记为使用它抑制摸人Java访问控制检查的功能, 同时上述的反射类中的Field,Method和Constructor继承自AccessibleObject。 所以我们在这些类方法基础上调用setAccessible()方法,既可对这些私有字段进行操作。

命令执行

示例一:
调用java.lang.Runtime下的exec(String)来命令执行

package MyClassLoader;

import java.lang.reflect.Constructor;

public class RuntimeTest {
    public static void main(String[] args) throws Exception {
        //获取类对象
        Class runtime = Class.forName("java.lang.Runtime");
        //获取Runtime的构造方法
        Constructor constructor = runtime.getDeclaredConstructor();
        
        constructor.setAccessible(true);

        runtime.getMethod("exec", String.class).invoke(constructor.newInstance(),"calc");
    }
}

示例二:

package MyClassLoader;

import java.lang.reflect.Method;

public class RuntimeTest {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("java.lang.Runtime");
        Method method = c1.getMethod("exec",String.class);
        Method RuntimeMethod = c1.getMethod("getRuntime");
        Object obj = RuntimeMethod.invoke(c1);
        method.invoke(obj,"calc");
    }
}

将示例二代码压缩一下:
Class c1 = Class.forName("java.lang.Runtime"); c1.getMethod("exec", String.class).invoke(c1.getMethod("getRuntime").invoke(c1),"/System/Applications/Calculator.app/Contents/MacOS/Calculator");

获取父类、所有实现的接口

package Reflect;

public class Reflect15 {
    public static void main(String[] args) throws Exception {
        Class StringClass = Class.forName("java.lang.String");
        Class superclass = StringClass.getSuperclass();	//获取父类
        System.out.println(superclass.getName());      //父类的名称
       
        //获取String类所实现的所有接口
        Class[] interfaces = StringClass.getInterfaces();
        for (Class in:interfaces  ) {
            System.out.println(in.getName());
        }
    }
}

@Override注解

标识性注解,给编译器做参考的
编译器看到方法上有@Override注解,会自动检查改方法是否重写了父类的方法。如果没有重写,报错
这个注解知识再编译阶段起作用,和运行期无关

    @Override
    public String toString() {
        return super.toString();
    }