java中的反射机制

1、反射机制
---1.1概述
---1.2获取Class的三种方式
---1.3通过反射实例化对象
---1.4通过读属性文件实例化对象
---1.5Class.forName()会导致类加载
2、关于路径
---2.1类路径下的绝对路径(通用路径)
---2.2以流的形式返回路径
---2.3资源绑定器
3、类加载器
4、Field
---4.1获取Field
---4.2反编译Field
---4.3通过反射机制访问对象属性
5、Method
---5.1可变长度参数
---5.2反射Method
---5.3反编译Method
6、反射机制调用方法
7、Constructor
---7.1反编译类的Constructor(构造方法)
---7.2反射机制调用构造方法
8、获取父类和父接口

反射机制

概述

1、反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件,有点类似于黑客。(可以读和修改字节码文件)通过反射机制可以操作代码片段。(class文件)
2、反射机制的相关类在哪个包下?
java.lang.reflect.*;
3、反射机制相关的类有哪些?
java.lang.Class<T>:整个字节码文件,一个类型(一个类)
java.lang.reflect.Method:代表字节码中的方法字节码(类中的方法)
java.lang.reflect.Constructor<T>:代表字节码中的构造方法字节码(类中的构造方法)
java.lang.reflect.Field:代表字节码中的属性字节码(成员变量)

获取Class的三种方式
  • 第一种:Class c = Class.forName("完整英名带包含");
    这是静态方法。
    方法的参数是一个字符串。
    字符串需要的是一个完整类名。
    完整类名必须带有包名。 java.lang包也不能省略。
  • 第二种:Class c = 对象.getClass();
    java中任何一个对象都有getClass()方法
  • 第三种:Class c = 任何类型.class;
    java语言中任何一种类型,包括基本数据类型,它都有.class属性。
  • 代码示例
import java.util.Date;

public class Demo{
    public static void main(String[] args){
        /*第一种*/
        Class c1 = null;
        Class c2 = null;
        Class c3 = null;
        try {
            //c1代表String.class文件或String类型
            c1 = Class.forName("java.lang.String");
            //c2代表Integer类型
            c2 = Class.forName("java.lang.Integer");
            //c3代表Date类型
            c3 = Class.forName("java.util.Date");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        /*第二种*/
        //java中任何一个对象都有getClass()方法
        String s = "abc";
        //x代表String.class字节码文件,x代表String类型。
        Class x = s.getClass();
        //判断内存地址
        System.out.println(c1 == x);
        /*输出:true,
        字节码文件装载到JVM中的时候只装载一份*/

        Date date = new Date();
        Class y = date.getClass();
        System.out.println(y == c3);
        //输出:true

        /*第三种*/
        /*java语言中任何一种类型,
        包括基本数据类型,它都有.class属性。*/
        Class q = String.class;
        Class w = int.class;
        Class e = Date.class;
        System.out.println(x == q);
        //输出:true
        System.out.println(y == e);
        //输出:true
    }
}
通过反射实例化对象
  • 概述
    1、通过反射机制,获取Class,通过Class的newInstance()方法来实例化对象。(更加灵活)
    2、注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
  • 代码示例
public class DemoTest{
    public DemoTest() {
        System.out.println("无参构造");
    }

    /**只定义有参构造(没有无参构造)*/
    public DemoTest(String s) {
    }
    /*只定义有参构造后出现异常:
    java.lang.InstantiationException: DemoTest*/
}
public class Demo{
    public static void main(String[] args){
        try {
            Class c = Class.forName("DemoTest");
            /*这个方法会调用DemoTest这个类的无参数构造方法,
            完成对象的创建。*/
            Object obj = c.newInstance();
            System.out.println(obj);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

输出:
在这里插入图片描述

通过读属性文件实例化对象
  • 通过反射实例化对象的优点
    1、java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。
    2、通过修改配置文件,配置文件修改之后,可以创建出不同的实例对象,非常灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)
  • 代码示例
    配置文件:
    在这里插入图片描述
public class DemoTest{
    public DemoTest() {
        System.out.println("无参构造");
    }

    /**只定义有参构造(没有无参构造)*/
    public DemoTest(String s) {
    }
    /*只定义有参构造后出现异常:
    java.lang.InstantiationException: DemoTest*/
}
import java.io.FileReader;
import java.util.Properties;

/**验证反射机制的灵活性*/
public class Demo{
    public static void main(String[] args) throws Exception{
        //通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("classinfo.properties");
        //创建属性对象Map
        Properties pro = new Properties();
        //加载
        pro.load(reader);
        //关闭流
        reader.close();
        //通过value获取key
        String className = pro.getProperty("className");
        /*System.out.println(className);
        //输出:DemoTest*/

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

        //例2
        String date = pro.getProperty("date");
        Class cc = Class.forName(date);
        Object obj1 = cc.newInstance();
        System.out.println(obj1);
    }
}

输出:
在这里插入图片描述

Class.forName()会导致类加载
  • 概述
    1、静态代码块在类加载时只执行一次
    2、只让静态代码块执行可以使用forName()方法。
  • 代码示例
public class Demo{
    public static void main(String[] args){
        try {
            Class.forName("Myclass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

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

输出:
在这里插入图片描述

关于路径

类路径下的绝对路径(通用路径)
  • Class.forName("Myclass");像这种路径的缺点是:
    移植性差,在IDEA中默认的当前路径是project的根。如果不是IDEA,换到了其它位置,可能当前路径就不是project的根了,路径就会无效。
  • 一种比较通用的路径
    即使代码换位置了,这样编写仍然是通用的。(适用于任何操作系统)
    注意:使用以下通用方式的前提是:这个文件必须在类路径下。(放在src下的都是类路径下,src是类的根路径。)
  • 代码示例
public class Demo{
    public static void main(String[] args){

        /*
        Thread.currentThread():当前线程对象。
        getContextClassLoader():是线程对象的方法,
        可以获取到当前线程的类加载器对象。
        getResource():(获取资源)这是类加载器对象的方法,
        当前线程的类加载器默认从类的根路径下加载资源。
        */

        String path = Thread.currentThread()
                .getContextClassLoader()
                .getResource("classinfo.properties")
                .getPath();
        System.out.println(path);
    }
}

输出:
在这里插入图片描述
这里返回的是class文件所在的目录。所以不能获取java文件的路径,只能把.java改为.class:
getResource("Demo.class")
输出:
在这里插入图片描述

以流的形式返回路径
  • 代码示例
    在这里插入图片描述
import java.io.InputStream;
import java.util.Properties;

public class Demo{
    public static void main(String[] args) throws Exception{
        InputStream path = Thread.currentThread()
                .getContextClassLoader()
                .getResourceAsStream("classinfo.properties");
        Properties pro = new Properties();
        pro.load(path);
        path.close();
        String className = pro.getProperty("className");
        System.out.println(className);
    }
}

输出:
在这里插入图片描述

资源绑定器
  • 概述
    1、使用资源绑定器便于获取属性配置文件中的内容。
    2、只能绑定xxx.properties文件,但是路径后面的扩展名不能写。(.properties不能写)
    3、使用的时候,属性配置文件必须放到类路径下。
  • 代码示例
    在这里插入图片描述
import java.util.ResourceBundle;

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

输出:
在这里插入图片描述

类加载器

关于类加载器的更多理解:
链接一
链接二

  • 概述
    1、JDK中自带类加载器,什么是类加载器?
    专门负责加载类的命令/工具(ClassLoader)
    2、JDK中自带了3个类加载器:
    启动类加载器
    扩展类加载器
    应用类加载器
    3、假设有这样一段代码:
    String s = "abc"
    代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载。
    4、那么是怎么进行加载的呢?
    首先通过“启动类加载器"加载。(启动类加载器专门加载:rt.jar),rt.jar中都是JDK最核心类库。
    5、如果通过“启动类加载器"加载不到的时候,会通过"扩展类加载器"加载。(扩展类加载器专门加载:ext*.jar)
    6、如果”扩展类加载器"没有加载到,那么会通过“应用类加载器”加载(应用类加载器专门加载:classpath(环境变量里的)中的类。)
  • 双亲委派机制
    1、启动类加载器(父加载器)
    扩展类加载器(母加载器)
    2、java中为了保证类加载的安全,使用了双亲委派机制。
    优先从启动类加载器中加载,这个称为“父”。"父”无法加载到,再从扩展类加载器中加载,这个称为“母"。如果都加载不到才会考虑从应用类加载器中加载,直到加载到为止。

Field

获取Field
  • 概述
    Field翻译为字段,其实就是属性/成员。
  • 与Field有关的方法
    1、Field[] getFields()(java.lang.Class
    返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段类对象
    2、Field[] getDeclaredFields()(java.lang.Class
    返回的数组Field对象反映此表示的类或接口声明的所有字段类对象
    3、String getName()(java.lang.reflect.Field)
    返回由此Field对象表示的字段的名称。
    4、String getSimpleName()(java.lang.Class
    返回源代码中给出的基础类的简单名称。
    5、Class<?> getType()(java.lang.reflect.Field)
    返回一个类对象标识了此表示的字段的声明类型Field对象。
    6、int getModifiers()(java.lang.reflect.Field)
    返回由该Field对象表示的字段的Java语言修饰符,作为整数。
    7、static String toString(int mod)(java.lang.reflect.Modifier)
    返回描述指定修饰符中的访问修饰符标志的字符串。
  • 代码示例
public class Demo {
    public int no;
    private String name;
    protected int age;
    boolean sex;
    public static final double PI = 3.1415926;
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**反射Demo类中的所有Field*/
public class DemoTest {
    public static void main(String[] args) throws Exception {
        //获取整个类
        Class demoClass = Class.forName("Demo");
        //获取所有的public修饰的Field
        Field[] fs = demoClass.getFields();
        System.out.println("fields数组中元素个数为:"
                + fs.length + "\n"
                + "元素是:"+fs[0]+"\n"
                + "获取元素名字为:"+fs[0].getName());
        System.out.println("=========");
        //获取所有的Field
        Field[] fields = demoClass.getDeclaredFields();
        //遍历
        for (Field field:fields) {
            //获取属性的修饰符列表
            int gm = field.getModifiers();
            String modifier = Modifier.toString(gm);
            System.out.println(modifier);
            //获取属性的类型
            Class fieldType = field.getType();
            System.out.println(fieldType.getSimpleName());
            //获取属性的名字
            System.out.println(field.getName());
        }
    }
}

输出:
在这里插入图片描述

反编译Field
  • 代码示例
public class Demo {
    public int no;
    private String name;
    protected int age;
    boolean sex;
    public static final double PI = 3.1415926;
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**反编译Field*/
public class DemoTest {
    public static void main(String[] args) throws Exception {
        StringBuffer s = new StringBuffer();
        //Demo可以换为其它类
        Class demoClass = Class.forName("Demo");
        s.append(Modifier.toString(demoClass.getModifiers())
                + " class " + demoClass.getSimpleName()
                + "{\n");
        Field[] fields = demoClass.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);
    }
}

输出:
在这里插入图片描述

通过反射机制访问对象属性
  • 访问对象属性的一些方法
    1、void set(Object obj, Object value)(java.lang.reflect.Field)
    将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
    2、Object get(Object obj)(java.lang.reflect.Field)
    返回该所表示的字段的值Field,指定的对象上。
    3、void setAccessible(boolean flag)(java.lang.reflect.AccessibleObject)
    将此对象的accessible标志设置为指示的布尔值。
  • 代码示例
public class Demo {
    public int no;
    private String name;
    protected int age;
    boolean sex;
    public static final double PI = 3.1415926;
}
import java.lang.reflect.Field;

/**通过反射机制访问对象属性
 *给属性赋值set
 *获取属性的值get*/
public class DemoTest {
    public static void main(String[] args) throws Exception {
        //Demo可以换为其它类
        Class demoClass = Class.forName("Demo");
        Object obj = demoClass.newInstance();
        //获取no属性(根据属性的名称来获取Field)
        Field noField = demoClass.getDeclaredField("no");
        //给obj对象(Demo对象)的no属性赋值
        noField.set(obj,11);
        //获取对象属性的值
        System.out.println(noField.get(obj));

        /*给私有属性赋值 */
        Field nameField = demoClass.getDeclaredField("name");
        //打破封装
        nameField.setAccessible(true);
        //给name属性赋值
        nameField.set(obj,"zhangsan");
        System.out.println(nameField.get(obj));
    }
}

输出:
在这里插入图片描述

Method

可变长度参数
  • 概述
    1、int... args这就是可变长度参数
    语法是:类型...(注意:一定是3个点。)
    2、可变长度参数要求的参数个数是:0~N个。
    3、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
  • 代码示例
public class Demo {
    public static void main(String[] args) {
        m();
        m(10,20);
        m2("zhangsan", "lisi");
        m3("wangwu", "zhaoliu");
    }
    public static void m(int... args){
        System.out.println("m方法执行");
    }
    public static void m2(String... args){
        System.out.println("m2方法执行");
    }
    public static void m3(String... args){
        for (int i=0; i < args.length; i++){
            System.out.println(args[i]);
        }
    }
}

输出:
在这里插入图片描述

反射Method
  • 关于反射Method的一些方法
    1、Method[] getDeclaredMethods()(java.lang.Class
    返回包含一个数组方法对象反射的类或接口的所有声明的方法,通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
    2、int getModifiers()(java.lang.reflect.Method)
    返回由该对象表示的可执行文件的Java语言modifiers 。
    3、Class<?> getReturnType()(java.lang.reflect.Method)
    返回一个 类对象,它表示由该表示的方法的正式返回类型 方法对象。
    4、Class<?>[] getParameterTypes()(java.lang.reflect.Method)
    返回一个 类对象的数组, 类以声明顺序表示由该对象表示的可执行文件的形式参数类型。
  • 代码示例
public class Demo {
    public boolean login(String name,String password){
        if ("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    public void logout() {
        System.out.println("已退出!");
    }
}
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**反射Method*/
public class DemoTest {
    public static void main(String[] args) throws Exception{
        //Demo可以换为其它类
        Class demoClass = Class.forName("Demo");
        //获取所有的Method
        Method[] methods = demoClass.getDeclaredMethods();
        for (Method method:methods) {
            //获取修饰符列表
            System.out.println(Modifier.toString(method.getModifiers()));
            //获取方法的返回值类型
            System.out.println(method.getReturnType().getSimpleName());
            //获取方法名
            System.out.println(method.getName());
            //方法的修饰符列表(一个方法的参数可能有多个)
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType:parameterTypes) {
                System.out.println(parameterType.getSimpleName());
            }
        }
    }
}

输出:
在这里插入图片描述

反编译Method
  • 相关方法
    1、char charAt(int index)
    返回char在指定索引在这个序列值。(根据索引返回对应的字符)
    2、static String valueOf(char c)
    返回char参数的字符串(将char类型转换为String类型)。
    3、StringBuffer deleteCharAt(int index)
    删除char在这个序列中的指定位置。(删除指定位置的字符)
  • 代码示例
public class Demo {
    public boolean login(String name,String password){
        if ("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    public void logout() {
        System.out.println("已退出!");
    }
}
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**反编译Method*/
public class DemoTest {
    public static void main(String[] args) throws Exception{
        StringBuffer s = new StringBuffer();
        //Demo可以换为其它类
        Class demoClass = Class.forName("Demo");
        s.append(Modifier.toString(demoClass.getModifiers())
                + " class "
                + demoClass.getSimpleName()
                + " {\n");
        //获取所有的Method
        Method[] methods = demoClass.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 parameterType:parameterTypes) {
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            if (",".equals(String.valueOf(s.charAt(s.length()-1)))){
                s.deleteCharAt(s.length()-1);
            }
            s.append("){}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

输出:
在这里插入图片描述

反射机制调用方法

  • 调用方法四要素:对象、方法名、实参列表、返回值。
  • 代码示例
public class Demo {
    public boolean login(String name,String password){
        if ("admin".equals(name)&&"123".equals(password)){
            return true;
        }
        return false;
    }

    public void logout() {
        System.out.println("已退出!");
    }
}
import java.lang.reflect.Method;

public class DemoTest {
    public static void main(String[] args) throws Exception{
        Class demoClass = Class.forName("Demo");
        //通过反射机制创建对象
        Object obj = demoClass.newInstance();
        //获取Method
        Method loginMethod = demoClass.getDeclaredMethod("login",
                String.class, String.class);
        /*四要素:
        * loginMethod方法
        * obj对象
        * "admin", "123"实参
        * retValue返回值
        * */
        Object retValue = loginMethod.invoke(obj, "admin", "123");
        System.out.println(retValue);
    }
}

输出:
在这里插入图片描述

Constructor

反编译类的Constructor(构造方法)
  • 相关方法
    Constructor<?>[] getConstructors()
    返回包含一个数组Constructor对象反射由此表示的类的所有公共构造类对象。
  • 代码示例
public class Demo {
    int no;
    String name;

    public Demo() {
    }

    public Demo(int no) {
        this.no = no;
    }

    public Demo(int no, String name) {
        this.no = no;
        this.name = name;
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

public class DemoTest {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        Class demoClass = Class.forName("Demo");
        s.append(Modifier.toString(demoClass.getModifiers()));
        s.append(" class ");
        s.append(demoClass.getSimpleName());
        s.append("{\n");
        /*拼接构造方法*/
        Constructor[] constructors = demoClass.getConstructors();
        for (Constructor constructor:constructors) {
            s.append("\t");
            s.append(Modifier.toString(demoClass.getModifiers()));
            s.append(" ");
            s.append(demoClass.getSimpleName());
            s.append("(");
            //拼接参数
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class parameterType:parameterTypes) {
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            if (parameterTypes.length > 0){
                s.deleteCharAt(s.length()-1);
            }
            s.append(") {}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

输出:
在这里插入图片描述

反射机制调用构造方法
  • 代码示例
public class Demo {
    int no;
    String name;

    public Demo() {
    }

    public Demo(int no) {
        this.no = no;
    }

    public Demo(int no, String name) {
        this.no = no;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Demo{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}
import java.lang.reflect.Constructor;

public class DemoTest {
    public static void main(String[] args) throws Exception{
        Class demoClass = Class.forName("Demo");
        /*调用无参1*/
        Object obj = demoClass.newInstance();
        System.out.println(obj);
        /*调用无参2*/
        Constructor cons0 = demoClass.getDeclaredConstructor();
        Object obj1 = cons0.newInstance();
        System.out.println(obj1);
        /*调用有参*/
        //第一步:获取有参数的构造方法
        Constructor cons = demoClass.getDeclaredConstructor(int.class, String.class);
        //第二步:调用构造方法new对象
        Object newObj = cons.newInstance(11,"Thompson");
        System.out.println(newObj);
    }
}

输出:
在这里插入图片描述

获取父类和父接口

  • 相关方法
    1、Class<? super T> getSuperclass()
    返回 类表示此所表示的实体(类,接口,基本类型或void)的父类 。
    2、Class<?>[] getInterfaces()
    确定由该对象表示的类或接口实现的接口。
  • 代码示例
public class DemoTest {
    public static void main(String[] args) throws Exception{
        //举例:String
        Class stringClass = Class.forName("java.lang.String");
        //获取String的父类
        Class superClass = stringClass.getSuperclass();
        System.out.println("父类:"+superClass.getSimpleName());
        //获取String类实现的所有接口
        Class[] interfaces = stringClass.getInterfaces();
        for (Class in:interfaces) {
            System.out.println("实现的接口有:"+in);
        }
    }
}

输出:
在这里插入图片描述

posted @ 2020-06-13 00:55  YU_UY  阅读(219)  评论(0)    收藏  举报