反射

反射首先要得到类的字节码

得到字节码有三种方式

  1. 对象的getClass()方法,这是Object的方法;
  2. 类的classs属性
  3. Class的forName()方法
class Father{
    public Father(){
    }
    public Father(int a){
    }

    public void test(){
    }

    public void test(int a){
    }
}

Class a = f.getClass();

Class b = Father.class;

try {
    Class c = Class.forName("test.java.Father");
    System.out.println(c.getName());
} catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

这里需要注意,第三种方法需要抛出异常或者catch,并且forName中的参数是字符串。

这样得到的三个Class对象都是一样的,因为每一个class只会有一个Class对象。所以如果用==来判断这三个引用的关系的话,是相等的。Class对象在类加载的时候就会创建在堆中。

获取了Class对象之后,可以通过该对象得到类的属性和方法

import java.lang.reflect.*;


Class a = Father.getClass();

Constructors[] cons = a.getConstructors();  //得到public的构造方法

Constructors[] cons = a.getDeclaredConstructors();  //得到public, protected, default(package) access, and private 的构造方法

Methods[] meths = a.getMethods();           //public 方法,包括父类或者父接口的方法

Methods[] meths = a.getDeclaredMethods();           //public, protected, default (package)access, and private 方法, 不包含继承的方法,也不包含构造方法

Field[] fils = a.getFields();                      //public 的字段,包括父类或者父接口的字段

fils = a.getDeclaredFileds();          //public, protected, default(package) access, and private 字段, 不包含继承的字段 

注意Constructor、Method、Field是reflect中的类,需要导包,字段包含static修饰的字段还有final修饰的字段

得到了构造方法之后,可以通过该构造方法新建对象,同样的,需要throws 或catch异常

Class a = Father.class();

Constructor[] conArray = a.getConstructors();

		
for(Constructor i : conArray) {
    Father fa = (Father) i.newInstance();           //创建的对象是Object,所以需要转型,有些构造方法可能需要参数,newInstance中也可以放入参数。
}

Constructor con = a.getConstructor(int.class);   //也可以直接得到某个具体的构造方法,参数就是构造方法的参数的型材类型,也就是后面添加class

得到特定的字段并且调用

Father father = new Father();

Class a = father.getClass();

Field fie = a.getField("a");

Constructor con = a.getConstructor();

Object o = con.newInstance();

fie.set(o, 5);           //或者fie.set(f, 5);

得到特定的方法并且调用

package java

Class a = Class.forName("java.Father");

Constructor con = a.getConstructor();

Method med1 = a.getMethod("test");

Method med2 = a.getMethod("test", int.class);   //找到的方法可能重载过,所以有时候要传入形参的类型

Object o = con.newInstance();

med.invoke(o, null);              //两个参数,前面是对象,后面是参数,如果是null,可以不写

反射也可以调用main方法

package java

Class a = Class.forName("java.Father");

Constructor con = a.getConstructor();

Method med = a.getMethod("main", String[].class);

Object o = con.newInstance();

med.invoke(o, new String[]{"a"});              

反射的作用
数据库的驱动加载的时候,一般使用的是反射来动态加载,好处在于耦合度低,在需要更换驱动的时候,只需要更改配置文件,如下;

package learnforwhat;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class LearnReflect {
	
	public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
		
		
		String root = System.getProperty("user.dir");
		Properties pro = new Properties();
		FileReader fil = new FileReader("./src/learnforwhat/properties.txt");
		pro.load(fil);
		fil.close();
		
		String className = (String) pro.get("className");
		String methodName = (String) pro.get("methodName");
		
		Class clazz = Class.forName(className);
		
		Method med = clazz.getMethod(methodName, null);
		
		med.invoke(clazz.getConstructor().newInstance());
		 
	}
}
package jartestforwhat;

public class myDriver {
	static {
		System.out.println("it is my driver from jar");
	}
	
	public void showTheCode() {
		System.out.println("it is method");
	}
	
	public static void main(String[] args) {
		
	}
}

jartestforwhat已经打成jar包,导入到项目中

properties.txt中的内容

className = jartestforwhat.myDriver
methodName = showTheCode

这里说一句FileReader,参数中可以是相对路径,但是得知道现在的根目录是什么,一开始一直报错,后来通过System.getProperty("user.dir")知道了根目录

如果我们需要更改反射加载的class,只需要更改配置文件就好了。

反射还可以越过泛型检查

class MyList<E>{
	Object[] o = new Object[1];
	
	public void add(E e) {
		o[0] = e;
	}
}

存在这么一个类

那么如何添加一个String类型的参数呢,如果直接new一个对象,并且调用add方法,编译器会报错

可以通过反射来越过,因为反射是在运行的时候动态加载的,而泛型检查是在编译阶段,之后 就会被擦写掉;

Class clazz2 = Class.forName("learnforwhat.MyList");
		
MyList<Integer> my = new MyList<Integer>();

my.add("String");             //报错

Method med2 = clazz2.getMethod("add", Object.class);

med2.invoke(my, "String");  //没问题

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值