Java基础之反射知识点总结


反射的基石:class类
Java类用于描述一类事物的属性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的。不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一类来描述这些事物呢?
可以用一个这个类的名字就是Class,要注意与小写的class关键字的区别之处。Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称列表,方法名称的列表,等等。学习了反射,我们要首先明白这个Class类。
如何得到各个字节码对应的实例对象(Class类型)?
有三种方式:
     1)类名.class    eg:System.class
     2) 对象.getClass  eg:new Date().getClass()
     3) Class.forName(“类名”)   eg:Class.forName(“java.util.Date”)
反射推荐使用第三种方式.
九个预定义Class实例对象。
      (基本类型)  (包装类型)
      boolean ---Boolean.TYPE
           byte ---Byte.TYPE
          char ---Character.TYPE
          short ---Short.TYPE
          int  ---Integer.TYPE
          long ---Long.TYPE
          float ---Float.TYPE
          double ---Double.TYPE
          void --Void.TYPE
数组类型的Class实例对象:Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如:int[] ,void…
反射就是把Java类中的各种成分映射成相应的Java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包,等等的信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
Constructor类代表某个类的一个构造方法:
得到某个类所有的构造方法:
例子:
      Constructor[] constructor=Class.forName(“java.lang.String”).getConstructors()
得到某一个构造方法:
例子:
      Constructor constructor= Class.forName(“java.lang.String”).getConstructor(StringBuffer.class)
注意:获得方法时要用到类型
创建实例对象:
通常方式:
     Stringstr = new String(new StringBuffer(“abc”));
反射方式:
     Stringstr = (String)constructor.newInstance(newStringBuffer(“abc”))
注意:在调用获得的方法时,要用到上面的相同类型的实例对象。
Class.newInstance()方法:
    例子:String obj=(String)Class.forName(“java.lang.String”).newInstance();
该方法内部首先要得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?
     用到了缓存机制来保存默认的构造方法实例对象。
Field类代表某个类中的一个成员变量。
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldX代表的是x的定义,而不是具体的x的变量。

示例代码:

        ReflectPoint pt1 = new ReflectPoint(3, 5);
	Field fieldX = pt1.getClass().getField("x");  //getField()方法是得到那些可见的,或者说是公有的属性
	Field fieldY = pt1.getClass().getDeclaredField("y");  //getDeclaredField()得到那些私有的属性
        //fieldY的值是多少啊?
        //理解这时的filedY只是一个形式上的变量,没有具体的值。
        //也可以理解为fieldY不是对象身上的变量,而是类上的,要用它去取某个对象上对应的值。
        //获得fieldY在pt1对象上,所对应的参数变量的值
        System.out.println(fieldX.get(pt1));   
        fieldY.setAccessible(true);  //暴力反射(相当于,不管别人同意不同意,都要取出该值)
        System.out.println(fieldY.get(pt1));   //但是,通过getFiled()方法得到的变量,只能是公共的,要是私有的变量,则会报错

作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容的”b”改成“a”。

示例代码:

ReflectPoint.Java
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itheima";
@Override
	public String toString() {
		return "ReflectPoint [str1=" + str1
				+ ", str2=" + str2 + ", str3=" + str3 + "]";
	}

ReflectTest.java
private static void ChageStringValue(Object obj) throws Exception {
		Field[] fields = obj.getClass().getFields();
		for (Field field : fields) {
			//因为使用的是同一份字节码,所以使用双等号比较合适
			if(field.getType() == String.class){  //这里最好用“==”来比较,如果使用equals比较时,语义不准确
				String oldValue = (String)field.get(obj);
				String newValue =oldValue.replace('b', 'a');
				field.set(obj, newValue);
			}
		}
}

Method类代表某个类中的一个成员变量。
得到类中的某一个方法:
   例子:
       MethodcharAt =Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
调用方法:
       通常方式:System.out.println(str.charAt(1));
       反射方式:System.out.println(charAt.invoke(str,1));
注意:如果传递给Method对象的invoke()方法的一个参数为null,这有着什么样的意义呢?
回答:说明该Method对象对应的是一个静态的方法。
用反射方式执行某个类中的main()方法。
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main()方法。分析为什么要用反射去调用呢?
问题:启动Java程序的main()方法的参数是一个字符串数组,即
Publicstatic void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke()方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数当把一个字符串数组作为参数传递给invoke()方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,按jdk1.4的语法进行处理时,即把数组打散称为单独的参数。所以,在给main方法传递参数时,不能使用代码
    mainMethod.invoke(null,newString[]{“xxx”}),javac只把它当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
   mainMethod.invoke(null,new Object[]{newString[]{“xxx”}});
   mainMethod.invoke(null,(Object)newString[]{“xxxx”});
注意:编译器会做特殊处理,编译时不会把参数当做数组看待,也就是数组打散成若干个参数。
示例代码:
//TestArguments.main(new String[]{"111","222","333"});
String mainMethod = args[0];
 Method mainMehtod = 
Class.forName(mainMethod).getMethod("main", String[].class);
mainMehtod.invoke(null,(Object)new String[]{"111","222","333"});
//mainMehtod.invoke(null,new Object[]{new String[]{"111","222","333"}});
数组反射:
     1.具有相同的维数和元素类型的数组属于用一个类型,即具有相同的Class实例对象。
     2.代表数组的Class实例对象的getSuperClass()方法返回父类为Object类对应的Class.
     3.基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,即可以当做Object类型使用,又可以当做Object[]类型使用。
     4.Arrays.asList()方法处理int[]和String[]时的差别。
     5.Arrays工具类用于完成对数组的反射操作。
思考题:怎样得到数组中的元素类型?
没有办法数组中元素的类型,因为Object obj = new Object[]{可以放String,
可以放Int},例如:Object obj = new Object[]{“a”,1};
只能利用obj[0].getClass().getName()得到一个元素的类型
    ArrayList是一个有顺序的集合,就相当于一个有序的数组。
    HashSet是一个无序的集合,没有重复的数据。
HashCode的作用?(可以防止内存泄露)
Java中有内存泄露吗?请举例说明?-HashCode


Java按照万物皆对象,将复杂的事物都封装成了对象。
比如:字节码文件封装成了Class对象。并将类中的成员变量,封装了Field对象。类中的方法也封装成了Method对象。类中的构造函数也封装成了Constructor对象。Java将字节码文件,以及字节码文件中的成员都封装了对象。
Class类中的这些方法可以获取到或者访问到指定一个字节码文件中的任意的成员。其实是对类的解刨。反射技术其实代码体现就是依赖于Class和java.lang.reflect包中的对象来完成的。
例子1.用3中方式获取一个类的字节码文件的对象。
package cn.itheima.day14;
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		getClassObject();
	}
	/**
	 * 要想解刨一个类,必须先要获取到该类的字节码文件对象。
	 * 而解刨使用就是Class类中的方法,所以先要获取到每一个
	 * 字节码文件对应的Class类型对象
	 * @throws ClassNotFoundException 
	 */
	public static void getClassObject() throws ClassNotFoundException{
		//获取Class对象的第一种方式
		/*Person p = new Person();
		Class clazz = p.getClass();
		System.out.println(clazz);

		Person p1 = new Person();
		Class clazz1 = p1.getClass();
		System.out.println(clazz==clazz1);*/
		
		//获取Class对象的第二种方式
		//任意数据类型都具备一个class静态属性。
		//Class clazz2 = Person.class;
		//System.out.println(clazz2);
		/**
		 * 前两种方式都有一个弊端,都得在方法中使用Person类。
		 * 但是在一个写好的应用程序中,你是写不进去Person类的。
		 */
                //获取Class对象的第三种方式,将类名作为字符串传递给Class类中的静态static方法foeName()即可。
	        //其他两种方式必须要明确具体的数据类型,而这种方式只需要类的名称作为字符串传递即可。
	        //forName会自动查找该类进行加载和封装,返回该类的Class对象。
	        //注意:传入的字符串必须是类的全类名,即cn.itheima.day14.Person(包含包在内的类名)
		Class clazz3 = Class.forName("cn.itheima.day14.Person");
		System.out.println(clazz3);
	}
}
例子2:获取类的方法的实例演示。
package cn.itheima.day14;
import java.lang.reflect.Method;
public class ReflectDemo2 {
	public static void main(String[] args) throws Exception{
		//getMethodDemo();
		//getSingleMethodDemo();
		getPrivateMethodDemo();
	}
	//获取私有的方法
	private static void getPrivateMethodDemo() throws Exception {
		//第一步:获取Person类的字节码文件对象
		Class clazz = Class.forName("cn.itheima.day14.Person");
		//第二步:获取指定方法名称的一个方法
		//Method method2 = clazz.getMethod("function", null);  //getMethod():只能获取一个公有的方法
		Method method2 = clazz.getDeclaredMethod("function", null);  //getDeclaredMethod():可以获取指定私有的方法
		System.out.println(method2);
		//因为是私有,,不可以直接访问,但是可以通过其父类的方法改变其访问权限。
               method2.setAccessible(true); //暴力访问:突然将方法的修饰符扩大其权限,使其变成可以访问的(取消权限访问的控制)
		Object obj2 = clazz.newInstance();
		method2.invoke(obj2, null);
		
	}
	//获取当个的方法
	private static void getSingleMethodDemo() throws Exception{
		//第一步:获取Person类的字节码文件对象
		Class clazz = Class.forName("cn.itheima.day14.Person");
		//第二步:获取指定方法名称的一个方法
                Method method1 = clazz.getMethod("show", null);  //获取名称为show的方法,传入一个null参数
		System.out.println(method1);
                Object obj = clazz.newInstance();  //创建实例对象,通过你Class对象newInstance()方法获取指定类的一个实例
		method1.invoke(obj, null);  //invoke(): 运行获取到的method方法
	}
	//获取方法演示
	public static void getMethodDemo() throws ClassNotFoundException {
		//解刨出Person类中的方法
		//第一步:获取Person类的字节码文件对象
		Class clazz = Class.forName("cn.itheima.day14.Person");
		//第二步:通过Class类中的方法,来获取指定的内容。
		Method[] methods = clazz.getMethods();  //getMethods():获取Person类中的所有公有的方法。
		//getDeclaredMethod():可以获取本类中的包括私有的所有方法,但是不包括继承的方法。
		Method[] methods2 = clazz.getDeclaredMethods(); 
		for (Method method : methods2) {
			System.out.println(method.toString());
		}
	}
}
例子3:获取Person类的构造函数的实例演示。
package cn.itheima.day14;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo3 {
	public static void main(String[] args) throws Exception{
	     getConstructorDemo();
	}
	//获取构造函数
	private static void getConstructorDemo() throws Exception {
		Class clazz = Class.forName("cn.itheima.day14.Person");
		//Constructor constructor = clazz.getConstructor();
		//Object obj = clazz.newInstance();  //newInstance():其实就是在调用当前类的字节码文件对象中的空参数构造函数在进行初始化
		//如何获取一个指定参数的构造函数,并通过该构造函数对象进行对象初始化。
		Constructor constructor = clazz.getConstructor(int.class,String.class);
		Object obj = constructor.newInstance(30,"zhangsan");  
               //newInstance():作用调用当前类的字节码文件
		                             //对象中的带参数构造函数在进行初始化一个obj实例对象。
		//Method method = clazz.getMethod("show", null);
		//method.invoke(obj, null);
		Method method = clazz.getMethod("method", String.class);
		method.invoke(obj, "...heima.hello");
	}
}
例子4:通过流对象读取配置文件中的的信息数据,然后通过反射技术,动态的加载该配置文件中的类和方法。
package cn.itheima.day14;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectDemo4 {
	public static void main(String[] args) throws Exception{
	     File file = new File("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day14\\config.properties");
	     //file.createNewFile();  //创建一个配置文件
	     Properties prop = new Properties();
	     FileInputStream fis = new FileInputStream(file);   //读取流中的数据
	     prop.load(fis);  //将流中的数据加载到集合当中
	     String className = prop.getProperty("className");
	     String methodName = prop.getProperty("methodName");
	     if(className == null || methodName == null){
	    	 throw new RuntimeException("配置文件书写错误!");
	     }
	    Class clazz = Class.forName(className);
	    Object obj = clazz.newInstance();   //得到一个字节码的实例对象
	    Method method = clazz.getMethod(methodName, null); //得到一个methodName()方法
	    method.invoke(obj, null);
	}
}
例子5.反射技术的具体应用的例子
定义PCI接口:
package cn.itheima.day14;
public interface PCI {
	public void open();
	public void close();
}
定义网卡:
package cn.itheima.day14;
public class NetCard implements PCI {
	@Override
	public void open() {
		System.out.println("netcard run");
	}
	@Override
	public void close() {
		System.out.println("netcard close");
	}
}
定义声卡:
package cn.itheima.day14;
public class SoundCard implements PCI {
	@Override
	public void open() {
		System.out.println("SoundCard run");
	}
	@Override
	public void close() {
		System.out.println("SoundCard close");
	}
}

利用反射定义具体主函数:
package cn.itheima.day14;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
/**
 * 添加了反射机制。
 * @author wl-pc
 */
public class MainBordTest {
	public static void main(String[] args) throws Exception {
		File file = new File("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day14\\card.properties");
		//file.createNewFile();
		Properties properties = new Properties();
		FileInputStream fis = new FileInputStream(file);
		properties.load(fis); 
		//下面是一些健壮性判断
		String countValue = properties.getProperty("cardcount");
		if(countValue==null || countValue.equals("")){
			//throw new RuntimeException("没有板卡设备!");
			System.out.println("没有板卡设备!");
			return;
		}
		int count = 0;
		try {
			count = Integer.parseInt(countValue);
			if(count!=(properties.size()-1)){
				System.out.println("板卡个数不匹配");
				return;
			}
		} catch (NumberFormatException e) {
			System.out.println("cardcount值错误!");
			return;
		}
		//利用反射和for循环得到对应的类中的方法,然后运行。
		for (int i = 1; i < properties.size(); i++) {
			String className = properties.getProperty("card"+i);
			Class clazz = Class.forName(className);
			PCI pci = (PCI) clazz.newInstance();
			pci.open();
			pci.close();
		}
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值