框架学习必备!java基础学习之《反射》

本文阅读 11 分钟
首页 代码,Java 正文

动态语言和静态语言

动态语言

  • 是一类运行时可以改变结构的语言,例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或者是其它结构上的变化
  • 主要动态语言:Objcet-C,C#,JavaScript,PHP,Python

静态语言

  • 运行时结构不可改变的语言,Java,C,C++
  • Java不是动态语言,但是可以称之为准动态语言,具有一定的动态性,我们可以通过反射机制获得类似动态语言的特性,时代码更加灵活

反射定义

反射能够分析类能力的程序叫做反射

反射机制可以用来:

  1. 在运行时分析类的能力
  2. 在运行时检查对象,例如,编写一个适用于所有类的tostring方法
  3. 实现泛型数组操作代码
  4. 利用Method对象,这个对象很像C++中的函数指针

Class类

在程序运行期间,java运行时系统始终为所有对象维护一个运行时类型标识,这个信息会跟踪每个对象所属的类,虚拟机利用运行时类型信息选择要执行的正确方法。而使用一个特殊的java类来访问这些信息,保存这些信息的类名为Class,Object类中的getClass()方法可以返回一个Class类型的实例。请看如下代码:

编写一个父类Student:

public class Student { 
    private String name;
    private String school;
    private String sex;
    private int age;
}

编写一个子类XiaoMing继承Student:

public class XiaoMing extends Student{ 
    private String home;
    private String phone;
}

编写测试类主函数:

import java.util.Random;

public class Test { 
    /** * 反射机制的测试 * @param args */
    public static void main(String[] args) { 
        Student s = new Student();
        Class<Student> aClass = s.getClass();
        System.out.println(aClass.getName());
        //输出“Student”
        System.out.println(aClass);
        //输出“class Student”

        Student s2=new XiaoMing();
        Class aClass2 = s2.getClass();
        System.out.println(aClass2.getName());
        //输出“XiaoMing”
        System.out.println(aClass2);
        //输出“class XiaoMing”

        //如果对象在一个包里,那么包名也可以作为类名的一部分
        Random random=new Random();
        Class cl=random.getClass();
        System.out.println(cl.getName());
        //输出java.util.Random

        //还可以使用静态方法Class.formName()获得类名对应的Class对象
        String name="java.util.Random";
        try { 
            Class c=Class.forName(name);
            System.out.println(c);
            //输出class java.util.Random,代表获得这个包下对应类的Class
        } catch (ClassNotFoundException e) { 
            e.printStackTrace();
        }
    }
}

获得Class类其实有另一种更加便捷的方法,如果T是任意类型的java类型(或者void关键字),T.Class就是代表匹配的类对象。

//Class其实是个泛型类,只不过我们将他省略了
Class c3=Random.class;
System.out.println(c3);
//输出java.util.Random
Class c4=int.class;
System.out.println(c4);
//输出int

虚拟机为每个类型管理一个唯一的Class对象,可以用运算符”==“来实现两个类的对象比较:

同样是上面的例子,看如下代码

public class Test2 { 
    public static void main(String[] args) { 
        Student student=new Student();
        Student student1=new XiaoMing();
        System.out.println(student.getClass()==Student.class);//输出true
        System.out.println(student1.getClass()==XiaoMing.class);//输出true
        System.out.println(student1.getClass()==Student.class);//输出false
        /** *1.instanceof 的左右两边必须是引用类型,java 八大基本数据类型无法使用 instanceof 来进行对比 2.instanceof 用来判定左边的引用类型是否与右边的引用类型的类型是否相同,或左边引用类型是右边引用类型的子类或实现类(右边引用类型可以是类、抽象 类、接口) 3.instanceof 的对比结果为 boolean 类型,如果左右两边比对成功,返回 true ;否则返回 flase 4.null 与任何引用类型进行 instanceof 对比的结果都是 flase,null 不属于任何类型,更不属于 object 基类的派生类(子类),需要特别注意 */
        System.out.println(student1 instanceof Student);//输出true

        XiaoMing xiaoMing=new XiaoMing();
        System.out.println(xiaoMing.getClass()==XiaoMing.class);//输出true
        /** * 思考:那么, System.out.println(xiaoMing.getClass()==Student.class);会输出什么呢? */
    }
}

分析:

  1. 父类声明,父类实现变量student,当打印是否和Student.class相等时,因为是父类型的对象,应该打印true
  2. 父类声明,子类实现变量student1,与instanceof不同的是,当与Student比较时,会输出false,应为每一个类型是唯一的Class对象,子类实现,那么就是子类的类型,与父类进行比较,那么一定会输出false
  3. 子类声明,子类实现xiaoMing,与XiaoMing比较时,一定会输出true

思考题:

会报错,类型无法比较,其实运算符左边返回的是Class,但是运算符右边是Class,Class是一个泛型类,只不过我们通常不写,在这里进行比较时,泛型变量不一致,所以无法比较

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yom3TZPA-1627018937020)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20210721122720970.png)]

创建类实例的另一种办法

如果又一个Class类型的对象,可以用它来创造实例,看一下代码

import java.lang.reflect.InvocationTargetException;

public class Test3 { 
    public static void main(String[] args) { 
        /** * 创建类实例的另一种方法 */
        String s="Student";
        Object student=null;
        try { 
            Class cl=Class.forName(s);
            student=cl.getConstructor().newInstance();
        } catch (ClassNotFoundException | NoSuchMethodException e) { 
            e.printStackTrace();
        } catch (InvocationTargetException e) { 
            e.printStackTrace();
        } catch (InstantiationException e) { 
            e.printStackTrace();
        } catch (IllegalAccessException e) { 
            e.printStackTrace();
        }
        //判断是否真的创建了一个Student实例
        System.out.println(student.getClass()==Student.class);//输出true
    }
}

可以通过Class.formName来找到Class对象,再通过getConstrutor来创建一个Constructor对象,再用newInstance方法来实例化对象

一个类在内存中只有一个Class

public class test { 


    public static void main(String[] args) throws ClassNotFoundException { 
        Class c1 = Class.forName("User");
        System.out.println(c1);

        Class c2 = Class.forName("User");
        Class c3 = Class.forName("User");
        Class c4 = Class.forName("User");

        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
    }
}
class User{ 
    private String name;
    private int id;
    private int age;
    public User(){ 

    }
    (此处为了简洁,省略有参构造方法以及get,set方法,请读者务必写上)
}
//输出class User
//1163157884
//1163157884
//1163157884

利用反射分析类的能力

三个类:

  1. Field:这个类描述被分析类的实例字段
  2. Method:这个类描述被分析类的类方法
  3. Construtor:这个类描述被分析类的构造器

例如:

Field[] field=Student.class.getDeclaredFields();
for (Field f:
     field) { 
    System.out.println(f.getName().toString());
}
/** * 输出 * name * school * sex * age */

具体可以查看api文档

调用任何方法和设置属性值

这一块内容在学习Spring时需要用到,而且底层基本上不会看见new这个关键字,所以我整理了一部分,按照这样的方法将field,method,和Construtor都自己学一遍是很有必要的,所以,请认真体会并且一定要实操,否则过目则忘! User

public class User { 
    private String name;
    public String sex;
    public User() { 
    }

   (此处为了简洁,省略有参构造方法以及get,set方法,请读者务必写上)
}

通过反射获取对象的属性以及直接对其属性赋值

import java.lang.reflect.Field;

public class Main { 

    /** * 关于对象属性的操作需要掌握的几个方法以及传参的意义 */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException { 

        User user=User.class.newInstance(); //new一个新对象,利用无参构造器
        System.out.println(user);//输出“User{name='null', sex='null'}”,此时对象是空
        /** * 测试getDeclaredField方法 */
        Field field=User.class.getDeclaredField("name");//获得指定参数名属性的Filed,private 和 public 都可以获取到
        field.setAccessible(true);//可以理解为将对象的属性设置为可编辑,不然会抛出一个IllegalAccessException异常提示:modifiers "private"
        field.set(user,"芜湖");//将用无参构造器构造出来的user设置name属性值为”芜湖“
        System.out.println(user);//此时输出为:{name='芜湖', sex='null'}
        System.out.println(field);//输出“private java.lang.String User.name”
        System.out.println(field.getName());//输出“name”,只输出名字

        /** * 测试getField方法 */
        Field field1=User.class.getField("sex");//获得指定参数名属性的Filed,只能获取public
        field1.set(user,"男");//此时输出为User{name='芜湖', sex='男'},表名以及成功赋值
        System.out.println(user);

        /** * 测试getDeclaredFields方法 */
        Field[] fields = User.class.getDeclaredFields();//获得对象类中的全部属性,private 和 public 都可以获取到
        Field[] fields1 = User.class.getFields();//获得对象类中的公共属性,只能获取 public
        System.out.println(field1);//输出“public java.lang.String User.sex”
        System.out.println(field1.getName());//输出“sex”

        for (Field field2 : fields) { 
            /* 输出: name sex */
            System.out.println(field2.getName());
        }

        for (Field field2 : fields1) { 
            /* 输出: sex */
            System.out.println(field2.getName());
        }
    }


}

通过反射机制调用方法

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main2 { 
    /** * 关于对象的方法需要掌握的一些方法以及使用的技巧 */

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { 
        User user=User.class.newInstance();

        /** * 测试 getMethod 方法,此方法第一个参数为对象中的方法名,第二个参数为需要传入的值的类型class对象,例如传入String就是String.class,如果不需要传入就输入null * 执行方法需要使用获取到的Method中的invoke方法,返回对应的参数 * invoke第一个参数是对象的实例,第二个参数是传入的值 * 每次执行invoke都有返回值,返回值是Object,如果是void函数,那么返回的是个null,否则为该方法的实际返回值 */
        Method method=user.getClass().getMethod("getName",null);
        Method method1=user.getClass().getMethod("setName", String.class);
        System.out.println(method);//输出”public java.lang.String User.getName()“
        Object invoke = method.invoke(user, null);
        System.out.println(invoke);//输出”null“
        Object a = method1.invoke(user, "芜湖");
        System.out.println(a);//输出”null“
        System.out.println(user);//输出”User{name='芜湖', sex='null'}“
    }
}

打印操作示例代码

import java.lang.reflect.Method;

public class Test03 { 

    public static void main(String[] args) throws ClassNotFoundException { 
        Class c1 = Class.forName("pojo");


        Method[] declaredMethods = c1.getDeclaredMethods();//本类中所有的方法甚至私有方法的也能打印
        for(Method method:declaredMethods)
        { 
            System.out.println(method.toString());
        }

        System.out.println("===============================================");
        Method[] methods = c1.getMethods();//本类中所有的方法以及父类中所有的方法,但是都只能打印public方法,不能打印私有方法
        for(Method method:methods)
        { 
            System.out.println(method.toString());
        }

        /** * 类似的还有Field,Construtor都是一样的用法,这里就不一一赘述,declared作为前缀的都是可以打印私有方法的 */

    }

}
class pojo{ 
    private int a;
    private int b;
    private int c;

   (此处为了简洁,省略有参,无参构造方法以及get,set方法,请读者务必写上)
}

反射操作注解

ORM:对象映射关系,例如

class Student{     
    int id;    String name;    
    int age;
}

对应表

img

利用反射和注解完成类和表结构的映射关系

综合代码:

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Test04 { 


    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { 

        //获得Class
        Class c1=Class.forName("student");

        //通过反射机制获得类注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) { 
            System.out.println(annotation.toString());//输出:”@TableAnnotation(value=db_student)“
        }

        //通过反射机制获得类注解的value值
        TableAnnotation annotation = (TableAnnotation)c1.getAnnotation(TableAnnotation.class);
        System.out.println(annotation.value());//输出:”db_student“

        //通过反射机制获取类中属性的注解

        Field[] declaredFields = c1.getDeclaredFields();//这是获取全部属性
        for (Field field : declaredFields) { 
            FieldAnnotation annotation1 = field.getAnnotation(FieldAnnotation.class);
            System.out.println(annotation1);
            /* 输出: @FieldAnnotation(columnName=id, columnType=int, length=10) @FieldAnnotation(columnName=name, columnType=varchar, length=10) @FieldAnnotation(columnName=age, columnType=int, length=10) */
        }

        Field name = c1.getDeclaredField("name");//获取单个属性的注解,再输出单个注解的属性值
        FieldAnnotation annotation12 = name.getAnnotation(FieldAnnotation.class);
        System.out.println(annotation12.columnName());//输出:”name“
        System.out.println(annotation12.columnType());//输出:”varchar“
        System.out.println(annotation12.length());//输出:”10“

    }

}


@TableAnnotation("db_student")//从这里可以读出,在数据库中,存储实体类的叫db_student
class student{ 
    @FieldAnnotation(columnName = "id",columnType = "int",length = 10)//从这里可以读出,在数据库中该字段名是columnName的值也就是id,数据库中的存储类型是int,长度是10
    private int id;
    @FieldAnnotation(columnName = "name",columnType = "varchar",length = 10)
    private String name;
    @FieldAnnotation(columnName = "age",columnType = "int",length = 10)
    private int age;

   (此处为了简洁,省略有参构造方法以及get,set方法,请读者务必写上)
}

//自定义注解,用于存储表名,作用在类上
@Target(ElementType.TYPE)//类注解
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@interface TableAnnotation{ 
    String value();
}



//自定义注解,作用于类属性,可以用于表示数据库中字段的基本信息
@Target(ElementType.FIELD)//作用于属性
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{ 
    //规范:属性类型+属性名()
    String columnName();
    String columnType();
    int length();
}
本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/m0_46435741/article/details/119031933
-- 展开阅读全文 --
Redis底层数据结构--跳跃表
« 上一篇 04-28
BUUCTF Web [强网杯 2019]随便注
下一篇 » 06-24

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复