张孝祥J2SE加强自学笔记(17-24)

17、透彻分析反射的基础类Class类:
Person p1 = new Person();
Class cls1 = java.util.Date.class //字节码1
Class cls2 = Person.class //字节码2
Class cls3 = p1.getClass()


当.java的源程序文件编译完成后,会编译成.class的字节码文件 当某个字节码文件要执行的时候首先会被load到内存
然后用这些字节码文件创建相应的对象。
面试题:Class.forName()的作用:
答:他的作用就是返回类的字节码,而返回的方式有两种,一种是这个类曾经加载过已经加载到JVM当中来了,那么可以直接得到 例如:p1.getClass()
或者Person.class就可以拿到字节码,另外一种方式是还没有加载过,要通过类加载器将它加载上来然后把它缓存起来例如:Class.forName(java.lang.String);
相同类型的类的字节码只有一份如:下面str1 = "abc"的例子就能说明这一点!!
代码举例:
public static void main(String[] args) throws Exception{
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		//true
		System.out.println(cls1 == cls2);
		//true 得到的都是String类的字节码
		System.out.println(cls1 == cls3);
		
		//String.class是不是原始类型:false String 是一个类它不是原始类型
		System.out.println(cls1.isPrimitive());
		//Integer是int的包装类,所以false
		System.out.println(int.class == Integer.class);
		//Integer.TYPE返回的字节码指的是他所包装的那个类int的字节码,true
		System.out.println(int.class == Integer.TYPE);
		//整型数组他是数组类型他不是原始类型,所以最后一句是true,下面一句是false
		System.out.println(int[].class.isPrimitive());
		System.out.println(int[].class.isArray());

	}
总结:得到各个字节码对应的实例对象(Class类型)
(1)类名.class 例如:System.class
(2)对象.getClass() 例如:new Date().getClass()
(3)Class.forName("类名"),例如:Class.forName("java.lang.String");

九个预定义的Class实例对象:八种基本类型的.class + void.class void也是一种类型

18、理解反射的概念:的反射就是把Java类中的各个成分映射成相应的Java类

19、构造方法的反射应用:
代码举例:
//String.class拿到的那份字节码文件然后再得到他的一个参数为StringBuffer的那个构造方法
		Constructor construct = String.class.getConstructor(StringBuffer.class);
		//根据得到的这个构造方法,构造一个对象
		String abc = (String) construct.newInstance(new StringBuffer("abc"));
		System.out.println(abc);
		注意:反射的应用会导致程序的性能下降,因为他在内部处理的时候要缓存对象。

20、成员变量的反射:
举例代码:
//定义实体类
		public class ReflectionPoint {
			private int x;
			public int y;
	
			public ReflectionPoint(int x, int y) {
				super();
				this.x = x;
				this.y = y;
			}
		}
		//测试代码
		ReflectionPoint rp = new ReflectionPoint(3,5);
		//通过反射拿到RelectionPoint这个类的y成员变量
		Field pointY = rp.getClass().getField("y");
		//此时通过反射拿到的这个属性是属于整个类的,不属于某个对象,所以如果直接打印是打印不出具体的值的
		//如果想得到具体的值必须用get(Object obj)方法来说明取得的是那一个对象的值
		System.out.println(pointY.get(rp));
		
		//通过反射拿到RelectionPoint这个类的x成员变量
		//Field pointX = rp.getClass().getField("x");
		//如果用上面这句话来拿到x成员变量的话会出错,因为x是private的所以在外部是看不到这个方法的(NoSuchMethodExcepion),
		//所以用getDecleardField()方法
		//意为拿到类中声明过的变量,只要你在类中声明了不管是否是私有的,我都能拿到这个值
		Field pointX = rp.getClass().getDeclaredField("x");
		//设置x这个变量的访问权限为“可以访问”,虽然在上一句代码中我们已经拿到了x变量,但它是私有的还不能访问
		//所以在更改了他的访问权限之后才能够打印他的值。
		pointX.setAccessible(true);
		System.out.println(pointX.get(rp));

21、成员变量反射的综合案例: 将一个类中的String类型的成员变量的值中包含字符b的都替换成字符a
示例代码:
//定义实体类
			public class ReflectionPoint {
	
			private int x;
		
			public int y;
			
			public String str1 = "ball";
			
			public String str2 = "basketball";
			
			public String str3 = "itcast";
			
			public ReflectionPoint(int x, int y) {
				super();
				this.x = x;
				this.y = y;
			}
		
				@Override
				public String toString() {
			
				return "str1:" + str1 + "str2:" + str2 + "str3:" + str3; 
				}
		}
		//测试代码:
		public static void main(String[] args) {
			ReflectionPoint rp = new ReflectionPoint(3,5);
			changeStringValue(rp);
			System.out.println(rp);
		}
		
		private static void changeStringValue(ReflectionPoint rp) throws Exception {
			Field[] fields = rp.getClass().getFields();
			for(Field field : fields) {
				//此处比较的时候比较的是字节码:因为字节码只有一份所以用==比较合适,而不适合
				//用equals虽然也能够得出正确的结果
				if(field.getType() == String.class) {
					String oldValue = (String)field.get(rp);
					String newValue = oldValue.replace('b', 'a');
					field.set(rp, newValue);
				}
			}
		}

22、成员方法的反射:调用String类的charAt方法
示例代码:
String str1 = "abc";
		Method method = String.class.getMethod("charAt", int.class);
		System.out.println(method.invoke(str1, 1));
		//自动拆箱和装箱会将new Object[]{1}封装成一个Integer类型的对象
		System.out.println(method.invoke(str1, new Object[] {1}));

23、对接受数组参数的成员方法进行反射:
代码示例:
问题:通过反射的方式来调用TestArgments类的main方法时如何为invoke方法传递参数呢?按照jdk1.5的语法
整个数组是一个参数,当把一个字符串数组作为参数传递给invoke方法,javac会到底按照哪种语法进行处理呢
jdk1.5肯定要兼容jdk1.4的语法,会按照1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以在给main
方法传递参数的时候,不能使用代码main.invoke(null, new String[]{"xxx"}),javac只把他当做jdk1.4的语法进行
理解,而不把他当做jdk1.5的语法进行理解,因此会出现参数类型不对的问题。
解决办法:
(1)main.invoke(null, (Object)new String[]{"xxx"});
		(2)main.invoke(null, new Object[]{new String[]{"xxx"}));
		编译器会做特殊的处理,编译时不会把参数当做数组来看待,也就不会把数组打散成若干个参数了.
		
		
		public class ReflectionTest {
			public static void main(String[] args) throws Exception{
				String startingClassName = args[0];
				Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);
				mainMethod.invoke(null, (Object)new String[]{"123", "222","33"});
				
			}

		class TestArguments{
			public static void main(String[] args){
				for(String arg : args){
					System.out.println(arg);
				}
			}
		}
24、数组与Object的关系及其反射类型:
示例代码:
int[] a1 = new int[]{1,2,3};
		int[] a2 = new int[5];
		int[][] a3 = new int[2][3]; 
		String[] a4 = new String[]{"a","b","c"};
		//true.因为同样都是数组类型且维数一样,他们的字节码肯定一样.
		System.out.println(a1.getClass() == a2.getClass());
		//false.虽然都是数组类型,但是数组的维数不一样,在1.5的编译器中甚至不能变异通过,说是
		//不兼容的操作数类型 
		System.out.println(a2.getClass() == a3.getClass());
		System.out.println(a1.getClass().getName());
		//false.数据的类型不一样,那么他们字节码的name当然不一样
		System.out.println(a1.getClass().getName() == a4.getClass().getName());
		
		Object aObj1 = a1;
		Object aObj2 = a4;
		Object[] aObje3 = a3;
		//Object[] aObje4 = a1;
		Object[] aObje5 = a4;
		
总结:
(1)具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
(2)代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
(3)基本类型的一维数组可以被当做Object类型来使用,不能当做Object[]来使用,因为int
属于基本类型.非基本类型的一维数组既可以当做Object类型使用有可以当做Object[]来使用
(4)Arrays.asList()方法处理int[]和String[]时的差异:
//在jdk1.4中asList方法的定义:public static List asList(Object[] a);
//在jdk1.5中asList方法的定义:public static List<T> asList(T ... a);
//当我们传递a1的时候,因为a1是一个int[]不能转化为Object[],所以不能满足jdk1.4中方法参数的
//定义标准,转而交给jdk1.5所定义的方法来处理,在jdk1.5的定义中参数是一个可变的参数列表,会把
//a1看成是一个对象,所以转化为List之后打印出来就是[[I@de6ced],而传递a4的时候因为a4是String[],他符合jdk1.4方法
//中定义的参数类型Object[],因为jdk1.5要兼容1.4所以会首先按照jdk1.4所定义的方法来执行转换成List后
//打印结果为:[a, b, c]
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));//[a, b, c]

你可能感兴趣的:(jvm,面试,J2SE)