Java反射

反射(重点)

能够分析类能力的程序成为反射,反射是灵活的,通过修改配置文件,源代码自动创建响应类型的对象

背景

进程和线程的一个区别就是有没有主方法,有主方法才可以独立启动。如servlet就没有主方法,需要依赖tomcat(servlet容器)的主方法启动。servlet的doget等方法不是静态方法,也就是说我们要实例化对象才可以调用方法,tomcat无法通过new的方式来实例化对象进而调用方法(通过扫描文件来获取文件的路径,但是即使扫描获取到它所在的路径也无法使用new)这种情况下,反射产生了。

类的生命周期

java源代码通过jacac编译成为java字节码文件,再通过java运行成为类对象,类\对象通过new实例化对象,最后通过gc(java垃圾回收机制)卸载对象(释放对象的堆空间,类还在)

image-20220518162411849

获取指定类的类对象(反射的第一步)

image-20220518165314388

如图三个阶段,分别有三个方法,每个阶段调用各自的方法都可以获取class类对象

方法一:Class.forName(“类全名”)————最常用,还没进入内存就可以获取想要类的类对象 类全名为包名+类名

方法二:类名.class——————通过类的成员变量

方法三:对象.getClass()——————通过实例化对象的方法

获取Class对象的方式的场景

Class.forName(“类全名”):多用于配置文件,将类名定义在配置文件中,读取配置文件,加载类。如JDBC读取Mysql的文件

类名.class:多用于参数的传递

对象名.getClass():多用于对象获取类对象

package com.qcby.demo.reflect;
public class TestReflectPerson {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.Class.forname()
        Class<?> class1 = Class.forName("com.qcby.demo.reflect.TestReflectPerson");
        System.out.println(class1);
        //2.类名.class
        Class class2 = TestReflectPerson.class;
        System.out.println(class2);
        //3.对象名.getClass()
        Class class3 = new TestReflectPerson().getClass();
        System.out.println(class3);
        System.out.println(class1==class2);
        System.out.println(class2==class3);
    }
}

image-20220518171544857

总结:同一个类加载器加载的文件在一次程序运行过程中,只会被加载一次,无论使用那种方式获得的类对象都是同一个

class对象(类对象)常见功能

Person类

package com.qcby.demo.reflect;
public class Person {
    private String name;
    private int age;
    public String sex;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {//无参构造很重要,反射是通过无参构造方法来进行是实例化的

    }
    private Person(String name) {
        this.name = name;
    }

    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;
    }
    private void test(String... strings){//可变参数

    }

    public static void main(String[] args) {
        Person p =new Person();
        p.test("sd","sd");//可以输入任意个参数
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}
成员变量对象

getDeclaredFields获取成员变量对象

//获取所有访问权限的成员变量对象
    Field[] fields = pcla.getDeclaredFields();
    for (Field field:fields){
    System.out.println(field);
}
//获取公共访问权限的成员变量对象
    Field[] fields1 = pcla.getFields();
    for (Field field:fields1){
    System.out.println(field);
}

设置值:set(Object obj,Object value)

获取值:get(Object obj)

对于私有权限修饰符的成员变量获取设置值,需要使用setAccessible(true)暴力反射 --忽略访问权限修饰符的安全检查

例子

Person类在上面

package com.qcby.demo.reflect;
import org.omg.CORBA.ARG_OUT;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class TestField {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Class pcla = Person.class;
        Field sex = pcla.getDeclaredField("sex");
        System.out.println(sex);
        Field name = pcla.getDeclaredField("name");
        System.out.println(name);
        Person person = new Person();
        //获取公共成员变量对象的值
        Object value =sex.get(person);
        System.out.println(value);
        //设置公共成员变量对象的值
        sex.set(person,"张三");
        Object value1 =sex.get(person);
        System.out.println(value1);

        //暴力反射 --忽略访问权限修饰符的安全检查
        name.setAccessible(true);
        //获取任意访问权限的成员变量的值
        Object value2 =name.get(person);
        System.out.println(value2);
    }
}

image-20220518200221033

成员方法对象

getDeclaredMethods获取成员方法对象

执行方法:invoke(Object object,Object… args) ,Object… 表示可变参数

获取方法名:getName()

对于私有权限修饰符的成员方法获取设置值,需要使用setAccessible(true)暴力反射 --忽略访问权限修饰符的安全检查

例子

Person类在上面

package com.qcby.demo.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethod {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class pcla =Person.class;
        //获取方法对象
        Method getAge = pcla.getDeclaredMethod("getAge");
        Method setAge = pcla.getDeclaredMethod("setAge", int.class);
        Person p =new Person();
        System.out.println(setAge.getName());
        setAge.invoke(p,400);
        //方法调用
        Object res = getAge.invoke(p);
        System.out.println(res);
    }
}

image-20220518221920817

构造方法对象

getDeclaredConstructors获取构造方法对象

newInstance()Class pcla =Person.class;
//获取公共的构造方法对象
Constructor[] con = pcla.getConstructors();
//获取所有的构造方法对象
Constructor[] cons = pcla.getDeclaredConstructors();

创建对象:newInstance()

newInstance()如果是想利用无参构造方法去创建对象,可以直接使用类对象来创建,跳过构造方法对象

对于私有权限修饰符的构造方法获取设置值,需要使用setAccessible(true)暴力反射 --忽略访问权限修饰符的安全检查

例子

Person类在上面

package com.qcby.demo.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestContructor {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class pcla =Person.class;
        //获取公共的构造方法对象
        Constructor[] con = pcla.getConstructors();
        for (Constructor constructor:con){
            System.out.println(constructor);
        }
        System.out.println("....");
        //获取所有的构造方法对象
        Constructor[] cons = pcla.getDeclaredConstructors();
        for (Constructor constructor:cons){
            System.out.println(constructor);
        }
        //获取无参构造方法对象
        Constructor con1 = pcla.getDeclaredConstructor();
        //使用公共无参构造方法创建对象
        Object obj = con1.newInstance();
        System.out.println(obj);
        //获取有参构造方法对象
        Constructor cons2 =pcla.getDeclaredConstructor(String.class,int.class);
        Object obj1 = cons2.newInstance("张三", 29);
        System.out.println(obj1);

    }
}

image-20220518222016432

可变参数
private void test(String... strings){//可变参数

}
public static void main(String[] args) {
    Person p =new Person();
    p.test("sd","sd");//可以输入任意个参数
}

…可以表示输入任意个参数

例子
package com.qcby.demo.reflect;
public class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {//无参构造很重要,反射是通过无参构造方法来进行是实例化的

    }

    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;
    }
    public void test(String... strings){//可变参数

    }

    public static void main(String[] args) {
        Person p =new Person();
        p.test("sd","sd");//可以输入任意个参数
    }
}
package com.qcby.demo.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestReflectPerson {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        //1.获取类对象
        Class tclass = Class.forName("com.qcby.demo.reflect.Person");
        //2.通过类对象获取成员变量
        System.out.println("获取Person的所有属性对象");
        Field[] fields =tclass.getDeclaredFields();
        for (Field field:fields){
            System.out.println(field);
        }
        System.out.println("获取Person的属性对象");
        Field age = tclass.getDeclaredField("age");
        System.out.println(age);

        //3.通过类对象获取构造方法
        System.out.println("获取Person的所有构造方法对象");
        Constructor[] declaredConstructors = tclass.getDeclaredConstructors();
        for (Constructor constructor:declaredConstructors){
            System.out.println(constructor);
        }
        System.out.println("获取Person的无参构造方法对象");
        Constructor declaredConstructorconstructor = tclass.getDeclaredConstructor();
        System.out.println(declaredConstructorconstructor);
        System.out.println("获取Person的有参构造方法对象");
        Constructor declaredConstructorconstructor1 = tclass.getDeclaredConstructor(String.class,int.class);
        //注意int不是类,当是java反射做了优化(9个预先定义好的Class对象代表8个基本类型和void),int.class不等于Integer.class
        System.out.println(declaredConstructorconstructor1);
        //4.通过类对象获取成员方法
        System.out.println("获取Person的所有成员方法对象");
        Method[] methods = tclass.getDeclaredMethods();
        for (Method method:methods){
            System.out.println(method);
        }
        System.out.println("获取Person的setAge方法对象");
        Method method = tclass.getDeclaredMethod("setAge",int.class);
        System.out.println(method);
    }
}

image-20220518191204741

getName()与getSimpleName()

getName():获取的类的全名称

getSimpleName():获取类的名称

属性文件(创建对象,读取内容)

内容以等号连接形如key=value形式,文件后缀为.properties

  • 第一步:配置文件
  • 第二步:输入流加载到 Map集合 properties
  • 第三步:通过Map集合 的getProperty(左边键值) 获取value(右边)
  • 第四步:通过完整类名 value ,forName();//获取类型
  • 最后一步: 实例化 Object obj = a.newInstance( );

image-20220518222105772

reflect.className = com.qcby.demo.reflect.Student
reflect.MethodName = getAge

加载配置文件,获取配置文件内的数据

package com.qcby.demo.reflect;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class TestConfig {
    public static void main(String[] args) throws IOException {
        //创建properties对象
        Properties properties =new Properties();
        //加载配置文件(通过流的形式,加载成输入流)
        InputStream resourceAsStream = Person.class.getClassLoader().getResourceAsStream("com/qcby/demo/reflect/refconfig");
        properties.load(resourceAsStream);
        //获取配置文件中的数据
        String className = properties.getProperty("reflect.className");
        String methodName = properties.getProperty("reflect.MethodName");
        System.out.println(className);
        System.out.println(methodName);
    }
}

image-20220518222157741

综合例子

Student类,路径如上图所示,写在配置文件中

package com.qcby.demo.reflect;

public class Student {
    public void eat(){
        System.out.println("eat");
    }
    public void sleep(){
        System.out.println("sleep");
    }
}
package com.qcby.demo.reflect;
import com.company.Main;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Properties;

public class CommonReflect {
    //获取该类的名称
    public static void getClassName(Object object){
        String simpleName = object.getClass().getSimpleName();
        System.out.println("类的名称为"+simpleName);
    }
    //获取该类的成员变量名称
    public static void getField(Object object){
        Field[] declaredFields = object.getClass().getDeclaredFields();
        for (Field field:declaredFields){
            System.out.println("成员变量名称为"+field);
        }
    }
    //获取该类的成员方法
    public static void getMethod(Object object){
        Method[] declaredMethods = object.getClass().getDeclaredMethods();
        for (Method method:declaredMethods){
            System.out.println("成员方法为"+method);
        }
    }
    //调用该类的成员方法,以get为例
    public static void getAction(Object object) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Field[] declaredFields = object.getClass().getDeclaredFields();
        for (Field field:declaredFields){
            String fieldName = field.getName();
            //toUpperCase()全部转换成大写
            //获取首字母大写
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String methodName = "get" +firstLetter+fieldName.substring(1);
            Method method = object.getClass().getMethod(methodName);
            System.out.println("get的输出结果"+method.invoke(object));
        }
    }

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        Properties properties =new Properties();
        //加载配置文件(通过流的形式,加载成输入流)
        InputStream resourceAsStream = Person.class.getClassLoader().getResourceAsStream("com/qcby/demo/reflect/refconfig");
        properties.load(resourceAsStream);
        String className = properties.getProperty("reflect.className");
        System.out.println(className);
        // Person person = Person.class.getConstructor(String.class, int.class).newInstance("张三", 20);
        if (className!=null && !"".equals(className)) {
            Object person = Class.forName(className).newInstance();
            getClassName(person);
            getField(person);
            getMethod(person);
            getAction(person);
        }
    }
}

image-20220518222503659

反射的优缺点
1、优点:

静态编译:

在编译时确定类型,绑定对象,即通过。

动态编译:

运行时确定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,体现了多态的应用,有利于降低类之间的耦合性。

一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

2、缺点:

是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值