反射

反射

一.定义

  • 反射是指在程序运行期间,能够观察和修改类或者类的对象的属性和行为的特性

项目开发中常见的使用反射的场景

  • 使用JDBC连接数据库
  • Servlet在Web容器中的加载和运行

二.作用

Java反射机制提供了以下的功能

  1. 在运行时获取类的修饰符,包名,类名,实现的接口,继承的父类
  2. 在运行时获取类的所有属性名,修饰符,属性类型
  3. 在运行时获取所有方法,方法的返回值类型,方法名,方法参数数量,方法参数类型
  4. 在运行时调用加载类的方法

三.常用API

Java反射API常用的类

  • java.lang.Class
  • java.lang.reflect.Method
  • java.lang.Constructor
  • java.lang.Field
  • java.lang.Modifier

四.获取class对象的方式

public class TestClass {
    public static void main(String[] args) throws Exception{
        //第一种获取class对象的方式(常用方法)
        //Class.forName(),括号内是字符串,表示想要获取类的全类名
        //比如我想要获取当前类,获取的是当前类的class类型
        Class<?> tClass = Class.forName("cn.kgc.kb11.TestClass");
        //第二种获取class对象的方式
        //用对象获取类型
        //建一个对象,用对象.getClass()来获取
        TestClass tc=new TestClass();
        Class<? extends TestClass> tcClass = tc.getClass();
        //第三种获取class对象的方式
        //直接使用类型来获取class对象
        Class<TestClass> typeClass = TestClass.class;
    }
}

五.获取class对象的构造方法

Student类
放在cn.kgc.kb11包中

public class Student {
    public int id;
    String name;
    private String gender;

    private Student() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    private String getGender() {
        return gender;
    }

    private void setGender(String gender) {
        this.gender = gender;
    }
}

测试类
放在cn.kgc.kb11.ref包中

public class TestStudent {
    public static void main(String[] args) throws Exception{
       //使用反射获取某个类的属性和方法以及构造方法
        //1、获取Class对象,直接使用类型来获取
        Class<Student> c = Student.class;
        //获取包名
        System.out.println(c.getPackage().getName());
        //获取全类名
        System.out.println(c.getName());
        //获取构造方法
        //当构造方法为公共的时候,可以用c.getConstructor()来获取
        //当构造方法为私有的时候,用c.getDeclaredConstructor()来获取
        Constructor<Student> cons = c.getDeclaredConstructor();
        //虽然获取到构造方法了,但没有权限,还是私有的
        // 这时候需要用setAccessible(true)来强行破解
        cons.setAccessible(true);
        //再用newInstance方法就可以获取到Student类型的对象了
        Student s = cons.newInstance();
        //打印一下,结果就是Student类型的地址
        System.out.println(s);
    }
}

运行结果

cn.kgc.kb11
cn.kgc.kb11.Student
cn.kgc.kb11.Student@1b6d3586

注:一些公司为了安全,采用枚举的方法,枚举可以防反射

六.获取属性以及给属性赋值

Student类
放在cn.kgc.kb11包中

public class Student {
    public int id;
    String name;
    private String gender;
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
    private Student() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    private String getGender() {
        return gender;
    }

    private void setGender(String gender) {
        this.gender = gender;
    }
}

测试类
放在cn.kgc.kb11.ref包中

public class TestStudent {
    public static Student getStudent()throws Exception{
        //把获取类的代码放进一个方法里,便于后续操作
        Class<Student> c = Student.class;
        Constructor<Student> cons = c.getDeclaredConstructor();
        cons.setAccessible(true);
        Student s = cons.newInstance();
        return s;
    }
    public static void main(String[] args) throws Exception{
        //1.创建对象,调用之前写好的方法
        Student s = getStudent();
        //获取对象
        Class<? extends Student> aClass = s.getClass();
        //获取属性
        Field id = aClass.getField("id");
        //给属性赋值,用set(赋值到哪个对象里,赋值的值是多少)来进行赋值
        id.set(s,3);
        System.out.println(s);
        //但无法给私有属性赋值,编译不会出错,但运行是会出错
        //要想给私有属性赋值,用如下的方法
        Field df = aClass.getDeclaredField("gender");
        df.setAccessible(true);
        df.set(s,"男");
        System.out.println(s);
        //第二种给属性赋值的方法,获取属性数组
        Field[] dfs = aClass.getDeclaredFields();
        //然后遍历数组来给每个属性进行赋值
        for (int i = 0; i < dfs.length; i++) {
            //前提先破解
            dfs[i].setAccessible(true);
            if (null!=dfs[i].getName()&&"id".equals(dfs[i].getName())) {
                dfs[i].set(s,6);
            }else if (null!=dfs[i].getName()&&"name".equals(dfs[i].getName())){
                dfs[i].set(s,"小宇");
            }else if (null!=dfs[i].getName()&&"gender".equals(dfs[i].getName())){
                dfs[i].set(s,"女");
            }
        }
        System.out.println(s);
    }
}

运行结果

Student{id=3, name='null', gender='null'}
Student{id=3, name='null', gender='男'}
Student{id=6, name='小宇', gender='女'}

七.获取方法

Student类
放在cn.kgc.kb11包中

public class Student extends Parent{
    public int id;
    String name;
    private String gender;
    private String show(int cnt,String good){
        return "我叫"+name+",我的学号是:"+id+",我的性别是:"+gender
                +",老师叫我拿"+cnt+"杯"+good+"来给大家";
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
    private Student() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    private String getGender() {
        return gender;
    }

    private void setGender(String gender) {
        this.gender = gender;
    }

}

父类Parent
放在cn.kgc.kb11包中

public class Parent {
    private double money;
}

测试类
放在cn.kgc.kb11.ref包中

public class TestStudent {
    public static Student getStudent()throws Exception{
        //把获取类的代码放进一个方法里,便于后续操作
        Class<Student> c = Student.class;
        Constructor<Student> cons = c.getDeclaredConstructor();
        cons.setAccessible(true);
        Student s = cons.newInstance();
        return s;
    }
public static void main(String[] args) throws Exception{
        Student s = getStudent();
        Class<? extends Student> aClass = s.getClass();
        //获取方法(私有方法的获取就不用多说了,参照私有属性的获取)
        //getDeclaredMethod(方法名,参数类型的反射方式)
        Method gender = aClass.getDeclaredMethod("setGender", String.class);
        gender.setAccessible(true);
        //方法用invoke进行传参
        gender.invoke(s,"女");
        //两个公共方法就正常传参就行
        s.setId(1);
        s.setName("鱼子酱");
        System.out.println(s);
        //多参数类型私有方法的传参
        Method show = aClass.getDeclaredMethod("show", int.class, String.class);
        show.setAccessible(true);
        Object o = show.invoke(s, 25, "卡布奇诺");
        System.out.println(o);
        //获取父类的类型名
        Type dad = aClass.getGenericSuperclass();
        System.out.println(dad.getTypeName());
        //获取父类的类型
        Class<?> superclass = aClass.getSuperclass();
        System.out.println(superclass);
    }
}

运行结果

Student{id=1, name='鱼子酱', gender='女'}
我叫鱼子酱,我的学号是:1,我的性别是:女,老师叫我拿25杯卡布奇诺来给大家
cn.kgc.kb11.Parent
class cn.kgc.kb11.Parent

八.总结

  1. Java反射机制是Java语言被视为准动态语言的关键特性
  2. Java反射机制有优点也有缺点
  3. 一般用于构建框架
上一篇:2.MyBatis 使用算术


下一篇:Java进阶篇设计模式之六 - 组合模式和过滤器模式