JavaSE -进阶基础---反射技术

反射常见用法:

 Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机制。
一个实体类

@Data
public class Student {
    private String name;
    private int color;

    @Name("方法")
    public void sayHello(String Kname) {
        this.name = Kname;
        System.out.println(Kname);
    }
}

Java 反射的主要组成部分有4个:

  • Class:任何运行在内存中的所有类都是该 Class 类的实例对象,每个 Class 类对象内部都包含了本来的所有信息。

  • Field:描述一个类的属性,内部包含了该属性的所有信息,例如数据类型,属性名,访问修饰符······

  • Constructor:描述一个类的构造方法,内部包含了构造方法的所有信息,例如参数类型,参数名字,访问修饰符······

  • Method:描述一个类的所有方法(包括抽象方法),内部包含了该方法的所有信息,与Constructor类似,不同之处是 Method 拥有返回值类型信息,因为构造方法是没有返回值的。

一切java反射操作是基于目标类的类对象完成

java获取这个类对象的三种方式

	//方式一(通过建立对象)
        Student stu = new Student() ;
        Class classobj1 = stu. getClass();
        System.out.print1n(classobj1.getName());
        //方式二(所在通过路径-相对路径)
        Class classobj2 = Class.forName ("com.gton.entity.Student") ;
        System.out.println(classobj2.getName()) ;
        //方式三(通过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName()) ;

类比拓展java创建对象的几种方式

- 使用关键字  new  调用类的构造函数 创建对象
- 使用反射技术:
 	Class<?> forName = Class.forName("com.entity.Student");
	//JDK1.8 及以前 class.newInstance() 可以实例化一个对象
        Student student = (Student) forName.newInstance();
        //JDK9之后,包括11
        Object newInstance = forName.getDeclaredConstructor().newInstance();
- 使用关键字 clone clone,jvm创建一个实例对象,将数据复制进去,不调用构造函数,实现接口Cloneable,重写 clone方法,clone需要捕获异常
- 反序列化 重文件创建  实现接口Serializable,需要捕获异常 运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.

**具体如下**
 // 方法一:new object();
        TestOneView first = new TestOneView();
        // 方法二:使用Object.clone()
        // 因为clone放回的是Object类,所以需要强转一下
        TestOneView second = (TestOneView) first.clone();
        // 方法三:反射
        TestOneView third_1 = (TestOneView) Class.forName("ys.manufacture.ipc.action.TestOneView").newInstance();
        TestOneView third_2 = second.getClass().newInstance();
        // 方法四:构造方法
        TestOneView forth = second.getClass().getConstructor().newInstance();
        // 方法五:序列化
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("TestOneView.per"));
        outputStream.writeObject(first);
        outputStream.writeObject(second);
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("TestOneView.per"));
        TestOneView fifth = (TestOneView) inputStream.readObject();

总结创建对象的方法:

1.用new语句创建对象,这是最常用的创建对象的方式。
2.运用反射手段,调用Java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
3.调用对象的clone()方法。
4.运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.

反射基本运用

反射操作这个类

 @SneakyThrows
    @Test
    public void Star() throws ClassNotFoundException {
        Class<?> forName = Class.forName("com.entity.Student");
        //利用反射创建对象 JDK1.8 及以前 class.newInstance() 可以实例化一个对象
        Student student = (Student) forName.newInstance();
        //JDK9之后,包括11
        Object newInstance = forName.getDeclaredConstructor().newInstance();
        //获取所有的方法
        Method[] methods = forName.getMethods();
        for (Method method : methods) {
            System.out.print(method.getName() + ":");
            //反射调用方法
            if ("sayHello".equals(method.getName())) {
                //获取指定方法:第一个参数方法名。第二个参数是参数类型
                Method sayHello = forName.getMethod("sayHello", String.class);
                sayHello.invoke(student, "Hello World");
                //获取构造器
                Constructor<?> constructor = forName.getConstructor();
                Student o = (Student) constructor.newInstance();
                o.setName("Hello");
                System.out.println(o);
                //获取get-set
            }
        }
        //获取指定方法:第一个参数方法名。第二个参数是参数类型
        Method sayHello = forName.getMethod("setName", String.class);
        sayHello.invoke(student, "Hello setName");
        System.out.println(student);

        //获取方法上的注解
        sayHello = forName.getMethod("sayHello", String.class);
        Name annotation = sayHello.getAnnotation(Name.class);
        //获取注解的值
        String value = annotation.value();
        System.out.println(value);

        //获取字段
        Field[] fields = forName.getDeclaredFields();
        for (Field field : fields) {
            //属性是私有的,需要设置跳过安全模式取消
            field.setAccessible(true);
            System.out.println(field);
        }
        //获取当前本类属性 作用域 属性类型 属性名
        Field name = forName.getDeclaredField("name");
        System.out.println(name);

        //生成一个空白的对象
        Object newObj = forName.newInstance();
        //允许私有属性被操作
        name.setAccessible(true);
        //反射给这个对象的·私有属性设置值
        name.set(newObj, "设置的名字");
        System.out.println(newObj);
    }

反射的优点:

  • 可扩展性 :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。

  • 类浏览器和可视化开发环境 :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。

  • 调试器和测试工具 : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。

反射的缺点:

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。

  • 性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。

  • 安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。

  • 内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

posted on 2021-06-21 17:19  白嫖老郭  阅读(51)  评论(0编辑  收藏  举报

导航