java反射基础
一,概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,
都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。
在执行程序(java.exe)时候,将字节码文件读入JVM中--->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象-->这个对象会被放入字节码信息中,这个Class对象,就对应加载那个字节码信息,这个对象将被作为程序访问方法区中的这个类的各种数据的外部接口。
所以:我们可以通过这个对象看到类的结构,这个对象就好像是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射
这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
说明:在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。
如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
补充:
动态语膏vs静态语言
1、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以
被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运
行时代码可以根据某些条件改变自身结构。
主要动态语言: Object-C、 C#、JavaScript、 PHP、 Python、 Erlang 。
2、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、
C++。
所以Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动
态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活!
二,获取字节码信息的四种形式
public class Person { private String a; public String b; }
public class Test { public static void main(String[] args) throws ClassNotFoundException { //案例:以Person的字节码信息为案例 //方式1:通过getClass()方法获取 Person p = new Person(); Class c1 = p.getClass(); System.out.println(c1); //方式2:通过内置class属性: Class c2 = Person.class; System.out.println(c2); System.out.println(c1==c2); //注意:方式1和方式2 不常用 //方式3:--》用的最多:调用Class类提供的静态方法forName Class c3 = Class.forName("com.zhaoss.test02.Person"); //方式4:利用类的加载器(了解技能点) ClassLoader loader = Test.class.getClassLoader(); Class c4 = loader.loadClass("com.zhaoss.test02.Person"); } }
三,可以作为Class类的实例的种类
Class类的具体的实例:
(1)类:外部类,内部类
(2)接口
(3)注解
(4)数组
(5)基本数据类型
(6)void
public class Demo { public static void main(String[] args) { /* Class类的具体的实例: (1)类:外部类,内部类 (2)接口 (3)注解 (4)数组 (5)基本数据类型 (6)void */ Class c1 = Person.class; Class c2 = Comparable.class; Class c3 = Override.class; int[] arr1 = {1,2,3}; Class c4 = arr1.getClass(); int[] arr2 = {5,6,7}; Class c5 = arr2.getClass(); System.out.println(c4==c5);//结果:true .同一个维度,同一个元素类型,得到的字节码就是同一个 Class c6 = int.class; Class c7 = void.class; } }
四,获取运行时类的完整结构
以下面两个类为例:
package testreflet; import java.io.Serializable; /** * @author zl * @date 2022/11/6 20:31 */ public class Person implements Serializable { private String a; public String b; private void eat() { System.out.println("Person---eat"); } public void sleep() { System.out.println("Person---sleep"); } }
package testreflet; import java.io.Serializable; /** * @author zl * @date 2022/11/6 20:12 */ public class Student extends Person implements Serializable { private String name; protected String sex; public int age;
public Student(){
}
private Student(String name) { this.name = name; } protected Student(int age) { this.age = age; } public Student(String name, String sex) { this.name = name; this.sex = sex; } private void work() { System.out.println("我以后会找工作--》成为码农 程序员 程序猿"); } void happy() { System.out.println("做人最重要的就是开心每一天"); } protected String getName() { return name; } public void goUp(){ System.out.println("滚蛋"); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }
1,获取构造器,通过构造器创建对象
public class Test { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class cls = Student.class; //getConstructors只能获取当前运行时类的被public修饰的构造器 Constructor[] constructors = cls.getConstructors(); for (Constructor c : constructors) { System.out.println(c); } //获取运行时类的全部修饰符的构造器 Constructor[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } ////获取指定的构造器: Constructor c1 = cls.getDeclaredConstructor(String.class, String.class); //通过构造器创建对象:(注意:不能是private修饰的) Object o = c1.newInstance("111", "99"); System.out.println(o);
} }
2,获取属性,赋值
Class cls = Student.class;
//getFields:获取运行时类和父类中被public修饰的属性 Field[] fields = cls.getFields(); for (Field field : fields) { System.out.println(field); } //获取这个运行时类中的所有属性(没有父类) Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } //获取指定的属性:(public修饰) Field age = cls.getField("age"); System.out.println(age); //获取指定的属性:(非public修饰) Field name = cls.getDeclaredField("name"); System.out.println(name); //属性的具体结构: //获取修饰符 /*int modifiers = sno.getModifiers(); System.out.println(modifiers); System.out.println(Modifier.toString(modifiers));*/ System.out.println(Modifier.toString(age.getModifiers())); //获取属性的数据类型: Class clazz = age.getType(); System.out.println(clazz.getName()); //获取属性的名字: System.out.println(age.getName()); //给属性赋值:(给属性设置值,必须要有对象) cls.newInstance():当前类要有非private修饰的空构造方法 Object obj = cls.newInstance(); age.set(obj, 98);//给obj这个对象的score属性设置具体的值,这个值为98 System.out.println(obj);
3,获取方法和调用方法
Class cls = Student.class; //getMethods:获取运行时类的方法还有所有父类中的方法(被public修饰) Method[] methods = cls.getMethods(); for (Method m : methods) { System.out.println(m); } System.out.println("-----------------------"); //getDeclaredMethods:获取这个运行时类中的所有方法:(没有父类) Method[] declaredMethods = cls.getDeclaredMethods(); for (Method m : declaredMethods) { System.out.println(m); } //获取指定的方法: //Method goUp = cls.getMethod("goUp"); Method goUp = cls.getDeclaredMethod("goUp"); Object o = cls.newInstance(); //调用方法(只能调用public修饰) goUp.invoke(o); //获取方法的具体结构: /* @注解 修饰符 返回值类型 方法名(参数列表) throws XXXXX{} */ Method work = cls.getDeclaredMethod("work"); //方法名: System.out.println(work.getName()); //修饰符: int modifiers = work.getModifiers(); System.out.println(Modifier.toString(modifiers)); //返回值: System.out.println(work.getReturnType()); //参数列表: Class[] parameterTypes = work.getParameterTypes(); for (Class c : parameterTypes) { System.out.println(c); } //获取异常: Class[] exceptionTypes = work.getExceptionTypes(); for (Class c : exceptionTypes) { System.out.println(c); } //获取注解: Method w = cls.getDeclaredMethod("work"); Annotation[] annotations = w.getAnnotations(); for (Annotation a : annotations) { System.out.println(a); }
四,获取类的接口,所在包,注解
//获取字节码信息: Class cls = Student.class; //获取运行时类的接口: Class[] interfaces = cls.getInterfaces(); for(Class c:interfaces){ System.out.println(c); } //得到父类的接口: //先得到父类的字节码信息: Class superclass = cls.getSuperclass(); //得到接口: Class[] interfaces1 = superclass.getInterfaces(); for(Class c:interfaces1){ System.out.println(c); } //获取运行时类所在的包: Package aPackage = cls.getPackage(); System.out.println(aPackage); System.out.println(aPackage.getName()); //获取运行类的注解: Annotation[] annotations = cls.getAnnotations(); for(Annotation a:annotations){ System.out.println(a); }