JavaSE高级开发之反射

Java反射(没有反射就没有任何EE框架)

1.认识反射

反射指的是对象的反向处理。根据对象倒推类的组成,通过Java反射机制,可以访问程序中已经加载进内存的Java对象描述
反射由JDK1.0产生

反射的核心处理在于Object类的方法:
取得类的对象public final native Class<?> getClass();

Class类描述各个类的组成(构造方法,普通方法,普通属性)

取得Class对象的三种方式
任何一个类的Class对象由JVM加载后产生(该对象在JVM中全局唯一),用户只能调用指定方法来取得该对象
1.任何类的对象可以通过调用Object类提供的getClass()取得该类Class对象
2.“类名称.class”可以直接根据某个具体类来取得其Class对象
3.调用Class类的静态方法Class.forName(String className)传入类的全名称来取得其Class对象。注:传入forName()中的参数必须是完整包含“包.类名称”的形式,就算两个类都处在同一个目录下也不能省略包名称

取得一个类的Class对象后,可以通过反射来实例化对象
在Class类中有如下方法:
1.public T newInstance()
此方法是利用类的无参构造来获取类的实例化对象,且无参构造必须是public权限。

反射改造工厂方法

public class FactoryTest {
    public static void main(String[] args) {
        System.out.println(Factory.getInstance("Apple"));
    }
}
interface IFruit{
    void eat();
}
class Apple implements IFruit{

    @Override
    public void eat() {
        System.out.println("eat apple");
    }
}
class Factory{
    //构造方法私有化
    private Factory(){}
    //通过Factory的静态方法getInstance获得对象
    public static IFruit getInstance(String className){
        IFruit fruit=null;
        try {
            //通过反射取得类
            Class<?> cls=Class.forName(className);
            //通过反射取得的类获得实例化对象
            fruit= (IFruit) cls.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return fruit;
    }
}

2.反射与类操作

反射取得父类、父接口的信息
取得类的包名称 public Package getPackage()
取得父类的Class对象 public native Class<? super T> getSuperClass();
取得实现的父接口public Class<?>[] getInterfaces();

public static void main(String[] args) {
        //通过反射取得类
        Class<?> cls=String.class;
        //通过反射取得类所在的包名
        System.out.println(cls.getPackage().getName());
        //取得父类信息
        Class<?> cls1=Students.class;
        System.out.println(cls1.getSuperclass().getName());
        //取得父类接口信息
        Class<?>[] classes=cls.getInterfaces();
        for(Class cla:classes){
            System.out.println(cla.getName());
        }

反射调用类中的构造方法(Constructor类–描述类中构造方法)

取得类中指定参数类型的构造

//仅限public权限的构造方法

 public Constructor<T> getConstructor(Class<?>... parameterTypes)

//能取得所有权限的构造方法

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
取得类中所有构造方法

//仅限public权限的构造方法

public Constructor<?>[] getConstructors()

//能取得所有权限的构造方法

public Constructor<?>[] getDeclaredConstructors()

Constructor提供了实例化对象的方法:
public T newInstance(Object ...initargs)//可以调用类中其他有参构造

反射调用类中普通方法-Method(描述类中普通方法)

取得类中指定普通方法

//方法有重载,因此需要方法名与返回类型双重验证
//取得本类以及父类中所有public方法

public Method getMethod(String name, Class<?>... parameterTypes)
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)    
取得类中全部普通方法

//取得本类以及父类中所有public方法

public Method[]  getMethods()

//取得本类中所有普通方法,包含私有方法

public Method[] getDeclaredMethods()    
Method类中提供调用类中普通方法的API

普通方法需要对象调用,因此第一个参数是对象

public Object invoke(Object obj,Object...args)
 public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class claP=Person.class;
        Class claS=Students.class;
      Method[] method= claP.getMethods();
      for(Method me:method){
          System.out.println(me);
      }

      //1.拿到PersonClass对象
        Class<?> cls=Person.class;

        //2.创建Person实例化对象
Person p= (Person) cls.newInstance();
//3.拿到setName的Method对象
        Method method1=cls.getMethod("setName", String.class);
        //4.通过invoke调用
        method1.invoke(p,"蛋蛋");
        System.out.println(p);
        //拿到name属性的Filed对象
        Field file=cls.getDeclaredField("AGE");
        //设置动态封装
        file.setAccessible(true);

    }

反射调用类中属性(属性的核心描述类–Field)

1 第一组(父类中)-取得类中全部属性: public Field[] getFields()
2. 第一组(父类中)-取得类中指定名称属性: public Field getField(String name)
3第二组(本类中)-取得类中全部属性: public Field[] getDeclaredFields()
4. 第二组(本类中)-取得类中指定名称属性 : public Method getDeclaredMethod(String name, Class<? >... parameterTypes)
注:java.lang.reflect.Field,在这个类之中有两个重要方法:
设置属性内容 : public void set(Object obj, Object value)
取得属性内容 : public Object get(Object obj)
取得属性的类型:public Class<?> getType()

通过反射操作数组

返回指定数组对象中的索引组件的值。

static Object get(Object array, int index) 

返回指定数组对象的长度 。

 static int getLength(Object array) 

创建具有指定组件类型和尺寸的新数组。

static Object newInstance(类<?> componentType, int... dimensions) 

将指定数组对象的索引组件的值设置为指定的新值。

static void set(Object array, int index, Object value)
public static void main(String[] args) {
        int[] num = {1, 2, 3, 4, 5};
        //取得元素类型
        Class<?> cla = num.getClass().getComponentType();
        System.out.println("数组元素类型:" + cla.getName());
        System.out.println("数组元素长度:" + Array.getLength(num));
        System.out.println("修改前的数组");
        for (int i = 0, len = Array.getLength(num); i < len; i++) {
            System.out.print(Array.get(num, i) + " ");
        }
        System.out.println();
        System.out.println("修改后的数组");
        for (int i = 0, len = Array.getLength(num); i < len; i++) {
            Array.set(num,i,i*2+1);
            System.out.print(Array.get(num,i)+ " ");
        }
//创建新的数组
        String[] str=null;
        str = (String[]) Array.newInstance(String.class, 5);
        for(int i=0;i<str.length;i++){
            Array.set(str,i,"i="+i);
        }
        for(int i=0;i<str.length;i++){
            System.out.println( Array.get(str,i));
        }
    }

动态设置封装

Field、Method、Constructor类都是AccessibleObject子类
AccessibleObject提供动态设置封装方法(在本次JVM进程中有效,且只能通过反射调用,正向调用编译不通过)
public void setAccessible(boolean flag)

ClassLoadeer类加载器

calssPath:类加载路径
1.认识ClassLoader类加载器
类加载器就是通过一个类的全名称来获取子类的二进制字节流,实现操作的代码模块称为类加载器.

JDK内置三大类加载器:

Bootstrap(启动加载器):

1.使用C++实现,是JVM的一部分,其他所有类加载器均使用java实现
2.负责将存放于JAVA_HOME\lib目录下的能被JVM识别的类库(rt.jar–存放了java所有基础类库,java.lang,jav.util)加载到JVM中.
3.启动类加载器无法被java程序直接引用.

ExtClassLoader(扩展类加载器):

1.使用java实现,并且可以被java程序直接引用
2.加载JAVA_HOME\lib\ext目录下能被识别的类库

AppClassLoader(应用程序类加载器)
1.负责加载用户路径(classPath)上指定的类库
2.如果应用程序中没有自定义类加载器,则此加载器就是java程序默认的类加载器.

类加载器的双亲委派模型

在这里插入图片描述
JDK内置的三种类加载器与用户自定义类加载器之间的层次关系成为类加载器的双亲委派模型.

要求除了顶层之外的加载器外,其余的类加载器都应用有自己的父类加载器.

执行流程:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载此类,而是把类加载请求委托给父类加载器完成,每一个层次类加载器均是如此.

只有当父类加载器无法完成加载请求时(在自己搜索范围内没有搜索到此类),子加载器才会尝试自己去加载.双亲委派模型保证了java程序稳定运行.java中基础类库一定有顶层类库BootStrap类加载器加载,因此,诸如Object等核心类在各种类记载环境下都是通过同一个类.

自定义类加载器

比较两个类相等的前提:这两类必须是由同一个类加载器加载的前提下才有意义.

动态代理

基础代理:一个接口,两个子类,并且都实现接口其中一个子类是真正的业务类,另一个子类是代理类,在代理类中传入真正的业务实例化对象.当有一个业务就有一个代理类和业务类
动态代理:一个代理类代理所有类似接口.要进行代理类实现,代理类不再具体实现某一个接口,而是实现InvocationHandler接口
抽象方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

public class DynamicProxy {
    public static void main(String[] args) {
        Isubject isubject= (Isubject) new ProxySubject().bind(new RealSubject());
        isubject.eat("麻辣烫",2);
    }
}

interface Isubject {//核心接口

    public void eat(String name, int num);
}

class RealSubject implements Isubject {

    @Override
    public void eat(String name, int num) {
        System.out.println("我要吃" + num + "分量的" + name);
    }
}

//动态代理类
class ProxySubject implements InvocationHandler {
//通过实现InvocationHandler 接口实现动态代理
    //通过绑定任意接口对象,使用Object描述
    private Object target;

 
    @Override
    //在覆写接口的抽象方法中实现要实现的处理逻辑
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      this.preHandle();
      //invoke()实现真实对象调用
      Object ret=method.invoke(this.target,args);
      this.afterHandle();
        return ret;
    }
//在bind方法中传入真实对象,绑定真实对象,返回代理对象
    public Object bind(Object target) {
        this.target = target;
        return
                //    public static Object newProxyInstance(ClassLoader loader,
                //                                          Class<?>[] interfaces,
                //                                          InvocationHandler h)
                Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);//利用代理类的静态方法绑定真实类的对象
    }
    
    public void preHandle(){
        System.out.println("代理方法处理前");
    }
    public void afterHandle(){
        System.out.println("代理方法处理后");
    }
}

反射与Annotation

几个基本的注解:

  • @Override:限定重写父类方法
  • @Deprecated:标示已过时,可用于方法或类
  • @SuppressWarnngs:抑制编译器警告
  • @FunctionalInterface:函数式接口(只允许声明一个抽象方法)

取得全部注解:public Annotation[] getAnnotation()
取得指定注解:public T getAnnotation(Class annotationClass)
Annotation本身有自己的保存范围,不同的Annotation的返回也不同,这些范围在一个枚举类中定义:
SOURCE:在源码中出现的注解
CLASS:在*.class中出现的注解
RUNTIME:在类执行的时候出现的注解

@Retention

修饰Annotation定义可以用@Retention,其中包含一个RetentionPolicy类型的value成员变量,为成员变量赋值可指定Annotation保存的位置。
@Retention(value=RetentionPolicy.CLASS):编译器将注解记录在class文件中,当Java程序运行时,JVM不可获取Annotation信息。
@Retention(value=RetentionPolicy.RUNTIME):编译器将注解记录在class文件中,当Java程序运行时,JVM可以通过反射获取Annotation信息。
@Retention(value=RetentionPolicy.SOURCE):Annotation只保留在源码中,编译器会直接丢弃这种注解。

@Target

Target也只能修饰一个Annotation定义,其中也包含value成员,为其赋值可指定Annotation修饰的范围。

  • @Target(value=ElementType.ANNOTATION_TYPE):指定该策略的Annotation只能修饰Annotation
  • @Target(value=ElementType.CONSTRUCTOR):指定该策略的Annotation只能修饰构造器
  • @Target(value=ElementType.FIELD):指定该策略的Annotation只能修饰成员变量
  • @Target(value=ElementType.LOCAL_VARIABLE):指定该策略的Annotation只能修饰局部变量
  • @Target(value=ElementType.METHOD):指定该策略的Annotation只能修饰方法定义
  • @Target(value=ElementType.PACKAGE):指定该策略的Annotation只能修饰包定义
  • @Target(value=ElementType.PARAMETER):指定该策略的Annotation只能修饰参数
  • @Target(value=ElementType.TYPE):指定该策略的Annotation可以修饰类\接口\枚举定义

@Inherited

被 @Inherited元修饰的Annotation定义将具有继承性

自定Annotation用@Interface关键字定义

public class AnnotationFruit {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        IFruit fruit=Factory.getInstance();
        fruit.eat();
    }
}

@Target(value=ElementType.TYPE)//注解元:指定注解可以修饰类\接口\枚举
@Retention(RetentionPolicy.RUNTIME)//注解元:指定注解保存在class文件中,可以被JVM获取

@interface MyAnnotation1{
    //通过注解简化编程,方便的获取到需要的反射对象
  public   Class<?> target();
}
interface IFruit{
    void eat();
}
class Apple implements IFruit{

    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}

@MyAnnotation1(target = Apple.class)//获取Apple的反射对象
class Factory{
    public static <T> T getInstance() throws IllegalAccessException, InstantiationException {
        MyAnnotation1 mt=Factory.class.getAnnotation(MyAnnotation1.class);
        return (T)mt.target().newInstance();
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42962924/article/details/86506580