------- android培训、java培训、期待与您交流! ----------
视频17 透彻分析反射的基础 - Class类
1.引入
反射是从JDK1.2就有的新特性,以后学习框架都要用到反射技术。
要了解反射,就要先了解Class这个类。
Class,是一个类,注意首字母是大写的。
2.PPT内容
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,
至于这个属性的值是什么,则由这个类的实例对象来确定,不同的实例对象有不同的属性值。
Java程序中的各个Java类,它们属于同一类事物,可以用一个类来描述这类事物,这个类的名字就是Class(注意与小写class关键字的区别)。
Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。
学习反射,首先要明白Class这个类。
个人总结:Class类就是Java中各种各样的类,提取构造方法、属性、方法等等共性内容所得的类。
API文档说:Class
类的实例表示正在运行的 Java 应用程序中的类和接口。
再换一个角度思考:
众多的人用一个什么类表示? 人 → Person类
众多的Java类用一个什么类表示? Java类 → Class
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。
提问:
Person类的实例是张三、李四这样一个个人,Class类的各个实例对象又分别对应什么呢?
- 对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList的字节码,等等。
- 一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?答:Class类型。
如何得到各个字节码对应的实例对象,请下面的小程序:
首先,一共有三种方式获取Class类的实例对象,看下面的代码:
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
String str = "获取Class类的实例对象";
Class cls1 = str.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2); //true
System.out.println(cls2 == cls3); //true
}
}
从上面可见,有三种方式得到各个字节码对应的实例对象(Class类型):
- 类名.class,例如,System.class
- 对象.getClass(),例如,new Date().getClass()
- Class.forName(),例如,Class.forName("java.util.Date")
另外,有九个预定义Class实例对象:
API文档描述:“有九种预定义的 Class
对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,
与其表示的基本类型同名,即 boolean
、byte
、char
、short
、int
、long
、float
和double
。”
参见Class.isPrimitive()方法:判定指定的 Class
对象是否表示一个基本类型。
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
String str = "获取Class类的实例对象";
Class cls1 = str.getClass();
System.out.println(cls1.isPrimitive()); // false
System.out.println(int.class.isPrimitive()); // true
System.out.println(int.class == Integer.class); // false
System.out.println(int.class == Integer.TYPE); // true
System.out.println(int[].class.isPrimitive()); // false
}
}
视频18 理解反射的概念
视频17 说了反射的基石 —— Class类,现在才真正讲反射的概念。
反射:就是把Java类中的各种成分映射成相应的Java类。
Java类中有什么啊?
有:它所在那个包的信息,可以通过getPackage()方法得到;
它具有哪些方法,可以通过getMethod()方法得到;它的成员变量,可以通过getFileld()方法得到。
学反射,就是把Java类中各种各样的东西,解析成为一个类。
例如Method这个类,
对于 System.exit
(int status)
、System.getProperties() ,它们都是方法,
都是看做是Method方法类的实例对象,
我们可以这样思考,就像下面这样:
Method → methodeObj1 → System.exit
(int status)
Method → methodeObj2 → System.getProperties()
它们其实就相当于Methode这个类中的methodobj1,obj2。
public class ReflectTest2 {
public static void main(String[] args) throws ClassNotFoundException,
SecurityException, NoSuchMethodException {
Method methodObj1 = System.class.getMethod("exit", int.class);
System.out.println(methodObj1);
// public static void java.lang.System.exit(int)
Method methodObj2 = System.class.getMethod("getProperties", null);
System.out.println(methodObj2);
// public static java.util.Properties java.lang.System.getProperties()
}
}
像上面这样,我们得到了Method这个类的对象,就相当于得到了System这个类的一个方法
PPT上的内容:
1.反射就是把Java类中的各种成分映射成对应的Java类。
例如,一个Java类中用一个Class类的对象来表示,
一个类中组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来标表示,
就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示Java类的Class类显然要提供一系列的方法,
来获得其中的变量,方法,构造方法,修饰符,包等信息,
这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
2.一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,
通过调用Class类的方法可以得到这些实例对象后,
得到这些实例对象后有什么用呢?
怎么用呢?这正是学习和应用反射的要点。
视频19 构造方法的反射应用
1.PPT内容
Constructor 类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码要怎么写呢? 用了缓存机制来保存默认构造方法的实例对象。
Constructor : Constructor类代表某个类中的一个构造方法。
API文档描述: Constructor
提供关于类的单个构造方法的信息以及对它的访问权限。
import java.lang.reflect.*;
public class ConstructorDemo {
public static void main(String[] args) throws SecurityException,
NoSuchMethodException, IllegalArgumentException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
Constructor constructor = String.class
.getConstructor(StringBuffer.class);
String str1 = new String(new StringBuffer("abc")); //通常方式
String str2 = (String) constructor.newInstance(new StringBuffer("abc")); //反射方式
//调用获得的方法时,要用到上面相同类型的实例对象
System.out.println("str1: " + str1 + " ; str2: " + str2);
}
}
注意:
(1) 构造方法没有顺序,你不能getConstructors(2)这样去得到,但可以通过参数类型来获取。
(2) JDK1.5后有可变参数的新特性,你可以写多个参数,getConstructor(Class<?>... parameterTypes) ;
如果是1.5之前,你可以通过传一个数组来传多个参数,getConstructor(Class[] parameterTypes) 。
有了构造方法,能够干嘛呢?
可以得到得到那个类,得到修饰符,最重要的是可以得到实例对象。
2.关于得到Constructor实例对象,要主意的两点
注意看下面:
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = (String)constructor1.new Instance(new StringBuffer("abc"));
第一句编译时,只检查等号左边的语句是否符合语法规则,
并不会执行等号右边的语句,所以它只知道constructor1是一个构造方法,
而不知道具体是什么类的构造方法。
第二句,根据这个构造方法new Instance创建的实例,得到的是一个Object,要强制转换为String类型才行。
如果第二句传给constructor1的参数是"abc"这样一个String,
编译能够通过,但运行时会有类型不匹配的异常,因为这个constructor1是接收StringBuffer.class的构造方法。
关键:要注意,得到方法的时候要传入类型,调用方法时也需要传入同样类型的对象。
3.Class.newInstance()方法
//请留意:Class和Constructor两个类,都有newInstance()方法的。
String obj = (String)Class.forName("java.lang.String").newInstance();
该方法用空参数的构造方法创建实例对象。
API文档:Class的newInstance()方法,如同用一个带有一个空参数列表的 new
表达式实例化该类。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保持默认构造方法的实例对象。
Constructor已经有newInstance()了,为什么Class还要有呢?
其实不要上面这个newInstance()方法也没有影响,
但我们平时是通过 class → constructor → obj 这样来得到对象的,
它这里就给我们一个便利,让我们得到空参数构造方法时,可以省去了中间constructor那步。
通过阅览源码可知,反射会导致程序性能下降。
视频20 成员变量的反射
1.PPT内容
Field类
Field类代表某个类中的一个成员变量。
演示用eclipse自动生成Java类的构造方法
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量呢?
类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?
所以字段fieldX代表的是x的定义,而不是具体的x变量。
2.示例代码
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args) throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
ReflectPoint pt1 = new ReflectPoint(3, 5);
Field fieldY = pt1.getClass().getField("y");
// filedY的值是多少?是5? 错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。
System.out.println(fieldY.get(pt1)); // 输出5
//fieldY只代表类身上的成员变量,不代表某个对象的值。
}
}
public class ReflectPoint { private int x; public int y; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; }}
fieldY只代表类身上的成员变量,不代表某个对象的值。
3.关于暴力反射
暴力反射:无视一个类中私有成员变量、构造方法、方法等等访问权限修饰符,用反射技术提供的方法,"抢"到这些私有的资源。
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args) throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
ReflectPoint pt1 = new ReflectPoint(3, 5);
/***************************************************************/
//Field fieldX = pt1.getClass().getField("x");
//System.out.println(fieldX);
//如果ReflectPoint的x是private修饰,那就会抛NoSuchFieldException异常。
/***************************************************************/
//Field fieldX = pt1.getClass().getDeclaredField("x");
//System.out.println(fieldX.get(pt1));
//会抛IllegalAccessException异常,"让你看见但不让你用"
/***************************************************************/
Field fieldX = pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1)); //3
}
}
视频21 成员变量反射的综合案例
题目:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改为“a”。
import java.lang.reflect.Field;
public class ReflectTest {
public static void main(String[] args) throws IllegalArgumentException,
IllegalAccessException {
MyConstructor mc = new MyConstructor();
changeStringValue(mc);
System.out.println(mc);
}
private static void changeStringValue(Object obj)
throws IllegalArgumentException, IllegalAccessException {
Field[] fields = obj.getClass().getFields();
for (Field field : fields) {
if (field.getType() == String.class) {
String oldValue = (String) field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
}
public class MyConstructor { private int x; private int y; public String a = "bed"; public String b = "aaa"; public String c = "abc"; public String d = "beg"; @Override public String toString() { return "MyConstructor [a=" + a + ", b=" + b + ", c=" + c + ", d=" + d + ", x=" + x + ", y=" + y + "]"; }}
field.getType() == String.class这句,是equals好还是==好?
字节码有几份?一份。
所以,只要是比较字节码就用==比。
视频22 成员方法的反射
1.PPT内容
Method类
1.Method类代表某个类中的一个成员方法。
2.得到类中的某一个方法:
例子:Method charAt = 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对象对应的是一个静态方法!
3.JDK1.4 和 JDK1.5 的invoke方法的区别:
JDK1.5 : public Object invoke(Object obj,Object...args);
JDK1.4 :public Object invoke(Object obj,Object[ ] args),即按JDK1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中每个元素分别对应被……
2.示例代码
import java.lang.reflect.*;
public class MethodDemo {
public static void main(String[] args) throws SecurityException,
NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
String str = new String("ABC");
Method methodCharAt1 = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt1.invoke(str, 2)); // 输出C,invoke就是调用的意思
Method methodCharAt2 = String.class.getMethod("valueOf", char.class);
System.out.println(methodCharAt2.invoke(null, 'C')); // 输出C
}
}
invoke是Method类对象身上的方法,
上面的程序,通过得到String类中的charAt方法,然后调用它。
3.“专家模式”的小例子
例子1:画圆的方法用到圆心和半径,是圆身上的方法。
只有圆自己知道自己怎么画。
例子2:
列车司机刹车,司机只是给列车发信息,要列车自己才知道自己怎么刹住。
上面两个例子,就是“专家模式”的例子:谁有这个数据,谁就应该拥有操作这个数据的方法。
4.JDK1.4前后invoke方法的区别
- JDK1.5 :invoke(Object obj, Object... args)
- JDK1.4 : invoke(Object obj, Object[] args)
import java.lang.reflect.*;
public class MethodDemo {
public static void main(String[] args) throws SecurityException,
NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
String str = new String("ABC");
Method methodCharAt1 = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt1.invoke(str, 2)); // JDK1.5做法
System.out.println(methodCharAt1.invoke(str, new Object[] { 2 })); // JDK1.4做法
}
}
按照1.4的做法,多个参数的时候
import java.lang.reflect.*;
public class MethodDemo {
public static void main(String[] args) throws SecurityException,
NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
String str = new String("ABCDEFG");
Method methodIndexOf = String.class.getMethod("indexOf", new Class[] { String.class,
int.class}); //注意new Class[]
System.out.println(methodIndexOf.invoke(str, new Object[] { "C",new Integer(1)})); //注意这里
}
}
视频23 对接收数组参数的成员方法进行反射
1.PPT内容
目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
用普通方式调完后,要明白为什么要用反射的方式去调?
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),
通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
按JDK1.5的语法,整个数组是一个参数,而按JDK1.4的语法,
数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,
javac会到底按照哪种语法进行处理呢?
JDK1.5肯定要兼容JDK1.4的语法,会按JDK1.4的语法进行处理,
即把数组打散成为若干个单独参数。
所以,给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{"XXX"}),
javac只把它当作JDK1.4的语法进行理解,而不把它当作JDK1.5的语法理解,
因此会出现参数类型不对的问题。
解决方法:
mainMethod.invoke(null,new Object[]{new String[]{"XXX"}});
mainMethod.invoke(null,(Object)new String[]{"XXX"}); //编译器会作特殊处理,
编译时不把参数当作数组看待,也就不会把数组打散成若干个参数了
2.示例代码
import java.lang.reflect.*;
public class ReflectMainDemo {
public static void main(String[] args) throws SecurityException,
NoSuchMethodException, ClassNotFoundException,
IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main",
String[].class);
mainMethod.invoke(null, (Object) new String[] { "111", "222", "333" });
}
}
class TestArguments {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
}
上面程序,输入的参数是:TestArguments
平时我们这样调:TestArguments.main(new String[]{});
视频24 数组的反射
1.PPT内容
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[] 类型使用;
非基本类型的一位数组,既可以当作Object类型使用,又可以当作Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
思考题:怎么得到数组中的元素类型?
数组也是一种类型,API文档:具有相同的元素类型及维数的每一个数组,反射出来的字节码都是相同的。
2.示例代码
public class ReflectArrayDemo {
public static void main(String[] args) {
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[2][3];
String[] a4 = new String[4];
System.out.println(a1.getClass() == a2.getClass());
// System.out.println(a1.getClass() == a3.getClass()); 不能通过编译
// System.out.println(a1.getClass() == a3.getClass()); 不能通过编译
System.out.println(a1.getClass().getName()); // [I
System.out.println(a1.getClass().getSuperclass().getName()); // java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName()); // java.lang.Object
Object obj1 = a1;
Object obj2 = a4;
// Object[] aObj3 = a1; //不能转换,因为a1装的是int,是基本类型,不是Object
Object[] aObj4 = a3;
Object[] aObj5 = a4;
}
}
看上面的代码:
- .getclass():返回此
Object
的运行时类/得到对象的字节码。 - System.out.println(a1.getclass().getName()); //输出[I
- 除了Object,每个类都有父类,System.out.println(a1.getClass().getSuperClass().getName()); //输出java.lang.Object
- 把a3当成一个数组来看待,然后就可以理解为:一个数组里面装了一个数组(一个Object)
3.Class类的getName()方法
public String getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。中括号表示数组,I表示int。
如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '[' 字符加元素类型名。元素类型名的编码如下:
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"
byte.class.getName()
returns "byte"
(new Object[3]).getClass().getName()
returns "[Ljava.lang.Object;"
(new int[3][4][5][6][7][8][9]).getClass().getName()
returns "[[[[[[[I"
4.Arrays工具类的asList方法
一般来说,我们希望打印这个数组,就直接输出里面的元素内容,
而不是返回它的哈希码:System.out.println(new String[] { "a", "b", "c" }); //[Ljava.lang.String;@1bab50a
这时候可以使用Arrays里面的asList方法,将数组转换为List。
import java.util.Arrays;
public class ReflectArrayDemo {
public static void main(String[] args) {
int[] a1 = new int[] { 1, 2, 3 };
String[] a4 = new String[] { "a", "b", "c" };
System.out.println(a1); // [I@1e5e2c3
System.out.println(a4); // [Ljava.lang.String;@18a992f
System.out.println(Arrays.asList(a1)); // [[I@18a992f]
// 对于int,转换成List后,List后只装了原来那个数组对象,还是输出哈希码
System.out.println(Arrays.asList(a4)); // [a, b, c]
// 对于字符串,成功了
}
}
Q:为什么int类型的数组不能用asList方法输出元素呢?
A:首先,String数组符合了JDK1.4的语法,asList(Object[] a),因此String数组的情况就被当作一个数组来处理。
而int数组不是Object[]数组,只能按JDK1.5的语法处理,asList(T... a),整个数组被当作一个对象,
就输出了这个 数组对象 的哈希码了。
视频25 数组的反射
1.示例代码
如果我们想通过反射方式,得到数组的length、其中的值,我们应该怎么做?
可以通过 java.lang.reflect.Array; 这个类,看下面的例子程序。
import java.lang.reflect.*;
import java.util.Arrays;
public class ReflectArrayDemo {
public static void main(String[] args) {
String[] str = new String[] { "111", "222", "333" };
int[] a = new int[] { 1, 2, 3 };
printObject(str);
printObject(a);
}
private static void printObject(Object obj) {
// 给我是一堆,就一个个地打印;如果是一个,就打印一个。
Class clazz = obj.getClass();
if (clazz.isArray()) {
// 是数组,要取出每一个,就要知道它的长度
int len = Array.getLength(obj);
for (int i = 0; i < len; i++) {
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}
}
输出:
111
222
333
1
2
3
2.思考:怎么得到数组里面元素的类型?
结论是没有办法得到,只能得到某一个具体元素的类型,不能得到整个数组的类型。Object[] b = new Object[]{"a",1};
System.out.println(b[0].getClass().getName());//java.lang.String
视频26 ArrayList/HashSet的比较 及 HashCode分析
1.示例代码
import java.util.*;
public class ReflectTest2 {
public static void main(String[] args) {
Collection collections = new ArrayList();
//Collection collections = new HashSet();
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);
System.out.println(collections.size()); // ArrayList的size是4,HashSet的size是3
}
}
该例中,如果是 ArrayList则size是4,HashSet则size是3。
2.ArrayList和HashSet的区别
简单概括:
ArrayList类似于数组,它有顺序,不是指大小顺序,而是指排序顺序。
HashSet则是先判断有没有这个对象,有就不放,要么你先把有的那个对象删掉再放。
详细说明:hashCode方法与HashSet类
3.改写示例代码后
现在我们改写ReflectPoint的equals和hashcode方法,只要x/y相等,就是同一个点。
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@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;
}
}
这时候HashSet的情况就输出2了。
HashSet 是先比较hashCode,相等再进一步比较equals。
ArrayList则是按顺序存放,不会进行什么比较操作。
只有把你的对象存到哈希集合(不仅仅指HashSet)中,
你的hashCode才有价值,才会按照hashCode进行区域分放。
你不存到哈希集合中,你也就不需要修改hashCode了。
面试题:请说一下equals和hashCode的作用。
4.内存泄漏
内存泄漏 就是该回收的内存没有被回收(对象不用了,又不被释放)。比较好的译文:简述Java内存泄漏
落实到HashSet的情况,就是下面这个例子:
package Package1;
import java.util.Collection;
import java.util.HashSet;
public class HashCodeDemo {
public static void main(String[] args) {
Collection collections = new HashSet();
ReflectPoint pt1 = new ReflectPoint(3, 3);
collections.add(pt1);
pt1.setY(7);
collections.remove(pt1);
System.out.println(collections.size()); // 输出1,没有被remove掉,因为改完后hashCode改变了
}
}
另外一道面试题:Java中有内存泄露吗?为什么?
就举上面这个例子,ReflectPoint pt1加入到collections中后,
被修改了成员变量Y,导致它的哈希码改变了,
当你要把它从collections中remove的时候,不能remove掉,
pt1这是还持有ReflectPoint(3,7)的引用,而Java虚拟机这种情况下垃圾收集器是不会把ReflectPoint(3,7)从内存中释放的,
ReflectPoint(3,7)不会被回收,并占用着内存空间,它就是该被回收又没有被回收的对象。
这样就产生了内存泄漏了。
视频27 框架的概念及用反射技术开发框架的原理
了这么多反射类,到底有什么用? 用来做框架.
1.PPT内容
1. 框架与框架要解决的核心问题
我做房子卖给用户住,用户自己安装门窗和空调等等,我做的房子就是框架,
用户需要使用我的框架,把门窗插入进我提供的框架。
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
2. 框架要解决的核心问题
·我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?
我写框架程序怎样能调用到你以后写的类(门窗)呢?
·因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象,
而要用反射方式来做。
3. 综合案例
(1)先直接用new语句创建ArrayList和HashSet的实例对象,
演示用eclipse自动生成ReflectPoint类的equals和hashCode方法,比较两个集合的运行结果差异。
(2)然后改为采用“配置文件+反射”的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异。
(3)引入了eclipse对资源文件的管理方式的讲解。
以后调用别人写的类,有两种方式:
你调用别人的类
别人的类调用你的类(也算是你用别人的)
工具类与框架的性质,就类似于 你自己在里面装修 和 开发商给你的房子 的区别。
明白了HashSet和ArrayList的区别,又知道了hashCode方法的作用,
接下来就把那个程序改为反射的方式来做,改为从配置文件读取。
2.示例代码
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
InputStream ips = new FileInputStream("config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
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);
System.out.println(collections.size());
}
}
config.properties 文件:
className=java.util.ArrayList注意是放在 项目的文件夹中,跟src同目录的,不是放src里面。