【Java核心技术】Java反射技术详解

反射

耦合度:过个模块的关联关系(高内聚,低耦合)

反射发生在运行期

定义

反射:解析类的信息,获取字节码对象,然后产生实例对象的过程

被加载到内存中的字节码文件(有且只有一份)的类名的透明操作(想得到类里的任何数据都可以)

效率相对较低

反射使用的类:Class

  • 在运行状态中,对于任意一个类,都能获取属性和方法;

  • 对于任意一个对象,都可以调用属性和方法;

  • 我们就把可以动态获取和调用属性和方法的过程,叫反射

反射在平时写程序的时候,基本不用,通常完成框架

  • 特点:使工程更加灵活,效率相对硬编码低

缺点

  • 反射打破了封装原则

  • 反射跳过了类型检查

代表类的类Class

不管使用哪一种方法获取对象的Class对象,都相等,在内存中有且只有一份

已知类名

Class<E> xxx = 类名.class

//只知道类名的情况下
//字符串的字节码对象
Class<String> clz = String.class;
//class java.lang.String

//接口的字节码对象
Class<List> clz1 = List.class;
//interface java.util.List

//数组的字节码对象
Class<int[]> clz2 = int[].class;//class [I

//基本类型的字节码对象
Class<Integer> clz3 = int.class;//int

已知对象

使用对象的getClass()方法,获取Class对象

  • Class<E> xxx = 对象名.getClass();
//已知对象,使用对象的getClass()方法,获取Class对象
Student stu = new Student();
Class clazz2 = stu.getClass();

String str = "abc";
Class<String> clz = (Class<String>) str.getClass();

框架中最经常用的方法,来获取Class对象

Class<E> xxx = Class.forName("包名.类名");

forName()获取Class对象:首先检查内存中是否存在字节码文件

  • 如果存在,返回Class对象
  • 如果没有加载到内存,那么先加载该类的字节码文件到内存,返回该类的Class对象
Class clazz3 = Class.forName("cn.sxh.Student");
System.out.println(clazz3);//class cn.sxh.Student

代表构造方法的类Constructor

通过反射获取构造方法,使用构造方法创建对象

newInstance():调用指定类的无参构造

Class[] cs: getParameterTypes():获取所有的参数类型

isPrimitive():判断是否是基本类型

避免有的类没有无参构造

public static Object clone1(Object o) throws 
 IllegalAccessException, InstantiationException, 
InvocationTargetException {
     
     
 //1.获取原对象的字节码对象
 Class<Object> clz = (Class<Object>) o.getClass();

 //把所有构造方法放到数组中,只拿数组中第一个元素,保证一定会拿到一个构造方法
 Constructor<Object> c = (Constructor<Object>) clz.getDeclaredConstructors()[0];
 Class[] cs = c.getParameterTypes();

 //新建数组,用于存储参数值
 Object[] os = new Object[cs.length];
 //定义遍历,表示os数组下标
 int index = 0;
 //遍历cs数组
 for (Class i : cs) {
     
     //每个i代表构造方法的每个参数类型
     //判断是否是基本数据类型
     if (i.isPrimitive()) {
     
     
         //判断是否是字符类型
         if (i  char.class) {
     
     //判断类型是否一致
             os[index++] = '\u0000';
         }
         if (i  boolean.class) {
     
     
             os[index++] = false;
         }
         if (i  float.class) {
     
     
             os[index++] = 0.0f;
         }
         if (i  double.class) {
     
     
             os[index++] = 0.0;
         }
         //整形
         os[index++]=0;
     }else {
     
     
         os[index++] = null;
     }
 }

 //2.获取实例对象---克隆对象
 Object obj = c.newInstance(os);
 //3.获取原对象身上的所有属性
 Field[] fields = clz.getDeclaredFields();
 //4.把原对象所有属性赋值给实例对象(克隆)身上
 for (Field field : fields) {
     
     
     field.setAccessible(true);
     field.set(obj, field.get(o));
 }
 return obj;
}

获取所有的非私有的构造方法

类名.class.getConstructors():Constructors[ ]

//获取所有的非私有的构造方法
Constructor[] cons = Student.class.getConstructors();
for(Constructor con : cons) {
    
    
    System.out.println(con);
}

获取指定的构造方法

  • 获取无参的构造方法:

类名.class.getConstructor(null);:Constructor<类名>

Constructor<Student> con1 = Student.class.getConstructor(null);
//通过无参的构造方法创建对象
Student stu1 = con1.newInstance();
stu1.sleep();
stu1.name = "admin";
stu1.eat("苹果");

获取有参的构造方法:

getConstructor(String.class,int.class);:参数对应构造方法的参数列表的类型的Class类型

  • String:String.class

  • int:int.class

//有参的构造方法
Class<Student> clz = Student.class;
Constructor<Student> con2 = clz.getConstructor(String.class,int.class);
Student stu2 = con2.newInstance("张三",20);
stu2.eat("包子");

获取非公开的构造方法

getDeclaredConstructor():获取指定的非公开的构造方法

setAccessible(true):暴力破解,允许突破修饰符权限传入值

Class<String> clz = String.class;
//获取有参构造
Constructor<String> con;
con = clz.getDeclaredConstructor(char[].class,boolean.class);
con.setAccessible(true);//暴力破解

String s = con.newInstance(new char[]{
    
    'a','b'},true);
System.out.println(s);

代表属性的类Field

获取类的所有非私有属性

getFields():Field[ ] 返回所有非私有的成员变量,存在数组中

getModifiers():int 返回成员变量的访问修饰符,public-1

getName():String 返回成员变量的名字

getType():String 返回成员变量的类型(int,String,double……)

Class clazz = Student.class;
Student stu = (Student) clazz.newInstance();

//获取类的所有非私有属性
Field[] fields = clazz.getFields();
for(Field field : fields) {
    
    
    System.out.println(field.getModifiers());
    System.out.println(field.getName());
    System.out.println(field.getType());
}

获取指定的属性

类名.class.getField(String name);:name为要获取的类的成员变量名,返回类型:Field

//获取指定的属性
Field field1 = Student.class.getField("name");

给属性设置值,获取值

set(obj,String);给变量赋值

  • obj : 对象名,在该对象的变量中操作属性
  • String :内容,要设置属性的内容

get(obj)获取变量的值

  • obj :要获取该变量的对象
//给属性设置值,获取值
field1.set(stu, "张三");
System.out.println(field1.get(stu));

获取私有属性,设置值,获取值

类名.class.getDeclaredField(String name);获取变量age

setAccessible(true);设置私有属性访问权限

//获取私有属性,设置值,获取值
Field field2 = Student.class.getDeclaredField("age");
field2.setAccessible(true);//设置私有属性访问权限
field2.set(stu, 22);
System.out.println(field2.get(stu));

代表方法的类Method

获取所有的非私有成员方法

类名.class.getMethods();: Method[ ]

  • 将类中所有的非私有方法返回到一个Method类型的数组中

getModifiers():int 获取方法的访问修饰符

getReturnType():String 获取方法的返回类型

getName():String 获取方法的名字

Class clazz = Student.class;
//获取所有的非私有成员方法
Method[] methods = clazz.getMethods();
for(Method method:methods) {
    
    
    System.out.print(method.getModifiers()+"\t");
    System.out.print(method.getReturnType()+"\t");
    System.out.println(method.getName());
}

获取指定的成员方法

类名.class.getMethod(String name,Class parameterType);

  • name:获取的方法的名称
  • parameterType:获取的该方法的参数的Class类型,如果方法没有参数列表,就写null
    • String:String.class
    • int:int.class
    • 等等

调用方法:

  • invoke(Object obj,Object args);
  • obj:要调用该方法的对象的对象名
  • args:该方法的参数列表,方法为无参时,写null
//获取指定的成员方法
Method m1 = clazz.getMethod("eat", String.class);
//创建对象,clazz = Student.class,没有获取构造方法,所有要强制类型转换
Student stu = (Student) clazz.newInstance();
m1.invoke(stu, "苹果");

获取私有的成员方法

类名.class.getDeclaredMethod(String name,Class parameterType);

  • name:获取的方法的名称
  • parameterType:获取的该方法的参数的Class类型,如果方法没有参数列表,就写null
    • String:String.class
    • int:int.class
    • 等等

因为是私有的方法,要设置访问权限才可使用

setAccessible(true)设置私有成员方法的访问权限

//获取私有的成员方法
Method m2 = clazz.getDeclaredMethod("study",null);
m2.setAccessible(true);
m2.invoke(stu,null);

代表注解的类Annotation

配置文件利用反射

配置文件

配置文件通过类似键值对的方式存放数据

路径通过16进制的Unicode码来表示

className:表示类名

  • \u6848:案
  • \u4f8b:例
  • DDC:类名
  • \u6848\u4f8b.DDC:案例.DDC

methodName:表示方法名

  • qi:DDC中的方法名称
className=\u6848\u4f8b.DDC
methodName=qi

类中加载配置文件

加载配置文件要通过IO流读取配置文件的内容

Properties:读取配置文件的类

load(InputStream input):加载配置文件

Properties p = new Properties();
InputStream input = new FileInputStream("pro.properties");
p.load(input);

提取类路径和方法名

getProperty(String Key);:获取配置文件中的key值

  • key:配置文件中的key值
String className = p.getProperty("className");
String methodName = p.getProperty("methodName");

通过反射创建对象,调用方法

Class clazz = Class.forName(className);
Method m = clazz.getMethod(methodName, null);

m.invoke(clazz.newInstance(), null);

DDC类

public class DDC {
    
    
	
	public void qi() {
    
    
		System.out.println("骑电动车出去兜风");
	}	
}

猜你喜欢

转载自blog.csdn.net/weixin_54707168/article/details/114125792