Java 设计模式(一)反射

生来就可爱ヽ(ⅴ<●) 提交于 2020-01-29 07:39:48

1. 概述

反射:将类的各个组成部分封装为其他对象,可以在程序运行过程中操作这些对象,还可以解耦,提高程序的可扩展性。Java反射技术应用广泛,它能配置类的全限定名、方法和参数,完成对象的初始化,甚至反射某些方法。

2.获取Class对象的方式

* Class.forName(“全限定名”);将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中。读取文件,加载类

* 类名.class:通过类名的属性class获取。多用于参数传递

* 对象.getClass():getClass()在Object类中定义着的。多用于对象获取字节码格式

代码演示:

package com.wanfei.domain;

/**
 * 创建一个Student类:
 */
public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

获取Class对象的三种方式的演示及物理地址比较:

public static void main(String[] args) throws ClassNotFoundException{

        //使用Class.forName()
        Class class_01 = Class.forName( "com.wanfei.domain.Student" );
        System.out.println(class_01);


        //使用 类名.Class
        Class class_02 = Student.class;
        System.out.println(class_02);

        //使用对象名.getClass()
        Class class_03 = new Student().getClass();
        System.out.println(class_03);

        //比较
        System.out.println(class_01 == class_02); //true
        System.out.println(class_01 == class_03); //true
       
        /**
         * 结论:同一个字节码文件(*.class)在一次程序运行过程中,
         * 只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
         */
    }

打印结果:
在这里插学习辅导费

3.字节码对象Class的功能

1)获取成员变量们

  • Field[] getFields() :获取所有public修饰的成员变量
  • Field getField(String name) : 获取指定名称的成员变量
  • Field[] getDeclaredFields() : 获取所有成员变量,不考虑修饰符
  • Field getDeclaredField(String name)


2)获取构造方法们

  • Constructor<?>[] getConstructors()
  • Constructor getConstructor(类<?>… parameterTypes)
  • Constructor getDeclaredConstructor(类<?>… parameterTypes)
  • Constructor<?>[] getDeclaredConstructors()

3)获取成员方法们

  • Method[] getMethods()
  • Method getMethod(String name, 类<?>… parameterTypes)
  • Method[] getDeclaredMethods()
  • Method getDeclaredMethod(String name, 类<?>… parameterTypes)

4)获取全类名

String getName()

4.Field成员变量的使用:

修改Student.java文件中的内容

    private String name;
    private int age;
    public String a;
    public  String b;
    String c;


测试获取和设置成员变量:

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

         Class studentClass = Student.class;

        System.out.println("------------- 获取public修饰的成员变量-----------------");
        Field[] fields = studentClass.getFields();
        for (Field f: fields) {
            System.out.println(f);
        }

        System.out.println("------------- 获取所有成员变量-,不考录修饰符----------------");
        Field[] fields2 = studentClass.getDeclaredFields();
        for (Field f2: fields2) {
            System.out.println(f2);
        }

        //获取指定的成员变量并设置值
        Student student = new Student();
        Field fieldName  = studentClass.getDeclaredField( "a" );
        fieldName.set(student,"Pink Floyd" );

        //输出打设置后的值
        Object value = fieldName.get( student );
        System.out.println(student.toString());

        //不能直接访问私有变量,若需要访问。需忽略访问权限修饰符的安全检查
        Field fieldName2 = studentClass.getDeclaredField( "c" );
        fieldName2.setAccessible( true );//暴力反射
        System.out.println(fieldName2.get( student ));
    }


运行结果截图如下:
D:\Java\jdk1.8.0_221\bin\java.exe "-javaagent:D:\IntelliJ IDEA2018\lib\idea_rt.jar=59928:D:\IntelliJ IDEA2018\bin" -Dfile.encoding=UTF-8 -classpath D:\Java\jdk1.8.0_221\jre\lib\charsets.jar;D:\Java\jdk1.8.0_221\jre\lib\deploy.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_221\jre\lib\javaws.jar;D:\Java\jdk1.8.0_221\jre\lib\jce.jar;D:\Java\jdk1.8.0_221\jre\lib\jfr.jar;D:\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_221\jre\lib\jsse.jar;D:\Java\jdk1.8.0_221\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_221\jre\lib\plugin.jar;D:\Java\jdk1.8.0_221\jre\lib\resources.jar;D:\Java\jdk1.8.0_221\jre\lib\rt.jar;D:\IdeaProjects\SSM实战\day05_mybatis06_useful\day06_ssm01_java设计模式\target\classes;C:\Users\16565\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\16565\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\16565\.m2\repository\org\mybatis\mybatis\3.4.5\mybatis-3.4.5.jar;C:\Users\16565\.m2\repository\mysql\mysql-connector-java\5.1.6\mysql-connector-java-5.1.6.jar;C:\Users\16565\.m2\repository\log4j\log4j\1.2.12\log4j-1.2.12.jar;C:\Users\16565\.m2\repository\dom4j\dom4j\1.6.1\dom4j-1.6.1.jar;C:\Users\16565\.m2\repository\xml-apis\xml-apis\1.0.b2\xml-apis-1.0.b2.jar;C:\Users\16565\.m2\repository\jaxen\jaxen\1.1.6\jaxen-1.1.6.jar com.wanfei.test.Main
------------- 获取public修饰的成员变量-----------------
public java.lang.String com.wanfei.domain.Student.a
public java.lang.String com.wanfei.domain.Student.b
------------- 获取所有成员变量-,不考录修饰符----------------
private java.lang.String com.wanfei.domain.Student.name
private int com.wanfei.domain.Student.age
public java.lang.String com.wanfei.domain.Student.a
public java.lang.String com.wanfei.domain.Student.b
java.lang.String com.wanfei.domain.Student.c
Student{name='null', age=0, a='Pink Floyd', b='null', c='null'}
null

Process finished with exit code 0

5.Constructor 构造方法的使用

      Constructor 可以用来创建对象,在使用不带参数的构造器创建对象时,可直接使用Class类提供的newInstance()方法.

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


        Class studentClass = Student.class;

        //获取构造器,用来创建对象
        Constructor constructor = studentClass.getConstructor( String.class,int.class );
        System.out.println(constructor);


        //使用带参数的构造器创建对象
        Object object = constructor.newInstance( "King Crimson",100 );
        System.out.println(object.toString());

        //使用不带参数的构造器创建对象
        Constructor constructor2 = studentClass.getConstructor(  );
        Object object2 = constructor2.newInstance(  );
        System.out.println(object2.toString());

        //使用不带参数的构造器创建对象简化版
        Object object3 = studentClass.newInstance();
        System.out.println(object3.toString());
    }

运行结果:

public com.wanfei.domain.Student(java.lang.String,int)
Student{name='King Crimson', age=100, a='null', b='null', c='null'}
Student{name='null', age=0, a='null', b='null', c='null'}
Student{name='null', age=0, a='null', b='null', c='null'}

6.Method方法的使用

在Student.java文件中添加eat()和info(String name,int age)两个方法.

 public void eat(){
        System.out.println("eat--------");
    }

    public void info(String name,int age){
        System.out.println(name+"-----> "+age);
    }

调用方法是用到Method类的invoke(Object object,…)方法

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

        //不带参的方法
        Class studentClass = Student.class;
        Method eat_method =  studentClass.getMethod( "eat");
        Student student = new Student(  );
        eat_method.invoke( student );

        //带参的方法
        Method info_method = studentClass.getMethod("info", String.class, int.class);
        Student student1 = new Student(  );
        info_method.invoke( student1,"camel",99 );
    }

执行后的结果

eat--------
camel-----> 99
7.案例

        写一个小框架,要求在不改变代码的前提下,可以创建任意的对象,执行任意的方法,实现思路如下:

这里是引用

  • 将要生成的对象的全类名和要执行的方法写入配置文件中
  • 在框架中读取配置文件
  • 使用反射技术加载类文件进内存
  • 创建对象
  • 执行方法

步骤:

  1. 编写Person类和Student类并编写方法
public class Person {

    public void eat(){
        System.out.println("吃东西");
    }
}
public class Student {
    public void sleep(){
        System.out.println("在寝室睡大觉");
    }
}

2)编写配置文件pro.properties,只需要修改该文件里的内容便可以创建各种类

className=com.wanfei.re.domain.Person
methodName=eat

3)编写框架程序

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

        //1.加载配置文件
        Properties properties = new Properties(  );

        //2.读取class目录下的文件
       ClassLoader classLoader = ReflectTest.class.getClassLoader();

       InputStream inputStream = classLoader.getResourceAsStream( "pro.properties" );
       properties.load( inputStream);

       String className = properties.getProperty( "className" );
       String methodName = properties.getProperty( "methodName" );

       //使用反射机制
        Class cls = Class.forName( className);
        Object object = cls.newInstance();

        Method method = cls.getMethod( methodName );
        method.invoke( object );

    }
}

执行上面代码的结果

D:\Java\jdk1.8.0_221\bin\java.exe "-javaagent:D:\IntelliJ IDEA2018\lib\idea_rt.jar=52872:D:\IntelliJ IDEA2018\bin" -Dfile.encoding=UTF-8 -classpath D:\Java\jdk1.8.0_221\jre\lib\charsets.jar;D:\Java\jdk1.8.0_221\jre\lib\deploy.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_221\jre\lib\javaws.jar;D:\Java\jdk1.8.0_221\jre\lib\jce.jar;D:\Java\jdk1.8.0_221\jre\lib\jfr.jar;D:\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_221\jre\lib\jsse.jar;D:\Java\jdk1.8.0_221\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_221\jre\lib\plugin.jar;D:\Java\jdk1.8.0_221\jre\lib\resources.jar;D:\Java\jdk1.8.0_221\jre\lib\rt.jar;D:\IdeaProjects\SSM实战\ssm\target\classes ReflectTest
吃东西

Process finished with exit code 0

然后修改pro.properties里面的内容

className=com.wanfei.re.domain.Student
methodName=sleep

再次执行ReflectTest.java文件

D:\Java\jdk1.8.0_221\bin\java.exe "-javaagent:D:\IntelliJ IDEA2018\lib\idea_rt.jar=52922:D:\IntelliJ IDEA2018\bin" -Dfile.encoding=UTF-8 -classpath D:\Java\jdk1.8.0_221\jre\lib\charsets.jar;D:\Java\jdk1.8.0_221\jre\lib\deploy.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_221\jre\lib\javaws.jar;D:\Java\jdk1.8.0_221\jre\lib\jce.jar;D:\Java\jdk1.8.0_221\jre\lib\jfr.jar;D:\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_221\jre\lib\jsse.jar;D:\Java\jdk1.8.0_221\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_221\jre\lib\plugin.jar;D:\Java\jdk1.8.0_221\jre\lib\resources.jar;D:\Java\jdk1.8.0_221\jre\lib\rt.jar;D:\IdeaProjects\SSM实战\ssm\target\classes ReflectTest
在寝室睡大觉

Process finished with exit code 0
总结

没啥好说的,到此结束

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!