• Java 反射机制 初探*


    反射机制初探 *

    走进沼泽

    在正常的程序中,先有类,然后再有对象。

    • 取得Class对象(类对象)
    public final Class<?> getClass() ;
    
    • 实例观察:
    public class TestDemo {
    	public static void main(String [] args) {
    		Date date = new Date() ; // 产生对象
    		System.out.println(date.getClass());
            // 因为所有类都是 Object的子类,而getClass是final型所以所有类都有getClass()方法
    	}
    }
    
    • 运行结果
    class java.util.Date
    

    发现:调用getClass()方法输出的是类的完整名称,等于找到了对象出处:

    ——这就是“

    Class 类对象实例化

    • java.lang.Class是一个类,这个类就是反射操作的源头,即:所有的反射都要从Class类开始运行 三种实例化方法;

      • 调用Object中的Class方法
      Class<?> cls = date.getClass();
      // 此代码将Class类进行了实例化;getClass()是class类的实例化方法
      
      • 使用 类.class 方法
      Class<?> cls = Date.class;
      

      前者是产生了类的实例化对象之后取得Class类对象,但是类.class方法并没有实例化对象产生

      • 调用Class类提供的 forName() 方法
      Class<?> cls = Class.forName(String ClassName) ;
      

      此时可以不使用 import 语句导入一个明确的类名称,而类名称采用字符串的方式进行描述

    反射实例化对象

    当获得一个类时,使用关键字 new 进行类对象实例化操作;但是如果有了Class对象,那么就可以利用反射进行对象实例化操作。

    实例化对象的方法:

    public T newInstance();
    
    • 实例分析:
    class Book {
    	public Book() {
    		System.out.println("**********Book类 ***********");
    	}
    	@Override
    	public String toString() {
    		return "Hello,World!";
    	}
    }
    
    public class TestDemo {
    	public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
    		Class<?> cls = Class.forName("helloworld.Book");
    		@SuppressWarnings("deprecation")
    		Object obj = cls.newInstance();
    		Book book = (Book) obj ;// 向下转型
    		System.out.println(book);// 调用了Book类的对象
    		// 此过程没有 new 关键字实例化Book类,而是通过反射来实例化对象		
    	}
    }
    

    在反射机制的加持下,类的对象实例化则不再单独依靠 new关键字

    • new 与 反射 实例化对象的区别
    package helloworld;
    interface Fruit { // 函数是接口
    	public void eat() ; 
    }
    class Apple implements Fruit {
    	@Override
    	public void eat() {
    		System.out.println("苹果");
    	}
    }
    class Factory {
    	public static Fruit getInstance(String className) {
    		if ("apple".equals(className)) {
    			return new Apple();
    		}
    		return null ;
    	}
    }
    public class TestFactory {
    	public static void main(String[] args) {
    		Fruit f = Factory.getInstance("apple");// 实例化Fruit对象
    		f.eat();
    	}
    }
    
    • 如果Fruit接口子类被增加了一个,那么就表示程序需要修改工厂类
    package helloworld;
    interface Fruit { // 函数是接口
    	public void eat() ; 
    }
    class Apple implements Fruit {
    	@Override
    	public void eat() {
    		System.out.println("苹果");
    	}
    }
    class Orange implements Fruit{
    	@Override
    	public void eat() {
    		System.out.println("橘子");
    	}
    }
    class Factory {// 工厂类
    	public static Fruit getInstance(String className) {
    		if ("apple".equals(className)) {
    			return new Apple();
    		} else if ("orange".equals(className)) {
    			return new Orange();
    		} else {
    			return null ;
    		}
    	}
    }
    
    public class TestFactory {
    	public static void main(String[] args) {
    		Fruit f = Factory.getInstance("apple");// 实例化Fruit对象
    		f.eat();
    	}
    }
    
    • 如上两则程序可以看出:

      每增加一个类,就要修改工厂类,非常的繁琐复杂;因为工厂类中的对象都是通过new直接实例化,而new就造成了这种高耦合的问题。

    • 解决方法:

      依靠反射实例化对象来完成

      package helloworld;
      interface Fruit { // 函数是接口
      	public void eat() ; 
      }
      class Apple implements Fruit {
      	@Override
      	public void eat() {
      		System.out.println("苹果");
      	}
      }
      class Orange implements Fruit{
      	@Override
      	public void eat() {
      		System.out.println("橘子");
      	}
      }
      class Factory {// 工厂类
      	@SuppressWarnings("deprecation")
      	public static Fruit getInstance(String className) throws InstantiationException, ReflectiveOperationException, ClassNotFoundException {
      		Fruit f = null ; 
      		try {
      			f = ((Fruit) Class.forName(className).newInstance());
      		// 因为 Class.forName()方法返回的是Object(默认/推荐),所以需要向下转型为Fruit
      		} catch (Exception e) {	}
      		return f ;
      	}
      }
      
      public class TestFactory {
      	public static void main(String[] args) throws InstantiationException, ClassNotFoundException, ReflectiveOperationException {
      		Fruit f = Factory.getInstance("helloworld.Apple");
      		f.eat();
      	}
      }
      // 程序中出现疑虑,可以参照"实例分析"
      
    • 此时的程序就完成了解耦和的目的而且可扩展性非常的强

    反射调用方法

    • 定义Book类
    package helloworld;
    
    public class Book {
    	private String title ; 
    	public void setTitle(String title) {
    		this.title = title;
    	}
    	public String getTitle() {
    		return title;
    	}
    }
    

    类中有 无参构造方法 所以实例化对象的时候可以直接利用Class类中的newInstance()方法

    • 在Class类中定义有如下的取得类中Method的操作:

      • 取得类中的全部方法:
      public Method [] getMethods();
      
      • 取得指定类中方法:
      public Method getMethod(String className , Class<?>... parameterTypes);
      

      以上返回的是:java.lang.reflect.Method 类的对象

    • Methos类中的调用方法:

      public Object invoke(Object obj , Object … args);
      
    • 实例:

    package helloworld;
    import java.lang.reflect.Method;
    
    public class TestDemo {
    	public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
    		String classname = "title" ; 
    		Class<?> cls = Class.forName("helloworld.Book");
    		Object obj = cls.newInstance(); // 给出了Book类的实例化对象
    		Method setMet = cls.getMethod("set" + initcap(classname),String.class);
    		Method getMed = cls.getMethod("get" + initcap(classname));
    		setMet.invoke(obj, "Java");
    		System.out.println(getMed.invoke(obj));
    	}
    	private static String initcap(String str) { // 首字母大写
    		return str.substring(0,1).toUpperCase() + str.substring(1) ;
    	}
    }
    

    反射调用成员

    • 类中的属性,一定要在本类实例化对象产生之后才可以分配内存空间。

    • 取得全部成员:

      public Field [] getDeclaredFields() ;
      
    • 取得指定成员:

      public Field getDeclaredFields() ;
      
    • 返回的类型:java.lang.reflect.Field 类对象

      • 取得属性内容:
      public Object get(Object obj);
      
      • 设置属性内容:
      public void set(Object obj , Object Value);
      
    • 定义Book类:

    package helloworld;
    
    public class Book {
    	public String title ;
    }
    
    package helloworld;
    import java.lang.reflect.Field;;
    
    public class TestDemo {
    	public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
    		String classname = "title" ; 
    		Class<?> cls = Class.forName("helloworld.Book");
    		Object obj = cls.newInstance(); // 给出了Book类的实例化对象
    		java.lang.reflect.Field titleField = cls.getDeclaredField("title") ;
    		titleField.set(obj, "Java");
    		System.out.println(titleField.get(obj));
    	}
    }
    
    • 另外,还有一个访问private私有成员的方法,利用 setAccessible() 方法取消封装,然后调用成员。
    package helloworld;
    import java.lang.reflect.Field;;
    
    public class TestDemo {
    	public static void main(String [] args) throws InstantiationException, ReflectiveOperationException {
    		String classname = "title" ; 
    		Class<?> cls = Class.forName("helloworld.Book");
    		Object obj = cls.newInstance(); // 给出了Book类的实例化对象
    		Field titleField = cls.getDeclaredField("title") ;
    		titleField.setAccessible(true);// 取消封装
    		titleField.set(obj, "Java");
    		System.out.println( titleField.get(obj) );
    	}
    }
    

    构造方法和普通方法,也同样可以取消封装,但很少使用。

    总结

    • 实例化对象的方法增加一种“反射”
    • 反射调用类机构只是一个开始
  • 相关阅读:
    C#与C++中struct和class的小结
    C#中string的小结
    树的一些操作——遍历,前序和中序建立后续
    一个快速、高效的Levenshtein算法实现——代码实现
    整数拆分
    阶乘结果中0的个数
    普莱菲尔密码矩阵生成算法
    CTF密码学总结
    盲文对照表
    实验吧-古典密码
  • 原文地址:https://www.cnblogs.com/wangyuyang1016/p/11135254.html
Copyright © 2020-2023  润新知