• java-反射


    反射:在运行时期,通过反射可以动态地去获取类中的信息(类的信息,方法信息,构造器信息,字段等信息);

    类的加载过程(加载机制):

      1. 编码

      2. 类的加载器去加载(将字节码文件加载到JVM中,给每个类创建字节码对象)

      3. 初始化

      4. 运行期

    1. Class实例

    其实就是一些类型(类 接口 数组 基本数据类型  void)的字节码对象

    Class 类的实例表示正在运行的 Java 应用程序中的类和接口(字节码对象);

    枚举是一种类,注释(指的是注解Annotation)是一种接口;

    每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象;

    基本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象;

      注意

      1 Class和它的实例的产生: Class的实例是已经存在的类型,所以不能够直接new一个Class对象出来,而通过已知的类型和Class来获得

      2、同一种类型不管通过什么方式得到Class的实例都是相等的;一个类型的字节码对象只有一份

        线程同步:同步监听对象字节码对象来充当同步监听  始终保证都共享的是同一个同步监听对象

      3Class的实例就看成是Java中我们学过的所有的数据类型在JVM中存在的一种状态(字节码对象)

        String.class   int.class  List.class  int[].class  int[][].class

    在 Java API 中,获取 Class 类对象有三种方法:

    第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

    Class clz = Class.forName("java.lang.String");

    第二种,使用 .class 方法。

    这种方法只适合在编译前就知道操作的 Class。

    Class clz = String.class;

    第三种,使用类对象的 getClass() 方法。

    String str = new String("Hello");
    Class clz = str.getClass();
    

     Class实例 的其他获得方式的一些注意点

    public static void main(String[] args) throws Exception {
    	//1. 获取接口的字节码对象的两种方式
    		Class list1 = Class.forName("java.util.List");
    		Class list2 = List.class;
    		System.out.println(list1 == list2);  //true
    		
    		
    	//2. 获取数组的字节码对象的两种方式
    		int[] a = {1,5,8};
    		Class a1 = int[].class;
    		Class a2 = a.getClass();
    		System.out.println(a1 == a2);        //true
    		
    		int[] b = {};
    		Class b1 = b.getClass();
    		System.out.println(a1 == b1);		//true
    		
    		String[] s = {};
    		Class s1 = s.getClass();
    		String[][] ss = {};
    		Class s2 = ss.getClass();
    		System.out.println(s1 == s2);		//false
    //		综上:具有相同元素类型  和  维数的数组都共享该 Class 对象;
    		
    		
    	//3. 获取基本数据类型、包装类型 的字节码对象的几种方式
    		Class int1 = int.class;
    		Class int2 = Integer.TYPE;
    		//获取 包装类型  的字节码对象的三种方式
    		Class intc1 = Class.forName("java.lang.Integer");
    		Class intc2 = Integer.class;
    		Class intc3 = new Integer(10).getClass();
    		
    /*	注意点:	Integer 是 Object Type 对象类型, int 是 Primitive Type 原始类型
    			Integer 有成员变量,有成员方法,int 无成员变量,无成员方法
    			Integer.class 与 int.class 不同
    			Integer.TYPE 与 int.class 相同
    */
    		System.out.println(int1 == int2);   //true
    		System.out.println(intc1 == int2); 	//false
    		System.out.println(intc1 == intc3);  //true
    		
    	 
    /*	4. void 和基本数据类型只能通过类型名称获取该字节码对象
    		 还可以通过其对应的包装类的TYPE属性获取其字节码对象。
    		 * eg 获取int的字节码对象
    		 * 		int.class或者 Integer.TYPE
    		 * 	  获取void的字节码对象
    		 * 		void .class 或者 Void.TYPE
    		 * 
    	其中: Void类是一个不可实例化的占位符类,它持有对标识Java关键字void的Class对象的引用。
    		 并且本身的构造函数为private,即该类是不可以实例化的
    	底层代码:	
    		public final class Void {
    		    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
    		    private Void() {}
    		}
    */		
    		 Class v1 = void.class;
    		 Class v2 = Void.TYPE;
    		 
    		 Class vc1 = Void.class;
    		 Class vc2 = Class.forName("java.lang.Void");
    		 
    		 System.out.println(v1 == v2);    //true
    		 System.out.println(v1 == vc2);   //false
    	}
    

      

     

    2. Constructor类是什么?
      Constructor是一个类,位于java.lang.reflect包下。
      在Java反射中 Constructor类描述的是 类的构造方法信息

    如何获取Constructor类对象?
        - getConstructors(): 获取一个类中所有非私有化的构造方法

         - getConstructor(): 获取非私有 无参的构造方法
        - getConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型


        - getDeclaredConstructors(): 获取类中所有的构造方法(public、protected、default、private)

        - getDeclaredConstructor():   获取私有化的无参构造方法
        - getDeclaredConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型

    package com.ganshao.Test2;
    import java.lang.reflect.Constructor;
    
    //Person 类
    class Person {
    	private String name;
    	public Person(String name) {
    		this.name = name;
    	}
    	private Person() {}
    }
    //Student 类
    class Student {
    	public Student(String name) {}
    }
    public class Test1 {
    	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
    		Class c1 = Class.forName("com.ganshao.Test2.Person");//获取Person类的字节码对象
    		/*
    		 * public Constructor<T> getConstructor(Class<?>... parameterTypes)
    		 * 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
    		 * */
    		Constructor pc1= c1.getConstructor(String.class);		//不可以去获取私有化的构造方法
    		Constructor con1 = c1.getDeclaredConstructor();			//可以去获取私有化的无参构造方法
    		System.out.println(pc1);    //public com.ganshao.Test2.Person(java.lang.String)
    		System.out.println(con1);   //private com.ganshao.Test2.Person()
    		
    		System.out.println("---");
    	//获取一个类中所有非私有化的构造方法
    		Constructor[] cons = c1.getConstructors();
    		for (Constructor constructor : cons) {
    			System.out.println(constructor);
    		}
    		System.out.println("---");
    		
    	//获取一个类中所有的构造方法(跟权限无关)
    		Constructor[] cons1 = c1.getDeclaredConstructors();   //注意末尾加了   "s"
    		for (Constructor constructor : cons1) {
    			System.out.println(constructor);
    		}
    		System.out.println("---");
    		
    		Constructor s1 = Student.class.getDeclaredConstructor();
    		System.out.println(s1);     //报错:NoSuchMethodException,因为没有私有的构造方法
    	}
    }
    

      

    3. 创建对象五种的方式
      * 1.  new 构造器 ,可以创建有参数对象、无参数对象,不能调用priavte的构造方法
      * 2.  字节码对象.newInstance()   只能够调用 无参数的 非私有的 构造方法, 故当类的 无参的构造方法 被私有化就会报错。
      * 3.  通过Constructor的对象.newInstance(Object...init)获得    //可以创建有参数对象、无参数对象,通过setAccessible(true)的方式去调用priavte的构造方法

      * 4. 使用clone方法,我们需要先实现Cloneable接口            例如:  Employee emp4 = (Employee) emp3.clone();

      * 5.  使用反序列化  (这个在io 流中说过)

    ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
    Employee emp5 = (Employee) in.readObject();
    

      

     注意:方法2.3是通过反射创建类对象的

    AccessibleObject 类   是Method、Field、Constructor类的基类,

    它提供了标记反射对象的能力,以抑制在使用时使用默认Java语言访问控制检查,从而能够任意调用被私有化保护的方法、域和构造函数;

      提供了:void setAccessible(boolean flag) 方法

        值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射*的对象应该实施 Java 语言访问检查。

    //Student 类
    class Student {
    	private String name;
    	
    	public Student(String name) {this.name = name;}
    	private Student() {}
    	
    	@Override
    	public String toString() {
    		return "Student [name=" + name + "]";
    	}
    }
    public class Test1 {
    	public static void main(String[] args) throws Exception {	
    	//2. 字节码对象.newInstance()
    //		Class c = Student.class;
    //		Student s = (Student)(c.newInstance());
    //		System.out.println(s);   // 此时 如果 Student()构造方法 访问权限设置为 private 会报错。
    		
    	//3. 通过Constructor方法获得
    		Class c = Student.class;
    		Constructor constructor = c.getDeclaredConstructor();
    		constructor.setAccessible(true);     //让方法的private修饰符在这一次创建对象时失效
    		Student s2 =(Student)(constructor.newInstance());
    		System.out.println(s2);            //Student [name=null]
    		
    		Constructor constructor1 = c.getConstructor(String.class);
    		Student s3 =(Student)(constructor1.newInstance("张三"));
    		System.out.println(s3);          //Student [name=张三]
    	}
    }
    

      

    4. 通过反射获取类属性、方法、构造器

    我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

    而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

    与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

    其中 :注意末尾加不加  “s” ,加s 获取指定的,    不加 s 获取所有的。     getMethod(可以有参数)         getMethods()   。

    getMethods方式返回的自己以及父类的所有的公共的方法

    getDeclaredMethod 返回自己的所有方法  不包含基类中的方法

     getMethod("方法的对象",形参列表的字节码对象)

    5. 通过反射执行方法

    获得字节码对象-->获取方法对象  -->通过方法的对象.invoke() 去执行方法

    Object invoke(Object obj, Object... args)
    返回值类型Object 参数:该方法所属的对象, [...]多个实际参数

    package 反射的三种用法;
    interface A{}
    @SuppressWarnings(value = { "123" })/*压缩警告*/
    public class Person implements A {
    	private String name;
    	private int age;
    	
    	@Deprecated/*表示定义的方法或者字段或者类过时了*/
    	public int getAge() {
    		return age;
    	}
    	public Person(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	public Person() {
    	}
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + "]";
    	}
    //私有的方法
    	private void fun(String name){
    		System.out.println("你好:"+name);
    	}
    //静态的方法	
    	public static void fun1(int a){
    		System.out.println("你好:"+a);
    	}
    	
    	
    }
    

      

    public static void main(String[] args) throws Exception{
    //1. 反射调用方法
    //	 1. 通过反射调用 普通方法        比如:toString()方法
    		Class c1 = Person.class;             //获得字节码对象
    		Method m1 = c1.getMethod("toString");// 获取方法对象
    		Object obj = m1.invoke(new Person());//通过方法的对象.invoke() 去执行方法
    		System.out.println(obj);          //Person [name=null, age=0]
    
    //	 2. 通过反射调用 私有的方法
    		Method m2 = c1.getDeclaredMethod("fun", String.class);
    		m2.setAccessible(true);         //  若没有setAccessible设置这一次的访问权限。   会报错:IllegalAccessException
    		m2.invoke(new Person(),"李四");  //你好:李四
    //	 3. 反射调用  静态的方法      只需要将invoke方法的第一个参数设为null即可.
    		Method m3 = c1.getMethod("fun1",int.class);
    		m3.invoke(null,11);   //你好:11
    		
    //2. 反射获取字段
    		Field f1 = c1.getDeclaredField("name");
    		System.out.println(f1);   //private java.lang.String 反射的三种用法.Person.name
    		
    		Field[] f2 = c1.getDeclaredFields();
    		for (Field i : f2) {
    			System.out.println(i);
    		}
    		//private java.lang.String 反射的三种用法.Person.name
    		//private int 反射的三种用法.Person.age
    		
    //3. 反射获得字段的类型  (  字段对象.getType()  )
    		System.out.println(f1.getType());   //class java.lang.String
    //4. 获得修饰符
    		int modify = f1.getModifiers();  //获取字段的访问修饰符
    		System.out.println(modify);      //2
    //5. 获得包  c1.getPackage()  和实现的接口  c1.getInterfaces()
    //6. 获得注解(注释Annotation) 
    		Method m4 = c1.getDeclaredMethod("getAge");//获取方法
    		System.out.println(m4.getAnnotation(Deprecated.class));  //@java.lang.Deprecated()
    //7. 获得类型的简称 getName()
    		System.out.println(m4.getName());  //getAge
    	} 
    }
    

      

     

    6. 反射可以越过集合类的泛型检查 

    public static void main(String[] args) throws Exception {
    	//反射可以越过集合类的泛型检查
    		ArrayList<Integer> list = new ArrayList<>();
    		list.add(123);
    		list.add(new Integer(45));
    		
    		//通过反射添加泛型以外的类型的数据
    		Class c = list.getClass();
    		Method m1 = c.getMethod("add",Object.class);
    		m1.invoke(list,"nihao");  //调用list集合的add方法,添加数据
    		
    		System.out.println(list);  //[123, 45, nihao]
    	}
    

     

    7. 通过反射运行配置文件内容 

    思想:  (其实在实际开发中经常遇到需求变更)那可不可以不改源程序就能应对大量的需求变更呢?

    答案是可以的,通过Java给我们提供的反射机制,不改源程序,只对配置文件做修改即可,   spring框架就是基于反射机制,通过修改配置文件来实现需求

    class Person{
    	private int id;
    	private String name;
    	public Person(int id,String name){
    		this.id = id;
    		this.name = name;
    	}
    	@Override
    	public String toString() {
    		return "Person [id=" + id + ", name=" + name + "]";
    	}
    }
    public class Test2 {
    	public static void main(String[] args) throws Exception{
    		//反射机制,不改源程序,只对配置文件做修改
    		Constructor<Person> cons = Person.class.getConstructor(int.class,String.class);
    		//Properties类用于读取配置文件的键值对
    		Properties prop = new Properties();
    		prop.load(new FileInputStream("person.properties"));  //读取
    		Person p  = cons.newInstance(Integer.parseInt(prop.getProperty("id")),prop.getProperty("name"));
    		System.out.println(p);    //Person [id=3242, name=李四]
    	}
    }

    
    

      

  • 相关阅读:
    Spring 注解@Transactional
    数据库中为什么要建立视图,它有什么好处?
    类、抽象类与接口的区别
    Comparable和Comparator的区别?
    jetty安装
    python 命令行参数sys.argv
    python--用户认证登录实现
    python--查询员工信息
    python基础
    python学习之路---编程风格规范
  • 原文地址:https://www.cnblogs.com/gshao/p/10181651.html
Copyright © 2020-2023  润新知