zoukankan      html  css  js  c++  java
  • 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=李四]
    	}
    }

    
    

      

  • 相关阅读:
    Java Native Method
    SQL语句优化
    Ibatis的环境搭建以及遇到的问题解决
    Java 构建器
    SpringMVC自定义视图 Excel视图和PDF视图
    java 枚举的常见使用方法
    mysql 根据某些字段之和排序
    MFC The Screen Flickers When The Image Zoomed
    How To Debug Qmake Pro File
    Gcc And MakeFile Level1
  • 原文地址:https://www.cnblogs.com/gshao/p/10181651.html
Copyright © 2011-2022 走看看