反射可以在程序运行时发现并使用对象的类型信息。
反射使我们摆脱了只能在编译时执行面向类型操作的限制,并且让我们能够编写一些非常强大且灵活的程序。
程序中的每个类都有对应的唯一一个Class对象。也就是说,每次编写并编译一个新类时,都会生成一个Class对象(并被相应地存储在同名的 .class文件中)。
事实上,Class对象被用来创建类的所有“常规”对象。
类在首次使用时才会被动态加载到JVM中。当程序第一次引用该类的静态成员时,就会触发这个类的加载。
构造器是类的一个静态方法,尽管没有明确使用static关键字。因此,使用new操作符创建类的新对象也算作对该类静态成员的引用,构造器的初次使用会导致该类的加载。
Class类是描述类的类(类是用来描述一系列拥有共同属性的对象的)
获取Class实例的三种方式
通过类名称.class来获取
Class<?> cls1 = Person.class;
Class<?> cls2 = int.class;//基本数据类型也能.class
Class<?> cls3 = void.class;//修饰符也可以.class
Class<?> cls4 = int[].class;//甚至数组也可以.class
通过对象.getClass()方法来获取
getClass是Object类中的方法,所以任何对象都有getClass方法
Person p1 = new Person();
Class<?> cls6 = p1.getClass();
Class<?> cls7 = new int[5].getClass();
通过类的全限定名来获取(全限定名:包名+类名)
Class<?> cls8 = Class.forName("cn.tinsur.lianxi.st6.Person");
需要注意的是,同一个类,不管用什么方法获取,都是一样的,也就是存在如下关系:
IO.println(cls1 == cls7);//true
IO.println(cls6 == cls8);//true
列举常用的一些方法
1.打印类的全限定名
cls1.getName();//cn.tinsur.lianxi.st6.Person
cls1.getSimpleName();//Person
2.获取父类
cls1.getSuperclass();//返回父类类型的Class对象
//打印父类名称
IO.println(cls1.getSuperclass().getName());
IO.println(Student.class.getSuperclass().getName());
3.获取接口
Class<?>[] itfs = cls1.getInterfaces();//返回值是一个数组
IO.println(itfs.length);//1 即只有一个接口
IO.println(itfs[0].getName());//java.io.Serializable
仅演示部分方法
Field(字段)
在反射中,Field是描述字段的类。
例如可以通过以下方式获取上述Person类中的字段:
Field[] fields = cls1.getDeclaredFields();
IO.println(fields.length);
for (int i = 0; i < fields.length; i++) {
//修饰符
IO.print(Modifier.toString(fields[i].getModifiers()));
IO.print(" ");
//字段类型
IO.print(fields[i].getType().getSimpleName());//getTyoe获取字段类型,返回一个Class对象,getName获取字段类型名称
//字段名称
IO.print(" ");
IO.println(fields[i].getName());
}
和字段相关的操作
Field nameField = cls1.getDeclaredField("name");
Person p2 = new Person();
//通过反射的方式给字段赋值
nameField.setAccessible(true);//强制将字段改为公有的
nameField.set(p2,"张三");
IO.println(p2.getName());
//通过反射的方式从字段取值
IO.println(nameField.get(p2));
THE END