Java反射机制

Java反射机制

一、反射的概念

  Java反射是基于正射而言的,所谓正射就是通过new的形式创建对象,也就是我们常说的new()方法。我们知道,代码编译后.java的文件被编译成.class的二进制文件,在运行过程中只有用到了这个类JVM类加载器才会将.class文件加载到内存中。

  Java反射是指Java在运行时具有自观察的能力,能对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能调用它的方法和属性;这种动态获取信息以及动态调用该对象的方法的功能称为Java的反射机制。

二、为什么需要Java反射机制

  以Java的工厂模式举例:

  产品接口类

public interface Shape {
    void produce();
}

  产品实现类

public class Square implements Shape {

    @Override
    public void produce() {
        System.out.println("produce Square!");
    }
}

public class Rectangle implements Shape {

    @Override
    public void produce() {
        System.out.println("produce Rectangle!");
    }
}

public class Circle implements Shape {

    @Override
    public void produce() {
        System.out.println("produce Circle!");
    }
}

  工厂类

public class ShapeFactory {
    //使用 getShape 方法获取形状类型的对象
    public Shape getShape(String shapeType){
        if(shapeType == null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
            return new Rectangle();
        } else if(shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }
        return null;
    }
}

  测试类

@SpringBootTest
public class FactoryTest {

    @Test
    public void test1(){
        ShapeFactory shapeFactory = new ShapeFactory();

        //获取 Circle 的对象,并调用它的 draw 方法
        Shape shape1 = shapeFactory.getShape1("CIRCLE");
        //调用 Circle 的 draw 方法
        shape1.produce();

        //获取 Rectangle 的对象,并调用它的 draw 方法
        Shape shape2 = shapeFactory.getShape1("RECTANGLE");
        //调用 Rectangle 的 draw 方法
        shape2.produce();

        //获取 Square 的对象,并调用它的 draw 方法
        Shape shape3 = shapeFactory.getShape1("SQUARE");
        //调用 Square 的 draw 方法
        shape3.produce();
    }
}

  测试结果

produce Circle!
produce Rectangle!
produce Square!

  在工厂模式下,如果需要new什么对象就在shapeFactory.getShape()中传入需要的图形,看似很方便其实潜在这一个问题。当如果需求变了,需求不在是CIRCLE、RECTANGLE、SQUARE这三种图形,而是第四种图形,就需要修改工厂类的getShape()方法,增加一个if判断,这种方式需要修改源代码的方式及其不优雅。因此,引出了我们接下来要讨论的问题,如何通过Java反射的形式动态的获取对象。

三、通过Java反射的方式动态的获取对象

  修改上面工厂模式的工厂类

public class ShapeFactory {

    //使用 getShape 方法获取形状类型的对象
    public Shape getShape1(String shapeType){
        if(shapeType == null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
            return new Rectangle();
        } else if(shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }
        return null;
    }
    
    public Shape getShape2(String className){
        try {
            Class clazz = Class.forName(className);
            Constructor con = clazz.getConstructor();
            return (Shape) con.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  添加新产品类

public class Triangle implements Shape {

    @Override
    public void produce() {
        System.out.println("produce Triangle!");
    }
}

  测试类修改

@SpringBootTest
public class FactoryTest {

    @Test
    public void test2(){
        ShapeFactory shapeFactory = new ShapeFactory();

        Shape shape1 = shapeFactory.getShape2("com.franky.modules.factory.product.impl.Square");
        shape1.produce();

        Shape shape2 = shapeFactory.getShape2("com.franky.modules.factory.product.impl.Rectangle");
        shape2.produce();

        Shape shape3 = shapeFactory.getShape2("com.franky.modules.factory.product.impl.Circle");
        shape3.produce();

        Shape shape4 = shapeFactory.getShape2("com.franky.modules.factory.product.impl.Triangle");
        shape4.produce();
    }
}

  控制台打印结果

produce Square!
produce Rectangle!
produce Circle!
produce Triangle!

  改造后的工厂类,当需要增加新的产品后则不需要再进行修改工厂类。在程序运行过程种,需要调用哪个类就加载哪个类。这就是Java反射的便利之处。

四、Java反射优劣势

  通过上面得例子,我们已经体会到了Java反射在某些方便的便利之处,Java反射除了优势肯定还有劣势,这里我们详细的分析下Java反射的优劣势。

1、优势

  • 增加程序的灵活性,避免将程序写死到代码里
  • 代码简洁,提高代码复用率
  • 破坏类的封装,可以访问类的私有方法、属性;

2、劣势

  • 相比于直接new的方式,Java反射操作的效率会比非反射操作的效率低很多,因为中间需要多个检查和解析步骤,JVM无法进行优化;
  • 反射提高了代码的复杂性,维护代码较为困难;
  • 反射机制使得类的私有属性和方法暴露出来,可能导致其他意外副作用,降低了可移植性。
  • 反射需要运行时权限,在安全管理器下运行时可能不存在。对于在受限安全上下文运行的代码,例如Applet中,这是一个重要的考虑因素;

五、Java反射的使用

  以下是测试使用的类

@Data
@TableName("student")
@ApiModel(value="学生实体")
public class Student {

    @TableId(type = IdType.UUID)
    @ApiModelProperty(value="id")
    private String id;
    @ApiModelProperty(value="姓名")
    private String name;
    @ApiModelProperty(value="年龄")
    public String age;
    @ApiModelProperty(value="年级")
    private String grade;
    @ApiModelProperty(value="创建人")
    private String createBy;
    @TableField(fill = FieldFill.INSERT)
    @ApiModelProperty(value="创建时间")
    private Date createTime;
    @ApiModelProperty(value="更新人")
    private String updateBy;
    @TableField(fill = FieldFill.UPDATE)
    @ApiModelProperty(value="更新时间")
    private Date updateTime;
    @ApiModelProperty(value="所属租户")
    public String corpCode;
    @TableLogic
    @ApiModelProperty(value="删除状态 0正常 1已删除")
    public String delFlag;

    public Student() {
    }

    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }

    private Student(String id, String name, String age, String grade) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    private void sayHello(String name) {
        System.out.println("hello: " + name);
    }

    public void sayHi(String name) {
        System.out.println("hi: " + name);
    }
}

1、获取Class对象

  获取Class对象的方式有三种,不管哪种方式,都是获取同一个对象。

  • 类明.class 方式获取Class对象
Class clazz1 = Student.class;
  • 实例.getClass() 方式获取Class对象
Student student = new Student();
Class clazz2 = student.getClass();
  • Class.forName(className) 方式获取Class对象
try {
        Class clazz3 = Class.forName("com.franky.modules.student.entity.Student");
        System.out.println("clazz3: " + clazz3);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

  测试类

@SpringBootTest
public class ReflectTest {

    @Test
    public void test(){
        // 类明.class 方式获取Class对象
        Class clazz1 = Student.class;
        System.out.println("clazz1: " + clazz1);

        // 实例.getClass() 方式获取Class对象
        Student student = new Student();
        Class clazz2 = student.getClass();
        System.out.println("clazz2: " + clazz2);

        // Class.forName(className) 方式获取Class对象
        try {
            Class clazz3 = Class.forName("com.franky.modules.student.entity.Student");
            System.out.println("clazz3: " + clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

  控制台输出结果

clazz1: class com.franky.modules.student.entity.Student
clazz2: class com.franky.modules.student.entity.Student
clazz3: class com.franky.modules.student.entity.Student

2、通过获取的Class类获取实例对象

  • 通过Class对象调用newInstance()方法,此方法有一定的局限性,只能调用无参构造方法;
Class clazz1 = Student.class;
System.out.println("clazz1: " + clazz1);
try {
    // 通过Class对象调用newInstance()方法
    Student student1 = (Student)clazz1.newInstance();
    System.out.println(student1.toString());
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}
  • 通过Constructor构造器调用newInstance()方法,此方法可以调用任意的构造方法;
Class clazz1 = Student.class;
try {
    Constructor constructor = clazz1.getConstructor(String.class, String.class);
    try {
        Student student2 = (Student) constructor.newInstance("123", "小米");
        System.out.println(student2.toString());
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}

3、获取类中的变量Field

  • Field[] getFields():获取类中所有被public修饰的所有属性;
Class clazz = Student.class;
Field[] fields = clazz.getFields();
  • Field getField(String name):根据变量名获取类中的一个变量,该变量必须被public修饰;
Class clazz = Student.class;
Field field2 = clazz.getField("age");
  • Field[] getDeclaredFields():获取类中所有的变量,但无法获取继承下来的变量;
Class clazz = Student.class;
Field[] fields2 = clazz.getDeclaredFields();
  • Field getDeclaredField(String name):根据姓名获取类中的某个变量,无法获取继承下来的变量;
Class clazz = Student.class;
Field field1 = clazz.getDeclaredField("name");

4、获取类中的方法Method

  • Method[] getMethods():获取类中被public修饰的所有方法;
  • Method getMethod(String name, Class…<?> paramTypes):根据方法名和参数类型获取对应方法,该方法必须被public修饰;
Class clazz = Student.class;
Student student = (Student)clazz.newInstance();
Method method1 = clazz.getMethod("sayHi", String.class);
method1.invoke(student,"maomao");
  • Method[] getDeclaredMethods():获取所有方法,但无法获取继承下来的方法;
  • Method getDeclaredMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法,在调用私有方法前,需要先获取权限setAccessible(true);
Class clazz = Student.class;
Student student = (Student)clazz.newInstance();
Method method2 = clazz.getDeclaredMethod("sayHello", String.class);
method2.setAccessible(true);
method2.invoke(student,"caiying");

5、获取类中的构造器Constructor

  构造方法归根结底也是方法,除了使用获取Method方法获取构造器外,与此类似Java还提供了获取构造器的独立方法。

  • Constuctor[] getConstructors():获取类中所有被public修饰的构造器;
  • Constructor getConstructor(Class…<?> paramTypes):根据参数类型获取类中某个构造器,该构造器必须被public修饰;
Class clazz = Student.class;
Constructor constructor = clazz.getConstructor(String.class, String.class);
Student student = (Student)constructor.newInstance("123", "maomao");
  • Constructor[] getDeclaredConstructors():获取类中所有构造器,私有构造方法使用前需要先获取权限setAccessible(true);
  • Constructor getDeclaredConstructor(class…<?> paramTypes):根据参数类型获取对应的构造器,私有构造方法使用前需要先获取权限setAccessible(true);
Class clazz = Student.class;
Constructor constructor2 = clazz.getDeclaredConstructor(String.class, String.class, String.class, String.class);
constructor2.setAccessible(true);
Student student2 = (Student)constructor2.newInstance("456", "caiying", "28", "6");
System.out.println(student2.toString());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值