第八章:反射机制

第八章:反射

一. 反射的基本概念

1.什么是反射??

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2.反射机制有什么用??

  • 通过java语言中的反射机制可以操作字节码文件。
  • 有点类似于黑客。(可以读和修改字节码文件)
  • 通过反射机制可以操作代码片段。(class文件)

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

java.lang.reflect.*

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

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

5.在Java中获取Class的三种方式

  • 第一种:Class c = Class.forName(“完整类名带包名”);
  • 第二种:Class c = 引用.getClass(); 得到的是这个引用数据类型的class字节码文件。
  • 第三种: Class c = 数据类型.class;

/*
    要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例??
        三种方式
            第一种:Class c = Class.forName("完整类名带包名");
            第二种:Class c = 引用.getClass(); 得到的是这个引用数据类型的class字节码文件。
            第三种: Class c = 数据类型.class;
 */
public class ReflectTest01 {
    
    
    public static void main(String[] args) {
    
    
        /*
            Class.forName()
            1. 静态方法。
            2. 方法的参数是一个字符串。
            3. 字符串需要的是一个完整的类名。
            4. 完整的类名必须带有报名。

         */
        Class c1 = null;
        Class c2 = null;
        try {
    
    
            c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说代表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();
        }
        //java中任何一个对象都有一个方法:getClass()
        //字节码装载到方法区中时只装载一份
        String s = "abc";
        Class x = s.getClass();//x代表String.Class字节码文件,x代表Sting类型。
        System.out.println(x == c1);//true(==判断的是对象的内存地址)

        Date time = new Date();
        Class y = time.getClass();
        System.out.println(y == c2);//true(c2 和y变量保存的内存地址一样的,都指向方法区中Date字节码文件)

        //第三种方式,java语言中任何一种类型,包括基本数据类型,它们都有.class属性
        Class z = String.class;
        Class k = Date.class;
        Class f = int.class;
        Class e = double.class;

        System.out.println(x == z);

    }
}

6.如何通过反射机制创建对象。

 获取Class文件后,可以调用无参构造方法来实例化对象。
    Class c = Class.forName("java.lang.Date")
    Object obj = c.newInstance();
    一定要注意:newInstance底层调用的是该类型的无参构造方法,如果没有这个无参数构
    造方法,会出现异常。
/*
    获取到Class能干什么??
        通过Class的newInstance()方法来实例化对象
        注意:newInstance()调用的是无参构造,必须保证无参构造是存在的
 */
public class ReflectTest02 {
    
    
    public static void main(String[] args) {
    
    
        //以反射机制的方式创建对象
        try {
    
    
            //通过反射机制,获取Class,通过Class来实例化对象
            Class<?> c = Class.forName("进阶.反射08.bean.User");
            //newInstance()这个方法,会调用User这个类的无参数构造方法,完成对象的创建
            //重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的
            Object obj = c.newInstance();
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        } catch (InstantiationException e) {
    
    
            e.printStackTrace();
        }
    }
}

7.反射机制和配置文件的使用


/*
    验证反射机制的灵活性.
        java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象的实例化。
            非常灵活(符合OCP原则:对扩展开放,对修改关闭)
 */
public class ReflectTest03 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //这种方式代码就写死了,之鞥呢创建一个User类型的对象
        //User user = new User();

        //以下代码是灵活的,代码不需要改动,只需要修改配置文件就行了,

        //通过IO流读取classinfo.properties文件(属性配置文件)
        FileReader reader = new FileReader("javase/classinfo2.properties");
        //创建属性类对象Map
        Properties pro = new Properties();
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

        //通过Key获取value
        String calssName = pro.getProperty("calssName");

        Class c = Class.forName(calssName);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

二. 反射中最重要的类

1. Class类中相关方法

Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。

  • 获得类相关的方法
方法 用途
forName(String className) 根据类名返回类的对象
getName() 获得类的完整路径名字
newInstance() 创建类的实例
getSimpleName() 获得类的名字
getSuperclass() 获得当前类继承的父类的名字
getInterfaces() 获得当前类实现的类或是接口
getClassLoader() 获得类的加载器
  • 获得类中属性(Filed)相关的方法
方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象
  • 获得类中注解(Annotation)相关的方法
方法 用途
getAnnotation(Class annotationClass) 返回该类中与参数类型匹配的公有注解对象
getAnnotations() 返回该类所有的公有注解对象
getDeclaredAnnotation(Class annotationClass) 返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations() 返回该类所有的注解对象
  • 获得类中构造器(Constructor)相关的方法
方法 用途
getConstructor(Class…<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getAnnotations() 获得该类的所有公有构造方法
getDeclaredConstructor(Class…<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法
  • 获得类中方法(Method)相关的方法
方法 用途
getMethod(String name, Class…<?> parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes) 获得该类某个方法
getDeclaredMethods() 获得该类所有方法
  • 类中其他重要的方法
方法 用途
isAnnotation() 如果是注解类型则返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定类型注解类型则返回true
isAnonymousClass() 如果是匿名类则返回true
getDeclaredMethods() 获得该类所有方法
isArray() 如果是一个数组类则返回true
isEnum() 如果是枚举类则返回true
isInstance(Object obj) 如果obj是该类的实例则返回true
isLocalClass() 如果是局部类则返回true
isMemberClass() 如果是内部类则返回true

2. Filed 类中相关方法

Field代表类的成员变量(成员变量也称为类的属性)。

/*
    反射Student类当中所有的Filed
        Field getField(String name)
          返回一个 Field 对象,它反映此Class对象所表示的类或接口的指定公共成员字段。
        Field[] getFields()
          返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段

 */
public class ReflectTest05 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //获取整个类
        Class studentClass = Class.forName("进阶.反射08.bean.Student");
        //获取这个类的名字
        System.out.println("完整的类名: " + studentClass.getName());
        //获取这个类的简单的名字
        System.out.println("简易的名字: " + studentClass.getSimpleName());

        /*
            获取类中所有public修饰的Field
            Field[] getFields()
          返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段
         */
        Field[] fields = studentClass.getFields();
        //取出这Field
        Field f = fields[0];
        //取出这个Field的名字
        String firstName = f.getName();
        System.out.println(firstName);
        System.out.println("================");

        /*
        获取所有的Filed
          Field getDeclaredField(String name)
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
          Field[] getDeclaredFields()
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段
         */
        Field[] fs = studentClass.getDeclaredFields();
        for (Field field:fs) {
    
    
            //获取修饰符
            //int getModifiers()
            //以整数形式返回由此 Field 对象表示的字段的Java语言修饰符。
            System.out.println(field.getModifiers());
            System.out.println(Modifier.toString(field.getModifiers()));

            //获取属性类型的名字
            //Type getGenericType()
            // 返回一个Type对象,它表示此 Field 对象所表示字段的声明类型。
            //System.out.println(field.getGenericType());
            Class fieldType = field.getType();
            System.out.println(fieldType.getSimpleName());

            //获取属性的名字
            System.out.println(field.getName());
        }
    }
}
/*
    通过反射机制,反编译一个类的属性Field
 */
public class ReflectTest06 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        Class studentClass = Class.forName("进阶.反射08.bean.Student");
        //创建这个是为了字符串拼接
        StringBuilder s = new StringBuilder();
        s.append(Modifier.toString(studentClass.getModifiers()) + " class " +  studentClass.getSimpleName() +  "{" + "\n");
        Field[] fields = studentClass.getDeclaredFields();
        for (Field field:fields) {
    
    
            s.append("\t" + Modifier.toString(field.getModifiers()) + " " +
                    field.getType().getSimpleName() + " " +
                    field.getName() + ";" + "\n"
                    );

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

    }
}

/*
    怎么通过反射机制访问一个java对象的属性
        给属性赋值
        获取属性的值
 */
public class ReflectTest07 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        /*
            不适用反射机制对属性赋值
         */
        Student s = new Student();
        s.no = 111;//给属性赋值
        System.out.println(s.no);//读属性值

        /*
            使用反射机制对属性赋值(set get)
         */
        Class studentClass = Class.forName("进阶.反射08.bean.Student");
        Object obj = studentClass.newInstance(); //obj是一个Student对象,可以使用向下转型的方式来操作,也可以通过反射机制操作。
        //获取no属性
        Field noField = studentClass.getField("no");
        //给obj对象(Student对象)的no属性赋值
        noField.set(obj,2222);//给obj对象的no属性赋值
        System.out.println(noField.get(obj));//获取属性值

        /*
            如何访问私有的属性:打破封装
         */
        Field nameField = studentClass.getDeclaredField("name");
        //打破封装(反射机制的去电:打破封装,可能会给不法分子留下机会)
        //这样设置完后可以在外部访问被封装的属性(私有的属性)
        nameField.setAccessible(true);
        nameField.set(obj,"jackson");
        System.out.println(nameField.get(obj));
    }
}

3. Method类

/*
    反射Method
 */
public class ReflectTest08 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        //获取类
        Class userServiceClass = Class.forName("进阶.反射08.service.UserService");
        //获取所有的Method(包括私有的)
        Method[] methods = userServiceClass.getDeclaredMethods();

        //遍历Method
        for (Method method:methods) {
    
    
            //获取方法名
            System.out.println(method.getName());
            //获取方法的返回值类型
            System.out.println(method.getReturnType());
            //获取方法的修饰符
            System.out.println(Modifier.toString(method.getModifiers()));
            //获取方法的参数的类型,一个方法的参数可能会有多个
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
    
    
                System.out.println(parameterType.getSimpleName());
            }
            System.out.println("=================");
        }
    }
}

/*
    反编译一个类,包括方法
 */
public class ReflectTest09 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        StringBuilder s = new StringBuilder();
        Class userServiceClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(userServiceClass.getModifiers()) + " " + "class" + " " + userServiceClass.getSimpleName() + "{" + "\n");

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

/*
    通过反射机制怎么调用一个方法
 */
public class ReflectTest10 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //不使用反射机制调用方法
        /**
         * 调用方法的要素:
         *   1. 获取到对象
         *   2.获取到方法名
         *   3. 实参列表
         *   4. 返回值
         */
        UserService userService = new UserService();
        boolean loginSuccess = userService.login("admin","123");
        System.out.println(loginSuccess?"登陆成功":"登录失败");

        //使用反射机制来调用一个方法
        Class userServiceClass = Class.forName("进阶.反射08.service.UserService");
        //创建对象
        Object obj = userServiceClass.newInstance();
        //获取Method
        Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
        Object retValue = loginMethod.invoke(obj,"admin","123");
        System.out.println(retValue);

    }
}

4.Constructor类

/*
    通过构造方法创建对象
 */
public class ReflectTest12 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //不适用反射机制
        Vip v1 = new Vip();
        Vip v2 = new Vip(110,"zhangsan","2020-10-11",true);

        //使用反射机制创建对象
        Class c = Class.forName("进阶.反射08.bean.Vip");
        //调用无参构造方法
        Object obj1 = c.newInstance();
        System.out.println(obj1);
        //调用有参构造
        //第一步先获取有参构造方法
        Constructor con = c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
        //第二步,调用构造方法new对象
        Object obj2 = con.newInstance(110,"jackcon","1999-10-10",true);
        System.out.println(obj2);
    }
}
/*
    如何通过反射机制,获取一个类的父类,和已实现的接口
 */
public class ReflectTest13 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        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.getSimpleName());
        }
    }
}

三. 添加内容(如何获取绝对路径)

/*
    关于文件绝对路径的问题??
        java中如何获取一个文件的绝对路径

 */
public class AboutPath {
    
    
    public static void main(String[] args) throws Exception{
    
    
        /*
            以下这种方式获取路径的缺点是:移植性差,在IDEA中默认的当前路径是project的根。
            这个代码在IDEA编辑器中可以,但换了其他编译软件就不行了,可能当前路径的跟就不是project的根了
            FileReader reader = new FileReader("javase/classinfo2.properties");
         */
        /*
            java中通用的获取文件的绝对路径的方式。
            注意这种方式的前提是:文件必须放在类的根路径下(src就是根)
            src是类的根路径
         */
        //Thread.currentThread() 当前线程对象(这里是main)
        //getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象。(当前是AboutPath类)
        //getResource() 【获取资源】这是类加载器对象的方法,这个方法默认是从类的根路径下开始加载资源。
        //getPath()  加载完资源后,我们在获取这个资源的绝对路径
        /*
            注意:这种方式获取的绝对路径是通用的,以后当我们要使用一个文件时比如使用IO流的时候不能直接输入绝对路径
            应该通过这种方式获取到绝对路径,然后输入,因为不同的操作系统绝对路径是有区别的。
         */
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath();


        String path2 = Thread.currentThread().getContextClassLoader()
                .getResource("进阶/反射08/笔记").getPath();
        System.out.println(path2);
    }
}


public class IoPropertiesTest01 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        //获取一个文件的绝对路径
        /*String path = Thread.currentThread().getContextClassLoader().
                getResource("classinfo2.properties").getPath();
        FileReader reader = new FileReader(path);
         */
        //直接以流的形式返回
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");

        Properties pro = new Properties();
        pro.load(in);
        String s = pro.getProperty("className");
        System.out.println(s);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_28384023/article/details/110091779