反射

反射

什么是反射

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。

 

反射能做什么

我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

反射的具体体现

Person类

package com.ys.reflex;
public class Person {
   //私有属性
   private String name = "Tom";
   //公有属性
   public int age = 18;
   //构造方法
   public Person() {
  }
   //私有方法
   private void say(){
       System.out.println("private say()...");
  }
   //公有方法
   public void work(){
       System.out.println("public work()...");
  }
}

对于一个Person类,得到class的三种方式

//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
// 类型的对象,而我不知道你具体是什么类,用这种方法
  Person p1 = new Person();
  Class c1 = p1.getClass();

//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
// 这说明任何一个类都有一个隐含的静态成员变量 class
  Class c2 = Person.class;

//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
//   但可能抛出 ClassNotFoundException 异常
  Class c3 = Class.forName("com.ys.reflex.Person");

需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true

通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等

查阅 API 可以看到 Class 有很多方法:

getName():获得类的完整名字。
 •getFields():获得类的public类型的属性。
 •getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
 •getMethods():获得类的public类型的方法。
 •getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
 •getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
 •getDeclaredMethod(String name, Class[] parameterTypes)获得私有的获得类的特定方法
 •getConstructors():获得类的public类型的构造方法。    
 •getDeclaredConstructors()获得类的private类型的构造方法。
 •getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
 •getDeclaredConstructor()获得私有类的特定构造方法
 •newInstance():通过类的不带参数的构造方法创建这个类的一个对象。(Object obj = cls.newInstance();)
 •getType(),拿到当前的属性
 •getModifiers():getModifiers()方法返回int类型值表示该字段的修饰符,对应如下:
  修饰符 对应的int类型
  public 1
  private 2
  protected 4
  static 8
  final 16
  synchronized 32
  volatile 64
  transient 128
  native 256
  interface 512
  abstract 1024
  strict   2048
   
• void set(Object obj, Object value):设置指定调用者中对应成员变量的数据
• Object get(Object obj):获取指定调用者中指定成员变量的数据

• invoke:
1、invoke是Method类下的方法,只有方法才能调用invoke方法,
2、invoke方法的含义:反射调用实例方法,返回的是实例方法的返回值。
3、哪个方法调用invoke方法,返回的就是哪个方法的返回值。
4、invoke方法的参数含义:第一个参数的含义是:实例对象,第二个方法的含义是:调用invoke方法的方法中的参数值。

以上方法实现的例子:

//获得类完整的名字
String className = c2.getName();
System.out.println(className);//输出com.ys.reflex.Person

//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
  System.out.println(field.getName());//age
}

//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
   System.out.println(field.getName());//name   age
}

//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
   System.out.println(method.getName());//work waid equls toString hashCode等
}

//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
   System.out.println(method.getName()+","+method.getModifiers()+","+method.getType());//work say
}

//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);

//创建这个类的一个对象
Object p2 =  c2.newInstance();
//将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob

//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
   System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
//invoke使用方法
      Class<?> cls = User.class;
      Method methods = cls.getMethod("S", int.class);
      Object obj = cls.newInstance();
      int a = (int) methods.invoke(obj,12);
      System.out.println(a);
//使用newInstance获得有参构造并赋值
       Class<?> cls = User.class;
       Constructor constructor = cls.getConstructor(int.class,String.class);
       Object obj = constructor.newInstance(1001,"s");
       User user = (User)obj;
       System.out.println(user.getId()+"\n"+user.getName());

对于注解:

 @Test
   public void test10(){
       //通过反射获取user中的value数据
       //注解对象能否获取到
       Class<?> cls = Stu.class;
       Annotation[] annotations = cls.getAnnotations();
       for (Annotation anno:annotations){
           if(anno instanceof MyAnno){
               MyAnno myAnno=(MyAnno)anno;
               System.out.println(myAnno.value());
          }
      }
  }

@Test
   public void test11() throws Exception{
       //能否获取到当前在id上的注解,比关切将数据获取到
       Class<?> cls = Stu.class;
       //获取到域(属性)对象 根据名字找到对象
       Field field = cls.getDeclaredField("id");
       //判断当前field对象上有没有注解
       //用于判断注解是否在这个field上
       Boolean flag =field.isAnnotationPresent(MyAnno.class);
       if(flag){
  //getAnnotations此方法返回注释类的指定对象数组
           Annotation[] annotations =field.getAnnotations();
           for(Annotation annotation:annotations){
               //需要向下转型 转成MyAnno类型
               //应为当前需求只有一个,所以可以直接强转
               MyAnno myanno = (MyAnno)annotation;
               System.out.println(myanno.value());
          }
      }
  }

@Test
   public void test12() throws Exception{
       Class<?> cls = Stu.class;
       Object obj = cls.newInstance();
       Method method = cls.getDeclaredMethod("setSex",char.class);
       Boolean flag = method.isAnnotationPresent(MyAnno.class);
       if(flag){
        //getAnnotation此方法返回注释类的指定对象
           Annotation annotation = method.getAnnotation(MyAnno.class);
           MyAnno myanno = (MyAnno)annotation;
           System.out.println(myanno.value());
           method.invoke(obj,myanno.value().charAt(0));
           System.out.println(obj);
      }
  }

对于泛型:

 

@Test
   public void test09(){
       //获取泛型中的具体类型
       User<String,String> user = new User<>();
       Class<?> cls = user.getClass();
       Field[] fields = cls.getDeclaredFields();
       for (Field field:fields){
           System.out.println(field.getType()+","+field.getGenericType());
           Type t = field.getGenericType();
      }
  }

 

//代码在demo03252
public class RefTest {
@Test
public void Test(){

}
/*
* 使用反射获取全部的stu中的属性
*/
@Test
public void Test01(){//结果为num
//模板
Class cls = Stu.class;
//通过字节码对象获取全部的属性,全部的域存储在数组中
Field[] fieldArray=cls.getFields();
//field 只可以获得公开的属性
for(Field f:fieldArray){
System.out.println(f.getName());
}
}
@Test
public void Test02(){//结果为id name sex num
Class cls = Stu.class;
Field[] declareFields=cls.getDeclaredFields();//拿到所有的域
for(Field f:declareFields){
System.out.println(f.getName());
}

}
   @Test
public void Test04(){//拿到所有方法(除了构造方法)
Method[] d = this.cls.getDeclaredMethods();
for(Method m:d){
System.out.println(m.getModifiers()+" "+m.getReturnType()+" "+m.getName()+" "+m.getParameterCount());
}
}
   @Test
public void Test05(){//获得所有公开的方法(除了构造方法)
Method[] d = this.cls.getMethods();
for(Method m:d){
System.out.println(m.getModifiers()+" "+m.getReturnType()+" "+m.getName()+" "+m.getParameterCount());
}
}
   @Test
public void Test03() throws NoSuchFieldException, SecurityException{//获得单独的域
Field idFiled = this.cls.getDeclaredField("id");
System.out.println(idFiled.getName());
}//结果:id

   @Test
public void Test06() throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//获取构造方法
Constructor[] constructor=this.cls.getConstructors();
for(Constructor c:constructor){
System.out.println(c.getName());
c.newInstance();//调用无参构造方法
}
}

--案例二:自己编写一个小框架,实现,给定任意一个类,任意一个方法,都可以执行调用

框架:软件的半成品。

---- 你只需要按照框架要求完成业务

---- 包同名(必须小写)

---- 类 类Test

---- 方法 @Test public void test( ){ }

任意:配置

当前必须使用配置文件

在配置文件中配置类,配置方法,编写代码执行 创建对象,调用方法

 

public class Main {
public static void main(String[] args) throws Exception{

//读取配置,获取任意类和方法信息
ReadPro rp = new ReadPro();

Properties clsPro = rp.getPro();

//从当前这个clsPro文件获取value,然后进行反射
Class<?> cls = Class.forName(clsPro.getProperty("ClassName"));

//创建一个对象,因为当前是配置 文件,可以填写任意类,所以类型对象是不确定的
Object obj = cls.newInstance();
//对象创建成功,找到要调用的方法
//获取当前方法对象

Method method = cls.getDeclaredMethod(clsPro.getProperty("methodName"));
//暴力破解 可以查看私有的方法
method.setAccessible(true);
//如何使用方法对象和obj对象进行调用
//因为是obj,不知道哪个方法,那么只能使用方法对象进行回调
Object result = method.invoke(obj);
System.out.println(result);
}
}




   
   
   cls.properties中:
      ClassName=com.dl.tt.bean.A                       methodName=getA

 

回调概念:

 

   A  a =new A();
•   a.getA();
----因为对象的不确定性,但是jvm是知道。Method对象(methoid)可以获取到Obj无法调用   method
----采用所谓调用方法对象调用 obj对象
属性分类:
-- 私有
-- 公有
方法:
-- 私有方法
-- 公有方法
-- 构造方法

问题:a对象也知道当前自己有什么属性和方法。

想一个办法将私有属性aa调用到

使用方法

String str = new String ("abc");

Class cls = String.class;//字节码对象
//根据反射定义,应该可以获取到属性和方法
//应该有一个methods方法
Method[] methods=cls.getMethods();
for(Method m:metString str = new String ("abc");

Class cls = String.class;//字节码对象
//根据反射定义,应该可以获取到属性和方法
//应该有一个methods方法
Method[] methods=cls.getMethods();
for(Method m:methods){
System.out.println(m.getName());
}hods){
System.out.println(m.getName());
}

 

通过反射回调

 

 

 

强制转换不是转发

 

 
posted @ 2023-12-20 19:17  夜柠檬  阅读(34)  评论(0)    收藏  举报