目录
3.反射机制能提供的功能:通过 Class实例 调用 ,获取运行时类的信息
3.2 获取属性:getFields()或getDeclaredFields()
3.3 获取方法:getMethods():或getDeclaredMethods()
3.4 获取构造器:getConstructors():或getDeclaredConstructors()
3.5 获取父类:getSuperclass(),getGenericSuperclass()=带泛型的父类
3.10 获取、设置指定变量名的属性:.getDeclaredField("变量名");
3.11 获取、调用指定的方法: getDeclaredMethod("方法名",方法的形参列表), 方法名.invoke(方法的调用者,形参赋值) 返回值为 调用的方法的返回值
1.对反射的理解
框架 = 反射 + 注解 + 设计模式
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取得任何类的内部信息,并能直接操作任意对象的内部属性及方法(static,private均可)。
类的加载过程:
程序 经过 javac.exe命令 以后,会生成一个或多个字节码文件(.class结尾)。
字节码文件 使用 java.exe命令 进行解释运行,加载到内存中。
运行时类 = 加载到内存中的类 = Class的一个实例
2.获取Class实例的几种方式:4种
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
方式一: 类名.class 调用运行时类的属性:.class
Class clazz1 = Person.class;
方式二: 类的对象.getClass() 通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
方式三:Class.forName(String classPath) 调用Class的静态方法:
Class clazz3 = Class.forName("com.atguigu.java.Person");//体现反射的动态性
方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
==》创建类的对象的方式:三种
- 方式一:new + 构造器
- 方式二:可以调用其静态方法,创建Xxx对象。要创建Xxx类的对象,可以考虑:Xxx、 Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。
- 方式三:通过反射
3.反射机制能提供的功能:通过 Class实例 调用 ,获取运行时类的信息
3.1 创建运行时类的对象:.newInstance()
Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
其中:在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象,newInstance()调用了运行时类的空参的构造器。
2.便于子类继承此运行时类时,默认调用super()时,保证父类此构造器
3.2 获取属性:getFields()或getDeclaredFields()
1. 类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
2.类中声明的所有属性(不包含父类的)
Field[] declaredFields = clazz.getDeclaredFields();
3.3 获取方法:getMethods():或getDeclaredMethods()
1. 类及其父类中声明为public访问权限的方法
Field[] fields = clazz.getMethods();
2.类中声明的所有方法(不包含父类的)
Field[] declaredFields = clazz.getDeclaredMethods();
3.4 获取构造器:getConstructors():或getDeclaredConstructors()
1. 类及其父类中声明为public访问权限的方法
Field[] fields = clazz.getConstructors();
2.类中声明的所有方法(不包含父类的)
Field[] declaredFields = clazz.getDeclaredConstructors();
3.5 获取父类:getSuperclass(),getGenericSuperclass()=带泛型的父类
3.6 获取带泛型的父类的泛型
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
3.7 获取接口:.getInterfaces()
3.8 获取包:.getPackage()
3.9 获取声明的注解:.getAnnotations()
3.10 获取、设置指定变量名的属性:.getDeclaredField("变量名");
Class clazz = Person.class;
Person p = (Person) clazz.newInstance(); //创建运行时类的对象
//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取、设置指定对象的此属性值
name.set(p,"Tom");
System.out.println(name.get(p));
3.11 获取、调用指定的方法: getDeclaredMethod("方法名",方法的形参列表), 方法名.invoke(方法的调用者,形参赋值) 返回值为 调用的方法的返回值
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
/*1.获取指定的某个方法 getDeclaredMethod("方法名",方法的形参列表)
:参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表 */
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保证当前方法是可访问的
show.setAccessible(true);
/*
3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值。
*/
Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
System.out.println(returnValue);
3.12 调用指定的构造器:
Class clazz = Person.class;
//private Person(String name)
/*
1.获取指定的构造器: getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom");
4. 代理模式
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。
任何对原始对象的调用都要通过代理。 (替你做+让你做)
代理对象决定是否以及何时将方法调用转到原始对象上。
4.1 静态代理:编译时确定代理类和目标类
MyThread t = new MyThread(); //创建 被代理类对象
Thread thread = new Thread(t); // 被代理类对象作为构造器参数,创建代理类对象,
thread.start();//启动线程;调用线程的run() //通过代理类对象调用方法
缺点:
① 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。
② 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。
4.1 动态代理:运行时根据需要动态创建目标类的代理对象
- 根据加载到内存中的被代理类,动态创建一个代理类及其对象。
(通过 Proxy.newProxyInstance() 实现) - 当通过代理类的对象调用方法a时,动态调用被代理类中的同名方法a。
(通过 InvocationHandler接口的实现类 及 其方法invoke() )
/**
*
* 动态代理的举例
*
* @author shkstart
* @create 2019 上午 10:18
*/
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class HumanUtil{
public void method1(){
System.out.println("====================通用方法一====================");
}
public void method2(){
System.out.println("====================通用方法二====================");
}
}
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);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil util = new HumanUtil();
util.method1();
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnValue = method.invoke(obj,args);
util.method2();
//上述方法的返回值就作为当前类中的invoke()的返回值。
return returnValue;
}
}
public class ProxyTest {
public static void main(String[] args) {
//创建被代理类对象
SuperMan superMan = new SuperMan();
//proxyInstance:代理类的对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("四川麻辣烫");
System.out.println("*****************************");
//创建被代理类对象
NikeClothFactory nikeClothFactory = new NikeClothFactory();
//动态创建代理类对象
ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
proxyClothFactory.produceCloth();
}
}