一、获取Class对象的四种方法
//方式一:调用运行时类的属性: .class
Class class1 = Person.class;
//方式二:通过运行时类的对象,调用getClass()
Person p = new Person("hah", 12);
Class class2 = p.getClass();
//方式三:调用Class的静态方法,forName(String classPath)
Class class3 = Class.forName("Reflection.Person");
//方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class class4 = classLoader.loadClass("Reflection.Person");
二、NewInstance()的用法和注意事项
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器,要想此方法正常的创建运行时类的对象,要求:
- 运行时类必须提供空参的构造器
- 空参的构造器的访问权限得够。通常,设置为public.
在javabean中要求提供一个public的空参构造器。 原因:
- 便于通过反射,创建运行时类的对象
- 便于子类继承此运行类时,默认调用super()时,保证父类有此构造器
public class NewInstanceTest {
public static void main(String[] args) {
Class<Person> class1 = Person.class;
Person p = null;
try {
p = class1.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(p);
}
}
三、获取属性、方法、构造器
Person.class
package Reflection;
/**
* @author zhaoyongchen
* @create 2020-08-11-15:43
*/
public class Person {
private String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.age = age;
this.name = name;
}
public void show(){
System.out.println("喔哈哈哈哈哈");
}
public String show(String s){
return s;
}
private static void showDesc(){
System.out.println("我是一个男孩子");
}
private void showNation(String nation){
System.out.println("我的国籍是"+nation);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Reflection.Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在调用方法时,尤其是静态方法时,传入的对象值得关注
@Test
public void testField() throws Exception {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
//获取运行时类中指定名的属性
Field name = clazz.getDeclaredField("name");
//设置可访问性
name.setAccessible(true);
//获取只当对象的属性值
name.set(p, "Tom");
System.out.println(name.get(p));
}
@Test
public void testMethod() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
/*
1.获取指定的某个方法
getDeclaredMethod():参数1 :指明获取的方法的名称参数2: 指明获取的方法的形参列
*/
Method show = clazz. getDeclaredMethod("show", String.class);
//2.保证当前方法是可访问的
show. setAccessible(true);
/*
2.调用方法的invoke():参数1: 方法的调用者参数2: 给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值。
*/
Object returnValue = show.invoke(p, "CHN");//String nation = p. show( "CHN");
System.out.println(returnValue);
System.out.println("----------调用静态方法------------");
//private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//invoke()肯定有返回值,被调用方法没有返回值则返回null
//Object returnVal = showDesc.invoke(Person.class);
//调用invoke的showDesc通过clazz拿到,clazz通过Person.class得到,里面静态内容都知道
Object returnVal = showDesc.invoke(null);
System.out.println(returnVal);
}
@Test
public void testConstructor() throws Exception {
//还是newInstance实在
Class clazz = Person.class;
Constructor constructor = clazz.getConstructor(String.class,int.class);
constructor.setAccessible(true);
Person person = (Person) constructor.newInstance("tom", 12);
System.out.println(person);
}
四、反射前后对比
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflection {
//反射之前,对Person的操作
@Test
public void test1() {
//1.创建Person类的对象
Person p1 = new Person("Tom", 12);
//2.通过对象,调用其内部的属性、方法
p1.age = 10;
System.out.println(p1.toString());
p1.show();
//在Person类外部,不可以通过Person类的对象调用其内部私有结构。
//比如: name、showNation() 以及私有的构造器
}
//反射之后,对于Person的操作
@Test
public void test2() throws Exception{
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz . getConstructor(String. class, int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person)obj;
System.out.println(p.toString());
//2.通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz. getDeclaredField( "age" );
age.set(p,10);
System. out . println(p.toString());
//调用方法
Method show = clazz . getDeclaredMethod( "show");
show.invoke(p);
//通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
Constructor cons1 = clazz. getDeclaredConstructor(String.class,int.class);
cons1. setAccessible(true);
Person p1 = (Person) cons1. newInstance( "Jerry",13);
System. out . println(p1);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1, "ppgod");
System.out.println(p1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
showNation.invoke(p1,"China");
}
}
五、封装性和反射的思考
反射可以调用私有结构,那么封装性和反射矛盾吗?
封装性告诉我们私有的别的地方不能用,而反射告诉我们可以用私有的,这样看不就白封装了吗?其实两者之间不矛盾。
封装性告诉我们的是:当我们看到一个类写了一些私有的方法,一些公共的方法时,就告诉我们私有的方法就不要用了,就用公共的方法就可以了,因为私有的方法可能在外部用不着,是用来辅助类内部公共方法的,公共的方法做得更好。比如单例模式:你要想造对象,就不要用私有的构造器了,我已经用公有的方法把对象造好了,能满足你程序的要求,你直接调就行了
反射告诉我们的是:私有的方法你也可以调,但是不建议调私有的方法,因为可能公共的方法更好,加了一些逻辑 。
封装性解决的问题是:建议调哪个的问题。公共的调就可以了,私有的不要就不要调了,私有的属性不建议你直接修改,建议你通过get set方法修改。
反射解决的的问题是:能不能调的问题。
所以两者不矛盾。
六、静态代理和动态代理
静态代理:缺点:
工厂类写死,不够灵活,随着需求越多,工厂类也是越来越多。
interface ClothFactory{//共同接口
void ProduceCloth();
}
class LowerClothFactory implements ClothFactory{//被代理类
@Override
public void ProduceCloth() {
System.out.println("工厂接收订单");
System.out.println("工厂生产衣服");
System.out.println("工厂提交订单");
}
}
class ProxyNike implements ClothFactory{//代理类
private LowerClothFactory lowerClothFactory;
public ProxyNike() {
}
public ProxyNike(LowerClothFactory lowerClothFactory) {
this.lowerClothFactory = lowerClothFactory;
}
@Override
public void ProduceCloth() {
System.out.println("Nike提交订单");
lowerClothFactory.ProduceCloth();
System.out.println("Nike接收订单");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
LowerClothFactory lowerClothFactory = new LowerClothFactory();
ProxyNike nike = new ProxyNike(lowerClothFactory);
nike.ProduceCloth();
}
}
动态代理:
要想实现动态代理,需要解决的问题?
- 如何根据加载到内存中的被代理类,动态的创建-一个代理类及其对象。
- 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- 加载器直接使用当前类的加载器就行了–obj.getClass().getClassLoader()
- 当前类实现的接口–obj.getClass().getInterfaces()
- 通过Handler将要运行的方法动态的绑定在一起
- 通过代理类的对象,调用方法a时,就会自动调用Handler中的invoke()方法
invoke(Object o, Method method, Object[] objects)- o:被代理类的对象,一般用不着
- method:即为代理类对象调用的方法,此方法也就作为了被代理类要调用的方法
- objects[]:被代理类要调用的方法的参数列表
- 将被代理类要执行的方法a的功能就声明在invoke()中
- 通过代理类的对象,调用方法a时,就会自动调用Handler中的invoke()方法
interface Human{
String getBelief();
void eat(String food);
}
class SuperMan implements Human{
@Override
public String getBelief() {
return "I do believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class ProxyFactory{
//调用此方法,返回一个代理类的对象,解决问题一
public static Object getProxyInstance(Object obj){//obj被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类要调用的方法
//obj:被代理类的对象
Object returnValue = method.invoke(obj, objects);
return returnValue;
}
}
public class DynamicProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance代理类的对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
proxyInstance.eat("奥里给");
}
}