一.理解Class类并实例化Class类对象
反射概述
Java Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。正常方式,引入需要的包类名称->通过new实例化->取得实例化对象。反射方式,实例化对象->getClass()方法->得到完整的包类名称。
Java反射机制提供的功能:在运行时判断任意一个对象所属的类,在运行时构造任意一个类的对象,在运行时判断任意一个类所具有的成员变量和方法,在运行时调用任意一个对象的成员变量和方法,生成动态代理。
有了反射,可以通过反射创建一个类的对象,并调用其中的结构。
@Test
public void test2() throws Exception{
Class clazz = Person.class;
//1.创建clazz对应的运行时类Person类的对象
Person p = (Person)clazz.newInstance();
//2.通过反射调用运行时类的指定的属性
Field f1 = clazz.getField("name");//public
f1.set(p,"LiuDeHua");
Field f2 = clazz.getDeclaredField("age");//private
f2.setAccessible(true);
f2.set(p, 20);
//3.通过反射调用运行时类的指定的方法
Method m1 = clazz.getMethod("show");//不带参数
m1.invoke(p);
Method m2 = clazz.getMethod("display",String.class);//带参数
m2.invoke(p,"CHN");
}
反射的源头:java.lang.Class。我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)。此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例!每一个运行时类只加载一次!有了Class的实例以后,我们才可以进行如下的操作:创建对应的运行时类的对象。获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...)。调用对应的运行时类的指定的结构(属性、方法、构造器)。反射的应用,动态代理。
获取Class的实例(4种)
//调用运行时类本身的.class属性:
Class clazz = Person.class;
//通过运行时类的对象获取:
Person p = new Person();Class clazz = p.getClass();
//通过Class的静态方法获取.通过此方式,体会一下,反射的动态性。
String className = "com.lidong.java.Person";
Class clazz = Class.forName(className);
//(了解)通过类的加载器:
String className = "com.lidong.java.Person";
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass(className);
ClassLoader,类加载器是用来把类(class)装载进内存的。JVM规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结构。
ClassLoader loader = this.getClass().getClassLoader();
InputStream is = loader.getResourceAsStream("com\\atguigu\\java\\jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String name = pros.getProperty("user");
System.out.println(name);
String password = pros.getProperty("password");
System.out.println(password);
二.行时创建类对象并获取类的完整结构
有了Class对象,能做什么?
创建类的对象:调用Class对象的newInstance()方法,类必须有一个无参数的构造器,类的构造器的访问权限需要足够。
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);。
Object obj = clazz.newInstance();
Person p = (Person)obj;
没有无参的构造器就不能创建对象了吗?不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器。向构造器中的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("罗伟",20);
在Constructor类中存在的一个方法:public T newInstance(Object ... initargs)这些是反射机制应用最多的地方,通过反射调用类的完整结构。
获取对应的运行时类的属性
@Test
public void test(){
Class clazz = Person.class;
//getFields():只能获取到运行时类中及其父类中声明为public的属性
Field[] fields1 = clazz.getFields();
for(int i = 0;i < fields1.length;i++){
System.out.println(fields1[i]);
}
System.out.println();
//getDeclaredFields():获取运行时类本身声明的所有的属性
Field[] fields2 = clazz.getDeclaredFields();
for(Field f : fields2){
System.out.println(f.getName());
}
}
获取属性的各个部分的内容:权限修饰符 变量类型 变量名
@Test
public void test(){
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f : fields){
//1.获取每个属性的权限修饰符
int i = f.getModifiers();
String str = Modifier.toString(i);
System.out.print(str + " ");
//2.获取属性的类型
Class type = f.getType();
System.out.print(type.getName() + " ");
//3.获取属性名
System.out.print(f.getName());
System.out.println();
}
}
调用运行时类指定的属性
@Test
public void test() throws Exception{
Class clazz = Person.class;
//1.获取指定的属性
//getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
Field name = clazz.getField("name");
//2.创建运行时类的对象
Person p = (Person)clazz.newInstance();
System.out.println(p);
//3.将运行时类的指定的属性赋值
name.set(p,"Jerry");
System.out.println(p);
System.out.println("%"+name.get(p));
System.out.println();
//getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
Field age = clazz.getDeclaredField("age");
//由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
age.setAccessible(true);
age.set(p,10);
System.out.println(p);
}
获取运行时类的方法
@Test
public void test(){
Class clazz = Person.class;
//getMethods():获取运行时类及其父类中所有的声明为public的方法
Method[] m1 = clazz.getMethods();
for(Method m : m1){
System.out.println(m);
}
System.out.println();
//getDeclaredMethods():获取运行时类本身声明的所有的方法
Method[] m2 = clazz.getDeclaredMethods();
for(Method m : m2){
System.out.println(m);
}
}
获取方法的各个部分的内容:注解 权限修饰符 返回值类型 方法名 形参列表 异常
@Test
public void test(){
Class clazz = Person.class;
Method[] me = clazz.getDeclaredMethods();
for(Method m : me){
//1.注解
Annotation[] an = m.getAnnotations();
for(Annotation a : an){
System.out.println(a);
}
//2.权限修饰符
String str = Modifier.toString(m.getModifiers());
System.out.print(str + " ");
//3.返回值类型
Class returnType = m.getReturnType();
System.out.print(returnType.getName() + " ");
//4.方法名
System.out.print(m.getName() + " ");
//5.形参列表
System.out.print("(");
Class[] params = m.getParameterTypes();
for(int i = 0;i < params.length;i++){
System.out.print(params[i].getName() + " args-" + i + " ");
}
System.out.print(")");
//6.异常类型
Class[] exps = m.getExceptionTypes();
if(exps.length != 0){
System.out.print("throws ");
}
for(int i = 0;i < exps.length;i++){
System.out.print(exps[i].getName() + " ");
}
System.out.println();
}
}
调用运行时类中指定的方法
@Test
public void test() throws Exception{
Class clazz = Person.class;
//getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
Method m1 = clazz.getMethod("show");
Person p = (Person)clazz.newInstance();
//调用指定的方法:Object invoke(Object obj,Object ... obj)
Object returnVal1 = m1.invoke(p);
System.out.println(returnVal1);
Method m2 = clazz.getMethod("toString");
Object returnVal2 = m2.invoke(p);
System.out.println(returnVal2);
//对于运行时类中静态方法的调用
Method m3 = clazz.getMethod("info");
m3.invoke(Person.class);
//getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
m4.setAccessible(true);
Object value = m4.invoke(p,"CHN",10);
System.out.println(value);
}
获取运行时类的父类
@Test
public void test(){
Class clazz = Person.class;
Class superClass = clazz.getSuperclass();
System.out.println(superClass);
}
获取带泛型的父类
@Test
public void test(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
System.out.println(type);
}
获取父类的泛型
@Test
public void test(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
ParameterizedType param = (ParameterizedType)type;
Type[] ars = param.getActualTypeArguments();
System.out.println(((Class)ars[0]).getName());
}
获取实现的接口
@Test
public void test(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class i : interfaces){
System.out.println(i);
}
}
获取所在的包
@Test
public void test(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
获取注解
@Test
public void test(){
Class clazz = Person.class;
Annotation[] anns = clazz.getAnnotations();
for(Annotation a : anns){
System.out.println(a);
}
}
三.通过反射调用类的指定方法、指定属性
调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:通过Class类的getMethod(String name,Class ... parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
@Test
public void test() throws Exception{
Class clazz = Person.class;
//getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定
的方法
Method m1 = clazz.getMethod("show");
Person p = (Person)clazz.newInstance();
//调用指定的方法:Object invoke(Object obj,Object ... obj)
Object returnVal1 = m1.invoke(p);
System.out.println(returnVal1);
Method m2 = clazz.getMethod("toString");
Object returnVal2 = m2.invoke(p);
System.out.println(returnVal2);
//对于运行时类中静态方法的调用
Method m3 = clazz.getMethod("info");
m3.invoke(Person.class);
//getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指
定的方法
Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
m4.setAccessible(true);
Object value = m4.invoke(p,"CHN",10);
System.out.println(value);
}
@Test
public void test() throws Exception{
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("罗伟",20);
System.out.println(p);
}
调用指定的属性
@Test
public void test() throws Exception{
Class clazz = Person.class;
//1.获取指定的属性
//getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
Field name = clazz.getField("name");
//2.创建运行时类的对象
Person p = (Person)clazz.newInstance();
System.out.println(p);
//3.将运行时类的指定的属性赋值
name.set(p,"Jerry");
System.out.println(p);
System.out.println("%"+name.get(p));
System.out.println();
//getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
Field age = clazz.getDeclaredField("age");
//由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
age.setAccessible(true);
age.set(p,10);
}
四.动态代理
Java动态代理
静态代理,特征:代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。
interface ClothFactory{
void productCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("Nike工厂生产一批衣服");
}
}
//代理类
class ProxyFactory implements ClothFactory{
ClothFactory cf;
public ProxyFactory(ClothFactory cf){
this.cf = cf;
}
@Override
public void productCloth() {
System.out.println("代理类开始执行,收代理费$1000");
cf.productCloth();
}
}
public class TestClothProduct {
public static void main(String[] args) {
NikeClothFactory nike = new NikeClothFactory();
//创建被代理类的对象
ProxyFactory proxy = new ProxyFactory(nike);
//创建代理类的对象
proxy.productCloth();
}
}
动态代理(体会反射):客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类 的代理对象。调试、远程方法调用。
interface Subject {
void action();
}
// 被代理类
class RealSubject implements Subject {
public void action() {
System.out.println("我是被代理类,记得要执行我哦!么么~~");
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;// 实现了接口的被代理类的对象的声明
// ①给被代理的对象实例化②返回一个代理类的对象
public Object blind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
//当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//method方法的返回值时returnVal
Object returnVal = method.invoke(obj, args);
return returnVal;
}
}
public class TestProxy {
public static void main(String[] args) {
//1.被代理类的对象
RealSubject real = new RealSubject();
//2.创建一个实现了InvacationHandler接口的类的对象
MyInvocationHandler handler = new MyInvocationHandler();
//3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对
象。
Object obj = handler.blind(real);
Subject sub = (Subject)obj;//此时sub就是代理类的对象
sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用
}
}
代理设计模式原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
动态代理与AOP(Aspect Orient Programming)
前面介绍的Rroxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制。
改进后说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1,2,3又和一个特定的方法A耦合了!最理想的效果是:代码块1、2、3既可以执行方法A,又无序在程序中以硬编码的方式直接调用深色代码的方法。
interface Human {
void info();
void fly();
}
// 被代理类
class SuperMan implements Human {
public void info() {
System.out.println("我是超人!我怕谁!");
}
public void fly() {
System.out.println("I believe I can fly!");
}
}
class HumanUtil {
public void method1() {
System.out.println("=======方法一=======");
}
public void method2() {
System.out.println("=======方法二=======");
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;// 被代理类对象的声明
public void setObject(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
HumanUtil h = new HumanUtil();
h.method1();
Object returnVal = method.invoke(obj, args);
h.method2();
return returnVal;
}
}
class MyProxy {
// 动态的创建一个代理类的对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler();
handler.setObject(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), handler);
}
}
public class TestAOP {
public static void main(String[] args) {
SuperMan man = new SuperMan();//创建一个被代理类的对象
Object obj = MyProxy.getProxyInstance(man);//返回一个代理类的对象
Human hu = (Human)obj;
hu.info();//通过代理类的对象调用重写的抽象方法
System.out.println();
hu.fly();
}
}