• JAVA学习第六十四课 — 反射机制


       Java反射机制是在执行状态中,对于随意一个类,都可以知道这个类的全部属性和方法,对于随意一个对象,都可以调用它的随意一个方法和属性,这样的动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制,简单说:能动态获取类中的信息(类中全部),就是java的反射,也可以理解为对类的解剖

    反射机制的基本应用场景:

       比方一个程序的应用程序(TomCat),为了提高其扩展性,会对外暴露一个接口。在外部定义一个类实现这个接口,可是在应用程序内部无法new对象,所以应用程序会提供一个配置文件,使用接口者能够将写好的而且实现接口的类名,写进配置文件。而应用程序利用反射机制会获取到指定名称的类的字节码文件并载入当中的内容进行调用,这就是反射机制的基本应用。

    总结:反射技术提高了程序的扩展性。且应用简单,用户和程序之间的桥梁变为了配置文件

    想要对一个类文件进行解剖。仅仅要获取该类的字节码文件就可以。

       现实中的事物,不断向上抽取其共性内容。能够得到类来描写叙述事物,反射机制中,也是将字节码向上抽取,用Class来描写叙述字节码文件,那么就能够产生对象,就能够提供方法,获取字节码文件的成员,如:名称、字段、构造函数、一般函数等全部内容,反射就是依靠该类来完毕的


    反射呢,就是拿到.class文件,得到构造器。....构造对象,反着用。而不是得到.java文件

    要想解剖一个类。必需要得到其字节码文件,就必须用到Class对象

    定义演示类

    public class Man {
    	private int age;
    	private String name;
    	public Man() {
    		super();
    		System.out.println("Man run");
    	}
    	public Man(int age, String name) {
    		super();
    		this.age = age;
    		this.name = name;
    		System.out.println("Man run yes"+this.name+this.age);
    	}
    	public void show(){
    		System.out.println(age+"..show.."+name);
    	}
    	private void privatemethod(){
    		System.out.println(age+"..method.."+name);
    	}
    	public void print(int num,String line){
    		System.out.println(num+"..print.."+line);
    	}
    	public static void staticmethod(){
    		System.out.println("..staticmethod..");
    	}
    }

    获取Class对象方式

    要想对字节码文件进行解剖,必须获取字节码文件对象

    获取字节码文件对象的方式有三种:

    1.Object类中的getClass方法
    用这样的方式。必须明白详细的类,并创建对象

    public static void get_Class_Object() {
    		
    		Man man1 = new Man();
    	    Class<?> clas1 = man1.getClass();
    	    Man man2 = new Man();
    	    Class<?

    > clas2 = man2.getClass(); System.out.println(clas1==clas2); }


    2.不论什么数据类型都具备一个静态属性.class来获取其相应Class对象
    相对简单,可是还是须要明白类中的静态成员,扩展性不高

    public static void get_Class_static() {
    		
    		Class<?

    > cls1 = Man.class; Class<?

    > cls2 = Man.class; System.out.println(cls1==cls2); }


    3.仅仅要通过给定的类的字符串名称就能够获取该类(重点掌握)
    能够用Class类中的方法来完毕

    该方法就是forName();这中方式仅仅要有名称就可以

    API:forName(String className)
    返回与带有给定字符串名的类或接口相关联的 Class 对象。

    API:forName(String name, boolean initialize,ClassLoader loader)
    使用给定的类载入器,返回与带有给定字符串名的类或接口相关联的 Class 对象。


    	public static void get_Class() throws ClassNotFoundException {
    		//G:javafaninfanMan.class
    		//而默认的classpath路径是src、bin
    		String classname = "fan.Man";//明白是哪个包中的类
    		Class<?> cls = Class.forName(classname);
    		System.out.println(cls);
    	}
    注意抛异常

    获取Class中的构造函数

    Man man = new Man();曾经new对象,先依据被new的类的名称。寻找该类的字节码文件,并载入进内存。并创建该字节码文件对象。并接着创建该字节码文件相应的Man对象

    API:newInstance()

    创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空參数列表的new 表达式实例化该类。假设该类尚未初始化,则初始化这个类。

    实际上就是Man中的空參的构造函数

    Man类中有空參构造函数

    public static void reflect_Demo() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    
    		String classname = "fan.Man";
    		//找寻该名称类文件。并载入进内存,产生Class对象
    		Class<?

    > cls = Class.forName(classname); Object obj = cls.newInstance(); }


    Man类中无空參构造函数

    Man man = new Man(11,"a");

    假设直接调用上述方法,会抛出初始化异常InstantiationException

    假设有空參构造函数,可是,是私有的private Man(){}。会抛出无效訪问异常IllegalAccessException

    所以:当获取指定名称类中所体现的对象时,而对象初始化不使用空參数构造时。应该先获取其构造函数,通过字节码文件对象就可以完毕。getConstructor

    API:getConstructor(Class<?

    >... parameterTypes)
    返回一个 Constructor 对象。它反映此 Class 对象所表示的类的指定公共构造方法。


    API:getDeclaredConstructors()
    返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的全部构造方法。

    Constructor对象中有方法new对象

    API:newInstance(Object... initargs)
    使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例。并用指定的初始化參数初始化该实例。

    public static void reflect_Demo() throws Exception {
    		
    		String classname = "fan.Man";
    		Class<?> cls = Class.forName(classname);
    		
    		//參数数组,一个int基本数据类型相应的字节码是int.class
    		Constructor<?> constructor = cls.getConstructor(int.class,String.class);
    		//获取到了指定构造函数的对象
    		
    		Object obj = constructor.newInstance(11,"a");
    	}

    仅仅只是在配置文件的时候须要写明类名和构造函数的參数信息

    PS:一般被反射的类一般都是带有空參,由于获取实例方便

    获取Class中的字段

    API:getField(String name)
    返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。


    API:getFields()
    返回一个包括某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的全部可訪问公共字段。


    API:getDeclaredField(String name)
    返回一个 Field 对象。该对象反映此 Class 对象所表示的类或接口的指定已声明字段。


    API:getDeclaredFields()
    返回 Field 对象的一个数组。这些对象反映此 Class 对象所表示的类或接口所声明的全部字段

    	public static void reflect_Demo() throws Exception {
    		
    		String classname = "fan.Man";
    		Class<?> cls = Class.forName(classname);
    		Object object = cls.newInstance();
    		
    		//Field field = cls.getField("name");//有age、name字段。仅仅能获取公有
    		Field field = cls.getDeclaredField("name");//仅仅能获取本类,包含私有
    		System.out.println(field);
    
    		//获取字段的值.........
    		
    		//Object obj = field.get(object); 
    		//System.out.println(obj);//反问失败由于其是私有的
    		
    		//对私有字段的訪问取消权限检查。-> 暴力訪问
    		field.setAccessible(true);//Field的父类方法,改动其訪问权限
    		field.set(object, "a");//设置值
    		Object obj = field.get(object); 
    		System.out.println(obj);//
    	}

    API:AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言訪问控制检查的能力。

    PS:不建议用暴力訪问的方式訪问数据

    获取指定Class中的方法

    获取一个无參方法

    public static void reflect_Demo() throws Exception {
    		
    		String classname = "fan.Man";
    		Class<?

    > cls = Class.forName(classname); // Method[] methods = cls.getMethods();//获取本类及其父类全部公共方法 // methods = cls.getDeclaredMethods();//仅仅获取本类中。全部方法。包含私有 // for(Method M : methods){ // System.out.println(M); // } //拿一个无參方法 Method method = cls.getMethod("show", null);//注意与拿构造函数不同,方法名,方法參数列表 //Object obj = cls.newInstance();//无參构造 //method.invoke(obj, null); Constructor<?> constructor = cls.getConstructor(int.class,String.class); Object obj = constructor.newInstance(1,"a");//有參构造 method.invoke(obj, null);//对带有指定參数的指定对象调用由此 Method 对象表示的底层方法。 }


    获取有參方法

    	public static void reflect_Demo() throws Exception {
    		
    		String classname = "fan.Man";
    		Class<?> cls = Class.forName(classname);
    		
    		Method method = cls.getMethod("print", int.class,String.class);//方法參数列表
    		Object obj = cls.newInstance();
    		
    		method.invoke(obj, 11,"a");//对获取到的方法进行调用
    	}


    反射练习:

    public class Computer {
    	public void run(){
    		System.out.println("Computer is run....");
    	}
    	public void UseInter(Inter p){//对外暴露接口。提高扩展性
    		if(p!=null){
    			p.open();
    			p.close();
    		}
    	}
    }
    public interface Inter {
    	public void open();
    	public void close();
    }
    public class NetCard implements Inter{
    	public void open() {
    		System.out.println("NetCard open....");
    	}
    	public void close() {
    		System.out.println("NetCard  close...");
    	}
    }
    public class SoundCard implements Inter {
    
    	public void open() {
    		System.out.println("SoundCard open...");
    	}
    	public void close() {
    		System.out.println("SoundCard close...");
    	}
    }
    public class Main {
    	public static void main(String[] args) throws Exception{
    	
    		Computer com = new Computer();
    		com.run();
    		//com.UseInter(new SoundCard());//多态,可是这样写须要改动已有代码
    		
    		//利用放射机制来完毕,获取Class文件,在内部实现创建对象
    		File conFile = new File("G:\java\fan\bin\fan\inter.properties");
    		
    		Properties pro = new Properties();
    		FileInputStream fis = new FileInputStream(conFile);
    		pro.load(fis);//将流中信息载入进集合
    		for(int x = 0; x < pro.size();x++){
    			String intername = pro.getProperty("Inter"+(x+1));
    			Class<?> cls = Class.forName(intername);//用Class去载入这个Inter子类
    			Object obj  = cls.newInstance();//创建Inter对象
    			Inter p = (Inter)obj;
    			com.UseInter(p);
    		}
    		fis.close();
    	}
    }

    由于还没有学习XML,所以用properties取代



    这两位大牛关于反射机制写的很好 点击打开链接   打开链接

    能够说是涵盖了反射的全部基础内容

  • 相关阅读:
    [原创]Office Word 2010如何使用printer drivers输出PostScript文件
    MATLAB启动时报错: pathdef.m not found 问题解决方法
    Ubuntu 下 Matlab R2010a 错误:`GLIBCXX_3.4.11' not found 的解决办法
    [转] 一阶导和二阶导的含义
    win7 搭建ftp 设置用户权限 远程访问设置
    Ubuntu 中软件的安装、卸载以及查看的方法总结
    64位ubuntu12.04 LTS安装oracle10g笔记
    如何在Ubuntu 12.04 LTS中使用低版本gcc/g++
    HTTP与HttpServlet
    EXP00091错误
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6901210.html
Copyright © 2020-2023  润新知