Java反射最全面总结
Java中的编译类型
①.静态编译:在编译时确定类型,绑定对象即通过
②.动态编译:在运行时确定对象类型,绑定对象体现了Java的灵活性和多态性,降低类之间的耦合度。
反射的原理
反射是什么?
Java在运行时,对于任何一个类都能获得这个类的信息(属性、方法、修饰符等等),还可以在运行时实例化对象,实现动态创建对象
反射的执行流程
Java程序通过javac产生.class文件,通过JVM的类加载器加载class文件,并生成Class对象(注意Class对象只创建一次)Class对象是反射的源头。反射的本质是获取Class对象之后反向获取被反射类的各种信息。请看下图:
为什么用反射
- 使用反射可以赋予JVM动态编译的能力,否则类的元数据信息只能使用静态编译的实现,当我们写的程序运行时,需要动态的加载,提升服务器的性能。
- 使用Java的方式机制可以增加程序的灵活性,避免程序写死
反射的实现类
Class类:反射的源头
主要描述: 当创建一个类的时候首先通过编译生成相应的class文件,之后通过JVM的类的加载器,将class文件加载到内存中,创建一个Class对象Class对象只会加载一次
获取class对象的四种方法:
// 方式1.调用运行时类本身的class属性
Class<Person> clazz1 = Person.class;
// getName获取完整路径(全限定名或者完全限定名)
System.out.println(clazz1.getName());
// 方式2.通过运行时类getClass方法
Person person = new Person();
Class<Person> clazz2 = (Class<Person>) person.getClass();
System.out.println(clazz2.getName());
//3.通过Class。forName(“全路径”)静态方法
Class clazz3 = Class.forName("com.hpe.reflect.Person");
System.out.println(clazz3.getName());
// 4.通过类加载器ClassLoader
ClassLoader classLoader = this.getClass().getClassLoader();
Class class4 = classLoader.loadClass("com.hpe.reflect.Person");
System.out.println(class4.getName());
常见的方法:
getDeclaredFields():获取类所有属性
getField(String name):获取类的指定属性
getMothods():获取类的public类型方法
getMethod (String name,Class [] args)获得类的指定方法
getConstrutors()获得类的public类型的构造方法
getConstrutor(Class[] args)获得类的特定构造方法
newInstance()通过类的无参构造方法创建一个对象
getName()获得类的完整名字
getPackage()获取此类所属的包
getSuperclass()获得此类的父类对应的Class对象
使用反射反射创建对象,访问类的结构
// 获取Person类的Class类对象
Class<Person> clazz = Person.class;
// 常见clazz对应的运行时类的Person对象
Person person = (Person) clazz.newInstance();
System.out.println(person);
// 通过反射获取运行时类的指定属性
Field f1 = clazz.getField("name");
// 设置属性
f1.set(person, "jack");
System.out.println(person);
Field f2 = clazz.getDeclaredField("age");
f2.setAccessible(true);
// 设置属性
f2.set(person, 20);
System.out.println(person);
// 通过反射获取运行时指定的方法
Method method = clazz.getMethod("show");
// 调用方法(指明实例对象和参数值)
method.invoke(person);
Method m2 = clazz.getMethod("display", String.class);
m2.invoke(person, "China");
获取Class对象的方式:
// 方式1.调用运行时类本身的class属性
Class<Person> clazz1 = Person.class;
// getName获取完整路径(全限定名或者完全限定名)
System.out.println(clazz1.getName());
// 方式2.通过运行时类getClass方法
Person person = new Person();
Class<Person> clazz2 = (Class<Person>) person.getClass();
System.out.println(clazz2.getName());
//3.通过Class。forName(“全路径”)静态方法
Class clazz3 = Class.forName("com.hpe.reflect.Person");
System.out.println(clazz3.getName());
// 4.通过类加载器ClassLoader
ClassLoader classLoader = this.getClass().getClassLoader();
Class class4 = classLoader.loadClass("com.hpe.reflect.Person");
System.out.println(class4.getName());
Field:
获取对应运行时类的属性
// 1.getFields():获取运行时类及其父类声明的public的属性
Field[] fields = clazz.getFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
// 2.getDeclareFields():获取运行时类的本身声明的所有属性
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field.getName());
}
获取属性的各个部分的内容(权限修饰符、类型、名称)
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
// 1.获取每个属性的权限修饰符
System.out.println(Modifier.toString(f.getModifiers()));
// 2.获得属性的类型
Class type = f.getType();
String typeName = type.getName();
System.out.println(typeName);
// 3.获取属性名称
System.out.println(f.getName());
}
获取运行时类的指定属性
Class clazz = Class.forName("com.hpe.reflect.Person");
// 1.获取指定的属性
// getFields(String name):获取运行时类中声明问public的类型名的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
// 2.创建运行时的随想
Person p = (Person)clazz.newInstance();
System.out.println(p);
// 3.设置属性值
name.set(p, "eric");
System.out.println(p);
// 4.getDeclareField(String filedName):获取运行时指定的属性值
Field age = clazz.getDeclaredField("age");
// 由于属性权限修饰符的问题,为了给属性赋值时,需要在操作前使得该属性可被操作
age.setAccessible(true);
age.set(p, 25);
System.out.println(p);
Constructor类:
// 创建对应的运行时的对象使用newInstance 实际上调用了运行时类的空参构造器
// 创建运行类的对象条件①。空参构造器,访问权限要足够
Object newInstance = clazz.newInstance();
Person p = (Person) newInstance;
11.获取构造器:
Class clazz = Person.class;
Constructor[] cons = clazz.getDeclaredConstructors();
for (Constructor c : cons) {
System.out.println(c);
}
.调用指定的构造器,创建运行时对象
Class clazz = Person.class;
// 调用空参构造器
Constructor c = clazz.getDeclaredConstructor();
Person p1 = (Person) c.newInstance();
System.out.println(p1);
// 通过getDeclaredConstructor方法传入指定的参数可以获取所有参数的构造器
Constructor cons = clazz.getDeclaredConstructor(String.class, int.class);
// 通过构造器newInstance方法传入参数值创建对象
Person person = (Person) cons.newInstance("jack",20);
System.out.println(person);
不能注释掉原类的无参构造器
Method类:表示类的方法:
获取运行类中的方法:
// 1.getMethods():获取运行时类及其父类声明问public的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("---------------------------");
// 2.getDeclaredMothods():获取运行类声明的所有方法
Method[] m = clazz.getDeclaredMethods();
for (Method method : m) {
System.out.println(method);
}
调用运行时类的指定的方法:
Class<Person> clazz = Person.class;
// 创建运行类的对象
Person p = (Person) clazz.newInstance();
// 获取属性
Field f_name = clazz.getDeclaredField("name");
Field f_age = clazz.getDeclaredField("age");
// 设置允许访问属性
f_age.setAccessible(true);
f_name.setAccessible(true);
// 设置属性值
f_name.set(p, "tina");
f_age.set(p, 30);
// 1. 获取运行时类中声明为public的方法
Method m1 = clazz.getMethod("show");
// 2.调用方法 invoke(指定对象,方法的参数)
m1.invoke(p);
// 调用有返回值的参数
Method m2 = clazz.getMethod("sayHello");
Object returnVall = m2.invoke(p);
System.out.println(returnVall);
// 3.调用静态方法
Method m3 = clazz.getMethod("info");
// 相当于类名。方法名
m3.invoke(Person.class);
// 4.调用带参数的方法
// 获取所有运行时类中所有的方法getDeclareMethod()
Method m4 = clazz.getDeclaredMethod("display", String.class);
// 调用方法的同时传入参数值
m4.invoke(p, "China");
调用有参数的构造器创建运行类对象,调用相关方法:
Class<Person> clazz = Person.class;
Constructor<Person> cons = clazz.getConstructor(String.class, int.class);
// 通过有残构造器近视运行类的赋值(初始化成员属性)
Person p = cons.newInstance("tom,25");
// 获取方法
Method m1 = clazz.getMethod("display", String.class);
// 调用方法
m1.invoke(m1, "CHN");
文件的操作:
// 读取配置文件(
FileReader in = new FileReader(new File("propertices"));
// 将流加载到配置文件
properties.load(in);
in.close();
// 获取用户名和密码
String username = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.println("用户名:"+ username + " 密码:"+password);
//通过反射获取Class对象
Class<?> clazz = Class.forName(properties.getProperty("className"));
Method m1 = clazz.getMethod(properties.getProperty("methodName"));
// 创建student的运行时对象
//Student student = (Student) clazz.newInstance();
// 调用show方法
m1.invoke(clazz.getConstructor().newInstance());
//m1.invoke(student);
泛型擦除 程序编译后产生的。Class文件没有泛型约束的,这种现象就做泛型的擦除:
ArrayList<Integer> arr = new ArrayList<>();
arr.add(123);
//获取ArrayLiist的字节码对象
Class<? extends ArrayList> c = arr.getClass();
// 通过反射的方式获得add方法
Method m = c.getMethod("add", Object.class);
// 调用方法
m.invoke(arr, "abc");
System.out.println(arr);