Java----反射

高新技术 反射
一、Class
Class类:Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class
疑问如何获得对象的字节码?
1.对象.getClass();
2.类名.class();
3.Class.fotNamre("类名");
package cn.itcast.day1;

public class ReflectTest {

	public static void main(String[] args) throws Exception {
		String s = "abcd";
		Class c1 = s.getClass();
		Class c2 = String.class;
		Class c3 = Class.forName("java.lang.String");//会抛异常,因为可能不存在。
		System.out.println(c1 == c2);
		System.out.println(c1 == c3);
		System.out.println(c2 == c3);
	}
}

解析:说明他们用的都是同一份字节码文件。
package cn.itcast.day1;

public class ReflectTest {

	public static void main(String[] args) throws Exception {
		String s = "abcd";
		Class c1 = s.getClass();
		Class c2 = String.class;
		Class c3 = Class.forName("java.lang.String");
		System.out.println(c1 == c2);
		System.out.println(c1 == c3);
		System.out.println(c2 == c3);
		System.out.println("是否是原始类型:"+c1.isPrimitive());//是否是元素类型(即基本数据类型)。
		System.out.println("是否是原始类型:"+int.class.isPrimitive());
		System.out.println("是否相同:"+(int.class == Integer.class));//判断int和Integer的字节码是否相同。
		System.out.println("是否相同:"+(int.class == Integer.TYPE));//TYPE代表所包装的基本数据类型。
		System.out.println("是否是原始类型:"+(int[].class.isPrimitive()));//判断数组类型是否是原始类型。
		System.out.println("是否是数组类型:"+(int.class.isArray()));//判断是否是数组类型。
	}
}

:只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[]void
二、反射
疑问神马是反射
反射:就是把Java类中的各种成分映射成相应的类。例如,一个Java中用一个Class类的对象来表示。一个类中的组成部分:成员变量方法构造方法等信息也用一个个Java类来表示。表示Java类的Class类显然要提供一系列的方法,来获取其中的变量方法构造方法修饰符等信息,这些信息就是用相应的实例对象来表示。它们是FieldMethodConstructorPackage等。
Constructor类:代表某个类中的一个构造方法。
Constructor<T> getConstructor(Class<?>... parameterTypes) 
          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 
Constructor<?>[] getConstructors() 
          返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
T newInstance() 
          创建此 Class 对象所表示的类的一个新实例。 

package cn.itcast.day1;

import java.lang.reflect.Constructor;

public class ConstructorTest {
	
	public static void main(String[] args) throws Exception {
		//调用方法时,要用到类型
		Constructor constructor = String.class.getConstructor(StringBuffer.class);
		//调用获得的方法时,要用到上面相同类型的实例对象。
		String s = (String) constructor.newInstance(new StringBuffer("abcd"));
		System.out.println(s.charAt(2));
	}
}

Filed类:代表某个类中的一个成员变量。
Field getField(String name) 
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 
Field[] getFields() 
          返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 
Field getDeclaredField(String name) 
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 
Field[] getDeclaredFields() 
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 
package cn.itcast.day1;

import java.lang.reflect.Field;

public class FieldTest {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		ReflectPoint fc = new ReflectPoint(3, 5);
		Class clazz = fc.getClass();
		Field fieldX = clazz.getField("x");//获取x字段封装成Field。
		System.out.println(fieldX.get(fc));//获取对象fc上的Field指定字段上的值。
		Field fieldY = clazz.getField("y");
		System.out.println(fieldY.get(fc));
	}

}

抛出异常找不到变量x,因为x是私有变量。要想获取到想,就要使用getDeclaredField()方法。要访问到私有变量还必须使用setAccessible(true)方法暴力反射。
改进
package cn.itcast.day1;

import java.lang.reflect.Field;

public class FieldTest {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		ReflectPoint fc = new ReflectPoint(3, 5);
		Class clazz = fc.getClass();
		Field fieldX = clazz.getDeclaredField("x");//将变量x封装成Field,即使该变量是私有的
		fieldX.setAccessible(true);//将该Field对象设置为暴力反射
		System.out.println(fieldX.get(fc));
		Field fieldY = clazz.getField("y");
		System.out.println(fieldY.get(fc));
	}

}

练习:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”。
package cn.itcast.day1;

import java.lang.reflect.Field;

public class Demo {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Test test = new Test("bb", "bccb", 20);
		Class clazz = test.getClass();
		Field[] fields = clazz.getDeclaredFields();//获取所有成员变量数组
		for(Field field : fields){
			field.setAccessible(true);//将field设置为暴力反射,访问私有变量
			if(field.getType() == String.class){//将变量类型字节码和String的字节码比较,因为String字节码只有一份,比较的是两引用地址,所有用“==”连接
				String oldstr = (String) field.get(test);//获取字符串
				String newstr = oldstr.replaceAll("b", "a");
				field.set(test, newstr);//将新字符串存覆盖旧字符串
			}
		}
		System.out.println(test);
	}

}

Method类:代表某个类中的一个成员方法。
Method getMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 
Method[] getMethods() 
          返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 
Object invoke(Object obj, Object... args) 
          对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 
package cn.itcast.day1;

import java.lang.reflect.Method;

public class Demo {

	public static void main(String[] args) throws Exception {
		String str = "abcd";
		Method method_charat = str.getClass().getMethod("charAt", int.class);//获取类的方法
		System.out.println(method_charat.invoke(str, 3));//将方法作用到对象上
	}

}

:如果invoke()第一个参数为null说明,该方法是静态的。
练习:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
package cn.itcast.day1;

import java.lang.reflect.Method;

public class Demo {

	public static void main(String[] args) throws Exception {
		String className = args[0];
		Class clazz = Class.forName(className);
		Method methodmain = clazz.getMethod("main", String[].class);//获取main方法
		methodmain.invoke(null, new String[]{});//调用main方法。
	}

}
package cn.itcast.day1;

public class Test {
	public static void main(String[] args) {
		System.out.println("Hello World");
	}
}

抛出数组角标越界异常,原因是没有传入类名。
点击Run Demo的下拉菜单——>Run Configurations

转到Arguments——>输入cn.itcast.day1.Test——Apply——>Run


抛出错误的参数个数,因为JDK1.5为了兼容1.4版本,虽然传入的是一个字符串数组,但是虚拟机会将其拆包,因而变成多个参数,也就发生了错误的参数个数异常。为了解决这一问题,需要把字符串数组装入Object数组中,这样虚拟机就只拆掉Object数组,而保留字符串数组了。
改进
package cn.itcast.day1;

import java.lang.reflect.Method;

public class Demo {

	public static void main(String[] args) throws Exception {
		String className = args[0];
		Class clazz = Class.forName(className);
		Method methodmain = clazz.getMethod("main", String[].class);//获取main方法
		methodmain.invoke(null, new Object[]{new String[]{}});//调用main方法。
	}

}

也可以将数组强转为Object
package cn.itcast.day1;

import java.lang.reflect.Method;

public class Demo {

	public static void main(String[] args) throws Exception {
		String className = args[0];
		Class clazz = Class.forName(className);
		Method methodmain = clazz.getMethod("main", String[].class);//获取main方法
		methodmain.invoke(null, (Object)new String[]{});//调用main方法。
	}

}
告诉虚拟机传入的是一个Object,避免被拆包。
数组反射:具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
package cn.itcast.day1;

public class Demo {

	public static void main(String[] args) throws Exception {
		int[] a1 = new int[3];
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[3];
		System.out.println(a1.getClass() == a2.getClass());
		//System.out.println(a1.getClass() == a3.getClass());不相等,因为维数不同
		//System.out.println(a1.getClass() == a4.getClass());不相等,因为类型不同
	}

}

:基本数据类型的一维数组可以被当作Object类型使用,不能当做Object[]类型使用;非基本数据类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
package cn.itcast.day1;

public class Demo {

	public static void main(String[] args) throws Exception {
		int[] a1 = new int[3];
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[3];
		System.out.println(a1.getClass() == a2.getClass());
		//System.out.println(a1.getClass() == a3.getClass());//不相等,因为维数不同
		//System.out.println(a1.getClass() == a4.getClass());//不相等,因为类型不同
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a4.getClass().getSuperclass().getName());
		Object obj1 = a1;//相当于把int[]数组封装成Object
		Object obj2 = a4;//相当于把String[]数组封装成Object
		//Object[] obj3 = a1;//相当于把int[]数组转换成Object[],这是不行的,因为基本数据类型的父类不是Object
		Object[] obj4 = a3;//把a3看作是一个一维数组里面装着一维数组。
		Object[] obj5 = a4;//相当于把String[]数组封装成Object[]数组。
	}

}
Arrays.asList()方法处理int[]String[]时的差异:
package cn.itcast.day1;

import java.util.Arrays;

public class Demo {

	public static void main(String[] args) throws Exception {
		int[] a1 = new int[]{1,2,3,4};
		String[] a2 = new String[]{"a","b","c","e"};
		int a = 6;
		System.out.println(Arrays.asList(a1));
		System.out.println(Arrays.asList(a2));
	}
}

解析JDK1.5asList(T...a),是用可变参数接收。JDK1.4则是用asList(Object[] a),是用Object数组接收。当String[]数组传入时,用的是1.4版本。而int[]传入时,Object[]数组不能接收。所以用1.5版本接收。这时可变参数是把int[]数组当作一个对象,而非一个数组。所以打印的就是地址了。
练习:用反射原理打印数组或非数组。
package cn.itcast.day1;

import java.lang.reflect.Array;
public class Demo {

	public static void main(String[] args) throws Exception {
		int[] a1 = new int[]{1,2,3,4};
		String a2 = "abcd";
		printArrys(a1);
		printArrys(a2);
	}

	private static void printArrys(Object obj) {
		// TODO Auto-generated method stub
		Class clazz = obj.getClass();
		if (clazz.isArray()) {
			int length = Array.getLength(obj);
			for(int i=0;i<length;i++){
				System.out.println(Array.get(obj, i));
			}
		} else {
			System.out.println(obj);
		}
	}

}

知识回顾:
ArrayList:底层是数组结果,元素可重复。比较元素的时候依赖于equals()方法。
HashSet:底层是哈希表结构,元素不可重复。比较元素的时候,先比较hashCode(),如果相同再比较equals()方法。
内存泄露
package cn.itcast.day1;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;

public class Demo {

	public static void main(String[] args) throws Exception {
		Collection hs = new HashSet();
		Test t1 = new Test(3, 3);
		Test t2 = new Test(5, 5);
		Test t3 = new Test(3, 3);
		
		hs.add(t1);
		hs.add(t2);
		hs.add(t3);
		System.out.println(hs.size());
		
		t1.x=8;
		
		System.out.println(hs.remove(t1));
		System.out.println(hs.size());
	}

}
package cn.itcast.day1;

public class Test {
	
	public int x;
	public int y;

	public Test(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;
		Test other = (Test) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	
}

:当一个对象被存储进HashCode集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露
练习:小型框架
package cn.itcast.day1;

import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Properties;

public class Demo {

	public static void main(String[] args) throws Exception {
		//读取配置文件
		Properties prop = new Properties();
		InputStream in = new FileInputStream("cn/itcast/day1/config.properties");
		prop.load(in);
		//关闭资源,防止内存泄露
		in.close();
		
		Class clazz = Class.forName(prop.getProperty("classname"));
		Constructor constructor = clazz.getConstructor(null);
		Collection coll = (Collection) constructor.newInstance(null);
		Method method = clazz.getMethod(prop.getProperty("addfun"), Object.class);
		method.invoke(coll, "a");
		method.invoke(coll, "b");
		method.invoke(coll, "c");
		method.invoke(coll, "d");
		
		System.out.println(coll.size());
	}

}


类加载器的方式管理资源和配置文件:
package cn.itcast.day1;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Properties;

public class Demo {

	public static void main(String[] args) throws Exception {
		//读取配置文件
		Properties prop = new Properties();
		InputStream in = Demo.class.getResourceAsStream("config.properties");
		prop.load(in);
		//关闭资源,防止内存泄露
		in.close();
		
		Class clazz = Class.forName(prop.getProperty("classname"));
		Constructor constructor = clazz.getConstructor(null);
		Collection coll = (Collection) constructor.newInstance(null);
		Method method = clazz.getMethod(prop.getProperty("addfun"), Object.class);
		method.invoke(coll, "a");
		method.invoke(coll, "b");
		method.invoke(coll, "c");
		method.invoke(coll, "d");
		
		System.out.println(coll.size());
	}

}
因为配置文件要和.class文件一同打成jar包,所以把配置文件放到classpath目录下,用类加载器加载资源和配置文件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值