反射
反射是动态性的
反射机制允许,在程序运行期间,使用反射来获取任何类的内部信息,并能操作任何类的属性和方法,框架中常有用到
在java中,加载完之后,会在堆内存中的方法区中会生成一个 Class类型对象(一个类只有一个Class对象)这个对象包含了这个类完整的结构信息
一般情况下
我们都是引入需要的包类名称 -> 然后通过 new 实例化->取得实例化对象
而在反射情况下
实例化对象-> getClass -> 得到完整的包类名称
关于java中 java.lang.Class类的理解
从类的加载过程来看:
程序在经过javac 命令以后会生成一个或多个 class(字节码)文件
接着我们使用java 这个命令对字节码文件进行解释运行,将其加载到内存中,(加载到内存中的过程,就称为类的加载)
加载到内存中的类,我们称之为运行时类,此运行时类就作为 Class 的一个实例
换句话说,Class对象对应着一个运行时类
获取 Class 对象的方式
前三个要求掌握,其中,第三个使用频率更多
//方式一
Class clazz = Person.class;
//Class<Person> clazz = Person.class;
System.out.println(clazz);
//方式二
Person p1 = new Person();
Class clazz1 = p1.getClass();
System.out.println(clazz1);
//方式三
//调用Class 的静态方法 forName ( String classPath)
Class<?> clazz2 = Class.forName("reflection.Person");
System.out.println(clazz2);
//方式四,作了解,不要求掌握
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("reflection.Person");
System.out.println(clazz4);
通过反射创建运行时类的对象
使用反射创建运行时对象,可以使用 Class.forName(String classPath)
这个方法来获取运行时对象 然后通过 newInstance() 方法获取类的对象。
Class<Person> clazz = (Class<Person>) Class.forName("reflection.Person");
Person o = clazz.newInstance();
System.out.println(o);
体现反射的动态性
public void main() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//体现反射的 动态性
int random = new Random().nextInt(3);//0,1,2
String classPath = "";
switch (random){
case 0:
classPath = "java.lang.String";
break;
case 1:
classPath = "reflection.Person";
break;
case 2:
classPath = "java.lang.Object";
break;
}
Object instance = getInstance(classPath);
System.out.println(instance.getClass());
}
public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName(classPath);
return clazz.newInstance();
}
反射机制与对象的封装行矛盾吗?
不矛盾
封装里private 更想告诉你,这个方法你是用不到的,就不要用了,一般是我自己内部使用的。
我给你公开提供的方法,更好
尽管我把权限私有起来了,但是你非要调用的话,也不是不可以,但一般你要通过反射之后自己做的操作,我在公开方法里都给你做好了。那又何必呢
通过反射获取运行时类的完整结构
获取运行时类的属性
public void TestField() throws ClassNotFoundException {
Class clazz = Class.forName("reflection.Test.Person");
Field[] fields = clazz.getFields();
//会发现都是 public 标识的成员变量,且包含父类属性
for (Field f : fields){
System.out.println(f);
}
System.out.println("------------------");
//获取当前类自己定义的所有(不看权限修饰符)属性,但不包括父类属性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields){
System.out.println(f);
}
System.out.println("------------------");
for (Field f : declaredFields){
//权限修饰符,但是返回的是 int 类型,这个时候我们要转为string类型 Modifier.toString()
System.out.print(Modifier.toString(f.getModifiers()) + "\t");
System.out.print(f.getType().getName() + "\t");
System.out.println(f.getName());
}
}
其他的可以不加演示,以获取属性为例:你可以从代码里发现
直接getXXX() 获取的是包含父类的,但是权限修饰符 必须是 public
如果 getDeclaredXXX()方法的话, 获取的是自己所定义的所有的,不包含父类,不受访问权限约束
获取运行时类的方法结构同理
获取运行时类的方法
public void TestMethod() throws ClassNotFoundException {
Class clazz = Class.forName("reflection.Test.Person");
//获取 当前类和父类 public权限的方法
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);
}
System.out.println("---------------");
//获取方法具体结构, 权限符 返回值 方法名字 (参数类型 参数名 )
for (Method method:declaredMethods){
//获取注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation :annotations) {
System.out.print(annotation + "\t");
}
//获取权限修饰符
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers) + "\t");
//返回值类型
System.out.print(method.getReturnType().getName() + "\t");
//方法名
System.out.print(method.getName() + "\t(");
//形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
if (!(parameterTypes.length == 0 || parameterTypes == null )) {
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(")");
System.out.println();
}
}
获得运行时类的指定结构
Class<Person> clazz = Person.class;
//通过反射创建对象
//获取构造器
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
Object tom = constructor.newInstance("tom", 12);
System.out.println(tom);
//通过反射。调用对象指定的属性和方法,这个时候,还不能直接获取private 修饰的属性
Field age = clazz.getDeclaredField("age");
age.set(tom,10);
System.out.println(tom);
//通过反射调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(tom);
//通过反射调用私有结构
Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Person jerry = (Person) constructor1.newInstance("jerry");
System.out.println(jerry);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(tom,"jerry");
System.out.println(tom);
//调用私有方法,有参数的话,要指定参数类型
Method hello = clazz.getDeclaredMethod("hello", String.class);
hello.setAccessible(true);
String returnValue = (String) hello.invoke(jerry, "tom");
System.out.println(returnValue);
这个与获取完整结构不同之处就是参数不同,指定出你要的方法名。
但是注意,如果你要获取的是 private 要设置 XXX.setAccessible(true);来保证当前方法可以被访问,否则会报错。
当你获取方法时,如果需要参数,那么就要指定参数类型,如果你想调用这个方法,那么就需要使用 invoke() 这个方法去执行,如果被调用的这个方法有返回值,那么 invoke方法会帮你返回这个值
invoke() 第一个参数是 调用类,也就是你需要调用哪个对象的该方法
举例来讲: hello.invoke(jerry, “tom”); 要调用的是 jerry.hello();
如果该类里面没有 hello 这个方法,则报错。