黑马程序员 —— Java基础加强 - 反射

时间:2020-12-24 00:42:36

------- 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类型):

  1. 类名.class,例如,System.class
  2. 对象.getClass(),例如,new Date().getClass()
  3. Class.forName(),例如,Class.forName("java.util.Date")


另外,有九个预定义Class实例对象:

API文档描述:“有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,

与其表示的基本类型同名,即 booleanbytecharshortintlongfloatdouble。”

参见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();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码要怎么写呢? 用了缓存机制来保存默认构造方法的实例对象。


ConstructorConstructor类代表某个类中的一个构造方法。

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)名称。 

如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '[' 字符加元素类型名。元素类型名的编码如下:

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"
中括号表示数组,I表示int。


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对资源文件的管理方式的讲解。

以后调用别人写的类,有两种方式:
  1. 你调用别人的类 

  2. 别人的类调用你的类(也算是你用别人的)  


工具类与框架的性质,就类似于 你自己在里面装修 和 开发商给你的房子 的区别。


明白了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里面。


------- android培训java培训、期待与您交流! ----------