JavaSE---15反射

该内容来源于学习尚硅谷java系列视频所作笔记,部分图来源于网上,侵权删

09 反射


9.1 概述

  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

  • Java反射机制提供的功能

    • 在运行时判断任意一个对象所属的类

    • 在运行时构造任意一个类的对象

    • 在运行时判断任意一个类所具有的成员变量和方法

    • 在运行时获取泛型信息

    • 在运行时调用任意一个对象的成员变量和方法

    • 在运行时处理注解

    • 生成动态代理

  • 反射相关的主要API

    • java.lang.Class:代表一个类

    • java.lang.reflect.Method:代表类的方法

    • java.lang.reflect.Field:代表类的成员变量

    • java.lang.reflect.Constructor:代表类的构造器

示例

Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Jon",18);
Person p = (Person) obj;
System.out.println(p.toString());
//2.通过反射,调用对象指定的属性和方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p.toString());

//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);

9.1.1 反射的强大:调用类的私有结构

//通过反射,是可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
//调用私有的构造器
Constructor cons2 = clazz.getDeclaredConstructor(String.class);
cons2.setAccessible(true);
Person p1 = (Person) cons2.newInstance("Tom");
System.out.println(p1);

//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"Jerry");
System.out.println(p1);

//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1,"China");
//相当于String nation = p1.showNation("China")
System.out.println(nation);

9.2 理解Class类并获取Class实例

关于java.lang.Class类的理解

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

3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。(而不是创建,所以不同方法指向的都是一个对象)

获取Class实例

//方式一:
Class c1 = Person.class;
System.out.println(c1);

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

//方式三:调用Class的静态方法:forName(String classPath) 体现反射的动态性
Class c3 = Class.forName("java.lang.String");
//c3 = Class.forName("java.lang.String");
System.out.println(c3);

System.out.println(c1 == c2);//true
System.out.println(c1 == c3);//true

//方式四:使用类的加载器:ClassLoader(了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class c4 = classLoader.loadClass("java.lang.String");
System.out.println(c4);

System.out.println(c1 == c4);

9.2.1 Class实例对应的结构

(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation:注解@interface
(6)primitivetype:基本数据类型
(7)void

Class s1 = Object.class;
Class s2 = Comparable.class;
Class s3 = String[].class;
Class s4 = int[][].class;
Class s5 = ElementType.class;
Class s6 = Override.class;
Class s7 = int.class;
Class s8 = void.class;
Class s9 = Class.class;

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

9.3 类的加载与ClassLoader的理解

9.3.1 类的加载

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

JavaSE---15反射_第1张图片 JavaSE---15反射_第2张图片

9.3.2 ClassLoader

  • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
  • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

9.3.3 使用ClassLoader加载配置文件Properties

//读取配置文件的方式一:
//此时的文件默认在当前的module下
FileInputStream fis = new FileInputStream("jdbc.properties");
pros.load(fis);

//读取配置文件的方式二:使用ClassLoader
//配置文件默认识别为:当前module的src下
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
Properties properties = new Properties();
pros.load(is);

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

9.4 通过反射创建运行时类的对象

  • newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器
    • 要想此方法正常的创建运行时类的对象,要求:
      • 1.运行时类必须提供空参的构造器
      • 2.空参的构造器的访问权限得够。通常,设置为public。
Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
System.out.println(obj);

9.4.1 体会反射的动态性

运行时才知道创建的什么类的对象,体现了动态性

/**
 * 通过反射创建对应的运行时类的对象
 */
public class NewInstanceTest {
    @Test
    public void test2(){
        for(int i = 0;i < 100;i++){
            int num = new Random().nextInt(3);//0,1,2
            String classPath = "";
            switch(num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "www.java.Person";
                    break;
            }
            try {
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建一个指定类的对象。
     * classPath:指定类的全类名
     */
    public Object getInstance(String classPath) throws Exception {
        Class clazz =  Class.forName(classPath);
        return clazz.newInstance();
    }
}

9.5 获取运行时类的完整结构

9.5.1 一些类

1、Person类

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

    private String name;
    int age;
    public int id;

    public Person() {
    }

    @MyAnnotation(value="hello")
    Person(String name){
        this.name = name;
    }

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

    @MyAnnotation
    private String show(String nation){
        System.out.println("我来自" + nation + "星系");
        return nation;
    }

    @Override
    public void info() {
        System.out.println("火星喷子");
    }

    public String display(String play){
        return play;
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }
}

2、Creature类

public abstract class Creature <T> implements Serializable {
    private char gender;
    public double weight;

    private void breath(){
        System.out.println("太阳系");
    }

    public void eat(){
        System.out.println("银河系");
    }
}

3、MyInterface

public interface MyInterface {
    void info();
}

4、MyAnnotation

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello world";
}

9.5.2 获取运行时类的属性

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

//分别获取权限修饰符  数据类型 变量名
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
    //1.权限修饰符
    int modifier = f.getModifiers();
    System.out.print(Modifier.toString(modifier) + "\t");
    System.out.println("+++++++++++++++++++++++++++");
    //2.数据类型
    Class type = f.getType();
    System.out.print(type.getName() + "\t");
    System.out.println("***************************");
    //3.变量名
    String fName = f.getName();
    System.out.print(fName);
}

9.5.3 获取运行时类的方法的内部结构

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);
}

9.5.4 获取方法的注解、权限修饰符、返回值类型、方法名、形参列表、异常

Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
    //1.获取方法声明的注解
    Annotation[] annos = m.getAnnotations();
    for (Annotation a : annos) {
        System.out.println(a + "KKKK");
    }
    //2.权限修饰符
    System.out.print(Modifier.toString(m.getModifiers()) + "\t");
    //3.返回值类型
    System.out.print(m.getReturnType().getName() + "\t");
    //4.方法名
    System.out.print(m.getName());
    System.out.print("(");
    //5.形参列表
    Class[] pTs = m.getParameterTypes();
    if(!(pTs == null && pTs.length == 0)){
        for(int i = 0;i < pTs.length;i++){
            if(i == pTs.length - 1){
                System.out.print(pTs[i].getName() + " args_" + i);
                break;
            }
            System.out.print(pTs[i].getName() + " args_" + i + ",");
        }
    }
    System.out.print(")");

    //6.抛出的异常
    Class[] eTs = m.getExceptionTypes();
    if(eTs.length > 0){
        System.out.print("throws ");
        for(int i = 0;i < eTs.length;i++){
            if(i == eTs.length - 1){
                System.out.print(eTs[i].getName());
                break;
            }
            System.out.print(eTs[i].getName() + ",");
        }
    }
    System.out.println("TQA");
}

9.5.5 获取运行时类的构造器结构

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);
}

9.5.6 获取运行时类的父类及父类的泛型

//父类
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
//带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);

9.5.7 获取运行时类的接口、所在包、注解等

Class clazz = Person.class;
//接口
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces){
    System.out.println(c);
}
 //获取运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class c : interfaces1){
    System.out.println(c);
}
//获取运行时类所在的包
Package pack = clazz.getPackage();
System.out.println(pack);
//获取运行时类声明的注解
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annos : annotations){
    System.out.println(annos);
}

9.6 调用运行时类的指定结构

9.6.1 调用运行时类中的指定属性

Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();

//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");

//2.保证当前属性是可访问的
name.setAccessible(true);//从而可以操作private等属性

//3.获取、设置指定对象的此属性值
name.set(p,"Jam");
System.out.println(name.get(p));

Person类

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

    private String name;
    int age;
    public int id;

    public Person() {
    }

    @MyAnnotation(value="C++")
    Person(String name){
        this.name = name;
    }

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

    @MyAnnotation
    private String show(String nation){
        System.out.println("我来自" + nation + "星系");
        return nation;
    }

    @Override
    public void info() {
        System.out.println("火星喷子");
    }

    public String display(String interests,int age) throws Exception{
        return interests + age;
    }
    @Override
    public int compareTo(String o) {
        return 0;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

9.6.2 调用运行时类中的指定方法

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);

//如何调用静态方法
//private static void showDesc()

Method showDesc = clazz.getDeclaredMethod("showDown");
showDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null
//Object returnVal = showDesc.invoke(null);//写不写都行
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);//null

9.6.3 调用运行时类中的指定构造器

 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");
System.out.println(per);

9.7 动态代理–反射的应用

interface Person{
    String getBelief();

    void eat(String food);
}

//被代理类
class Superman implements Person{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }

    @Override
    public void est(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

/**
 * 要想实现动态代理,需要解决的问题?
 * 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。Proxy.newProxyInstance
 * 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
 * InvocationHandler实现类及invoke方法
 */ 
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:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);

        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        Superman superman = new Superman();
        //proxyInstance:代理类的对象
        Person proxyInstance = (Person)ProxyFactory.getProxyInstance(superman);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.Object("四川麻辣烫");

        System.out.println("+++++++++++++++++++");

        NickTest fox = new NickTest();
        ClothFactory ween = (ClothFactory) ProxyFactory.getProxyInstance(fox);
        ween.produceCloth();
    }
}

你可能感兴趣的:(JAVA相关,java,spring,开发语言)