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

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

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

    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里面。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值