1. 反射
透彻分析反射的基础_Class类
1) 概述
class
Class--->代表一类什么样的事物?
Person类的实例对象
Person p1 = new Person();
Person p2 = new Person();
Data
Math
Java类(Class)的实例对象,对应各个类在内存中的字节码。
概念:
所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。
获取类的字节码:
1.类名.class
Class cls1 = Data.class//字节码1
Class cls2 = Person.class//字节码2
2.对象.getClass()
p1.getClass();
3.Class.forName("java.name.String"),主要用于反射
分两种情况:
类的字节码已经加载到内存,直接找到字节码,返回;
虚拟机还没有字节码,类加载器去加载,缓存起来,然后返回字节码;
九个预定义Class实例对象
表示八个基本类型和 void。这些类对象由 Java虚拟机创建,与其表示的基本类型同名,
即 boolean、byte、char、short、int、long、float和 double。对应获取Class对象的方法为:
Boolean.TYPE, Byte.TYPE,Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
int.class == Integer.TYPE
boolean isPrimitive() :判定指定的 Class对象是否表示一个基本类型。
数组类型的Class实例对象
boolean isArray():判定此 Class对象是否表示一个数组类。
Class.isArray();
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如:int[],void
2) 使用
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
String str = "fff";
Class c1 = String.class;
Class c2 = str.getClass();
Class c3 = Class.forName("java.lang.String");
System.out.println(c1==c2);//true
System.out.println(c1==c3);//true
System.out.println(c1.isPrimitive());//false
System.out.println(int.class.isPrimitive());//true
//static Class<Integer> TYPE 表示基本类型 int 的 Class 实例。
System.out.println(int.class == Integer.TYPE);//true
//boolean isArray():判定此 Class 对象是否表示一个数组类。
System.out.println(int[].class.isArray());//true
}
}
理解反射的概念
反射的概念
就是把java类中的各种成分映射成相应的java类。例如,一个java类是用一个class类的对象来表示,一个类的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包、数组等信息。这些信息就是用相应的类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象,
Package getPackage():获取此类的包。返回的是Package类的实例对象;
Method getMethod(String name, Class<?>... parameterTypes):返回一个 Method对象,代表某个类中的一个方法,它反映此 Class对象所表示的类或接口的指定公共成员方法。
Constructor<T> getConstructor(Class<?>... parameterTypes) :返回一个 Constructor对象,代表某个类中的一个构造方法,它反映此 Class对象所表示的类的指定公共构造方法。
.............
问题:
得到这些实例对象后有什么用?怎么用?
构造方法的反射应用
1) 概述
构造方法的反射应用
Constructor类代表某个类中的一个构造方法
得到某个类所有构造方法:
Constructor[] ct = Class.forName("java.lang.String ").getConstructors();
得到某个类的一个构造方法:
//获得String类的构造方法,只获得参数为一个,且为StringBuffer类型的构造方法
Constructor ct1 = Class.forName(java.lang.String).getConstructor(StringBuffer.class);//获得方法时要用StringBuffer.class类型
创建实例对象(三步:classàConstructorànew Object)
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:String str1 = (String)ct1.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法创建实例对象(两步:classà new Object)
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认构造方法(空参数的构造方法),然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象(Constructor对象)。
反射会导致程序性能严重下降
2) 使用
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
//用反射实现类似功能:new String(new StringBuffer("asd"));
Constructor con1 = String.class.getConstructor(StringBuffer.class);//编译器只看变量的定义(=左边),不看代码的执行(=右边),只是将右边编程二进制
//用构造方法new一个实例,此时创建的实例为Object类型,需要强转
String str1 = (String)con1.newInstance(new StringBuffer("asdf"));
System.out.println("str1="+str1);
String str2 = (String)Class.forName("java.lang.String").newInstance();
str2="123";
System.out.println("str2="+str2);
}
}
成员变量的反射
1) 概述
Field类:代表某个类中的一个成员变量
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?
类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?
所以字段FieldX代表的是x的定义,而不是具体的x变量
2) 使用(获取指定成员变量)
请看如下案例:
//参考类:
publicclass ReflectPoint {
privateintx;
publicinty;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
publicstaticvoid main(String[] args)
{
System.out.println("我是一个参考类");
}
}
//应用类
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
ReflectPoint r1 = new ReflectPoint(8,9);
Field fieldy = r1.getClass().getField("y");//获得其他类的成员变量,先从其他类中得到字节码,字节码中有成员变量
/*fieldy的值?
fieldy只代表这个类字节码上的变量,没有对应到对象身上;每一个对象身上都有fieldy,要用它去取某个对象上的值
*/
//取出变量在某个对象上的值
System.out.println("fieldy在r1对象上的值 = "+fieldy.get(r1));
//Field fieldx = r1.getClass().getField("x");//x是私有的,fieldx变量看不见。
Field fieldx = r1.getClass().getDeclaredField("x");//x是私有的,只要是声明过的,fieldx都看得见,但是不能访问私有的。
fieldx.setAccessible(true);//设置允许访问,暴力反射
System.out.println("fieldx在r1对象上的值 = "+fieldx.get(r1));
}
}
成员变量反射的综合案例(获取部分成员变量)
Class<?> getType() :返回一个 Class对象,它标识了此 Field对象所表示字段的声明类型。
Object get(Object obj) :返回指定对象上此 Field表示的字段的值。
void set(Object obj, Object value) :将指定对象变量上此 Field对象表示的字段设置为指定的新值
请看如下示例:
//参考类
class ReflectPoint1 {
public String str1;
public String str2;
public String str3;
public ReflectPoint1(String str1, String str2, String str3) {
super();
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
//要打印对象的属性值,需覆盖toString()方法
public String toString()
{
returnstr1+";"+str2+";"+str3;
}
}
//应用类
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
ReflectPoint1 rp = new ReflectPoint1("bababa","abdgfbbaaabbbb","string");
strchange(rp);
System.out.println(rp);
}
publicstaticvoid strchange(Object obj)throws Exception
{
Field[] fi = obj.getClass().getFields();
for(Field field:fi)
{
if(field.getType() == String.class);//字节码的比较用==,比较的都是一份字节码;
{
String strolds = (String)field.get(obj);
String strnews = strolds.replace('b','a');
field.set(obj,strnews);
}
}
}
}
成员方法的反射
1) 概述
成员方法的反射
Method类代表某个类中的一个成员方法;
得到类中的某一个方法:
Method method = String.class.getMethod("charAt", int.class);
调用方法:
通常方式:str.charAt(1);
反射方式:method.invoke(str,1);
如果invoke(null,1),表示该Method对象对应的是一个静态的方法
JDK1.4和JDK1.5的invoke方法的区别
1.4:public Object invoke(Object obj,Object[] args)
将数组作为参数传递给invoke方法,
数组中的每个元素分别对应被调用方法中的一个参数,
所以可写成method.invoke("str",new object[]{1})形式
1.5:public Object invoke(Object obj,Object... args)传入的参数为可变参数
2) 使用
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
//Object invoke(Object obj, Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
String str = "abcd";
//用反射实现str.charAt(1)功能
Method m = String.class.getMethod("charAt", int.class);
System.out.println(m.invoke(str,1));//jdk1.5 传入可变参数
System.out.println(m.invoke(str,new Object[]{2}));//jdk1.4 传入数组参数
//m.invoke(null,1),表示不用对象就能调用的方法,静态方法
}
}
对接收数组参数的成员方法进行反射
1) 概述
对接收数组参数的成员方法进行反射
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
调用方法
通常方式:类名.main(new String[]{"ttt","www","qq"});
反射方式:
String classstartname = args[0];
Method mainmethod = Class.forName(classstartname).getMethod("main", String[].class);
mainmethod.invoke(null, new Object[]{new String[]{"11","22"}});
为什么要用反射去调用?
程序中已知要调用main方法,但是不知道哪个类的main方法,要通过传进来的参数,确定类名,
然后调用传进来的类的main方法。
为什么会出现参数个数异常呢?
通过反射的方法调用main方法时,为invoke方法传递参数的方式是:
JDK1.5:整个数组是一个参数,JDK1.4:数组中每一个元素对应一个参数,
当把字符串数组作为参数传递给invoke方法时,JDK1.5肯定要兼容JDK1.4语法,
会按JDK1.4的去处理,即把数组打散成若干单独参数。所以在给main方法传递参数时,
不能使用mainmethod.invoke(null, new String[]{"11","22"});,会出现参数类型不对问题;
解决办法:
1.mainmethod.invoke(null, new Object[]{new String[]{"11","22"}});
2.mainmethod.invoke(null, (Object)new String[]{"11","22"});
2) 使用
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
//实现在此程序中调用testArguments类的main方法
//通常方式
testArguments.main(new String[]{"11","22"});
//反射方式
//假设传进main方法的参数中args[0]就是一个类名,且有main方法
String classstartname = args[0];
Method mainmethod = Class.forName(classstartname).getMethod("main", String[].class);
/*mainmethod.invoke(null, new String[]{"11","22"});
参数个数异常,本来是接收一个数组参数,JDK1.5为了兼容JDK1.4,会将数组拆一次,拆成一个个(多个)参数*/
//解决办法:1.将数组参数再次封装成数组; 2.将数组参数强转成Object对象,就不拆包了;
mainmethod.invoke(null, new Object[]{new String[]{"11","22"}});
mainmethod.invoke(null, (Object)new String[]{"11","22"});
}
}
class testArguments//按f2出现完整的类名,将完整的类名复制到run configurations界面
//中的Arguments界面的program arguments中,用来配置运行类的main方法参数
{
publicstaticvoid main(String args[])
{
for(String str:args)
{
System.out.println(str);
}
}
}
数组与Object的关系及其反射类型
1) 概述
数组的反射
数组元素类型相同,维度相同,得到的字节码就是同一份,属于同一个类型,即具有相同的Class实例对象;
基本数据类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用,
非基本数据类型的一维数组既可以当做Object类型使用,也可当做Object[]类型使用
Class的getName()方法:
以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称
如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '['字符加元素类型名。
元素类型名的编码如下:
Element Type Encoding
boolean Z
byte B
char C
class or interface Lclassname;
double D
float F
int I
long J
short S
类或接口名 classname是上面指定类的二进制名称。
String.class.getName():returns "java.lang.String"
(new int[3][4][5][6][7][8][9]).getClass().getName():returns "[[[[[[[I"
Class的getSuperclass()方法:
Class<? super T> getSuperclass() :返回表示此 Class所表示的实体(类、接口、基本类型或 void)的超(父)类的 Class。
2) 使用
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
int a0 = 3;
int[] a1 = newint[]{1,2};
int[] a2 = newint[]{3,4,5};
int[][] a3 = newint[][]{{4,4},{3,3},{2,2,2}};
String[] a4 = new String[]{"a","b","c"};
sop(a1.getClass() == a2.getClass());
sop(a1.getClass().getName());//[I
sop(a1.getClass().getSuperclass().getName());//java.lang.Object
sop(a4.getClass().getSuperclass().getName());//java.lang.Object
sop(a2.getClass().getSuperclass().getName());//java.lang.Object
sop(a3.getClass().getSuperclass().getName());//java.lang.Object
sop(String.class.getSuperclass().getName());
Object o0 = a0;
Object o1 = a1;
Object o2 = a2;
Object o3 = a3;
Object o4 = a4;
sop(o0);
sop(o1);
sop(o2);
sop(o3);
sop(o4);
//Object[] b0 = a0;编译不通过,因为a0是个基本数据类型,是个对象
//Object[] b1 = a1;编译不通过,因为a1,a2是基本数据类型的一维数组,是个对象,不是对象数组
//Object[] b2 = a2;
Object[] b3 = a3;//编译通过,因为a3是基本数据类型的二维数组(数组的数组),表示有一个数组,数组里面装的是int[](Objet),是个对象数组
Object[] b4 = a4;//编译通过,因为String是Object,String[]等于对象数组
//可得出:int(基本数据类型)不是对象,String是对象,所以前者不能转成Object数组,后者可以
/*结果没有意义
*sop(a1);
sop(a4);*/
//用Arrays工具类对数组进行操作
//JDK1.5:public static <T> List<T> asList(T... a)
//JDK1.4:public static List asList(Object[] a)
sop(Arrays.asList(a1));//JDK1.5处理
sop(Arrays.asList(a3));//JDK1.4处理+JDK1.5处理
sop(Arrays.asList(a4));//JDK1.4处理
}
public staticvoid sop(Object obj)
{
System.out.println(obj);
}
}<span style="font-family:Calibri;font-size:14px;"> </span>
数组的反射应用
1) 概述
数组的反射应用
获取,设置,数组的值,获取数组的长度,怎么做?
通过java.lang.reflect.Array类中的方法:
static Object get(Object array, int index)
返回指定数组对象中索引组件的值。
static int getLength(Object array)
以 int形式返回指定数组对象的长度。
static void set(Object array, int index, Object value)
将指定数组对象中索引组件的值设置为指定的新值。
怎么得到数组中的元素类型?没有办法
int [] a = new int[3]
怎么知道a的数组类型?没有办法
因为
Object[] a =new Object[]{"a",1}
a[0].getClass.getName();
a[0].getClass.getName();
只能得到每一个具体元素的类型,不能得到整个数组的元素类型
2) 使用
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
int a0 = 3;
int[] a1 = newint[]{1,2};
int[] a2 = newint[]{3,4,5};
int[][] a3 = newint[][]{{4,4},{3,3},{2,2,2}};
String[] a4 = new String[]{"a","b","c"};
sop(a2);
sop(a3);
sop(a4);
sop("asd");
}
public staticvoid sop(Object obj)//打印数组或基本数据类型
{
Class c = obj.getClass();
if(c.isArray())
{
int len = Array.getLength(obj);//通过Array类访问数组,获取数组长度
for(int i=0;i<len;i++)
{
System.out.println(Array.get(obj, i));//返回指定数组对象中索引组件的值。
}
}
else
System.out.println(obj);
}
}
ArrayList_HashSet的比较及Hashcode分析
1) 概述
反射的综合应用--ArrayList_HashSet的比较及Hashcode分析
哈希算法
哈希算法提高从集合中查找元素的效率,这种方式将集合分成若干区域,每个要存入哈希集合的对象可以计算出一个哈希码,
可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。
面试题1--HashCode()方法的作用?
返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable提供的哈希表)的性能。
当从hashset集合中查找某个对象时,java系统首先调用对象的hashcode()方法获得该对象的哈希码,然后根据哈希码,
找到相应的存储区域,最后取出该存储区域内的每个元素与该对象进行equals方法比较,不用遍历集合中的所有元素,就可以得到结论。
面试题2--内存溢出,内存泄露的例子?
当一个对象被存储进HsahSet集合中后,就不能修改这个对象中的那些参与计算哈希值得字段了,否则,对象修改后的哈希值就不同了,
在这种情况下,去哈希集合中检索对象时,就找不到原来的对象了。这也会导致无法从hashset集合中单独删除当前对象,
从而导致内存泄露。
提示:
一个类的两个实例对象用equals()方法比较的结果相等时,他们的哈希码也必须相等,但是,
equals(java.lang.Object) 方法比较结果不相等的对象可以有相同的哈希码,
或者哈希码相同的两个对象,调用equals()方法比较结果可以不等。
2) 使用
类1
publicclass ReflectPoint {
privateintx;
publicinty;
public String str1="aa";
public String str2="ss";
public String str3="dd";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
publicint hashCode() {
finalint prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
publicboolean equals(Object obj) {
if (this == obj)
returntrue;
if (obj == null)
returnfalse;
if (getClass() != obj.getClass())
returnfalse;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
returnfalse;
if (y != other.y)
returnfalse;
returntrue;
}
//要打印对象的属性值,需覆盖toString()方法
public String toString()
{
returnstr1+";"+str2+";"+str3;
}
}
类2
class ReflectTest
{
publicstaticvoid main(String[] args) throws Exception
{
Collection c = new HashSet();//new ArrayList();//
ReflectPoint r1 = new ReflectPoint(2,2);
ReflectPoint r2 = new ReflectPoint(3,3);
ReflectPoint r3 = new ReflectPoint(2,2);
c.add(r1);
c.add(r2);
c.add(r3);
c.add(r1);
r1.y = 8;
c.remove(r1);//将r1的y值更改后,哈希值改变,存储区域改变,remove()方法在原本区域里找不到r1的y值了
//这样原本该删除的对象没有删掉,又新增对象的话,日积夜累,就会导致内存溢出。
sop(c.size());
/*list集合是有序可重复的,存的是对象的引用;
set集合是无序不可重复的,默认equals()比较的是哈希值,hashcode值是根据内存地址值算出来的,
要让r1与r3完全相等,必须同时复写equals(),hashcode()方法(用eclipse自带生成)*/
}
public staticvoid sop(Object obj)//打印数组或基本数据类型
{
Class c = obj.getClass();
if(c.isArray())
{
int len = Array.getLength(obj);//通过Array类访问数组,获取数组长度
for(int i=0;i<len;i++)
{
System.out.println(Array.get(obj, i));//返回指定数组对象中索引组件的值。
}
}
else
System.out.println(obj);
}
}
反射的作用--实现框架功能
1) 概述
框架与工具类
自己做房子给用户住,用户自己安装门,我做的房子就是框架,用户需要使用我的框架,把门插进我提供的框架中。
框架与工具类有区别,工具类被用户的类调用,而框架是调用用户提供的类。
框架要解决的核心问题
我在写框架时,我的框架程序怎样能调用到你以后写的程序呢?
因为在写框架程序时,无法知道要被调用的类名,所以,
在程序中无法直接new某个类的实例对象了,而要用反射的方式(Class.forName(classname).new Instance)来做。
使用步骤:
先在程序中直接用new语句创建ArrayList和HashSet的实例对象,
然后创建一个properties文件,配置内容。
在程序中加载进配置文件,用配置文件加反射的方式创建ArrayList和HashSet的实例对象
提示
IO流.close();的底层含义是:先把widows的widow(系统资源,物理资源)关掉,然后由java VM回收本IO流对象
2) 使用
配置文件的获取方式
1.通过Properties得到资源文件
可以读写配置文件,因为有InputStream和OutputStream
/*资源文件放在project文件夹中,
* 要用完整的路径,但完整的路径不是硬编码,而是运算出来的。
* javaweb中getRealPath()方法动态的得到eclipse的安装目录,再拼上配置文件的路径
*/
InputStream ips = new FileInputStream("config.properties");//此处用的是相对路径,不符合开发。
实际开发中不这样放置配置文件,在javaweb中通过getRealpath()方法得到eclipse的真实安装完整路径,在拼接上配置文件的内置路径
2.通过类加载器得到资源文件的方式(不能替代上面的方式,此方式常用)
类加载器:
类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java类。通过此实例的newInstance()方法就可以创建出该类的一个对象。
加载类的方式有以下几种:
1)从本地系统直接加载
2)通过网络下载.class文件
3)从zip,jar等归档文件中加载.class文件
4)从专有数据库中提取.class文件
5)将Java源文件动态编译为.class文件(服务器)
开发中一般将配置文件放到classpath路径下(用户目录下,即全部存放的是.class文件的文件夹)
配置文件放到当前包里,保存的时候会直接保存在classpath路径下,即用户目录(.class目录)中生成该配置文件。
通过类加载器获取资源文件,只读。
格式:
/*1.资源文件放在同个包里,保存的时候eclipse会自动将配置文件放到classpath目录下
* 通过类加载器加载,在classpath路径指定目录下,逐一查找要加载的文件.
* 该方式获得资源文件首先要获得class文件,然后获得类加载器,然后才获得资源
* */
InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("com/config.properties");
//2.资源文件放在另一个子包里,通过类文件,直接获得资源文件,用相对路径
InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");
//3.资源文件放在另一个子包里,通过类文件,直接获得资源文件,用绝对路径
InputStream ips = ReflectTest.class.getResourceAsStream("/com/resources/config.properties");
动态代理:在程序运行时,运用反射机制动态创建代理类
参考:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
类1
publicclass ReflectPoint {
privateintx;
publicinty;
public String str1="aa";
public String str2="ss";
public String str3="dd";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
publicint hashCode() {
finalint prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
publicboolean equals(Object obj) {
if (this == obj)
returntrue;
if (obj == null)
returnfalse;
if (getClass() != obj.getClass())
returnfalse;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
returnfalse;
if (y != other.y)
returnfalse;
returntrue;
}
//要打印对象的属性值,需覆盖toString()方法
public String toString()
{
returnstr1+";"+str2+";"+str3;
}
}
<span style="font-family:Calibri;font-size:14px;"> </span>
类2
class ReflectTest
{
public static void main(String[] args) throws Exception
{
//类名.class.getResourceAsStream(String name) :在classpath路径下查找具有给定名称的资源。
//类名.class.getClassLoader().getResourceAsStream(String name)在classpath路径下查找,返回读取指定资源的输入流。
/*资源文件放在同个包里,保存的时候eclipse会自动将配置文件放到classpath目录下
* 通过类加载器加载,在classpath路径指定目录下,逐一查找要加载的文件.
* 该方式获得资源文件首先要获得class文件,然后获得类加载器,然后才获得资源
* */
InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("com/config.properties");
//资源文件放在另一个子包里,通过类文件,直接获得资源文件,用相对路径
//InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");
//资源文件放在另一个子包里,通过类文件,直接获得资源文件,用绝对路径
//InputStream ips = ReflectTest.class.getResourceAsStream("/com/resources/config.properties");
/*资源文件放在project文件夹中,
* 要用完整的路径,但完整的路径不是硬编码,而是运算出来的。
* javaweb中getRealPath()方法动态的得到eclipse的安装目录,再拼上配置文件的路径
* */
//InputStream ips = new FileInputStream("config.properties");//此处用的是相对路径,不符合开发。
Properties pp = new Properties();
pp.load(ips);
ips.close();//先把widows的widow(系统资源,物理资源)关掉,然后java VM回收本对象
String className = pp.getProperty("className");
//通过反射,传进一个类名,得到一个默认构造方法生成的对象
Collection c = (Collection)Class.forName(className).newInstance();
//Collection c = new HashSet();//new ArrayList();//
ReflectPoint r1 = new ReflectPoint(2,2);
ReflectPoint r2 = new ReflectPoint(3,3);
ReflectPoint r3 = new ReflectPoint(2,2);
c.add(r1);
c.add(r2);
c.add(r3);
c.add(r1);
//r1.y = 8;
//c.remove(r1);
sop(c.size());
}
public static void sop(Object obj)//打印数组或基本数据类型
{
Class c = obj.getClass();
if(c.isArray())
{
int len = Array.getLength(obj);//通过Array类访问数组,获取数组长度
for(int i=0;i<len;i++)
{
System.out.println(Array.get(obj, i));//返回指定数组对象中索引组件的值。
}
}
else
System.out.println(obj);
}
}