Java进阶学习之反射与动态绑定(5)
目录
1.反射
1.1.概述
Java的反射机制是指在程序的运行状态时,可以构建任何一个类的对象,对任何一个对象可以了解其所属的成员变量和方法,这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。
1.2.实现方式
在最初的 helloworld案例 中,我们知道源代码会编译成字节码文件.class,然后交给JVM运行,这里的字节码文件其实就是类的模板,所有的对象都需要通过这个模板进行创建。所以在使用反射之前需要获取字节码文件。
1.2.1.获取Class
方式一:通过对象获取
方式二:通过 类.class 获取
方式三:通过Class类的静态方法forName(String className)获取
例子:
实体类:
public class Person {
private String name;
public Integer age;
public Person() {}
public Person(String name) {
this.name = name;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String say(String name) {
return "hello, " + name;
}
private String secretlySay(Integer time) {
return "I love you " + time + " thousand times!";
}
}
测试类:
public class TestReflection {
public static void main(String[] args) {
//方式一
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
System.out.println(clazz1);
//方式一
Class clazz2 = Person.class;
System.out.println(clazz2);
//方式三
try {
Class clazz3 = Class.forName("com.liquor.reflection.Person");
System.out.println(clazz3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
执行结果:
我们知道这里的Class对象其实就是类的模板,因此有且只有一个。
1.2.2.通过反射获取属性、设置属性
以下都是用第一种方式获取Class的。
public class TestReflection {
public static void main(String[] args) {
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
System.out.println(clazz1);
System.out.println("---------------------------");
//获取public属性
Field[] fields = clazz1.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
System.out.println("---------------------------");
//获取包含private的所有属性
Field[] declaredFields = clazz1.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field.getName());
}
try {
//获取指定name的public属性
Field age = clazz1.getField("age");
System.out.println(age);
//获取指定name的包含private的属性
Field name = clazz1.getDeclaredField("name");
System.out.println(name);
//获取对象
Object o = clazz1.newInstance();
//设置属性不校验
name.setAccessible(true);
//设置属性值,私有属性需要关闭校验
name.set(o, "lisi");
age.set(o, 33);
System.out.println(o);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
执行结果:
1.2.3.通过反射获取方法、执行方法
public class TestReflection {
public static void main(String[] args) {
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
//获取类及其父类的public方法
Method[] methods = clazz1.getMethods();
for (Method method : methods) {
System.out.print(method.getName() + " ");
}
System.out.println();
//获取类包含private的所有方法
Method[] methods2 = clazz1.getDeclaredMethods();
for (Method method : methods2) {
System.out.print(method.getName() + " ");
}
System.out.println();
try {
//获取指定name的public方法
Method m1 = clazz1.getMethod("say", String.class);
System.out.println(m1);
//获取指定name的包含private的方法
Method m2 = clazz1.getDeclaredMethod("say", String.class);
System.out.println(m2);
Method m3 = clazz1.getDeclaredMethod("secretlySay", Integer.class);
System.out.println(m3);
//获取对象
Object o = clazz1.newInstance();
//设置方法不校验
m3.setAccessible(true);
//调用方法
Object rel1 = m1.invoke(o, "lisi");
System.out.println(rel1);
Object rel2 = m3.invoke(o, 3);
System.out.println(rel2);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
执行结果:
1.2.4.通过反射获取构造器并调用
public class TestReflection {
public static void main(String[] args) {
Person person = new Person("zhangsan", 22);
Class clazz1 = person.getClass();
//获取类的public构造器
Constructor[] constructors = clazz1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("------------------------");
try {
//获取指定name的public构造器
Constructor c1 = clazz1.getConstructor(String.class);
System.out.println(c1);
Constructor c2 = clazz1.getConstructor(String.class, Integer.class);
System.out.println(c2);
//通过构造器获取对象
Object o1 = c1.newInstance("zhangsan");
System.out.println(o1);
Object o2 = c2.newInstance("lisi", 33);
System.out.println(o2);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
执行结果:
2.动态代理
2.1.代理模式
使用一个类代表另一个类的功能,是一种设计模式。
2.2.静态代理
编译时代理类已经生成。
例子:
接口HelloService.java
public interface HelloService {
String say(String name);
}
实际实现HelloServiceImpl
public class HelloServiceImpl implements HelloService {
@Override
public String say(String name) {
return "Hello, " + name;
}
}
代理类HelloStaticProxy.java
public class HelloStaticProxy implements HelloService {
private HelloService helloService = new HelloServiceImpl();
@Override
public String say(String name) {
System.out.println("--------------------------");
return helloService.say(name);
}
}
测试类:
public class TestProxy {
public static void main(String[] args) {
HelloStaticProxy helloStaticProxy = new HelloStaticProxy();
System.out.println(helloStaticProxy.say("zhangsan"));
}
}
执行结果:
静态代理实现步骤:
- 创建接口
- 实现接口
- 创建代理类实现接口并且将原实现类引入,调用实现的方法
缺点:
- 由于实现同一个接口,接口变动则修改成本增加
- 如果接口实现方法很多的话再实现工作量很大
2.3.动态代理
相比静态代理,动态代理就是运行时动态创建代理的方式。
接口和被代理类与前面一样。
代理类:
public class HelloProxy implements InvocationHandler {
private Object obj;
public HelloProxy(Object obj) {
this.obj = obj;
}
public Object getNewInstance() {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(obj, args);
return invoke;
}
}
测试类:
public class TestProxy {
public static void main(String[] args) {
HelloProxy helloProxy = new HelloProxy(new HelloServiceImpl());
HelloService helloService = (HelloService) helloProxy.getNewInstance();
System.out.println(helloService.say("zhangsan"));
}
}
执行结果:
这是使用JDK源码实现的动态代理,使用接口等实现的。而如何被代理类没有实现接口呢?
被代理类HelloServiceClass.java
public class HelloServiceClass {
public String say(String name) {
return "Hello, " + name;
}
}
代理类:
public class HelloProxy implements InvocationHandler {
private Object obj;
public HelloProxy(Object obj) {
this.obj = obj;
}
public Object getNewInstance() {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(obj, args);
return invoke;
}
}
测试类:
public class TestProxy {
public static void main(String[] args) {
HelloServiceClass helloServiceClass = (HelloServiceClass) new CglibInterceptor().getNewInstance(HelloServiceClass.class);
System.out.println(helloServiceClass.say("zhangsan"));
}
}
执行结果:
静态代理相对于动态代理从代码的实现上更加容易理解和编写,代理类中调用的是真正的实现类。动态代理中,当我们需要使用到时才会创建类的模板然后创建对象调用方法。很显然反射在动态代理中举足轻重。