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