• 15-4 创建运行时类的对象和获、调用运行时类的完整结构


    创建运行时类的对象

    有了Class对象,能做什么?

    创建类的对象:调用Class对象的newInstance()方法
    要 求:

    1. 类必须有一个无参数的构造器。
    2. 类的构造器的访问权限需要足够。

    难道没有无参的构造器就不能创建对象了吗?
    不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。步骤如下:

    1. 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
    2. 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    3. 通过Constructor实例化对象。

    以上是反射机制应用最多的地方


    流程

    1. 根据全类名获取对应的Class对象
    String name =atguigu.java.Person";
    Class clazz = null;
    clazz = Class.forName(name);
    
    • 1
    • 2
    • 3
    1. 调用指定参数结构的构造器,生成Constructor的实例
    Constructor con = clazz.getConstructor(String.class,Integer.class);
    
    • 1
    1. 通过Constructor的实例创建对应类的对象,并初始化类属性
    Person p2 = (Person) con.newInstance("Peter",20);
    System.out.println(p2);
    
    • 1
    • 2

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

    @Test
        public void test() throws Exception {
            Class<Person> clazz = Person.class;
            /**
             * newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
             *
             * 要想此方法正常的创建运行时类的对象,要求:
             * 1.运行时类必须提供空参的构造器
             * 2.空参的构造器的访问权限得够。通常,设置为public。
             *
             * 在javabean中要求提供一个public的空参构造器。原因:
             * 1.便于通过反射,创建运行时类的对象
             * 2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
             */
            Person obj = clazz.newInstance();
            System.out.println(obj);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    举例体会反射的动态性

    @Test
        public void test2(){
            for(int i = 0;i < 100;i++){
                int num = new Random().nextInt(3);//0,1,2
                String classPath = "";
                switch(num){
                    case 0:
                        classPath = "java.util.Date";
                        break;
                    case 1:
                        classPath = "java.lang.Object";
                        break;
                    case 2:
                        classPath = "reflection.Person";
                        break;
                }
                try {
                    Object obj = getInstance(classPath);
                    System.out.println(obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 创建一个指定类的对象。
         * classPath:指定类的全类名
         *
         * @param classPath
         * @return
         * @throws Exception
         */
        public Object getInstance(String classPath) throws Exception {
            Class clazz =  Class.forName(classPath);
            return clazz.newInstance();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

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

    Field、Method、Constructor、Superclass、Interface、Annotation

    实现的全部接口
    所继承的父类
    全部的构造器
    全部的方法
    全部的Field

    使用反射可以取得:

    1. 实现的全部接口

    public Class[] getInterfaces()
    确定此对象所表示的类或接口实现的接口。

    1. 所继承的父类

    public Class getSuperclass()
    返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的
    Class。

    1. 全部的构造器

    public Constructor< T >[] getConstructors()
    返回此 Class 对象所表示的类的所有public构造方法。
    public Constructor< T >[] getDeclaredConstructors()
    返回此 Class 对象表示的类声明的所有构造方法。

    • Constructor类中:

    取得修饰符: public int getModifiers();
    取得方法名称: public String getName();
    取得参数的类型:public Class[] getParameterTypes();

    1. 全部的方法

    public Method[] getDeclaredMethods()
    返回此Class对象所表示的类或接口的全部方法
    public Method[] getMethods()
    返回此Class对象所表示的类或接口的public的方法

    • Method类中:

    public Class getReturnType()取得全部的返回值
    public Class[] getParameterTypes()取得全部的参数 public int getModifiers()取得修饰符
    public Class[] getExceptionTypes()取得异常信息

    1. 全部的Field

    public Field[] getFields()
    返回此Class对象所表示的类或接口的public的Field。
    public Field[] getDeclaredFields()
    返回此Class对象所表示的类或接口的全部Field。

    • Field方法中:

    public int getModifiers() 以整数形式返回此Field的修饰符
    public Class getType() 得到Field的属性类型
    public String getName() 返回Field的名称。

    1. Annotation相关

    get Annotation(Class annotationClass)
    getDeclaredAnnotations()

    1. 泛型相关
      获取父类泛型类型:Type getGenericSuperclass()
      泛型类型:ParameterizedType
      获取实际的泛型类型参数数组:getActualTypeArguments()
    2. 类所在的包 Package getPackage()

    小 结:

    1. 在实际的操作中,取得类的信息的操作代码,并不会经常开发。
    2. 一定要熟悉java.lang.reflect包的作用,反射机制。
    3. 如何取得属性、方法、构造器的名称,修饰符等。

    提供结构丰富Person类

    Person类

    @MyAnnotation(value="java")
    public class Person extends Creature<String> implements Comparable<String>,MyInterface{
    
        private String name;
        int age;
        public int id;
    
        public Person() {
        }
    
        @MyAnnotation(value="C++")
        Person(String name){
            this.name = name;
        }
    
        private Person(String name,int age){
            this.name = name;
            this.age = age;
        }
    
        @MyAnnotation
        private String show(String nation){
            System.out.println("我来自" + nation + "星系");
            return nation;
        }
    
        @Override
        public void info() {
            System.out.println("火星喷子");
        }
    
        public String display(String play){
            return play;
        }
    
        @Override
        public int compareTo(String o) {
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    Creature类

    import java.io.Serializable;
    
    public abstract class Creature <T> implements Serializable {
        private char gender;
        public double weight;
    
        private void breath(){
            System.out.println("太阳系");
        }
    
        public void eat(){
            System.out.println("银河系");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    自定义MyAnntation
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.*;
    
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value() default "hello world";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    MyInterface接口

    public interface MyInterface {
        void info();
    }
    
    
    • 1
    • 2
    • 3
    • 4

    获取运行时类属性结构和内部结构

    获取当前运行时类的属性结构

    /**
     * 获取当前运行时类的属性结构
     */
    public class FieldTest {
    
        @Test
        public void test1(){
            Class clazz = Person.class;
            //获取属性结构
            //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
            Field[] fields = clazz.getFields();
            for(Field f : fields){
                System.out.println(f);
            }
            System.out.println("*************************");
            //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field f : declaredFields){
                System.out.println(f);
            }
        }
    
        //权限修饰符  数据类型 变量名
        @Test
        public void test2(){
            Class clazz = Person.class;
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field f : declaredFields){
                //1.权限修饰符
                int modifier = f.getModifiers();
                System.out.print(Modifier.toString(modifier) + "\t");
                System.out.println("**************************");
                //2.数据类型
                Class type = f.getType();
                System.out.print(type.getName() + "\t");
                System.out.println("***************************");
                //3.变量名
                String fName = f.getName();
                System.out.print(fName);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    获取运行时类的方法结构

    public class MethodTest {
        @Test
        public void test1(){
            Class clazz = Person.class;
            //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
            Method[] methods = clazz.getMethods();
            for(Method m : methods){
                System.out.println(m);
            }
            System.out.println("****************************");
            //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for(Method m : declaredMethods){
                System.out.println(m);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    获取运行时类方法的内部结构

    /**
         * @Xxxx
         * 权限修饰符  返回值类型  方法名(参数类型1 形参名1,...) throws XxxException{}
         */
        @Test
        public void test2() {
            Class clazz = Person.class;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method m : declaredMethods) {
                //1.获取方法声明的注解
                Annotation[] annos = m.getAnnotations();
                for (Annotation a : annos) {
                    System.out.println(a + "KKKK");
                }
    
                //2.权限修饰符
                System.out.print(Modifier.toString(m.getModifiers()) + "\t");
    
                //3.返回值类型
                System.out.print(m.getReturnType().getName() + "\t");
    
                //4.方法名
                System.out.print(m.getName());
                System.out.print("(");
                //5.形参列表
                Class[] pTs = m.getParameterTypes();
                if(!(pTs == null && pTs.length == 0)){
                    for(int i = 0;i < pTs.length;i++){
                        if(i == pTs.length - 1){
                            System.out.print(pTs[i].getName() + " args_" + i);
                            break;
                        }
                        System.out.print(pTs[i].getName() + " args_" + i + ",");
                    }
                }
                System.out.print(")");
    
                //6.抛出的异常
                Class[] eTs = m.getExceptionTypes();
                if(eTs.length > 0){
                    System.out.print("throws ");
                    for(int i = 0;i < eTs.length;i++){
                        if(i == eTs.length - 1){
                            System.out.print(eTs[i].getName());
                            break;
                        }
                        System.out.print(eTs[i].getName() + ",");
                    }
                }
                System.out.println("TQA");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    获取运行时类的构造器方法

    /**
         * 获取构造器的结构
         */
        @Test
        public void test(){
            Class clazz = Person.class;
            //getConstructors():获取当前运行时类中声明为public的构造器
            Constructor[] constructors = clazz.getConstructors();
            for(Constructor c : constructors){
                System.out.println(c);
            }
            System.out.println("************************");
            //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for(Constructor c : declaredConstructors){
                System.out.println(c);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    获取运行时类的父类以及父类的泛型

        /**
         * 获取运行时类的父类
         */
        @Test
        public void test2(){
            Class clazz = Person.class;
            Class superclass = clazz.getSuperclass();
            System.out.println(superclass);
        }
    
        /**
         * 获取运行时类的带泛型的父类
         */
        @Test
        public void test3(){
            Class clazz = Person.class;
            Type genericSuperclass = clazz.getGenericSuperclass();
            System.out.println(genericSuperclass);
        }
    
        /**
         * 获取运行时类的带泛型的父类的泛型
         */
        @Test
        public void test4(){
            Class clazz = Person.class;
            Type genericSuperclass = clazz.getGenericSuperclass();
            ParameterizedType paramType = (ParameterizedType) genericSuperclass;
            //获取泛型类型
            Type[] actualTypeArguments = paramType.getActualTypeArguments();
    //        System.out.println(actualTypeArguments[0].getTypeName());
            System.out.println(((Class)actualTypeArguments[0]).getName());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    获取运行时类的接口、所在包、注解等

    
        /**
         * 获取运行时类实现的接口
         */
        @Test
        public void test5(){
            Class clazz = Person.class;
    
            Class[] interfaces = clazz.getInterfaces();
            for(Class c : interfaces){
                System.out.println(c);
            }
            System.out.println("*************************");
    
            //获取运行时类的父类实现的接口
            Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
            for(Class c : interfaces1){
                System.out.println(c);
            }
        }
    
        /**
         * 获取运行时类所在的包
         */
        @Test
        public void test6(){
            Class clazz = Person.class;
            Package pack = clazz.getPackage();
            System.out.println(pack);
        }
    
        /**
         * 获取运行时类声明的注解
         */
        @Test
        public void test7(){
            Class clazz = Person.class;
            Annotation[] annotations = clazz.getAnnotations();
            for(Annotation annos : annotations){
                System.out.println(annos);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    调用运行时类的指定结构

    1. 调用指定方法

    通过反射,调用类中的方法,通过Method类完成。步骤:

    1. 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
    2. 之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
      在这里插入图片描述

    Object invoke(Object obj, Object … args)
    说明

    1. Object 对应原方法的返回值,若原方法无返回值,此时返回null
    2. 若原方法若为静态方法,此时形参Object obj可为null
    3. 若原方法形参列表为空,则Object[] args为null
    4. 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

    2. 调用指定属性

    在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。

    public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
    public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

    在Field中:
    public Object get(Object obj) 取得指定对象obj上此Field的属性内容
    public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容


    关于setAccessible方法的使用

    • Method和Field、Constructor对象都有setAccessible()方法。
    • setAccessible启动和禁用访问安全检查的开关。
    • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

    提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。使得原本无法访问的私有成员也可以访问

    • 参数值为false则指示反射的对象应该实施Java语言访问检查。

    调用运行时类中的指定属性

    /**
     * 调用运行时类中指定的结构:属性、方法、构造器
     */
    public class ReflectionTest {
        /**
         * 不需要掌握
         */
        @Test
        public void testField() throws Exception {
            Class clazz = Person.class;
    
            //创建运行时类的对象
            Person p = (Person) clazz.newInstance();
    
            //获取指定的属性:要求运行时类中属性声明为public
            //通常不采用此方法
            Field id = clazz.getField("id");
    
            //设置当前属性的值
            //set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
            id.set(p,1001);
    
            //获取当前属性的值
            //get():参数1:获取哪个对象的当前属性值
            int pId = (int) id.get(p);
            System.out.println(pId);
        }
    
        /**
         * 如何操作运行时类中的指定的属性 -- 需要掌握
         */
        @Test
        public void testField1() throws Exception {
            Class clazz = Person.class;
    
            //创建运行时类的对象
            Person p = (Person) clazz.newInstance();
    
            //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
            Field name = clazz.getDeclaredField("name");
    
            //2.保证当前属性是可访问的
            name.setAccessible(true);
    
            //3.获取、设置指定对象的此属性值
            name.set(p,"Jam");
            System.out.println(name.get(p));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    调用运行时类中的指定方法

    /**
         * 如何操作运行时类中的指定的方法 -- 需要掌握
         */
        @Test
        public void testMethod() throws Exception {
            Class clazz = Person.class;
            //创建运行时类的对象
            Person p = (Person) clazz.newInstance();
    
            //1.获取指定的某个方法
            //getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
            Method show = clazz.getDeclaredMethod("show", String.class);
    
            //2.保证当前方法是可访问的
            show.setAccessible(true);
    
            //3.调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
            //invoke()的返回值即为对应类中调用的方法的返回值。
            Object returnValue = show.invoke(p,"CCA"); //String nation = p.show("CCA");
            System.out.println(returnValue);
    
            System.out.println("*************如何调用静态方法**********");
    
    //    private static void showDesc()
    
            Method showDesc = clazz.getDeclaredMethod("showDown");
            showDesc.setAccessible(true);
            //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    //    Object returnVal = showDesc.invoke(null);
            Object returnVal = showDesc.invoke(Person.class);
            System.out.println(returnVal);//null
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    调用运行时类中的指定构造器

        /**
         * 如何调用运行时类中的指定的构造器
         */
        @Test
        public void testConstructor() throws Exception {
            Class clazz = Person.class;
    
            //private Person(String name)
            //1.获取指定的构造器
            //getDeclaredConstructor():参数:指明构造器的参数列表
            Constructor constructor = clazz.getDeclaredConstructor(String.class);
    
            //2.保证此构造器是可访问的
            constructor.setAccessible(true);
    
            //3.调用此构造器创建运行时类的对象
            Person per = (Person) constructor.newInstance("Tom");
            System.out.println(per);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    如何使用Docker部署开源Leanote蚂蚁笔记并发布个人博客至公网
    C语言实现通讯录详细教学
    微服务—RabbitMQ高级(业务在各方面的可靠性)
    Kotlin编程实战——类与对象(05)
    【ccf-csp题解】第7次csp认证-第二题-俄罗斯方块-简单碰撞检测算法
    再有人问你虚拟机连接问题,把这篇文章丢给他
    K8s: 部署 kubernetes dashboard
    前后端数据互传问题
    IIC驱动OLED
    Java项目:ssm流浪狗领养系统
  • 原文地址:https://blog.csdn.net/qq_44774198/article/details/126000868