013理解Class类并实例化Class类对象+行时创建类对象并获取类的完整结构+通过反射调用类的指定方法、指定属性+动态代理

一.理解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();
    }
}

 

发布了45 篇原创文章 · 获赞 7 · 访问量 2478

猜你喜欢

转载自blog.csdn.net/weixin_44145972/article/details/88926798