24. 反射

day24复习

1. 反射的理解

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

  • 体会反射的动态性
//体现了反射的动态性
//获取指定的类的对象
public Object method(String className) throws Exception{
    Class clazz = Class.forName(className);
    Object instance = clazz.newInstance();
    return instance;
}
//调用指定类的指定的方法
public Object method(String className,String methodName){
    Class clazz = Class.forName(className);
    Object instance = clazz.newInstance();
    
    Method method = clazz.getDeclaredMethod(methodName);
    method.setAccessable(true);
    return method.invoke(instance);
    
}
  • 体会反射都可以做什么?
public class ReflectionTest {

    //不使用反射
    @Test
    public void test1(){
        //1. 创建Person类的对象
        Person p1 = new Person();
        //2. 通过对象,调用实例变量
        p1.age = 12;
        System.out.println(p1.age);
        //3. 通过对象,调用方法
        p1.show();

    }



    //使用反射,做上面同样的事
    @Test
    public void test2() throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        //1. 创建Person类的对象
        Class clazz = Person.class;
//        Object instance = clazz.newInstance();
        Person p1 = (Person) clazz.newInstance();
        //2. 通过对象,调用实例变量
        Field ageField = clazz.getField("age");
        ageField.set(p1,12);
        System.out.println(ageField.get(p1));
        //3. 通过对象,调用方法
        Method showMethod = clazz.getMethod("show");
        showMethod.invoke(p1);
    }

    //体现了反射的动态性
    public Object method(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clazz = Class.forName(className);
        Object instance = clazz.newInstance();
        return instance;
    }

    //通过反射,可以调用Person类中的私有结构
    @Test
    public void test3() throws Exception {
        Class clazz = Class.forName("com.atguigu.java.Person");
        //1. 调用私有的构造器:private Person(String name,int age)
        Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
        con.setAccessible(true);
        Person p1 = (Person) con.newInstance("Tom",12);
        System.out.println(p1);
        //2. 调用私有的属性:private String name;
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(p1,"Jerry");
        System.out.println(nameField.get(p1));
        //3. 调用私有的方法:private String showNation(String nation)
        Method showNationMethod = clazz.getDeclaredMethod("showNation", String.class);
        showNationMethod.setAccessible(true);
        String info = (String) showNationMethod.invoke(p1,"中国");
        System.out.println(info);
    }

    /*
    * 问题1:通过反射,也可以创建类的对象、调用方法等操作,那么跟以前的方式比,如何选择?
    *  使用频率上说:以前的方式更多,反射的方式很少。
    *  使用场景上说:大部分的场景中,创建的对象和要调用的方法都是确定的,所有都使用以前的方式。
    *               如果出现了不确定的对象的创建或方法的调用,此时才会考虑用反射。
    *
    * 问题2:反射打破了类的封装的特性?如何看待? 单例模式可能也不保?如何看待?
    * 不矛盾!
    * 类的封装性:建议我们是否要调用
    * 反射:体现我们是否能调用
    *
    * */
}

2. Class的理解

/**
 *
 * 1. 理解Class类
 * 2. 获取Class的实例
 * 3. 了解类的加载与类的加载器
 *
 *
 *  Class看做是反射的源头。
 *  java程序经过javac.exe编译之后,会生成一个或多个.class结尾的字节码文件。接着,我们使用java.exe 指令
 *  将指定.class文件对应的类加载到内存中。此类就称为运行时类。此运行时类本身就作为Class的一个实例。
 *  补充:此运行时类加载到方法区的缓存位置;运行时类的加载使用的是类的加载器。
 *
 *  换句话说:Class的一个实例就会对应一个加载到内存中的运行时类。(狭义)
 *
 *  说明:通常加载到方法区的运行时类只有一份,不会重复加载。
 *
 * @author shkstart
 * @create 10:28
 */
/*
    Class的实例都可以指向哪些结构呢?
   (1)class:
    外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
    (2)interface:接口
    (3)[]:数组
    (4)enum:枚举
    (5)annotation:注解@interface
    (6)primitive type:基本数据类型
    (7)void

* */
@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];
    System.out.println(a);
    Class c10 = a.getClass();
    Class c11 = b.getClass();
    //对于数组来说:只要元素类型与维度一样,就是同一个Class
    System.out.println(c10 == c11);

}

3. 获取Class的实例

//获取Class的实例(讲4种方式,需要大家掌握前3种方式)
@Test
public void test1() throws ClassNotFoundException {
    //方式1:调用运行时类的class属性
    Class<Person> clazz = Person.class;
    Class<String> clazz1 = String.class;

    //方式2:调用Class的静态方法:forName(String className)
    Class clazz2 = Class.forName("com.atguigu.java.Person");

    //方式3:调用对象的getClass()
    Person p = new Person();
    Class clazz3 = p.getClass();

    System.out.println(clazz == clazz2 && clazz == clazz3);

    //方式4:使用类的加载器
    Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.atguigu.java.Person");
    System.out.println(clazz == clazz4);

}

4. 了解类的加载过程及类的加载器

4.1 类的加载过程

在这里插入图片描述

4.2 了解类的加载器

在这里插入图片描述

/*
测试类的加载器
 */
@Test
public void test3() throws ClassNotFoundException {
    //自定义类使用的是系统类加载器进行的加载
    Class clazz1 = Class.forName("com.atguigu.java.Person");
    System.out.println(clazz1.getClassLoader());

    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    System.out.println(systemClassLoader);
    //java核心api使用的是引导类加载器。
    //引导类加载器不可以直接获取。
    Class clazz2 = String.class;
    System.out.println(clazz2.getClassLoader());

}

@Test
public void test4(){
    //获取系统类加载器
    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    System.out.println(systemClassLoader);
    //获取扩展类加载器
    ClassLoader parent = systemClassLoader.getParent();
    System.out.println(parent);
    //获取引导类加载器:获取失败
    ClassLoader parent1 = parent.getParent();
    System.out.println(parent1);
}
  • 如下的代码需要掌握
//需要掌握如下的代码
    @Test
    public void test5() throws IOException {
        Properties pros = new Properties();
        //方式1:此时默认的相对路径是当前的module
//        FileInputStream is = new FileInputStream("info.properties");
        FileInputStream is = new FileInputStream("src//info1.properties");

        //方式2:使用类的加载器
        //此时默认的相对路径是当前module的src目录
//        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("info1.properties");


        pros.load(is);

        //获取配置文件中的信息
        String name = pros.getProperty("name");
        String password = pros.getProperty("password");
        System.out.println("name = " + name + ", password = " + password);
    }

上述代码对应的配置文件内容及位置:

在这里插入图片描述

【info.properites】

name=Tom
password=abc123

【info1.properites】

name=Jerry
password=abc123

5. 反射的应用一:创建对应的运行时类的对象

  • 反射的应用一、二、三使用的大前提:
/**
 * @author shkstart
 * @create 14:07
 */
public class Creature<T> {

     boolean gender;
    public int id;

    public void breath(){
        System.out.println("呼吸");
    }
    private void info(){
        System.out.println("我是一个生物");
    }

}
package com.atguigu.java1;

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

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

/**
 * @author shkstart
 * @create 14:09
 */
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "abc";
}
package com.atguigu.java1;

/**
 * @author shkstart
 * @create 14:09
 */
public interface MyInterface {
    void method();
}
package com.atguigu.java1;

import java.util.ArrayList;

/**
 * @author shkstart
 * @create 9:35
 */
@MyAnnotation(value="hello")
public class Person extends Creature<String> implements Comparable<Person>,MyInterface{
    private String name;
    @MyAnnotation
    public int age;

    private static String info;

    public Person(){
//        System.out.println("Person()...");
    }

    protected Person(int age){
        this.age = age;
    }

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

    }
    @MyAnnotation
    public void show() throws RuntimeException,ClassNotFoundException{
        System.out.println("你好,我是一个Person");
    }

    private String showNation(String nation,int age){
        System.out.println("showNation...");
        return "我的国籍是:" + nation + ",生活了" + age + "年";
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

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

    @Override
    public void method() {

    }

    public static void showInfo(){
        System.out.println("我是一个人");
    }
}
/*
 * 测试反射的应用一:创建对应的运行时类的对象。
 *
 * @author shkstart
 * @create 11:31
 */
public class InstanceTest {

    @Test
    public void test1() throws IllegalAccessException, InstantiationException {
        //获取Class的实例,对应着一个运行时类
        Class clazz = Person.class;

        //如何创建Person类的对象
        /*
        * 要想保证newInstance()可以正常执行,必须:
        * ① 运行时类中必须提供空参的构造器
        * ② 空参的构造器的访问权限要够。不一定非得是public
        * */
        Person obj = (Person) clazz.newInstance();
        System.out.println(obj);
    }

}

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

  • 了解
package com.atguigu.java2;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.junit.Test;

import com.atguigu.java1.Person;

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);
//    }
      
      //getDeclaredFields():获取当前运行时类中声明的所有属性
      Field[] declaredFields = clazz.getDeclaredFields();
      for(Field f : declaredFields){
         System.out.println(f);
      }
      
      
   }
   
   //权限修饰符  变量类型  变量名
   @Test
   public void test2(){

      Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            //1.权限修饰符
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + "\t");

//            //2.数据类型
            Class type = f.getType();
            System.out.print(type.getName() + "\t");
//
//            //3.变量名
            String fName = f.getName();
            System.out.print(fName);
//
            System.out.println();
        }
   }
   
   
}
  • 了解
package com.atguigu.java2;

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

import org.junit.Test;

import com.atguigu.java1.Person;

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

      // getDeclaredMethods():获取当前运行时类中声明的所有方法
      Method[] declaredMethods = clazz.getDeclaredMethods();
      for (Method m : declaredMethods) {
         System.out.println(m);
      }

   }

   // 注解信息
   // 权限修饰符 返回值类型 方法名(形参类型1 参数1,形参类型2 参数2,...) throws 异常类型1,...{}
   @Test
   public void test2() {
      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);
         }

         // 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[] parameterTypes = m.getParameterTypes();
         if (!(parameterTypes == null && parameterTypes.length == 0)) {
            for (int i = 0; i < parameterTypes.length; i++) {

               if (i == parameterTypes.length - 1) {
                  System.out.print(parameterTypes[i].getName() + " args_" + i);
                  break;
               }

               System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
            }
         }

         System.out.print(")");

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

               System.out.print(exceptionTypes[i].getName() + ",");
            }
         }

         System.out.println();
      }
   }

}
  • 了解
package com.atguigu.java2;

import com.atguigu.java1.Person;
import org.junit.Test;

import java.lang.reflect.Constructor;

/**
 * @author shkstart
 * @create 14:31
 */
public class ConstructorTest {
    @Test
    public void test1(){
        Class clazz = Person.class;

        //getConstructors():获取当前运行时类中声明为public权限的构造器
//        Constructor[] cons1 = clazz.getConstructors();
//        for(Constructor c : cons1){
//            System.out.println(c);
//        }


        //getDeclaredConstructors():获取当前运行时类中所有的构造器
        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for(Constructor c : cons2){
            System.out.println(c);
        }
    }
}
  • 掌握
package com.atguigu.java2;

import com.atguigu.java1.Person;
import org.junit.Test;

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

/**
 * @author shkstart
 * @create 14:35
 */
public class OtherTest {

    //获取运行时类的父类
    @Test
    public void test1(){
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

    //获取带泛型的父类
    @Test
    public void test2(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

    //获取父类的泛型 
    @Test
    public void test3(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType type = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = type.getActualTypeArguments();//获取泛型的参数

        System.out.println(((Class)actualTypeArguments[0]).getName());
    }

    //获取实现的接口
    @Test
    public void test4(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class i : interfaces){
            System.out.println(i);
        }
    }

    //获取所在的包
    @Test
    public void test5(){
        Class clazz = Person.class;

        Package package1 = clazz.getPackage();
        System.out.println(package1.getName());

    }
	//获取注解
    @Test
    public void test6(){
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation a : annotations){
            System.out.println(a);
        }
    }
}

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

  • 重点掌握
package com.atguigu.java2;

import com.atguigu.java1.Person;
import junit.extensions.TestSetup;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 反射的应用三:调用对应的运行时类的指定的结构:属性、方法、构造器
 *
 * @author shkstart
 * @create 15:05
 */
public class ReflectionTest {

    //调用指定的属性
    @Test
    public void test1() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class clazz = Person.class;

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

        //public int age;
        //getField(String fieldName):可以获取当前运行时类中声明为public权限的属性。
//        Field ageField = clazz.getField("age");
//        //设置一个值
//        ageField.set(p,10);
//        //获取此值
//        System.out.println(ageField.get(p));

        //private String name;
        //使用反射获取指定属性并调用的通用的操作步骤:

        //第1步:getDeclaredField(String fieldName):获取当前运行时类中声明为指定名称的属性
        Field nameField = clazz.getDeclaredField("name");
        //第2步:设置此属性是可访问的
        nameField.setAccessible(true);
        //第3步:针对于属性的设置和调用的过程
        //设置一个值
        nameField.set(p,"Tom");
        //获取此值
        System.out.println(nameField.get(p));

    }

    //调用方法
    @Test
    public void test2() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Person.class;

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

        //使用反射调用指定的方法
        //private String showNation(String nation,int age)
        //1. getDeclaredMethod(String methodName,Class ... paramTypes):获取运行时类中的指定的方法
        Method showNationMethod = clazz.getDeclaredMethod("showNation", String.class, int.class);

        //2.设置此方法是可访问的
        showNationMethod.setAccessible(true);
        //3.调用此方法
        //invoke()方法的返回值即为要调用的方法的返回值。
        String returnValue= (String) showNationMethod.invoke(p,"CHN",20);
        System.out.println(returnValue);
    }

    //调用当前运行时类中静态的结构:属性、方法
    @Test
    public void test3() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Person.class;
        //调用指定的属性:
        //private static String info;
        //1.
        Field infoField = clazz.getDeclaredField("info");
        //2.
        infoField.setAccessible(true);
        //3.1
//        infoField.set(Person.class,"人");
        infoField.set(null,"人");
        //3.2
//        System.out.println(infoField.get(Person.class));
        System.out.println(infoField.get(null));

        //调用指定的方法
        //public static void showInfo()
        Method showInfoMethod = clazz.getDeclaredMethod("showInfo");
        showInfoMethod.setAccessible(true);
        Object returnValue = showInfoMethod.invoke(null);
        System.out.println(returnValue);

    }

    //调用指定的构造器
    @Test
    public void test4() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = Person.class;


        //private Person(String name, int age)
        //1. getDeclaredConstructor(Class ... args):获取指定参数的构造器
        Constructor con = clazz.getDeclaredConstructor(String.class, int.class);

        //2. 保证此构造器是可访问的
        con.setAccessible(true);

        //3. 调用此构造器,创建运行时类的对象
        Person p = (Person) con.newInstance("Tom",12);
        System.out.println(p);

        //使用如下的操作,替换原有的Class调用newInstance():
        //public Person()
        Constructor con1 = clazz.getDeclaredConstructor();
        con1.setAccessible(true);
        Person p1 = (Person) con1.newInstance();
        System.out.println(p1);
    }
}
的构造器
    @Test
    public void test4() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = Person.class;


        //private Person(String name, int age)
        //1. getDeclaredConstructor(Class ... args):获取指定参数的构造器
        Constructor con = clazz.getDeclaredConstructor(String.class, int.class);

        //2. 保证此构造器是可访问的
        con.setAccessible(true);

        //3. 调用此构造器,创建运行时类的对象
        Person p = (Person) con.newInstance("Tom",12);
        System.out.println(p);

        //使用如下的操作,替换原有的Class调用newInstance():
        //public Person()
        Constructor con1 = clazz.getDeclaredConstructor();
        con1.setAccessible(true);
        Person p1 = (Person) con1.newInstance();
        System.out.println(p1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值