当前位置 : 主页 > 编程语言 > java >

Java的枚举,注解和反射(二)

来源:互联网 收集:自由互联 发布时间:2021-08-21
目录 反射 什么是反射? 反射的用途 反射的具体作用 反射的主要API Class类 总结 反射 什么是反射? 反射是指在程序运行期间,可以通过Reflection Api提供方法可以获取任何类的内部的信
目录
  • 反射
    • 什么是反射?
    • 反射的用途
    • 反射的具体作用
    • 反射的主要API
    • Class类
  • 总结

    反射

    什么是反射?

    反射是指在程序运行期间,可以通过Reflection Api提供方法可以获取任何类的内部的信息,并能直接操作任意类的方法和属性。反射被视为动态语言的关键。

    //在反射之前可以做的事情
        @Test
        public void Test1() {
            //创建Person类的对象
            Person person = new Person("name", 78);
            //通过对象调用其内部的方法和属性
            person.setAge(20);
            System.out.println(person.toString());
            person.show();
            //在Person类的外部,不能通过对象调用其内部的私有的结构
        }
        //在反射之后可以做的事情
        @Test
        public void Test2() throws Exception {
            //通过反射创建Person类的对象
            Class classPerson = Person.class;
            Constructor constructor = classPerson.getConstructor(String.class, int.class);
            Object object = constructor.newInstance("Tom", 13);
            Person person = (Person) object;
            System.out.println(person.toString());
            //通过反射获取Person内部的属性和方法
            Field name = classPerson.getField("name");
            name.set(person, "Jack");
            System.out.println(person.toString());
            //调方法
            Method show = classPerson.getDeclaredMethod("show");
            show.invoke(person);
            //调用私有的构造方法
            Constructor constructor1 = classPerson.getDeclaredConstructor(String.class);
            constructor1.setAccessible(true);
            Person person1 = (Person) constructor1.newInstance("Marry");
            System.out.println(person1);
            //调用私有的方法
            Method showNation = classPerson.getDeclaredMethod("showNation", String.class);
            showNation.setAccessible(true);
            showNation.invoke(person1, "中国");
        }
    

    结果:

    未使用反射

    在这里插入图片描述

    使用反射:

    在这里插入图片描述

    Person类

    package reflection;
    /**
     * user:ypc;
     * date:2021-06-20;
     * time: 13:55;
     */
    public class Person {
        public String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Person() {
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public Person(String name, int age) {
            this.age = age;
            this.name = name;
        }
        private Person(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
        public void show() {
            System.out.println("I am a person");
        }
        private String showNation(String nation) {
            return nation;
        }
    
    }
    

    反射的用途

    1、在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。

    2、反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。

    Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类。

    在类被加载完成之后,就会在堆区,产生一个Class对象,这个对象就包含了存这个类的全部的结构信息。我们就可以通过这个对象看到这个类的全部的信息。

    这个对象就像一面镜子,通过这个镜子可以看到这个类的全部的信息结构。所以称之为反射。

    正常的方式:通过引入需要导入的包类的名称---->通过new来实例化---->得到实例化的对象

    反射的方式:实例化对象---->通过getClass()方法---->得到完整的包类的名称

    反射的具体作用

    • 在运行的时候判断任意的一个对象所属的类
    • 在运行的时候构造任意一个类的对象
    • 在运行 的时候判断任意一个类的成员变量和方法
    • 在运行的时获取泛型的信息
    • 在运行的时候调用任意一个类的成员变量和方法
    • 在运行的时候处理注解
    • 生成动态代理

    反射的主要API

    类名 用途

    Class类 代表类的实体,在运行的Java应用程序中表示类和接口

    Field类 代表类的成员变量/类的属性

    Method类 代表类的方法

    Constructor类 代表类的构造方法

    通过直接new 的方式和反射 都可以直接调用公共的结构,在开发的时候应该使用哪一个呢?

    建议:使用new 的方式来创建对象。

    什么时候使用反射呢?

    反射的特性:动态性。就是在编译的时候不知道要创建什么样的对象的时候,可以使用反射方式来创建对象。比如在后端部署的服务器,前端传来的时登录的请求的话,就创建登录对应的对象。前端传来的是注册所对应的请求的话,就创建登录所对应的对象,这就是反射的动态特性。

    反射的机制和封装是不矛盾的呢?

    封装是告诉你不要调,反射可以调。

    Class类

    在Object类中定义了以下的方法,此方法将被所有子类继承:

    public final Class getClass()
    

    以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称。

    对象使用反射后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构( class/interface/enum/annotation/primitive type/void/[])的有关信息。也就是这些类型可以有Class对象:class 成员内部类、 静态内部类、 局部内部类 、 匿名内部类、接口、数组、枚举、注解、基本的数据类型、void等。

    注意:

    Class本身也是一个类

    Class对象只能由系统建立对象

    一个加载的类在JVM中只会有一个Class实例

    一个Class对象对应的是一个加载到JVM中的一个.class文件

    每个类的实例都会记得自己是由哪个Class实例所生成

    通过Class可以完整地得到一个类中的所有被加载的结构

    Class类是Reflection的根源,针对任何你想动态加载、运行的类,只有先获得相应的Class对象,才能继续下去。

    关于java.lang.Class的理解:

    类的加载过程:

    程序通过javac.exe命令,生成一个或多个字节码文件。接着使用java.exe命令来对某个字节码文件来解释运行。将字节码文件加载到内存中,这个过程称为类的加载。加载到内存中的类,就被称为运行时类,此运行使类就称为Class 的一个实例。

    Class 实例就对应着一个运行时类,加载到内存中的运行时类,会缓存一段时间。在此时间之内,可以通过不同的方式来获取运行时类。

    获取Class实例的四种方式

        //     Class 实例就对应着运行时类
        @Test
        public void test3() throws ClassNotFoundException {
            //方式1 调用运行时类的属性:.class
            Class clazz1 = Person.class;
            System.out.println(clazz1);
            //方式2 通过运行时类的对象来调用
            Person person = new Person();
            Class clazz2 = person.getClass();
            System.out.println(clazz2);
    
            //方式3 通过Class的静态方法 forName(类的全路径名称)
            Class clazz3 = Class.forName("reflection.Person");
            System.out.println(clazz3);
            //方式4 通过类加载器:ClassLoader
            ClassLoader classLoader = ReflectionDemo1.class.getClassLoader();
            Class clazz4 = classLoader.loadClass("reflection.Person");
            System.out.println(clazz4);
            System.out.println(clazz1 == clazz2);
            System.out.println(clazz1 == clazz3);
            System.out.println(clazz1 == clazz4);
        }
    

    在这里插入图片描述

    通过反射创建运行时类的对象

    package reflection;
    import org.junit.Test;
    import java.util.Random;
    /**
     * user:ypc;
     * date:2021-06-21;
     * time: 20:36;
     */
    public class NewInstanceDemo {
        @Test
        public void test1() throws IllegalAccessException, InstantiationException {
            Class<Person> personClass = Person.class;
            /*
            newInstance()方法可以创建运行时类的实列,其内部也时调用内的无参的构造方法来创建对象
             */
            Person person = personClass.newInstance();
            System.out.println(person);
        }
        @Test
        public void test2() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
            //在编译的时候不知道要创建的对象。只有运行的时候才知道要创建的对象
            for (int i = 0; i < 100; i++) {
                int num = new Random().nextInt(3);
                String classPath = "";
                switch (num) {
                    case 0:
                        classPath = "java.util.Date";
                        break;
                    case 1:
                        classPath = "java.lang.Object";
                        break;
                    case 2:
                        classPath = "reflection.Person";
                        break;
                }
                Object object = getInstance(classPath);
                System.out.println(object);
            }
        }
        //创建一个指定类的对象
        public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            Class clazz = Class.forName(classPath);
            return clazz.newInstance();
        }
    }
    

    通过反射获取运行类的属性及权限修饰符、变量名 、数据的类型

        @Test
        public void test1(){
            //getFields()获取的是运行时类及其父类中public的属性
            Class clazz = Person.class;
            Field[] fields = clazz.getFields();
            for (Field field: fields) {
                System.out.println(field);
            }
            System.out.println();
            //getDeclaredFields():获取当前运行类的所有属性
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field:declaredFields) {
                System.out.println(field);
            }
        }
    

    在这里插入图片描述

        //权限修饰符  变量名  数据的类型
        @Test
        public void test2(){
            Class clazz = Person.class;
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field:declaredFields) {
                //权限修饰符
                System.out.println(Modifier.toString(field.getModifiers())+"\t");
    
                //变量名
                System.out.println(field.getType()+"\t");
                //数据的类型
                System.out.println(field.getName()+"\t");
            }
        }
    

    在这里插入图片描述

    通过反射获取运行时类的方法结构及其内部结构

    package reflection2;
    import org.junit.Test;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    /**
     * user:ypc;
     * date:2021-06-22;
     * time: 13:32;
     */
    public class MethodDemo {
        @Test
        public void test1() {
            //获取当前运行时类及其父类中所有声明为public的方法
            Class clazz = Person.class;
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            System.out.println();
            //获取当前运行时类所有的方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println(method);
            }
        }
        //@xxx注解
        //权限修饰符  方法结构   返回值的类型
        @Test
        public void test2() {
            Class clazz = Person.class;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                //获取方法的注解
                Annotation[] annotations = method.getDeclaredAnnotations();
                for (Annotation annotation : annotations) {
                    System.out.print(annotation);
                }
                //获取权限的修饰符
                System.out.print(Modifier.toString(method.getModifiers()) + "\t");
                //获取返回值的类型
                System.out.print(method.getReturnType().getName() + "\t");
                //方法名
                System.out.print(method.getName());
                System.out.print("(");
                //获取形参的列表
                Class[] parameterTypes = method.getParameterTypes();
                if (!(parameterTypes == null && parameterTypes.length == 0)) {
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (i == parameterTypes.length - 1) {
                            System.out.print(parameterTypes[i].getName() + " args__" + i);
                        } else {
                            System.out.print(parameterTypes[i].getName() + " args__" + i + ",");
                        }
                    }
                }
                System.out.print(")");
                //获取方法的异常
                Class[] exceptionTypes = method.getExceptionTypes();
                if (exceptionTypes.length != 0) {
                    System.out.print("throws");
                    for (int i = 0; i < exceptionTypes.length; i++) {
                        if (i == exceptionTypes.length - 1) {
                            System.out.print(exceptionTypes[i].getName());
                        } else {
                            System.out.print(exceptionTypes[i].getName() + ",");
                        }
                    }
                }
                System.out.println();
            }
        }
    }
    

    test1():

    在这里插入图片描述

    test2():

    在这里插入图片描述

    通过反射获取运行时类的构造结构

        @Test
        public void test1() {
            Class clazz = Person.class;
            //获取运行时类的public构造方法
            Constructor[] constructors = clazz.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println();
            //获取当前运行时类中的所有的构造方法
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for (Constructor declaredConstructor : declaredConstructors) {
                System.out.println(declaredConstructor);
            }
        }
    

    在这里插入图片描述

    通过反射获取运行时类的父类和父类的泛型、注解、接口、所在包

    反射所使用到的包、接口、类、注释等👇

    在这里插入图片描述

    package reflection2;
    /**
     * user:ypc;
     * date:2021-06-21;
     * time: 21:38;
     */
    public interface MyInterface {
        void info();
    }
    
    package reflection2;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import static java.lang.annotation.ElementType.*;
    
    @Target({TYPE, FIELD,CONSTRUCTOR,ANNOTATION_TYPE,PARAMETER,LOCAL_VARIABLE,METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value() default "hello";
    }
    
    package reflection2;
    import java.io.Serializable;
    /**
     * user:ypc;
     * date:2021-06-21;
     * time: 21:13;
     */
    public class Creature<T> implements Serializable {
        private char gender;
        public double weight;
        private void breath(){
            System.out.println("creature breath");
        }
        public void eat(){
            System.out.println("creature eat");
        }
    }
    
    package reflection2;
    @MyAnnotation(value = "hi")
    public class Person extends Creature<String> implements Comparable, MyInterface {
        private String name;
        public int age;
        int id;
        Person() {
        }
        @MyAnnotation(value = "a")
        private Person(String name) {
            this.name = name;
        }
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        private String show(String nation) throws NullPointerException,CloneNotSupportedException {
            System.out.println("I am form" + nation);
            return nation;
        }
        private static void showInterests(){
            System.out.println("I like programmer");
        }
        @MyAnnotation(value = "b")
        public String show2(String publicMethod) {
            return publicMethod;
        }
        @Override
        public int compareTo(Object o) {
            return 0;
        }
        @Override
        public void info() {
            System.out.println("I am a Person");
        }
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", id=" + id +
                    '}';
        }
    }
    
    package reflection2;
    import org.junit.Test;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.ParameterizedType;
    import java.util.Arrays;
    /**
     * user:ypc;
     * date:2021-06-22;
     * time: 15:11;
     */
    public class ConstructorDemo {
        @Test
        public void test1() {
            Class clazz = Person.class;
            //获取运行时类的public构造方法
            Constructor[] constructors = clazz.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println();
            //获取当前运行时类中的所有的构造方法
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for (Constructor declaredConstructor : declaredConstructors) {
                System.out.println(declaredConstructor);
            }
        }
        @Test
        public void test2() {
            //获取运行时类的父类
            Class clazz = Person.class;
            System.out.println(clazz.getSuperclass());
            //获取运行时类带泛型的父类
            System.out.println(clazz.getGenericSuperclass());
            //获取父类所带的泛型
            System.out.println(((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0]);
            System.out.println();
            //获取运行时类的接口
            Class[] interfaces = clazz.getInterfaces();
            for (Class intF : interfaces) {
                System.out.println(intF);
            }
            System.out.println();
            //获取运行类的父类的接口
            Class[] supInterfaces = clazz.getSuperclass().getInterfaces();
            for (Class intF : supInterfaces) {
                System.out.println(intF);
            }
            //获取运行时类的包
            System.out.println(clazz.getPackage());
            System.out.println();
            //获取运行时类的注解
            System.out.println(Arrays.toString(clazz.getAnnotations()));
            ;
        }
    }
    

    在这里插入图片描述

    通过反射调用运行时类的指定属性、方法、构造方法

    package reflection2;
    import org.junit.Test;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    /**
     * user:ypc;
     * date:2021-06-22;
     * time: 15:57;
     * 调用运行时类的指定的结构:方法、属性、构造方法
     */
    public class ReflectionDemo {
        @Test
        public void test1() throws Exception {
            Class clazz = Person.class;
            //创建运行时类的实例
            Person person = (Person) clazz.newInstance();
            //获取指定的属性,要求运行时类的属性为public
            Field id = clazz.getField("id");
            id.set(person, 10001);
            int fieldId = (int) id.get(person);
            System.out.println(fieldId);
        }
        @Test
        public void test2() throws Exception {
            Class clazz = Person.class;
            //创建运行时类的实例
            Person person = (Person) clazz.newInstance();
            //getDeclaredField获取运行时类指定的属性
            Field name = clazz.getDeclaredField("name");
            //保证当前的属性是可访问的
            name.setAccessible(true);
            //设置指定对象的属性值
            name.set(person, "Tom");
            System.out.println(name.get(person));
        }
        //操作运行时类的方法
        @Test
        public void test3() throws Exception {
            Class clazz = Person.class;
            //创建运行时类的实例
            Person person = (Person) clazz.newInstance();
            //1.获取某个指定的方法 getDeclaredMethod()第一个参数是指明获取的方法的名称,第二个参数是指明获取的方法的参数列表
            Method show = clazz.getDeclaredMethod("show", String.class);
            //2.保证当前的方法是可访问的
            show.setAccessible(true);
            //3.使用invoke()方法 第一个参数是方法的调用者,第二个参数是给方法形参赋值的实参
            show.invoke(person, " CHN");
            //invoke 方法的返回值就是调用的方法的返回值
            Object returnValue = show.invoke(person, " CHN");
            System.out.println(returnValue);
            //调用静态的方法
            Method showInterests = clazz.getDeclaredMethod("showInterests");
            showInterests.setAccessible(true);
            showInterests.invoke(Person.class);
            //如果运行时类地方没有返回值的话,那么invoke的返回值就是null
            System.out.println(showInterests.invoke(Person.class));
        }
    
        /*
        调用运行时类的指定的构造方法
         */
        @Test
        public void test4() throws Exception {
            Class clazz = Person.class;
            //1.参数是构造方法的参数列表
            Constructor constructor = clazz.getDeclaredConstructor(String.class);
            //2.保证此构造方法是可以访问的
            constructor.setAccessible(true);
            //3.调用次构造方法创建运行时类的对象
            Person person = (Person) constructor.newInstance("Tom");
            System.out.println(person);
        }
    }
    

    test1():

    在这里插入图片描述

    test2():

    在这里插入图片描述

    test3():

    在这里插入图片描述

    test4():

    在这里插入图片描述

    总结

    本篇文章的上半部分就到这里了,希望对你有所帮助,也希望您能够多多关注自由互联的更多内容!

    网友评论