Java_反射

反射

疑问:

/*
疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
       建议:直接new的方式。
       什么时候会使用反射的方式?反射的特征:动态性
       
疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
      不矛盾。
*/

一、反射的概述

在这里插入图片描述

1.反射的理解:

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Refection AP
取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

框架=反射+注解+设计模式。

2.动态语言&&静态语言

  1. 动态语言:是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。即在运行时代码可以根据某些条件改变自身结构。(主要的动态语言有:C#、JavaScript、Python、PHP)

  2. 静态语言:运行时结构不可变的语言就是静态语言(如:Java、C、C++)

  3. 对于Java来讲,Java不是动态语言,但是Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。

3.反射机制所能提供的功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

4.反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器

二、 Class类的理解

1.类的加载过程

1.类的加载过程:

类的加载——>类的链接——>类的初始化

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。
此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

2.换句话说,class的实例就对应着一个运行时类。

3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

2. Class实例可以是那些实例的说明

   @Test
    public  void test2(){
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[ ] a = new int[10];
        int[] b = new int[ 100];
        Class c10 = a.getClass ();
        Class c11 = b.getClass();
     //只要数组的元素类型与维度一样,就是同一个cLass
       System.out.println(c10 == c11);

    }

3. classLoader的理解

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。

    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化

    • 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
//classLoader的理解:
    @Test
    public  void test3(){
        //对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = One.class.getClassLoader ();
        System.out.println (classLoader);
        //调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent ();
        System.out.println (classLoader1);
        //调用扩展类加载器的getParent():无法获取引导类加载器
        //引导类加载器主要负责加载java的核心类库,无法加载自定义类的.
        ClassLoader classLoader2 = classLoader1.getParent ();
        System.out.println (classLoader2);

        ClassLoader classLoader3 = String.class.getClassLoader ();
        System.out.println (classLoader3);

//        jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
//        jdk.internal.loader.ClassLoaders$PlatformClassLoader@5891e32e
//        null
//        null

    }

4.***使用ClassLoader加载配置文件

    //使用ClassLoader加载配置文件
    @Test
    public  void test6() throws IOException, ClassNotFoundException {
        Properties pros = new Properties ();
        //读取配置文件的方式一:
        //默认在module下:
//      FileInputStream fis=new FileInputStream ("src\\jdbc1.properties");
//        FileInputStream fis=new FileInputStream ("jdbc.properties");
//        pros.load (fis);

        //读取配置文件方式二:
        //默认module的src下:
        ClassLoader classLoader = One.class.getClassLoader ();
        InputStream is = classLoader.getResourceAsStream ("jdbc1.properties");
        pros.load (is);

        String user = pros.getProperty ("user");
        String password = pros.getProperty ("password");
        System.out.println ("user="+user+",password="+password);
    }

三、获取Class实例的四种方法

①***调用Class的静态方法forName——常用

  //方式一:调用Class的静态方法forName(String classPath)——常用
        Class clazz1 = Class.forName ("reflect.Person");
        System.out.println (clazz1);

②调用运行时类的属性: .class

//方式二:调用运行时类的属性: .class
        Class clazz2 = Person.class;
        System.out.println (clazz2);

③通过运行时类的对象,调用getClass()

//方式三:通过运行时类的对象,调用getClass()
        Person p=new Person ();
        Class clazz3 = p.getClass ();
        System.out.println (clazz3);

④使用类的加载器: CLassLoader——了解

 //方式四:使用类的加载器: CLassLoader——了解
        ClassLoader classLoader = One.class.getClassLoader ();
        Class clazz4 = classLoader.loadClass ("reflect.Person");
        System.out.println (clazz4);

补充:Person类:

package reflect;

public class Person {
    private  String name;
    public  int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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 "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

四、反射的应用一:创建运行时类的对象

***newInstance ()

    @Test
    public  void test() throws IllegalAccessException, InstantiationException {
        Class clazz=Person.class;
        Object obj = clazz.newInstance ();
        System.out.println (obj);
        //Person{name='null', age=0}

/*
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器
要想此方法正常的创建运行时类的对象,要求:
    1.运行时类必须提供空参的构造器
    2.空参的构造器的访问权限得够。通常,设置为public.


在javabean中要求提供一个public的空参构造器:
原因:1.便于通过反射,创建运行时类的对象
      2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

 */
    }

五、反射的应用二:获取运行时类的完整结构

Person类

package reflectTest;
@MyAnnotation(value = "Hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface {

   private String name;
   public int age;
   int id;
   @MyAnnotation
   public  Person(){

   }
    Person(String name){
       this.name=name;
    }
    private  Person(String name,int age){
       this.name=name;
       this.age=age;
    }
       @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}';
    }
public  static  void show(){
    System.out.println ("我是一个可爱的人");
}
@MyAnnotation(value = "方法注解1")
public  int  sleep(int hour){
    return hour;
}
@MyAnnotation(value = "方法注解2")
private  String interests(String interests,int age){
       return  interests+age;
}
    @Override
    public int compareTo(String o) {
        return 0;
    }

    @Override
    public void info() {
        System.out.println ("我是一个人");
    }
}

Creature类—父类

package reflectTest;

import java.io.Serializable;

public class Creature<T> implements Serializable {
private  char gender;
public  int  age;

private  void breath(){
    System.out.println ("生物要呼吸");

}

public  void eat(){
    System.out.println ("生物药摄取营养");
}

}

注解

package reflectTest;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String[] value() default  "Hello";
}

接口

package reflectTest;

public interface MyInterface {
    void info();
}

测试类1—当前运行时类Person的属性结构

package reflectTest;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
//获取当前运行时类的属性结构
public class FieldTest {
    @Test
    public  void test1(){
        Class clazz = Person.class;
        //获取属性:
        //getFields ():获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields ();
        for (Field f:fields) {
            System.out.println (f);
        }
        System.out.println ("****************");
        //获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields ();
        for (Field f:declaredFields) {
            System.out.println (f);
        }
        System.out.println ("***************");

    }
//权限修饰符  数据类型   变量名
    @Test
    public  void test2(){
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields ();
        for (Field f:declaredFields) {
            //权限修饰符
            int modifiers = f.getModifiers ();
            System.out.print (Modifier.toString (modifiers)+"\t");
            //数据类型
            Class type = f.getType ();
            System.out.print (type+"\t");
            //变量名
            String name = f.getName ();
            System.out.println (name);

        }
    }

}

测试类2—当前运行时类Person的方法结构

package reflectTest;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;

//运行时类的方法结构
public class MethodTest {
    @Test
    public  void test1(){
        Class clazz = Person.class;
        //getMethods ():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods ();
        for (Method m:methods) {
            System.out.println (m);
        }
        System.out.println ("************");
        //getDeclaredMethods ():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods ();
        for (Method m:declaredMethods) {
            System.out.println (m);
        }

    }
    @Test
    public  void test2(){
        Class clazz = Person.class;
    /*
    @XxXX
    权限修饰符  返回值类型  方法名(参数类型1形参名1,...)throws XxxException{0}
    */
        Method[] declaredMethods = clazz.getDeclaredMethods ();
        for (Method m:declaredMethods) {
            //1.获取注解的方法
            Annotation[] annotations = m.getAnnotations ();
            for (Annotation a:annotations) {
                System.out.println (a);
            }
            //2.获得权限修饰符
            System.out.print (Modifier.toString (m.getModifiers ())+"\t");
            //3.获得返回值类型
            System.out.print (  m.getReturnType ()+"\t");
            //4.获得方法名
            System.out.print (m.getName ()+"\t");
            System.out.print ("(");
            //5.获得参数类型
            Class[] parameterTypes = m.getParameterTypes ();
            if(parameterTypes.length>0){
            for (int i=0;i<parameterTypes.length;i++) {
                System.out.print (parameterTypes[i].getName ()+"args_"+i);
            }
            }
            System.out.println (")");
            //6.获取异常
            //略
            System.out.println ();
        }

    }
}

测试类3—其他测试类

package reflectTest;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class OtherTest {
    //1.获取构造器
    @Test
    public  void test(){
        Class clazz = Person.class;
        //getConstructors ()获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors ();
        for (Constructor c:constructors) {
            System.out.println (c);
        }
        System.out.println ("*********************");

        //getDeclaredConstructors ():获取当前运行时类中声明的所有的构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors ();
        for (Constructor c:declaredConstructors) {
            System.out.println (c);
        }

    }
    //2.获取运行时类的父类
    @Test
    public  void test2(){
        Class clazz = Person.class;
        Class superclass = clazz.getSuperclass ();
        System.out.println (superclass);

    }
    //3.获取运行时类的带泛型的父类
    @Test
    public  void test3(){
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass ();
        System.out.println (genericSuperclass);

    }
    //4.获取运行时类的带泛型的父类de泛型
    @Test
    public  void test4(){
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass ();
        ParameterizedType parameterizedType= (ParameterizedType) genericSuperclass;
        //获取泛型类型
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments ();
        for (Type t:actualTypeArguments) {
            System.out.println (t);
        }
    }
    //5.获取运行时类的接口
    @Test
    public  void test5(){
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces ();
        for (Class c:interfaces) {
            System.out.println (c);
        }
        System.out.println ("*************");
        //获取运行时类的父类的接口
        Class[] interfaces1 = clazz.getSuperclass ().getInterfaces ();
        for (Class c:interfaces1) {
            System.out.println (c);
        }
    }
    //6.获取运行时类所在的包
    @Test
    public  void test6(){
        Class clazz = Person.class;
        Package pack = clazz.getPackage ();
        System.out.println (pack);
    }
    //7.获取运行时类声明的注解
    @Test
    public  void test7(){
        Class clazz = Person.class;
        Annotation[] annotations = clazz.getAnnotations ();
        for (Annotation a:annotations) {
            System.out.println (a);
        }
    }

}

六、反射的应用三:调用运行时类的指定结构

1.***操作运行时类的指定de属性

   @Test
    public void test1() throws Exception {
        Class clazz = Person.class;
        //1.创建运行时类的对象
        Person p = (Person) clazz.newInstance ();
              //getField()只能获取public的属性,所以不常用pass
              //  Field age = clazz.getField ("age");
        //2.getDeclaredField ():获取要用的属性
        Field age = clazz.getDeclaredField ("age");
        //3.设置可用性,保证可访问
        age.setAccessible (true);
        //4.进行你要进行的操作
        age.set (p,99);
        System.out.println (p.age);
    }

2.***操作运行时类的指定de 方法

    //方法的调用
    @Test
    public  void test2() throws Exception {
        Class clazz = Person.class;
        //1.创建运行时类的对象
        Person p = (Person) clazz.newInstance ();
        //2.进行方法的选择
             //clazz.getDeclaredMethod():
             // 参数1:方法名    参数2:方法的形参
        Method interests = clazz.getDeclaredMethod ("interests",String.class,int.class);
        //3.设置可用性,保证可以访问
        interests.setAccessible (true);
        //4.invoke():对方法进行调用
        Object o = interests.invoke (p, "唱歌", 9);
        System.out.println (o);


        System.out.println ("*********获取静态方法***********");
        //1.选择方法
        Method show = clazz.getDeclaredMethod ("show");
        //2.确定可用性
        show.setAccessible (true);
        //3.调用方法
//    方法一:    
        show.invoke (Person.class);
//    方法二:    show.invoke (null);
    }

3.操作运行时类的指定de 构造器

    @Test
    public  void test3() throws Exception {
        Class clazz = Person.class;
        Constructor declaredConstructor = clazz.getDeclaredConstructor (String.class);
        declaredConstructor.setAccessible (true);
        Object newInstance = declaredConstructor.newInstance ("海绵宝宝");
        System.out.println (newInstance);
        //Person{name='海绵宝宝', age=0, id=0}
    }

七、反射的应用四:动态代理

1.代理设计模式的原理:

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

2.动态代理相比于静态代理的优点

抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

3.静态代理举例

特点:代理类和被代理类在编译期间,就确定下来了。

interface  CloseFactory{
    void ProduceCloth();
}
//动态代理
class  agent implements  CloseFactory{
    private  CloseFactory factory;

    public agent(CloseFactory factory) {
        this.factory = factory;
    }

    @Override
    public void ProduceCloth() {
        System.out.println ("代理商开始收到代理项目");
        factory.ProduceCloth ();
        System.out.println ("代理完成");
    }
}
//当事人
class  PIKE implements CloseFactory{

    @Override
    public void ProduceCloth() {
        System.out.println ("Pike工厂生产一批鞋");
    }
}
public class client {
    public static void main(String[] args) {
        //创建被代理的对象
        PIKE p1=new PIKE ();
        //创建代理的对象
        agent a1=new agent (p1);

        a1.ProduceCloth ();

    }
}

4.动态代理举例

/*
动态代理:

要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
 */
interface Human{
    String getBelief();
    void east(String food);
}
//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "我相信我一定可以成功";
    }

    @Override
    public void east(String food) {
        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 {

    
    //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        Object returnValue = method.invoke (obj, args);
        return  returnValue;

    }
}

public class ProxyTest {
    public static void main(String[] args) {
     SuperMan superMan=new SuperMan ();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance (superMan);
        String belief = proxyInstance.getBelief ();
        System.out.println (belief);
        proxyInstance.east("螺狮粉");
  System.out.println ("****************");
        PIKE pike=new PIKE ();
        CloseFactory proxyInstance1 = (CloseFactory) ProxyFactory.getProxyInstance (pike);
        proxyInstance1.ProduceCloth ();
    }
}

5.动态代理与AOP

在这里插入图片描述

package reflectTest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
动态代理:

要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。

 */
interface Human{
    String getBelief();
    void east(String food);
}
class HumanUtil{
    public  void method1(){
        System.out.println ("**********方法一**************");
    }
    public  void method2(){
        System.out.println ("**********方法二**************");
    }
}
//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "我相信我一定可以成功";
    }

    @Override
    public void east(String food) {
        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;
}
HumanUtil util=new HumanUtil ();
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法: invoke()
// 将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    util.method1 ();
    //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        Object returnValue = method.invoke (obj, args);
        util.method2 ();
        return  returnValue;

    }
}

public class ProxyTest {
    public static void main(String[] args) {
     SuperMan superMan=new SuperMan ();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance (superMan);
        String belief = proxyInstance.getBelief ();
        System.out.println (belief);
        proxyInstance.east("螺狮粉");

        System.out.println ("——————————");
        PIKE pike=new PIKE ();
        CloseFactory proxyInstance1 = (CloseFactory) ProxyFactory.getProxyInstance (pike);
        proxyInstance1.ProduceCloth ();

    }
}

方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

util.method1 ();
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
    Object returnValue = method.invoke (obj, args);
    util.method2 ();
    return  returnValue;

}

}

public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan=new SuperMan ();
Human proxyInstance = (Human) ProxyFactory.getProxyInstance (superMan);
String belief = proxyInstance.getBelief ();
System.out.println (belief);
proxyInstance.east(“螺狮粉”);

    System.out.println ("——————————");
    PIKE pike=new PIKE ();
    CloseFactory proxyInstance1 = (CloseFactory) ProxyFactory.getProxyInstance (pike);
    proxyInstance1.ProduceCloth ();

}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值