反射
1.通过问题引出反射
配置信息:
传统的新建对象调用方法解决不了,具体的解决步骤如代码所示
//根据配置信息,新建一个Cat对象,并调用hi方法
package com.lwbedu.reflection.question;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
@SuppressWarnings("all")
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//拿到配置中类的全路径和方法名
Properties pro = new Properties();
pro.load(new FileInputStream("src\\re.properties"));
String classfullpath = pro.getProperty("classfullpath");
String methodName = pro.getProperty("method");
//传统方法行不通,等得到全路径是一个字符串,不是类
//new classfullpath();
//使用反射机制解决
//(1)加载类
Class cls = Class.forName(classfullpath);
//(2)通过cls得到你加载的类com.lwbedu.Cat的对象实例
Object o = cls.newInstance();
//(3)通过cls得到你加载的类的methodName("hi")的方法对象
//即在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
//通过方法对象实现调用方法
method1.invoke(o);
}
}
2.反射机制
注:一个类不管加载几次,都只有一个Class对象
反射的原理
编写完类的源代码后编译会产生一个字节码文件,该字节码文件包含类的成员变量,成员方法,构造器,注解,泛型等信息。
创建对象会导致类的加载,字节码文件通过类加载器加载到对内存当中并创建一个Class对象,该对象也包含了类的成员变量,成员方法,构造器,注解,泛型等信息,每一项信息(例如成员变量)都会生成对象并存放在对应的数组当中。
3.反射相关类
主要相关类
getXX只能访问不能访问私有成员。
代码
package com.lwbedu;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Properties pro = new Properties();
pro.load(new FileInputStream("src\\re.properties"));
String classfullpath = pro.getProperty("classfullpath");
String methodName = pro.getProperty("method");
//传统方法行不通,等得到全路径是一个字符串,不是类
//new classfullpath();
//使用反射机制解决
//(1)加载类
Class cls = Class.forName(classfullpath);
//(2)通过cls得到你加载的类com.lwbedu.Cat的对象实例
Object o = cls.newInstance();
//(3)通过cls得到你加载的类的methodName("hi")的方法对象
//即在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
//通过方法对象实现调用方法
method1.invoke(o);
//Field类得到成员变量
Field nameField = cls.getField("name");
System.out.println(nameField.get(o));
//Constructor类得到构造器
Constructor constructor = cls.getConstructor();//得到无参构造器
System.out.println(constructor);
Constructor constructor1 = cls.getConstructor(String.class);//得到带参构造器
System.out.println(constructor1);
}
}
4.反射调用优化
反射机制的优缺点
反射机制的优化
使用setAccessible()方法进行优化,取消访问检查,有一定的效果但不显著。
传统方法、反射机制、优化的反射机制对比:
package com.lwbedu.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflection01 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
m1();
m2();
m3();
}
//传统方法调和hi方法
public static void m1(){
long start = System.currentTimeMillis();
Cat cat = new Cat();
for (int i = 0; i < 900000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("传统方法:"+(end-start));
}
//反射机制调用hi方法
public static void m2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
long start = System.currentTimeMillis();
Class cls = Class.forName("com.lwbedu.reflection.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
for (int i = 0; i < 900000; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射机制:"+(end-start));
}
//取消访问检查的反射机制调用方法
public static void m3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
long start = System.currentTimeMillis();
Class cls = Class.forName("com.lwbedu.reflection.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true);
for (int i = 0; i < 900000; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("优化的反射机制:"+(end-start));
}
}
运行结果:
5.class类分析
6.Class类常用方法
package com.lwbedu.reflection;
import java.lang.reflect.Field;
public class Reflection02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classPath = "com.lwbedu.reflection.Car";
//1.获取Car类对应的Class对象
Class cls = Class.forName(classPath);
//2.输出cls
System.out.println(cls);//显示cls对象是哪个类的Class对象.class com.lwbedu.reflection.Car
System.out.println(cls.getClass());//真正的运行类型.class java.lang.Class
//3.得到包名
System.out.println(cls.getPackage().getName());//得到cls对象对应类(Car)对应的包名.com.lwbedu.reflection
//4.得到全类名
System.out.println(cls.getName());//得到类名.com.lwbedu.reflection.Car
//5.通过cls创建对象实例
Car car = (Car) cls.newInstance();
System.out.println(car);//调用toString方法.Car{brand='宝马', price=500000, color='白色'}
//6.通过反射获取属性brand
Field brand = cls.getField("brand");
System.out.println(brand.get(car));//宝马
//7.通过反射给属性赋值
brand.set(car,"奔驰");
System.out.println(brand.get(car));
//8.得到所有的属性
Field[] fields = cls.getFields();
for(Field f : fields){
System.out.println(f.getName());
}
}
}
7.获取Class对象六种方式
package com.lwbedu.reflection;
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName()
String classPath = "com.lwbedu.reflection.Car";
Class cls = Class.forName(classPath);
System.out.println(cls);
//2.类名.class,应用于参数传递
Class cls1 = Car.class;
System.out.println(cls1);
//3.对象.getClass(),应用场景,有对象实例
Car car = new Car();
Class cls2 = car.getClass();
System.out.println(cls2);
//4.通过类加载器【4种】来获取Class对象
//(1)先得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器创建Class对象
Class cls3 = classLoader.loadClass(classPath);
System.out.println(cls3);
//cls,cls1,cls2,cls3其实是同一个对象
System.out.println(cls.hashCode());
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
//5.基本数据类型.class获取Class对象
Class<Integer> integerClass = int.class;//int
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);
//6.基本数据类型对应的包装类.TYPE获取Class对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
System.out.println(type1);//int
//基本数据类型和它对应的包装类的Class对象是同一个
System.out.println(integerClass.hashCode());
System.out.println(type1.hashCode());
}
}
8.哪些类型有Class对象
9.类加载
静态加载,动态加载
10.类加载过程
编写完源码后编译生成字节码文件(class文件),运行时会将字节码文件进行加载并生成对应的Class对象,加载完后会进行连接(包括验证、准备、解析),验证阶段主要是对文件进行安全校验(文件格式进行校验、元数据验证是否正确、字节码是否正确、符号引用是否正确等),准备阶段主要是对静态变量分配内存并且完成默认初始化,解析阶段主要是虚拟机将常量池中的符号引用替换为直接引用。初始化阶段JVM对类进行初始化(真正意义上执行代码内容),主要针对静态成员。
类加载后再方法区中会将字节码文件以二进制书数据的形式保存起来,同时堆区会生成字节码对应的数据结构(类的Class对象)。
类加载各阶段完成的任务
验证阶段
package com.lwbedu.classLoad;
public class ClassLoad01 {
public static void main(String[] args) {
}
}
class A{
//属性-成员变量-字段
//分析类加载阶段-准备 属性是如何处理
//1.n1是实例属性,不是静态变量,因此在准备阶段不会分配内存
//2.n2是静态变量,分配内存,n2默认初始化0,而不是20
//3.n3是static final 是常量,和静态变量不一样,它一旦赋值就不变,所以在准备阶段n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}
初始化阶段跟实例没有关系。
初始化阶段的详细过程看代码
package com.lwbedu.classLoad;
public class ClassLoad02 {
public static void main(String[] args) {
//1.加载B类,并生成B的Class对象
//2.链接 num = 0
//3.初始化阶段
// 依次自动收集类中所有静态变量的赋值动作和静态代码块中的语句,并进行合并
/*
执行clinit()方法收集,按顺序收集
clinit(){
System.out.println("B静态代码被执行了");
num = 300;
num = 100;
}
合并完后num = 300就相当于没用,即num = 100
clinit()方法会自动加锁,多个线程同时同时初始化一个类时,只有一个线程会去执行clinit()方法,
其他线程阻塞等待,知道活动线程执行完clinit()方法,正因为有这个机制才能保证某个类在内存中
只有一个Class对象
*/
new B();//类加载
//4.输出构造器方法
System.out.println(B.num);//直接使用类的静态属性不新建类的对象,也会导致类的加载
}
}
class B{
static {
System.out.println("B静态代码被执行了");
num = 300;
}
static int num = 100;
public B(){
System.out.println("B的构造器被执行了");
}
}
11.获取类信息结构
package com.lwbedu.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionUtils {
public static void main(String[] args) throws ClassNotFoundException {
//得到Class对象
Class<?> cls = Class.forName("com.lwbedu.reflection.Person");
//得到全类名
System.out.println(cls.getName());
//获取简单类名
System.out.println(cls.getSimpleName());
//getFields获取所有public修饰的属性,包含本类以及父类的
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
//getDeclaredField:获取本类中所有的属性不包含父类
Field[] fields1 = cls.getDeclaredFields();
for (Field field : fields1) {
System.out.println(field.getName());
}
//getMethods:获取所有public修饰的方法,包含本类以及父类
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
//本类的所有方法
Method[] methods1 = cls.getDeclaredMethods();
for (Method method : methods1) {
System.out.println(method.getName());
}
//getConstrutors:索取所有public修饰的构造器,包括本类没包括父类
Constructor[] constructors = cls.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//getDeclaredConstrutor:获取本类中所有的构造器
Constructor[] constructors1 = cls.getDeclaredConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
//得到包的名字
System.out.println(cls.getPackage());
//getSuperClass:以Class形式返回父类信息
Class cls2 = cls.getSuperclass();
System.out.println(cls2.getName());
//getInterfaces:以Class[]的形式返回接口信息
Class[] clss = cls.getInterfaces();
for (Class aClass : clss) {
System.out.println(aClass);
}
//getAnnotation:以Annotation[]形式得到注解信息
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//api_02
api_02();
}
public static void api_02() throws ClassNotFoundException {
//得到Class对象
Class<?> cls = Class.forName("com.lwbedu.reflection.Person");
//获得本类中所有属性
//getModifiers:获取修饰符对应的整型值
//getType:获取对应属性的类型,返回该类型的Class对象
//getName:获取对应属性的名字
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println("本类中属性:"+field.getName()+",修饰符:"+field.getModifiers()+
"属性类型:"+field.getType());
}
//本类的所有方法
//getModifiers:获取修饰符对应的整型值
//getReturnType:返回该方法返回值类型的Class对象
//getName:返回方法名称
Method[] methods1 = cls.getDeclaredMethods();
for (Method method : methods1) {
System.out.println("方法名称:"+method.getName()+"方法修饰符:"+method.getModifiers()+
"方法返回类型:"+ method.getReturnType());
//getParameterTypes:返回方法形参的Class对象数组
Class[] classes = method.getParameterTypes();
for (Class aClass : classes) {
System.out.println("方法的形参:"+aClass);
}
}
//本类中所有构造器
//getModifiers:获取修饰符对应的整型值
//getName:返回构造器名称
Constructor[] constructors1 = cls.getDeclaredConstructors();
for (Constructor constructor : constructors1) {
System.out.println("构造器名称:"+constructor.getName()+
"构造器修饰符:"+constructor.getModifiers());
//getParameterTypes:返回构造器形参的Class对象数组
Class[] classes = constructor.getParameterTypes();
for (Class aClass : classes) {
System.out.println("构造器形参:"+aClass);
}
}
}
}
class A{
public String hobby;
public A(){
}
public void hi(){
}
}
interface IA{
}
interface IB{
}
@Deprecated
class Person extends A implements IA,IB{
public String name;
protected int age;
String job;
private String sal;
public Person(){
}
public Person(String name){
}
private Person(String name,int age){
}
public void m1(){
}
protected int m2(String s,int age){
return 0;
}
void m3(){
}
private void m4(){
}
}
12.通过反射创建实例对象
package com.lwbedu.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.获得User类的Class对象
Class<?> userClass = Class.forName("com.lwbedu.reflection.User");
//2.通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
//3.通过public的有参构造器创建实例,先获取构造器再创建实例
Constructor<?> constructor = userClass.getConstructor(String.class);
Object o1 = constructor.newInstance("林伟波");
System.out.println(o1);
//4.通过非public的有参构造器创建实例
//先得到私有构造器
Constructor constructor1 = userClass.getDeclaredConstructor(String.class,int.class);
//暴破【暴力破解】,使用访问可以访问私有构造器/方法/属性
constructor1.setAccessible(true);
//创建实例
Object o2 = constructor1.newInstance("林伟波", 21);
System.out.println(o2);
}
}
class User{
private String name;
private int age;
public User(){
}
public User(String name){
this.name = name;
}
private User(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
13.反射爆破操作属性
静态的属于所有对象,所以使用null也可以
package com.lwbedu.reflection;
import java.lang.reflect.Field;
public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
Class<?> cls = Class.forName("com.lwbedu.reflection.Student");
Object o = cls.newInstance();
//反射爆破操作属性
Field ageField = cls.getField("age");
ageField.set(o,88);
System.out.println(ageField.get(o));
//操作静态属性
Field nameField = cls.getDeclaredField("name");
//爆破
nameField.setAccessible(true);
//nameField.set(o,"林伟波");
nameField.set(null,"林伟波");//静态的属于所有对象,所以使用null也可以
System.out.println(nameField.get(null));
}
}
class Student{
public int age;
private static String name;
public Student(){
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
14.通过反射访问类中的方法
package com.lwbedu.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflecAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//得到Boss类对象Class对象
Class<?> bossClass = Class.forName("com.lwbedu.reflection.Boss");
Object o = bossClass.newInstance();
//调用public普通方法
Method hi1 = bossClass.getMethod("hi", String.class);
hi1.invoke(o,"林伟波");
//调用say方法
Method say = bossClass.getDeclaredMethod("say",int.class,String.class,char.class);
say.setAccessible(true);//私有方法暴破
//调用方法统一返回Object类型,但运行类型和定义是一致
Object invoke = say.invoke(null, 100, "张三", '男');
System.out.println(invoke);//静态方法可用对象也可用null
}
}
class Boss{
public int age;
private static String name;
public Boss(){
}
private static String say(int n,String s,char c){
return n+" "+s+" "+c;
}
public void hi(String s){
System.out.println("hi"+s);
}
}