Java的反射,动态代理,模版设计模式,
来源:互联网 发布:做菜单用什么软件 编辑:程序博客网 时间:2024/06/06 05:38
1、反射的概念:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
2、类加载:
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
3、有3种方法获得class对象:
a.Object类的getClass()方法,判断两个对象是否是同一个字节码文件b.静态属性class,锁对象c.Class类中静态方法forName(),读取配置文件因为class是java的关键字,所以我们用clazz来代替。
Class clazz1 = Class.forName("com.example.bean.Person");//第三种方法 Class clazz2 = Person.class;//第二种方法 Person p = new Person();//第一种方法,通过对象来获取Class对象 Class clazz3 = p.getClass();
4、通过Class.forName读取配置文件:
为什么要使用配置文件呢?因为我们可以只改配置文件就可以改变你的需求。例如:
public class Demo { /** * * 榨汁机(Juicer)榨汁的案例 * 分别有水果(Fruit)苹果(Apple)香蕉(Banana)桔子(Orange)榨汁(squeeze) * @throws IOException */ public static void main(String[] args) throws Exception { Juicer j = new Juicer();//创建榨汁机 j.run(new Apple()); j.run(new Orange()); } } interface Fruit {//定义一个接口,下面所有的水果都实现这个接口,一种多态的思想。 public void squeeze(); } class Apple implements Fruit { public void squeeze() { System.out.println("榨出一杯苹果汁儿"); } } class Orange implements Fruit { public void squeeze() { System.out.println("榨出一杯橘子汁儿"); } } class Juicer { //参数定义为接口,我们传实现了这个接口的对象就可以实现调用不同对象的方法。 public void run(Fruit f) { f.squeeze(); } }
上面的代码有个问题就是,我们如果不想苹果和橘子,就需要改源码 j.run(new Melon());我们的解决方法是,我们可以通过配置文件将你需要传的类写在配置文件中。例如: a.config.properties(工程目录下)文件内容: com.example.reflect.Apple b.我们将main函数改造下
public static void main(String[] args) throws Exception { Juicer j = new Juicer();//创建榨汁机 BufferedReader br = new BufferedReader(new FileReader("config.properties")); Class clazz = Class.forName(br.readLine()); //获取该类的字节码文件 Fruit f = (Fruit) clazz.newInstance(); //创建实例对象 j.run(f);//通过读取到配置文件,new的一个对象 }
这样我们就可以通过配置文件到达动态改变的目的的,不要修改代码。
5、反射-获取有参构造方法:
Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数,就不能这样创建了。可以调用Class类的getConstructor()方法,里面可以给参数。例如:
Person.java: public class Person { private String name; private int age; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } //...生成get set方法 @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
使用:
public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.example.bean.Person"); Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造 Person p = (Person) c.newInstance("张三",23);//通过有参构造创建对象 System.out.println(p); } 注意参数也是使用class字节码,clazz.getConstructor(String.class,int.class);
6、反射-获取成员变量:
Class.getField(String)方法可以获取类中的指定字段(可见的,如果是private就拿不到)。但是可以通过getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值。修改私有属性的值之前,需要先调用setAccessible(true)设置访问权限。用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值。例如:
public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.example.bean.Person"); Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造 Person p = (Person) c.newInstance("张三",23); Field f = clazz.getDeclaredField("name");//暴力反射获取字段 f.setAccessible(true);//去除私有权限 f.set(p, "李四"); System.out.println(p); }
这个可以修改一个对象的私有属性,可以看到反射机制强大。
7、反射-获取方法:
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String,Class...)方法可以获取类中的指定方法,加了Declasred的都是可以获取到私有的。调用invoke(Object,Object...)可以调用该方法。第一个Object是你传的对象,第二个Object是你方法的参数。例如:
我们在Person类里面加一个方法: public void eat() { System.out.println("吃饭"); } public void eat(int num) { System.out.println("吃了" + num + "顿饭"); } public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.example.bean.Person"); Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造 Person p = (Person) c.newInstance("张三",23); Method m = clazz.getMethod("eat");//获取eat方法 m.invoke(p);//调用方法 Method m2 = clazz.getMethod("eat", int.class); //获取有参的eat方法 m2.invoke(p, 10);//调用方法,有参的 }
注意:关键是invoke方法使用,在很多的地方都可看到。
8、反射-通过反射越过泛型检查:
泛型只在编译期有效,在运行期会被擦除掉例如:
public class Test { public static void main(String[] args) throws Exception { ArrayList<Integer> list = new ArrayList<>(); list.add(123); list.add(45); System.out.println(list); Class clazz = list.getClass(); Method method = clazz.getMethod("add", Object.class); method.invoke(list, "adb"); System.out.println(list); } }
神奇。
9、反射-动态代理:
先演示下代码:
有个User.java接口 public interface User { public void add(); public void delete(); } 有个User接口的实现类 public class UserImp implements User { @Override public void add() { System.out.println("权限校验"); System.out.println("添加"); System.out.println("日志"); } @Override public void delete() { //System.out.println("权限校验"); System.out.println("删除"); //System.out.println("日志"); } }
现在有个问题,就是我们添加和删除都需要权限校验和日志记录,这样每个方法都去写太麻烦所以我们可以使用反射功能生成代理类,使用代理去做。在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象.public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)最终会调用InvocationHandler的方法 InvocationHandler Object invoke(Object proxy,Method method,Object[] args)例如我们演示下:a.创建一个类,实现InvocationHandler。
MyInvocationHandler.java public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("权限校验"); method.invoke(target, args);//执行被代理target对象的方法 System.out.println("日志记录"); return null; } }
b.使用:
public static void main(String[] args) { UserImp ui = new UserImp(); ui.add(); ui.delete(); MyInvocationHandler m = new MyInvocationHandler(ui);//这个ui就是你要代理的对象 User u = (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m); u.add(); u.delete(); }
使用代理后,在调用方法的时候默认就添加了校验和记录功能。总结: 为什么叫动态代理?因为我们可以脱离具体的对象,我们传任意对象都可以进行这样的操作。 我们为什么要传一个接口过去呢?因为我们代理类需要实现和UserImp同样的接口,这样我们才能强转为User。 其实我们自己也可以手动写个实现User接口的代理类,实现代理功能,叫做静态代理。只是只能代理User的实现类, 比较局限,所以实际的开发中我们使用动态代理来扩展类的功能。
10、设计模式-模版(Template)设计模式:
例如我们想统计某段代码的运行时间,下面这段代码:
public static void main(String[] args) { long start = System.currentTimeMillis(); for(int i = 0; i < 1000000; i++) { System.out.println("test"); } long end = System.currentTimeMillis(); System.out.println(end - start); }
有个问题,这个代码写死了,我们需要在一个类实现,我们调用类里面的方法就可以,改造:
class GetTime { public long getTime() { long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); return end - start; } public void code(){//这个方法还是写死了 for(int i = 0; i < 1000000; i++) { System.out.println("test"); } } }
所以我们再改造:
abstract class GetTime {//抽象方法必须放在抽象类中 public final long getTime() {//定义为final是不让重写 long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); return end - start; } public abstract void code();//定义为抽象的,让子类实现 } 编写一个类,继承GetTime class Demo extends GetTime { @Override public void code() { int i = 0; while(i < 100000) { System.out.println("test"); i++; } } } 使用: public static void main(String[] args) { Demo d = new Demo(); System.out.println(d.getTime()); }
总结: 模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现 优点:使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求 缺点:如果算法骨架有修改的话,则需要修改抽象类
阅读全文
0 0
- Java的反射,动态代理,模版设计模式,
- 27 API-反射(类的加载器,反射的使用,动态代理)&设计模式(装饰设计模式,模版设计模式)&JDK新特性(JDK5,JDK6,JDK7,DK8)
- 反射-动态代理设计模式
- 【反射】JAVA代理模式与动态代理
- 15. JAVA 反射机制 Part 2(动态代理、类的生命周期、工厂设计模式) ----- 学习笔记
- -java 动态代理设计模式
- java动态代理设计模式
- java 设计模式 动态代理
- java设计模式--动态代理
- java模式设计- 动态代理
- java反射实现动态代理模式
- java设计模式-代理模式(静态代理,动态代理)
- Java设计模式-----Proxy模式(动态代理)
- Java设计模式-----Proxy模式(动态代理)
- JAVA设计模式--动态代理模式
- java设计模式—动态代理模式
- java设计模式之动态代理模式!
- java设计模式:动态代理模式 Proxy
- linux基本权限 --修改文件所有者和所有组命令
- Ubuntu系统Tab键不能自动补全问题解决
- HEVC函数入门(16)——Slice编码
- Boostrap--组件
- MVC
- Java的反射,动态代理,模版设计模式,
- socket(套接字)在服务器端和客户端之间的基本工作原理流程图
- caffe:create_txt.sh(数据预处理成txt文本文件)
- apply、call、bind
- vs2013+opencv1.0,cvvidsurv.hpp报错C4996
- 大明A+B
- [bzoj4720][Noip2016]换教室 签到概率DP
- C语言——实例025 阶乘和
- 2016年不可错过的21个深度学习视频、教程和课程