Java的反射机制

我们先来看看反射能干什么,不要求理解

一、反射的作用

这是我们要操作的Person类

public class Person {
    private String name;
    public  int age;
    public Person() {
    }

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

    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 +
                '}';
    }
    public void show(){
        System.out.println("我是一个人");
    }
    private String showNation(String nation){
        System.out.println("我的国籍是:"+nation);
        return nation;
    }
}

然后我们可以通过反射可以去创建对象,调取对象的指定属性和方法,还可以调私有的,这是以前不能实现的,具体看下代码
你可以测试一下,感受一下

import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectTest {
    @Test
    public void test1(){
        //没有反射之前我们是不能去使用类的私有结构
        Person p1=new Person();
        p1.age=10;
        System.out.println(p1);
        p1.show();
    }
    @Test
    public void test2() throws Exception{
        Class clazz=Person.class;
        //1、通过反射创建一个类的对象
        Constructor cons=clazz.getConstructor(String.class,int.class);
        Object obj=cons.newInstance("Tom",12);
        Person p2= (Person) obj;
        System.out.println(p2);

        //2、通过反射,调取对象指定属性和方法
        //调用属性
       Field age= clazz.getDeclaredField("age");
       age.set(p2,10);
        System.out.println(p2);
        //调用方法
        Method show=clazz.getDeclaredMethod("show");
        show.invoke(p2);

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

        //3、通过反射调用私有的构造器和方法。属性
        //调用私有构造器
        Constructor cons2=clazz.getDeclaredConstructor(String.class);
        cons2.setAccessible(true);
        Person p1= (Person) cons2.newInstance("Merry");
        System.out.println(p1);

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

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

    }

看完之后,你肯定然后会有两个疑问
1、什么时候用反射?
不确定要调用那个类,就是需要动态性的时候使用反射
2、怎么看反射可以调用私有的结构,是否与封装性矛盾?
不矛盾,反射能不能调的问题,封装是建议你去不去调的问题

二、关于java.Lang.Class类的理解

1、类的加载过程:
程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾)。
接着我们用java.exe命令对某个字节码文件进行解释运行,相当于把某个字节码
文件加载到内存中,此过程称为类的加载,加载到内存中的类,我们就称为运行时类,
此运行时类,就作为Class的一个实例。也可以理解为,运行时类是Class的一个对象,
也就是万事万物皆对象。无极之外复无极也。
2、加载到内存中的运行时类,会缓存一段时间,在此时间之内,我们可以通过不同的方式来
获取此运行时类。

*/
    //获取Class类的实例的方式
    @Test
    public void test3()throws Exception{
        //方式一:调用运行时类的属性
        Class clazz1=Person.class;
        System.out.println(clazz1);
        //方式二:通过调用运行时类的对象,调用getClass()
        Person p=new Person();
        Class clazz2=p.getClass();
        System.out.println(clazz2);
        //方式三:通过调用Class类的静态方法:forname(String classpath)
        Class clazz3=Class.forName("com.guo.java.Person");
        System.out.println(clazz3);

        System.out.println(clazz1==clazz2);
        //方式四:使用类的加载器:ClassLoader
        ClassLoader classLoader=ReflectTest.class.getClassLoader();
        Class clazz4=classLoader.loadClass("com.guo.java.Person");
        System.out.println(clazz4);
    }

    //Class实例可以是哪些结构的说明:
    /*
    1)、class(外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类)
    2)、interface(接口)
    3)、[](数组)
    4)、enum(枚举)
    5)、annotation(注解@interface)
    6)、primitive type(基本数据类型)
    7)void
     */
    @Test
    public void test4(){
        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);
    }

}

下面是一个体会反射动态性的一个例子,随机创建一个类的对象,test1()是创建对应运行时类的对象,test2()是通过getInstance()方法来随机创建一个运行时的对象,就不像以前那样需要传入一个具体的对象来创建了。

import org.junit.Test;

import java.util.Random;

/*
通过反射创建对应运行时的类的对象
 */
public class NewInstanceTest {
    @Test
    public void test1()throws Exception{
        Class<Person> clazz=Person.class;
        //调用newInstance()方法创建对应运行时的类的对象,内部是调用了运行时类的空参构造器
        /*
        下方法正常创建对象的要求是:
        1、运行是类有空参的构造器
        2、要求空参的构造器必须访问权限得够,通常设置为public

        在javabean中要求提供一个空参的构造器,原因:
        1,便于通过反射创建运行时类的对象,
        2,便于子类继承父类时,默认调用super时,保证父类有此构造器
         */
        Person obj=clazz.newInstance();
        System.out.println(obj);
    }

    //体会反射的动态性
    @Test
    public void test2(){
        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="com.guo.java.Person";
                break;
        }
        try {
            Object obj=getInstance(classpath);
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Object getInstance(String classpath)throws Exception{
        Class clazz= Class.forName(classpath);
        return clazz.newInstance();
    }
}

三、类加载器的使用

类加载器的作用:
将class文件字节码内容加载到内存中,并将这些静态数据转化成方法区的运行时的数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:
标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收这些Class对象。
类加载器作用是用来把类(class)装载进内存的,JVM规范定义了如下类型的类加载器
引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,无法被直接获取。
扩展类加载器:负责jre/lib/ext目录下的jar包或…D java.ext.dirs指定目录下的jar包装入工作库。
系统类加载器:负责java—classpath或…D java class.path所指的目录下的类与jar包装入工作,是常用的类加载器。
下面我们来看类加载器代码的具体使用,还有一个Properties配置文件的使用

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Properties;

//类加载器的使用
public class ClassLoaderTest {
    @Test
    public void test1(){
        //对于自定义的类,使用系统类加载器进行加载
        ClassLoader classLoader=ClassLoaderTest.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);
    }

    /*
    Properties:用来读取配置文件

     */
    @Test
    public void test2 ()throws Exception{
        Properties pros=new Properties();
        //此时文件默认在module下
        //读取配置文件方式一:
//        FileInputStream fis=new FileInputStream("jdbc.properties");
//        pros.load(fis);

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

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

    }
}

四、通过反射获取运行时类的完整结构

还是来一个Person的操作类,这个类创建的比较全面,便于反射的操作能面面俱到。
父类:

import java.io.Serializable;

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

    private void  breath(){
        System.out.println("生物呼吸");
    }
    public void eat(){
        System.out.println("生物吃东西");
    }

}

接口:

public interface Myinterface {
    void info();
}

注解:

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;

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

}

子类

public class Person extends Creature<String> implements Comparable<String>,Myinterface{
    private  String name;
    int age;
    public int id;

    public Person() {
    }
    @MyAnnotation(value = "abc")
    private Person(String name) {
        this.name = name;
    }
     Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @MyAnnotation
    private String show(String nation){
        System.out.println("我的国籍是:"+nation);
        return nation;
    }
    public String display(String insterests){
        return  insterests;
    }

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

    @Override
    public int compareTo(String o) {
        return 0;
    }
    private static void showDesc(){
        System.out.println("我是一个可爱的人");
    }

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

获取当前运行时类的属性结构

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

import java.io.File;
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();

         //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] df=clazz.getDeclaredFields();
        for (Field f:df){
            System.out.println(f);
        }
    }

    //权限修饰符,数据类型,变量名
    @Test
    public void test2(){
        Class clazz = Person.class;
        Field[] df=clazz.getDeclaredFields();
        for (Field f:df){
            //1、权限修饰符
            int modifiers=f.getModifiers();
            System.out.print(Modifier.toString(modifiers)+"\t");

            //2、数据类型
            Class type=f.getType();
            System.out.print(type.getName()+"\t");

            //3、变量名
            String fname=f.getName();
            System.out.println(fname);
        }
    }

}

获取运行时类的方法结构

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

import java.lang.reflect.Method;

//获取运行时类的方法结构
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[] methods1=clazz.getDeclaredMethods();
        for (Method m:methods1){
            System.out.println(m);
        }

    }

    //可以获取注解,权限修饰符,返回值类型,方法名(参数)throes xxxException{},与属性一样的调用相应方法
    //就行,查资料就可以了。。。。

}

获取其他的一些构造

import com.guo.java1.Person;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Annotated;
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 {
    //获取构造器
    @Test
    public void test1(){
        Class clazz= Person.class;

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

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

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

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

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

        Type genericSuperclass=clazz.getGenericSuperclass();
        ParameterizedType parameType=(ParameterizedType)genericSuperclass;
        //获取泛型类型
        Type[] actualType=parameType.getActualTypeArguments();
//        System.out.println(actualType[0].getTypeName());
        System.out.println(((Class)actualType[0]).getName());

    }

    //获取运行时类实现的接口
    @Test
    public void test4(){
        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);
        }
    }

    //获取运行时类所在的包
    @Test
    public void test5(){
        Class clazz=Person.class;
        Package Package = clazz.getPackage();
        System.out.println(Package);
    }

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

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

}

五、通过反射来调用运行时类的指定结构(重点)

具体看代码,都是一些实现的方法,在后面的框架学习中,都是用反射,注解,设计模式的组合来实现的,因为框架的复用性,所以在类不确定的情况下,我们可以运用反射机制来实现你运行中产生类的具体属性,方法操作。

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

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

//调用运行时类的指定结构:属性,方法,构造器
public class ReflectionTest {

    //获取指定属性方式一(不理想,不常用)
    @Test
    public void testField()throws Exception{
        Class clazz= Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        //获取指定的属性:要求运行时类中的属性为public
        Field id = clazz.getField("id");
        //设置指定属性的值
        //set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
        id.set(p,1001);
        //获取当前属性的值
        //get():参数1:获取哪个对象的当前属性值
        int pid=(int)id.get(p);
        System.out.println(pid);
    }

    //获取指定属性方式二(开发中都是使用这种)
    @Test
    public void testField1()throws Exception{
        Class clazz= Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
        //获取指定的属性:不需要运行时类中的属性为public
        Field name = clazz.getDeclaredField("name");
        //提高name的权限,保证当前属性可以被访问
        name.setAccessible(true);
        //设置指定属性的值
        //set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
        name.set(p,"tom");
        //获取当前属性的值
        //get():参数1:获取哪个对象的当前属性值
        String pid=(String)name.get(p);
        System.out.println(pid);
    }

    //如何操作运行时类的指定方法(重点)
    @Test
    public void testMethod()throws Exception{
        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 obj=show.invoke(p,"CHN");
        System.out.println(obj);

        System.out.println("===============如何调用静态方法===================");
        //private static void showDesc(){}
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        //如果调用的类没有返回值,则返回Null
        showDesc.setAccessible(true);
        Object o=showDesc.invoke(Person.class);
        System.out.println(o);
    }

    //调用指定类的构造器
    @Test
    public void testConstruct()throws Exception{
        Class clazz= Person.class;
        //private Person(String name)
        //1、getDeclaredConstructor():参数1:指明构造器的参数列表
        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
        //2、保证此构造器是可访问的
        declaredConstructor.setAccessible(true);
        //3、调用此构造器创建运行时类的对象
        Person p=(Person) declaredConstructor.newInstance("tom");
        System.out.println(p);



    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值