一、序言
二、引出Class对象
public class Demo { // 自建了一个Student类 class Student{ } public static void main(String[] args) { // 将Object 强转成Student类 Object o = new Object(); Student s = (Student) o; }}
- 一个.java的文件经过javac命令编译成功后,得到一个.class的文件
- 当我们执行了初始化操作(有可能是new、有可能是子类初始化 父类也一同被初始化、也有可能是反射...等),会将.class文件通过类加载器装载到jvm中
-
将.class文件加载器加载到jvm中,又分了 好几个步骤,其中包括 加载、连接和初始化
-
其中在加载的时候,会在Java堆中 创建一个java.lang.Class类的对象,这个Class对象代表着 类相关的信息。
三、反射介绍
- 知道获取Class对象的几种途径
- 通过Class对象创建出对象,获取出构造器,成员变量,方法
- 通过反射的API修改成员变量的值,调用方法
/* 下面是我初学反射时做的笔记,应该可以帮到大家,代码我就不贴了。(Java3y你值得关注)*/想要使用反射,我先要得到class文件对象,其实也就是得到Class类的对象Class类主要API: 成员变量 - Field 成员方法 - Constructor 构造方法 - Method获取class文件对象的方式: 1:Object类的getClass()方法 2:数据类型的静态属性class 3:Class类中的静态方法:public static Class ForName(String className)-------------------------------- 获取成员变量并使用 1: 获取Class对象 2:通过Class对象获取Constructor对象 3:Object obj = Constructor.newInstance()创建对象 4:Field field = Class.getField("指定变量名")获取单个成员变量对象 5:field.set(obj,"") 为obj对象的field字段赋值如果需要访问私有或者默认修饰的成员变量 1:Class.getDeclaredField()获取该成员变量对象 2:setAccessible() 暴力访问 --------------------------------- 通过反射调用成员方法 1:获取Class对象 2:通过Class对象获取Constructor对象 3:Constructor.newInstance()创建对象 4:通过Class对象获取Method对象 ------getMethod("方法名"); 5: Method对象调用invoke方法实现功能如果调用的是私有方法那么需要暴力访问 1: getDeclaredMethod() 2: setAccessiable();
四、为什么需要反射
- 提高程序的灵活性
- 屏蔽掉实现的细节,让使用者更加方便好用
4.1 案例一(JDBC)
Class.forName("com.mysql.jdbc.Driver");//获取与数据库连接的对象-Connetcionconnection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java3y", "root", "root");//获取执行sql语句的statement对象statement = connection.createStatement();//执行sql语句,拿到结果集resultSet = statement.executeQuery("SELECT * FROM users");
//获取配置文件的读入流InputStream inputStream = UtilsDemo.class.getClassLoader().getResourceAsStream("db.properties");Properties properties = new Properties();properties.load(inputStream);//获取配置文件的信息driver = properties.getProperty("driver");url = properties.getProperty("url");username = properties.getProperty("username");password = properties.getProperty("password");//加载驱动类Class.forName(driver);
- 三歪写了一个JDBC组件,把各种配置都写死在代码上,比如上面的driver/username/数据库连接数等等。现在三歪不干了,要跑路了。
- 敖丙来接手三歪的代码,敖丙刚开始接手项目,公司说要换数据库。敖丙给领导说:这玩意,我改改配置就好了,几分钟完事。
- 敖丙找了半天都没找到配置的地方,由于三歪写的代码又臭又烂,找了半天才找到入口和对应的位置。
这里只是说可能,但不全是。有的可配的参数可能就仅仅只是配置,跟反射无关。但上面jdbc的例子,就是通过反射来加载驱动的。
4.2 案例二(SpringMVC)
//通过html的name属性,获取到值String username = request.getParameter("username");String password = request.getParameter("password");String gender = request.getParameter("gender");//复选框和下拉框有多个值,获取到多个值String[] hobbies = request.getParameterValues("hobbies");String[] address = request.getParameterValues("address");//获取到文本域的值String description = request.getParameter("textarea");//得到隐藏域的值String hiddenValue = request.getParameter("aaa");
@RequestMapping(value = "/save")@ResponseBodypublic String taskSave(PushConfig pushConfig) { // 直接使用 String name= pushConfig.getName();}
- 如果你的JavaBean的属性名跟传递过来的参数名不一致,那就“自动组装”失败了。因为反射只能根据参数名去找字段名,如果不一致,那肯定set不进去了。所以就组装失败了呀~
五、我们写反射的代码多吗?
@permission("添加分类")/*添加分类*/ void addCategory(Category category);/*查找分类*/void findCategory(String id);@permission("查找分类")/*查看分类*/ List<Category> getAllCategory();
public class ServiceDaoFactory { private static final ServiceDaoFactory factory = new ServiceDaoFactory(); private ServiceDaoFactory() { } public static ServiceDaoFactory getInstance() { return factory; } //需要判断该用户是否有权限 public <T> T createDao(String className, Class<T> clazz, final User user) { System.out.println("添加分类进来了!"); try { //得到该类的类型 final T t = (T) Class.forName(className).newInstance(); //返回一个动态代理对象出去 return (T) Proxy.newProxyInstance(ServiceDaoFactory.class.getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PrivilegeException { //判断用户调用的是什么方法 String methodName = method.getName(); System.out.println(methodName); //得到用户调用的真实方法,注意参数!!! Method method1 = t.getClass().getMethod(methodName,method.getParameterTypes()); //查看方法上有没有注解 permission permis = method1.getAnnotation(permission.class); //如果注解为空,那么表示该方法并不需要权限,直接调用方法即可 if (permis == null) { return method.invoke(t, args); } //如果注解不为空,得到注解上的权限 String privilege = permis.value(); //设置权限【后面通过它来判断用户的权限有没有自己】 Privilege p = new Privilege(); p.setName(privilege); //到这里的时候,已经是需要权限了,那么判断用户是否登陆了 if (user == null) { //这里抛出的异常是代理对象抛出的,sun公司会自动转换成运行期异常抛出,于是在Servlet上我们根据getCause()来判断是不是该异常,从而做出相对应的提示。 throw new PrivilegeException("对不起请先登陆"); } //执行到这里用户已经登陆了,判断用户有没有权限 Method m = t.getClass().getMethod("findUserPrivilege", String.class); List<Privilege> list = (List<Privilege>) m.invoke(t, user.getId()); //看下权限集合中有没有包含方法需要的权限。使用contains方法,在Privilege对象中需要重写hashCode和equals() if (!list.contains(p)) { //这里抛出的异常是代理对象抛出的,sun公司会自动转换成运行期异常抛出,于是在Servlet上我们根据getCause()来判断是不是该异常,从而做出相对应的提示。 throw new PrivilegeException("您没有权限,请联系管理员!"); } //执行到这里的时候,已经有权限了,所以可以放行了 return method.invoke(t, args); } }); } catch (Exception e) { new RuntimeException(e); } return null; }}