• 理解Java反射


    一、反射简介

    Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。

    1、反射的描述

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
    想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
    反射就是动态的从内存加载一个指定的类,把java类中的各种成分映射成一个个的Java对象,获取该类中的所有的内容。

    2、反射的功能

    1.在运行时判断任意一个对象所属的类;
    2.在运行时构造任意一个类的对象;
    3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
    4.在运行时调用任意一个对象的方法
    重点:是运行时而不是编译时
    反射的优点:增加程序的灵活性;增强了程序的扩展性。
    反射的缺点:运用反射会使我们的软件的性能降低,复杂度增加。

    二、反射的使用

    1、获取Class对象

    1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
    2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
      前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
    3:使用的Class类中的方法,静态的forName方法。指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。
    示例代码如下:

    package com.luis.test;
    public class User {
    	private long id;
    	private String name;
    	public int age;
    	
    	public long getId() {
    		return id;
    	}
    	public void setId(long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	@Override
    	public String toString() {
    		return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
    	}
    }
    
    public class GetClass {
    	public static void main(String[] args) {
    		//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
    		//  类型的对象,而我不知道你具体是什么类,用这种方法
    		User user = new User();
    		Class c1 = user.getClass();
    			
    		//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
    		//  这说明任何一个类都有一个隐含的静态成员变量 class
    		Class c2 = User.class;
    			
    		//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
    		//抛出 ClassNotFoundException 异常
    		Class c3;
    		try {
    			c3 = Class.forName("com.luis.test.User");
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    2、获取类中的方法,属性,构造函数。

    public class GetClass {
    	public static void main(String[] args) {
    		try {
    			Class clazz = Class.forName("com.luis.test.User");
    			Constructor con = clazz.getConstructor(new Class[]{});
    			User instance = (User) con.newInstance();
    			instance.toString();
    			//获得类完整的名字
    			String className = clazz.getName();
    			System.out.println("---------完整类名----------");
    			System.out.println(className);
    					
    			//获得类的public类型的属性。
    			Field[] fields = clazz.getFields();
    			System.out.println("---------public属性----------");
    			for(Field field : fields){
    			   System.out.println("public类:"+field.getName());
    			}
    					
    			//获得类的所有属性包括私有的
    			Field [] allFields = clazz.getDeclaredFields();
    			System.out.println("---------所有属性----------");
    			for(Field field : allFields){
    				System.out.println(field.getName());
    			}
    					
    			//获得类的public类型的方法。这里包括 Object 类的一些方法
    			Method [] methods = clazz.getMethods();
    			System.out.println("---------public方法----------");
    			for(Method method : methods){
    				System.out.println(method.getName());
    			}
    					
    			//获得类的所有方法。
    			Method [] allMethods = clazz.getDeclaredMethods();
    			System.out.println("---------所有方法----------");
    			for(Method method : allMethods){
    				System.out.println(method.getName());
    			}
    					
    			//获得指定的属性
    			Field f1 = clazz.getField("age");
    			System.out.println("---------指定属性----------");
    			System.out.println(f1);
    			//获得指定的私有属性
    			System.out.println("---------指定私有属性----------");
    			Field f2 = clazz.getDeclaredField("name");
    			//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
    			f2.setAccessible(true);
    			System.out.println(f2);
    							
    			//创建这个类的一个对象
    			Object p2 =  clazz.newInstance();
    			//将 p2 对象的  f2 属性赋值为 Bob,f2 属性即为 私有属性 name
    			f2.set(p2,"luis");
    			//使用反射机制可以打破封装性,导致了java对象的属性不安全。 
    			System.out.println("---------创建对象----------");
    			System.out.println(f2.get(p2)); //Bob
    					
    			//获取构造方法
    			Constructor [] constructors = clazz.getConstructors();
    			System.out.println("---------获取构造方法----------");
    			for(Constructor constructor : constructors){
    				System.out.println(constructor.toString());
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    运行结果:

    ---------完整类名----------
    com.luis.test.User
    ---------public属性----------
    public类:age
    ---------所有属性----------
    id
    name
    age
    ---------public方法----------
    toString
    getName
    getId
    setName
    getAge
    setId
    setAge
    wait
    wait
    wait
    equals
    hashCode
    getClass
    notify
    notifyAll
    ---------所有方法----------
    toString
    getName
    getId
    setName
    getAge
    setId
    setAge
    ---------指定属性----------
    public int com.luis.test.User.age
    ---------指定私有属性----------
    private java.lang.String com.luis.test.User.name
    ---------创建对象----------
    luis
    ---------获取构造方法----------
    public com.luis.test.User()
    

    3、反射获取父类属性

    可以通过反射获取父类的私有属性,代码如下:

    public class Parent {
    	public String publicField = "parent_publicField";
    	protected String protectField = "parent_protectField";
    	String defaultField = "parent_defaultField";
    	private String privateField = "parent_privateField";
    }
    
    package com.luis.test;
    public class Admin extends Parent{
    }
    
    public class GetClass {
    	public static void main(String[] args) {
    		try {
    			Class clazz = Class.forName("com.luis.test.Admin");
    			//获取父类私有属性值
    			System.out.println(getFieldValue(clazz.newInstance(),"privateField"));
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static Field getDeclaredField(Object obj,String fieldName) {
    		Field field = null;
    		Class c = obj.getClass();
    			 for(; c != Object.class ; c = c.getSuperclass()){
    				 try {
    					 field = c.getDeclaredField(fieldName);
    					 field.setAccessible(true);
    					 return field;
    				 }catch (Exception e){
    					 //如果这里的异常打印或者往外抛,则就不会执行c = c.getSuperclass(),最后就不会进入到父类中了
    				 }
    			 }
    		return null;
    	}
    	public static Object getFieldValue(Object object,String fieldName) throws Exception{
    		  Field field = getDeclaredField(object,fieldName);
    		  return field.get(object);
    	}
    }
    

    运行结果:parent_privateField
    要注意:直接通过反射获取子类的对象是不能得到父类的属性值的,必须根据反射获得的子类 Class 对象在调用 getSuperclass() 方法获取父类对象,然后在通过父类对象去获取父类的属性值。

    三、动态代理

    代理类在程序运行时创建的代理方式被成为动态代理。代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
    接口和实现类:

    public interface Interface {
        void doSomething();
        void somethingElse(String arg);
    }
    
    public class RealObject implements Interface {
    
        @Override
        public void doSomething() {
            System.out.println("doSomething.");
        }
    
        @Override
        public void somethingElse(String arg) {
             System.out.println("somethingElse " + arg);
        }
    }
    

    动态代理对象处理器:

    public class DynamicProxyHandler implements InvocationHandler {
        private Object proxyed;
    
        public DynamicProxyHandler(Object proxyed) {
            this.proxyed = proxyed;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             System.out.println("代理工作了.");
             return method.invoke(proxyed, args);
        }
    }
    

    测试类:

    public class Main {
        public static void main(String[] args) {
            RealObject real = new RealObject();
            Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
                    new Class[] {Interface.class},new DynamicProxyHandler(real));
            proxy.doSomething();
            proxy.somethingElse("luis");
        }
    }
    

    反射极大地增强了代码的灵活性,广泛应用于各种框架中,因而对于反射的掌握是非常重要的。

    本文参考了:
    https://www.cnblogs.com/ysocean/p/6516248.html
    https://www.cnblogs.com/luoxn28/p/5686794.html
    https://blog.csdn.net/ShadowySpirits/article/details/79756259

  • 相关阅读:
    tif文件导入postgresql
    与你相遇好幸运,使用redis设置定时任务
    用sinopia搭建npm私服
    Postman设置Header不生效问题
    iOS 动态加载LaunchScreen上的图片
    iOS 封装一个带复制功能的UILabel
    ios开发文字排版,段落排版,富文本
    iOS使用hitTest和loadView处理UIView事件传递
    iOS 更改状态栏颜色和隐藏状态栏
    iOS scrollView嵌套tableView的手势冲突解决方案
  • 原文地址:https://www.cnblogs.com/liuyi6/p/10086917.html
Copyright © 2020-2023  润新知