Java基础24--反射的应用1:获取类的详细信息

    科技2023-09-10  122

    Java基础24–反射的应用1

    物理: 光的反射、声音的反射 光的反射,例如;镜面反射

    反射: 原来:声明类 --》 创建对象 现在反射: 获取Class对象 --> 创建对象 获取类的详细信息

    Class对象比喻为镜子中的那个影像。

    反射的作用: 运行时(区别与,编译时,状态不一样) 1、在运行时能够获取任意类型的详细信息 2、在运行时能够创建任意引用数据类型的对象 3、在运行时可以为任意对象的任意属性赋值,或者获取任意对象的任意属性的值 4、在运行时可以调用任意对象的任意方法 5、在运行时读取某个注解信息 6、在运行时读取某个类的泛型实参 …

    以上都是在运行时

    补充:动态语言与静态语言 动态的xx 静态的xx

    Java语言是静态语言。 如果没有反射,Java必须在编译期间确定所有类型。Student stu = new Student(); 为了Java也支持动态型,在运行期间,确定某个类型的功能,才引入了反射机制。准动态语言。 只是延迟了确定对象的类型而已。

    Javascript(例如,var是类型,没有说具体类型,根据赋的值的类型确认,运行时才确认),pythod是动态语言。

    静态语言:效率高,安全 动态语言:灵活,不安全,边解释边执行,效率低

    反射的应用1:获取类的详细信息

    举例 AtGuiguDemo类

    package com.atguigu.ext.demo; import java.io.Serializable; public class AtGuiguDemo implements Serializable,Comparable<AtGuiguDemo> { private static final long serialVersionUID = 1L; private int id; private String info; private int num; public AtGuiguDemo(int id, String info, int num) { super(); this.id = id; this.info = info; this.num = num; } public AtGuiguDemo() { super(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } @Override public String toString() { return "AtGuiguDemo [id=" + id + ", info=" + info + ", num=" + num + "]"; } @Override public int compareTo(AtGuiguDemo o) { return 0; } //编译没有及时更新,为了兼容老版本的代码 /* @Override public int compareTo(Object o) { return 0; }*/ public static void test(int a){ System.out.println("a = " + a); } }

    将AtGuiguDemo类导到桌面,再将项目中的AtGuiguDemo类删除,项目中没有这个类了(编译期间找不到这个类会报错)

    1、在运行时能够获取任意类型的详细信息

    后期的时候,在框架中,例如:spring框架,会帮我们管理很多的类, 而这些类不是spring写的,而是我们写的,然后在运行期间spring去加载获取的。

    步骤: (1)获取这个类的Class对象(之前讲的4种方法中选择最合适得) (2)获取类的信息(没有代码块,编译后静态代码块去类初始化clinit里面了,非静态代码块去实例初始化里去了,编译后的字节码是没有代码块的) ①包名 ②类名 ③类的修饰符 Modifier ④直接父类 ⑤父接口们 ⑥属性们 ⑦构造器们 ⑧方法们

    config文件下建立一个bean.properties文件,文件里内容如下图 className的值后期去配

    1:获取这个类的Class对象

    @Before//保证test01比test02先运行,即先获得Class对象 public void test01()throws Exception{ //如果这个类名是在配置文件中,先获取类名 Properties pro = new Properties(); pro.load(TestClassInfo.class.getClassLoader().getResourceAsStream("bean.properties")); String className = pro.getProperty("className");//key就是bean.properties文件中=左边的属性名 //(1)获取这个类的Class对象 clazz = Class.forName(className); System.out.println(clazz); }

    因为类名没有配置属性值,所以会报错,报找不到类 若是配置上类名: 在运行 获取到了类名.这时就可以获取类的信息 2:获取类的信息(各个方法不用记,使用时查API就行)

    @Test public void test02(){ //(2)获取类的详细信息 //clazz代表com.atguigu.ext.demo.AtGuiguDemo这个类 //获取包名 Package pkg = clazz.getPackage(); System.out.println("包名:" + pkg.getName()); //获取类名 System.out.println("类名:" + clazz.getName()); //类的修饰符,返回的是一个整数,Modifier类里定义了很多修饰符,每个修饰符底层都对应一个数字(每个数字都是2的n次方,可以数字按位与得出是哪个修饰符,即用一个位来代表有没有出现过) int mod = clazz.getModifiers(); System.out.println("修饰符的值:" + mod); System.out.println("修饰符:" + Modifier.toString(mod)); Class sc = clazz.getSuperclass(); System.out.println("父类的名称:" + sc.getName()); Class[] interfaces = clazz.getInterfaces();//数组的原因是接口可能多个 System.out.println("父接口们:"); for (Class inter : interfaces) { System.out.println(inter.getName()); } //每一个属性就是一个Field的对象 /* * (1)Field[] getFields() 得到所有公共的属性 * (2)Field[] getDeclaredFields() 得到所有声明的属性 */ Field[] fields = clazz.getDeclaredFields(); int count = 0; for (Field field : fields) { count++; int fMod = field.getModifiers(); System.out.println(count + ":属性的修饰符:" + Modifier.toString(fMod)); System.out.println(count + ":属性的数据类型:" + field.getType().getName()); System.out.println(count + ":属性的名称:" + field.getName()); } /* * Constructor[] getConstructors():得到所有的公共的构造器 * Constructor[] getDeclaredConstructors()():得到所有的声明的构造器 */ count = 0; Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors) { count++; int cMod = constructor.getModifiers(); System.out.println(count + ":构造器的修饰符:" + Modifier.toString(cMod)); System.out.println(count + ":构造器的名称:" + constructor.getName()); Class[] parameterTypes = constructor.getParameterTypes(); System.out.println(count + ":构造器的形参列表:" + Arrays.toString(parameterTypes)); } /* (1)Method[] getMethods(); 得到所有公共的方法 * (2)Method[] getDeclaredMethods(); 得到所有声明的方法 */ count=0; Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method method : declaredMethods) { count++; int mMod = method.getModifiers(); System.out.println(count + ":方法的修饰符:" + Modifier.toString(mMod)); System.out.println(count +":方法的返回值类型:" + method.getReturnType()); System.out.println(count + ":方法的名称:" + method.getName()); System.out.print(count + ":抛出的异常类型们:"); Class<?>[] exceptionTypes = method.getExceptionTypes(); System.out.println(Arrays.toString(exceptionTypes)); Class[] parameterTypes = method.getParameterTypes(); System.out.println(count + ":方法的形参列表:" + Arrays.toString(parameterTypes)); } }

    有两个compareTo方法,原因:当时写AtGuiguDemo时重写了compareT方法,编译器编译时会将这个方法编成两个版本,编译器里字节码有两个版本,如下图 若是泛型没有指定,默认参数为Object那个compareTo方法,泛型是1.5之后引入的,1.5之前的为object那个,引入泛型后没有更改,所以如果有泛型指定的compareTo方法,编译器会生成两个compareTo方法,是版本更替遗留问题,编译没有及时更新,为了兼容老版本的代码,调用执行的时候用的是指定了泛型参数的那个方法

    @Test public void test03(){ int mod = String.class.getModifiers(); System.out.println("修饰符的值:" + mod); System.out.println("修饰符:" + Modifier.toString(mod)); }

    一切皆对象:

    (1)所有类型在内存中都是Class对象(2)所有的属性都是Field对象 例如:private int age;

    属性类型:Field类型 类的概念:一类具有相同特性的事物的抽象描述。 所有的属性,有没有相同特征: 都是有修饰符、数据类型、名称 都有相同的行为操作:get获取值/set设置值 所以把属性抽象为Field类,那么一个属性被加载到内存后,是用一个Field对象表示的。(就内存里,age是一个field对象表示)

    (3)所有的构造器都是Constructor的对象 所有的构造器都有: 修饰符,名称,形参列表 都能 new 对象 所以把构造器抽象为Constructor类,那么一个构造器被加载到内存后,是用一个Constructor对象表示的。(4)所有的方法都是Method对象 所有的方法都有: 修饰符、返回值类型、方法名、形参列表、抛出的异常列表 都能 被调用invoke

    补充:面试题:如果有一篇很大的文章或者好几篇文章,字很多,问:哪些字出现过,用最节省内存的或者效率最高的方式查,解决:char的范围65535,声明一个数组,这个数组可以是int、byte类型等(如果是C的话可以是bit等),假如这个数组是byte(java最小的),用一位表示字符有没有出现过,若是值是1表示出现过,为0表示没出现过,存储的算法内存中用这个是最快的(最便利的算法是查找算法)

    Processed: 0.015, SQL: 8