反射的基石:class类
Java类用于描述一类事物的属性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的。不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一类来描述这些事物呢?
可以用一个这个类的名字就是Class,要注意与小写的class关键字的区别之处。Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称列表,方法名称的列表,等等。学习了反射,我们要首先明白这个Class类。
如何得到各个字节码对应的实例对象(Class类型)?
有三种方式:
1)类名.class eg:System.class
2) 对象.getClass eg:new Date().getClass()
3) Class.forName(“类名”) eg:Class.forName(“java.util.Date”)
反射推荐使用第三种方式.
九个预定义Class实例对象。
(基本类型) (包装类型)
boolean ---Boolean.TYPE
byte ---Byte.TYPE
char ---Character.TYPE
short ---Short.TYPE
int ---Integer.TYPE
long ---Long.TYPE
float ---Float.TYPE
double ---Double.TYPE
void --Void.TYPE
数组类型的Class实例对象:Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如:int[] ,void…
反射就是把Java类中的各种成分映射成相应的Java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包,等等的信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
Constructor类代表某个类的一个构造方法:
得到某个类所有的构造方法:
例子:
Constructor[] constructor=Class.forName(“java.lang.String”).getConstructors()
得到某一个构造方法:
例子:
Constructor constructor= Class.forName(“java.lang.String”).getConstructor(StringBuffer.class)
注意:获得方法时要用到类型
创建实例对象:
通常方式:
Stringstr = new String(new StringBuffer(“abc”));
反射方式:
Stringstr = (String)constructor.newInstance(newStringBuffer(“abc”))
注意:在调用获得的方法时,要用到上面的相同类型的实例对象。
Class.newInstance()方法:
例子:String obj=(String)Class.forName(“java.lang.String”).newInstance();
该方法内部首先要得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?
用到了缓存机制来保存默认的构造方法实例对象。
Field类代表某个类中的一个成员变量。
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldX代表的是x的定义,而不是具体的x的变量。
示例代码:
- ReflectPoint pt1 = new ReflectPoint(3, 5);
- Field fieldX = pt1.getClass().getField("x");
- Field fieldY = pt1.getClass().getDeclaredField("y");
-
-
-
-
- System.out.println(fieldX.get(pt1));
- fieldY.setAccessible(true);
- System.out.println(fieldY.get(pt1));
作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容的”b”改成“a”。
示例代码:
-
ReflectPoint.Java
-
public String str1 = "ball";
-
public String str2 = "basketball";
-
public String str3 = "itheima";
-
@Override
-
public String toString() {
-
return "ReflectPoint [str1=" + str1
-
+ ", str2=" + str2 + ", str3=" + str3 + "]";
-
}
-
-
ReflectTest.java
-
private static void ChageStringValue(Object obj) throws Exception {
-
Field[] fields = obj.getClass().getFields();
-
for (Field field : fields) {
-
//因为使用的是同一份字节码,所以使用双等号比较合适
-
if(field.getType() == String.class){ //这里最好用“==”来比较,如果使用equals比较时,语义不准确
-
String oldValue = (String)field.get(obj);
-
String newValue =oldValue.replace('b', 'a');
-
field.set(obj, newValue);
-
}
-
}
-
}
Method类代表某个类中的一个成员变量。
得到类中的某一个方法:
例子:
MethodcharAt =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对象对应的是一个静态的方法。
用反射方式执行某个类中的main()方法。
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main()方法。分析为什么要用反射去调用呢?
问题:启动Java程序的main()方法的参数是一个字符串数组,即
Publicstatic void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke()方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数当把一个字符串数组作为参数传递给invoke()方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,按jdk1.4的语法进行处理时,即把数组打散称为单独的参数。所以,在给main方法传递参数时,不能使用代码
mainMethod.invoke(null,newString[]{“xxx”}),javac只把它当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{newString[]{“xxx”}});
mainMethod.invoke(null,(Object)newString[]{“xxxx”});
注意:编译器会做特殊处理,编译时不会把参数当做数组看待,也就是数组打散成若干个参数。
示例代码:
-
- String mainMethod = args[0];
- Method mainMehtod =
- Class.forName(mainMethod).getMethod("main", String[].class);
- mainMehtod.invoke(null,(Object)new String[]{"111","222","333"});
-
数组反射:
1.具有相同的维数和元素类型的数组属于用一个类型,即具有相同的Class实例对象。
2.代表数组的Class实例对象的getSuperClass()方法返回父类为Object类对应的Class.
3.基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,即可以当做Object类型使用,又可以当做Object[]类型使用。
4.Arrays.asList()方法处理int[]和String[]时的差别。
5.Arrays工具类用于完成对数组的反射操作。
思考题:怎样得到数组中的元素类型?
没有办法数组中元素的类型,因为Object obj = new Object[]{可以放String,
可以放Int},例如:Object obj = new Object[]{“a”,1};
只能利用obj[0].getClass().getName()得到一个元素的类型
ArrayList是一个有顺序的集合,就相当于一个有序的数组。
HashSet是一个无序的集合,没有重复的数据。
HashCode的作用?(可以防止内存泄露)
Java中有内存泄露吗?请举例说明?-HashCode


Java按照万物皆对象,将复杂的事物都封装成了对象。
比如:字节码文件封装成了Class对象。并将类中的成员变量,封装了Field对象。类中的方法也封装成了Method对象。类中的构造函数也封装成了Constructor对象。Java将字节码文件,以及字节码文件中的成员都封装了对象。
Class类中的这些方法可以获取到或者访问到指定一个字节码文件中的任意的成员。其实是对类的解刨。反射技术其实代码体现就是依赖于Class和java.lang.reflect包中的对象来完成的。
例子1.用3中方式获取一个类的字节码文件的对象。
- package cn.itheima.day14;
- public class ReflectDemo {
- public static void main(String[] args) throws ClassNotFoundException {
- getClassObject();
- }
-
-
-
-
-
-
- public static void getClassObject() throws ClassNotFoundException{
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Class clazz3 = Class.forName("cn.itheima.day14.Person");
- System.out.println(clazz3);
- }
- }
例子2:获取类的方法的实例演示。
- package cn.itheima.day14;
- import java.lang.reflect.Method;
- public class ReflectDemo2 {
- public static void main(String[] args) throws Exception{
-
-
- getPrivateMethodDemo();
- }
-
- private static void getPrivateMethodDemo() throws Exception {
-
- Class clazz = Class.forName("cn.itheima.day14.Person");
-
-
- Method method2 = clazz.getDeclaredMethod("function", null);
- System.out.println(method2);
-
- method2.setAccessible(true);
- Object obj2 = clazz.newInstance();
- method2.invoke(obj2, null);
-
- }
-
- private static void getSingleMethodDemo() throws Exception{
-
- Class clazz = Class.forName("cn.itheima.day14.Person");
-
- Method method1 = clazz.getMethod("show", null);
- System.out.println(method1);
- Object obj = clazz.newInstance();
- method1.invoke(obj, null);
- }
-
- public static void getMethodDemo() throws ClassNotFoundException {
-
-
- Class clazz = Class.forName("cn.itheima.day14.Person");
-
- Method[] methods = clazz.getMethods();
-
- Method[] methods2 = clazz.getDeclaredMethods();
- for (Method method : methods2) {
- System.out.println(method.toString());
- }
- }
- }
例子3:获取Person类的构造函数的实例演示。
- package cn.itheima.day14;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Method;
- public class ReflectDemo3 {
- public static void main(String[] args) throws Exception{
- getConstructorDemo();
- }
-
- private static void getConstructorDemo() throws Exception {
- Class clazz = Class.forName("cn.itheima.day14.Person");
-
-
-
- Constructor constructor = clazz.getConstructor(int.class,String.class);
- Object obj = constructor.newInstance(30,"zhangsan");
-
-
-
-
- Method method = clazz.getMethod("method", String.class);
- method.invoke(obj, "...heima.hello");
- }
- }
例子4:通过流对象读取配置文件中的的信息数据,然后通过反射技术,动态的加载该配置文件中的类和方法。
- package cn.itheima.day14;
- import java.io.File;
- import java.io.FileInputStream;
- import java.lang.reflect.Method;
- import java.util.Properties;
- public class ReflectDemo4 {
- public static void main(String[] args) throws Exception{
- File file = new File("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day14\\config.properties");
-
- Properties prop = new Properties();
- FileInputStream fis = new FileInputStream(file);
- prop.load(fis);
- String className = prop.getProperty("className");
- String methodName = prop.getProperty("methodName");
- if(className == null || methodName == null){
- throw new RuntimeException("配置文件书写错误!");
- }
- Class clazz = Class.forName(className);
- Object obj = clazz.newInstance();
- Method method = clazz.getMethod(methodName, null);
- method.invoke(obj, null);
- }
- }
例子5.反射技术的具体应用的例子
- 定义PCI接口:
- package cn.itheima.day14;
- public interface PCI {
- public void open();
- public void close();
- }
- 定义网卡:
- package cn.itheima.day14;
- public class NetCard implements PCI {
- @Override
- public void open() {
- System.out.println("netcard run");
- }
- @Override
- public void close() {
- System.out.println("netcard close");
- }
- }
- 定义声卡:
- package cn.itheima.day14;
- public class SoundCard implements PCI {
- @Override
- public void open() {
- System.out.println("SoundCard run");
- }
- @Override
- public void close() {
- System.out.println("SoundCard close");
- }
- }
-
- 利用反射定义具体主函数:
- package cn.itheima.day14;
- import java.io.File;
- import java.io.FileInputStream;
- import java.util.Properties;
-
-
-
-
- public class MainBordTest {
- public static void main(String[] args) throws Exception {
- File file = new File("F:\\Javajichu\\JavaLianXi\\src\\cn\\itheima\\day14\\card.properties");
-
- Properties properties = new Properties();
- FileInputStream fis = new FileInputStream(file);
- properties.load(fis);
-
- String countValue = properties.getProperty("cardcount");
- if(countValue==null || countValue.equals("")){
-
- System.out.println("没有板卡设备!");
- return;
- }
- int count = 0;
- try {
- count = Integer.parseInt(countValue);
- if(count!=(properties.size()-1)){
- System.out.println("板卡个数不匹配");
- return;
- }
- } catch (NumberFormatException e) {
- System.out.println("cardcount值错误!");
- return;
- }
-
- for (int i = 1; i < properties.size(); i++) {
- String className = properties.getProperty("card"+i);
- Class clazz = Class.forName(className);
- PCI pci = (PCI) clazz.newInstance();
- pci.open();
- pci.close();
- }
- }
- }