java 反射

package reflect;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectTest {

	public static void main(String[] args) throws Exception {
		/*
		 * 反射基础Class类
		 * String str = "abc"; Class cls1 = str.getClass(); Class cls2 =
		 * String.class; Class cls3 = Class.forName("java.lang.String");
		 * System.out.println(cls1 == cls2); System.out.println(cls1 == cls3);
		 * System.out.println(int.class == Integer.class);
		 * System.out.println(int.class == Integer.TYPE);
		 * System.out.println(int.class.isPrimitive());
		 * System.out.println(int[].class.isArray());
		 * System.out.println(int[].class.isPrimitive());
		 */
		
		//Constructor类
		/*
		 * 得到这个类中构造方法的集合
		 */
		Constructor[] constructors = String.class.getConstructors();
		for (Constructor c : constructors) {
			// System.out.println(c.getModifiers());
		}
		// 得到某一个构造方法,参数类型是本类构造方法的类型的字节码
		Constructor constructor = String.class
				.getConstructor(StringBuffer.class);
		// 因为拿到了构造方法,所以就可以new 对象了,但是这个构造方法在编译
		// 的时候只知道是个构造方法,也不知道是谁的构造方法,也不知道参数是啥
		// 所以new对象的时候要传入对应的参数,返回值是Object,想要String要强转
		String str1 = (String) constructor.newInstance(new StringBuffer("aaa"));
		// 字节码文件中封装了一个空构造方法new对象的方法,方便操作
		String str2 = String.class.newInstance();
		
		//Field类
		/*
		 * 得到类中的成员变量 这个成员变量不是对象的,是类的
		 */
		ReflectPoint rp = new ReflectPoint(3, 5);
		// fieldY是类的字节码文件的,不是对象的,值不是5,所有对象都可以用
		// 这个getField("y");方法不能拿到私有的成员变量,这个方法看不见私有的成员变量
		Field fieldY = rp.getClass().getField("y");
		// 通过get(Object obj)方法取得变量的值,传入变量对应的对象的值
		System.out.println(fieldY.get(rp));
		// 这个方法可以拿到定义的成员变量,私有的也是定义的
		Field fieldX = rp.getClass().getDeclaredField("x");
		// 这个方法是设置这个变量可以被访问,不设置的话,即使拿到了也不能访问
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(rp));
		//Field类应用
		// 将对象中的是字符串的属性的值中所有b换成a
		changeFieldValue(rp);
		System.out.println(rp);
		
		//Method类
		//通过反射调用charAt方法取值
		String string = "abc";
		Method method = String.class.getMethod("charAt", /*参数列表*/int.class);
		//调用charAt方法
		System.out.println(method.invoke(string, /*实际参数*/1));
		//System.out.println(method.invoke(/*静态方法*/null, /*实际参数自动装箱*/1));
		//JDK1.4的调用方式
		System.out.println(method.invoke(string, new Object[]{/*自动装箱*/2}));
		System.out.println(method.invoke(string, new Object[]{new Integer(2)}));
		
		//Method类参数是数组的
		//通过类名调用main方法
		TestArguments.main(new String[]{"111","222","333"});
		//但是这个类名是靠参数传递进来的,你不知道类名是什么,所以要用到反射
		//获取类名
		String startClassName = args[0];
		Method mainMethod = Class.forName(startClassName).getMethod("main", String[].class);
		//这么调用的话会报错,原因是因为如果参数是数组的话,不会按照JDK1.5的语法去解析
		//会按照JDK1.4的语法去解析,因为第二个参数接收的是一个Object的可变参数
		//如果JDK1.5的话会认为传入的多少个就是多少个,但是JDK1.4认为传入的这个String数组就是
		//Object的数组,按照JDK1.4的语法,传入的是三个参数
		//mainMethod.invoke(null, new String[]{"111","222","333"});
		//应该这样写才对,传入的Object的可变参数就是Object的数组这是JDK1.4的语法,1.5的没有对数组参数的支持
		//1.5中参数列表只对非数组参数支持,比如method.invoke(string, new String("abc"),new String("bcd"))
		//代表方法是对应string对象调用的,方法有两个参数,abc和bcd
		mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
		
		//数组的反射,类型和维数相同就是一份字节码
		int[] a1 = new int[2];
		int[] a2 = new int[3];
		int[][] a3 = new int[3][2];
		String[] a4 = new String[5];
		System.out.println(a1.getClass() == a2.getClass());
		//jdk1.6加了泛型?
		//System.out.println(a1.getClass() == a3.getClass());
		//System.out.println(a1.getClass() == a4.getClass());
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a3.getClass().getSuperclass().getName());
		System.out.println(a4.getClass().getSuperclass().getName());
		//都是正确的,因为他们都是Object的字类
		Object obj1 = a1;
		Object obj2 = a3;
		Object obj3 = a4;
		//这个是错误的,int数组里面装的是int,int不是Object的子类
		//Object[] obj4 = a1;
		//这个是正确的,int二维数组里面装的是int[],int[]是Object的子类
		Object[] obj5 = a3;
		//这个是正确的,String数组里面装的是String,String是Object的子类
		Object[] obj6 = a4;
		//方法中参数是字符串数组和int数组得到的List打印后结果不一样
		//如果是字符串数组就会用1.4的语法解析,当成传入的Object数组,
		//将字符串数组中的每个元素加到List中
		//如果是整数数组,就会用1.5的语法解析,因为不是Object数组的子类,
		//传入的是一个参数,将这一个参数添加到List中
		//Arrays.asList(arg)
		int[] intArr = {1,2,3}; 
		String[] strArr = {"1","2","3"};
		System.out.println(Arrays.asList(intArr));
		System.out.println(Arrays.asList(strArr));
		
		//Array类,数组的反射类,不是通过Class类得到的
		//打印出内容,如果不是数组就直接打印,是数组就打印出每个值
		printObject(intArr);
		printObject("maven");
	}

	private static void printObject(Object obj) {
		Class clazz = obj.getClass();
		if(clazz.isArray()) {
			int length = Array.getLength(obj);
			for(int i=0;i<length;i++){
				System.out.println(Array.get(obj, i));
			}
		} else {
			System.out.println(obj);
		}
	}

	private static void changeFieldValue(Object obj) throws Exception {
		//getField方法只能拿到public的!其他三个修饰的都得不到
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {
			// 字节码文件就一份用==比较
			if (field.getType() == String.class) {
				String oldValue = (String) field.get(obj);
				String newValue = oldValue.replace('b', 'a');
				// 还要将新的值set回去
				field.set(/* 哪个对象的 */obj, /* 新值是什么 */newValue);
			}
		}
	}

}



class TestArguments {
	public static void main(String[] args) {
		for(String arg : args) {
			System.out.println(arg);
		}
	}
}


package reflect;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;

public class ReflectTest2 {
	
	public static void main(String[] args) throws Exception {
		//如果是ArrayList下面打印的长度是4,ArrayList有序可以重复
		//Collection collections = new ArrayList();
		//如果是ArrayList下面打印的长度是3,HashSet无序不可以重复
		//所以最后一个pt1没有加进来,如果想要pt3和pt1相等也不加进来
		//要重写hashCode和equals方法
		//重写equals的时候就要重写hashCode,但是如果不用这种Hash存储,用不到HashCode,也就不用重写hashCode方法了
		//Collection collections = new HashSet();
		
		//采用反射方式读取配置文件加载new Collection对象
		//在实际开发中这个config.properties路径可不是相对路径,因为你写完程序的时候
		//只会把bin目录中的可执行文件给别人,根本没有这个项目目录下的配置文件
		//所以配置文件得到的方式只有一种:在别人安装你程序的时候,让别人选择放在哪个目录下,
		//因为如果你写死D盘,别人没D盘很麻烦,所以要让别人选,然后你再得到
		//InputStream is = new FileInputStream("config.properties");
		
		//还有一种常用的加载配置文件的方式,是最常用的,但是不能替代上面的那种,用类加载器加载
		//会去classpath根目录下(bin)找这个文件,这个时候就可以把配置文件放在bin目录下和可执行文件放在一起
		//直接放在src中就会自动放入bin(classpath)中,一定要写全包名,路径最前面不要加/
		//但是这种是有缺陷的,没有outputStream,上面那种有,这种只能加载不能往回写
		//三大框架都是用的类加载器方式加载,所以配置文件要放在classpath中
		//InputStream is = ReflectTest2.class.getClassLoader().getResourceAsStream("reflect/config.properties");
		
		//还有种简单的方式字节码文件的一个方法,也是调用了类加载器
		//这个时候相对路径就是相对目录的
		//绝对路径是对项目根目录的(bin)并不是src,最终的还是bin目录,放在src中的文件会自动到bin中
		InputStream is = ReflectTest2.class.getResourceAsStream("config.properties");
		Properties prop = new Properties();
		prop.load(is);
		//关闭的是windows资源,java中流资源需要垃圾回收机制回收
		is.close();
		String className = prop.getProperty("className");
		/*这里一定是强制转换成父类,否则配置的第一次是ArrayList,
		 这里是ArrayList,下次配置的是HashSet,这里就报错了*/
		Collection collections = (Collection)Class.forName(className).newInstance();
		ReflectPoint pt1 = new ReflectPoint(3, 3);
		ReflectPoint pt2 = new ReflectPoint(5, 5);
		ReflectPoint pt3 = new ReflectPoint(3, 3);
		collections.add(pt1);
		collections.add(pt2);
		collections.add(pt3);
		collections.add(pt1);
		//当算hashCode的值的时候用到了变量就不要再修改了,否则对象hashCode值就变了
		//就不能再对本对象操作了,比如删除不了,就内存泄露了
		pt1.y = 10;
		collections.remove(pt1);
		System.out.println(collections.size());
	}

}

class ReflectPoint {

	private int x;
	public int y;

	protected String str1 = "blog";
	String str2 = "babby";
	String str3 = "itcast";

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}

	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	@Override
	public String toString() {
		return str1 + "," + str2 + "," + str3;
	}

}

congif.properties:className=java.util.HashSet

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值