什么是反射(Reflection )?
主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
什么是Java反射?
Java反射指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定的一个对象,都能够调用它的任意一个属性和方法。这种动态获取类的内容以及动态调用对象的方法称为反射机制。
Java反射机制主要提供了以下功能:
-
1.在运行时判断任意一个对象所属的类。
-
2.在运行时构造任意一个类的对象。
-
3.在运行时判断任意一个类所具有的成员变量和方法。
-
4.在运行时调用任意一个对象的方法。
Java的反射机制主要是用来分析类能力,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。在程序中一般的对象类型在编译期就确认下来了,Java的反射机制可以在运行期动态创建对象,并调用其属性。所以反射的核心是在运行期才动态加载类或调用方法访问属性,它不需要在事先知道运行对象是谁。
反射可以用来:
运行期构造任意一个类的对象运行期判断任意一个对象的所属类/是否是某个类的实例运行期获得任意一个类具有的属性和方法,包括继承的公有方法运行期调用任意一个对象的方法。
通过反射创建对象
Class.newInstance():调用此方法,创建对应的运行时类的对象。
内部调用了运行时类的空参的构造器。
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public。
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象
2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
Class.getConstructor(Object.class…)
Constructor.newInstance():调用此方法创建对应的运行时类的对象。
内部调用了运行时类的对应的构造器。
setAccessible(boolean);
将此对象的 accessible 标志设置为指示的布尔值。
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
值为 false 则指示反射的对象应该实施 Java 语言访问检查;
实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问
// 1. 无参构造
Class.newInstance();
// 2.有参构造
// 反射获取构造器 newInstance创建对象
Constructor = Class.getConstructor(Object.class...);
Constructor.newInstance(obj...);
// 通过反射,可以调用对象的私有结构的。
// 调用私有的构造器
Constructor = Class.getDeclaredConstructor(Object.class...);
Constructor.setAccessible(true);
Constructor.newInstance(obj...);
疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
建议:直接new的方式。
什么时候会使用:反射的方式。 反射的特征:动态性
疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
不矛盾。
关于java.lang.Class类的理解
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件
加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此
运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式
来获取此运行时类。
获取Class的实例的方式(前三种方式需要掌握)
// 方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
// 方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
// 方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.hak.advanced.reflect.Person");
// clazz3 = Class.forName("java.lang.String");
System.out.println(clazz3);
System.out.println(clazz1 == clazz2);// true
System.out.println(clazz1 == clazz3);// true
// 方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.hak.advanced.reflect.Person");
System.out.println(clazz4);
System.out.println(clazz1 == clazz4);// true
通过反射获取Field
可以获取
1.属性结构
2.数据类型
3.变量名
Class clazz = Object.class;
// 获取属性结构
// getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
// getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
// 1.权限修饰符 0 1 public 2 private 3 protected
int modifiers = f.getModifiers();
System.out.printf("%s - %s\t", modifiers, Modifier.toString(modifiers));
// 2.数据类型
Class type = f.getType();
System.out.printf("%s", type.getName() + "\t");
// 3.变量名
String fName = f.getName();
System.out.printf("%s", fName);
System.out.println();
}
通过反射获取Method
可以获取到方法的
1.获取方法声明的注解
2.权限修饰符
3.返回值类型
4.方法名
5.形参列表
6.抛出的异常
Class clazz = Object.class;
//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
// 1.获取方法声明的注解
Annotation[] annotations = m.getAnnotations();
for (Annotation annotation : annotations) {
System.out.printf("注解 => %s\n", annotation);
}
// 2.权限修饰符
System.out.printf("权限修饰符 => %s\n", Modifier.toString(m.getModifiers()));
//3.返回值类型
System.out.printf("返回值类型 => %s\n", m.getReturnType().getName());
//4.方法名
System.out.printf("方法名 => %s\n", m.getName());
//5.形参列表
Class<?>[] parameterTypes = m.getParameterTypes();
if (!(parameterTypes == null && parameterTypes.length == 0)) {
System.out.print("形参列表 => (");
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
}
}
System.out.print(")\n");
//6.抛出的异常
Class<?>[] exceptionTypes = m.getExceptionTypes();
if (!(exceptionTypes == null && exceptionTypes.length == 0)) {
System.out.print("throws => {");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.print("}\n");
System.out.println("------------------");
}
其他方法
// 1.构造器
// getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
// getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
Class[] declaredClasses = clazz.getDeclaredClasses();
// 2.父类
Type genericSuperclass = clazz.getSuperclass();
// 3.带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
// 4.获取运行时类的带泛型的父类的泛型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
// 5.获取运行时类实现的接口
Class[] interfaces = clazz.getInterfaces();
// 父类接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
// 6.获取运行时类所在的包
Package pack = clazz.getPackage();
// 7.获取运行时类声明的注解
Annotation[] annotations = clazz.getAnnotations();
调用运行时类中指定的结构:属性、方法、构造器
属性
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, "Tom");
方法
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, "CHN"); //String nation = p.show("CHN");
System.out.println(returnValue);
System.out.println("*************如何调用静态方法*****************");
// private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
// 如果调用的运行时类中的方法没有返回值,则此invoke()返回null
// Object returnVal = showDesc.invoke(null);
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);
构造器
Class clazz = Person.class;
/**
* 1.获取指定的构造器
* getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
/**
* 2.保证此构造器是可访问的
*/
constructor.setAccessible(true);
/**
* 3.调用此构造器创建运行时类的对象
*/
Person2 per = (Person2) constructor.newInstance("Tom");
类加载器
//对于自定义类,使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);
//调用系统类加载器的getParent():获取扩展类加载器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
//调用扩展类加载器的getParent():无法获取引导类加载器
//引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
ClassLoader classLoader3 = String.class.getClassLoader();
System.out.println(classLoader3);
加载配置文件
Properties pros = new Properties();
// 此时的文件默认在当前的module下。
// 读取配置文件的方式一:
// FileInputStream fis = new FileInputStream("jdbc.properties");//model 下
// FileInputStream fis = new FileInputStream("src\\jdbc.properties");//src下
// pros.load(fis);
// 读取配置文件的方式二:使用ClassLoader
// 配置文件默认识别为:当前module的src下
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("com/**/jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user = " + user + ",password = " + password);
本文链接:https://blog.csdn.net/Platinum_stars/article/details/108281679