Java反射,从0开始

目录

静态VS动态语言

Java Reflection(反射)介绍

Java反射机制提供的功能

Java反射优点和缺点

反射相关的主要API

获取运行时类的完整结构

反射的使用

有了Class对象,如何创建对象?

使用反射调用对象的指定方法

Object invoke(Object obj, Object[] args)

setAccessible

性能检测

反射操作泛型

反射操作注解


静态VS动态语言

动态语言:

    是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗短说就是在运行时代码可以根据某些条件改变自身结构。

    主要动态语言:Object-C、C#、JavaScript、PHP、Python等。

静态语言:

    与动态语言相对应的,运行时结构不可变的语言就是静态语言,如Java、C、C++。

    Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。

Java Reflection(反射)介绍

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

    Class c = Class.forName("java.lang.String")

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

Java反射机制提供的功能

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

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

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

    在运行时获取泛型信息。

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

    在运行时处理注解。

    生成动态代理。

    ......

Java反射优点和缺点

    优点:可以实现动态创建对象和编译,体现出很大的灵活性。

    缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

反射相关的主要API

    java.lang.Class:代表一个类

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

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

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

    ......

获取运行时类的完整结构

    通过反射获取运行时类的完整结构:Field、Method、Constructor、Superclass、Interface、Annotation。

    实现的全部接口、所继承的父类、全部的构造器、全部的方法、全部的Field、注解等等。

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

//获取类的信息
public class Test06 {

    public static void main(String[] args) throws Exception {

        Class c1 = Class.forName("com.myUtils.reflectUtils.learn.MyUser");
        MyUser user = new MyUser();
        c1 = user.getClass();
        //获得类的名字
        System.out.println(c1.getName());//包名 + 类名
        System.out.println(c1.getSimpleName());//获得类名
        //获得类的属性
        Field[] fields = c1.getFields();//获取所有(只能找到public的属性)属性
        fields = c1.getDeclaredFields();//获取所有(找到全部的属性)属性
        Field id = c1.getDeclaredField("id");//获取指定属性
        //获得类的方法
        Method[] methods = c1.getMethods();//获取所有(本类及其父类public的)方法
        methods = c1.getDeclaredMethods();//获取所有(本类全部的)方法
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);//需要参数类型(重载问题)
        //获得构造器
        Constructor[] constructors = c1.getConstructors();
        Constructor constructor = c1.getConstructor(null);//也需要参数类型
    }
}

class MyUser{
    public Integer id;
    public String name;
    private int age;

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

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

反射的使用

有了Class对象,如何创建对象?

创建类的对象:调用Class对象的newInstance()方法:

    1.类必须有一个无参数的构造器。

    2.类的构造器的访问权限需要足够。

没有无参构造器、构造器权限不足的情况创建对象:

    1.通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器。

    2.向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

    3.通过Constructor实例化对象。

使用反射调用对象的指定方法

通过反射,调用类中的方法,通过Method类完成。

    1.通过Class类的getMethod(String name,Class ... parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

    2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

Object invoke(Object obj, Object[] args)

    Object对应原方法的返回值,若原方法无返回值,此时返回null。

    若原方法为静态方法,此时形参Object obj可为null。

    若原方法形参列表为空,则Object[] args为null。

    若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法。将可访问private的方法。

setAccessible

    Method和Field、Construetor对象都有setAccessible()方法。

    setAccessible作用是启动和禁用访问安全检查的开关。

    参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

        提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true;

        使得原本无法访问的私有成员也可以访问。

    参数值为false则指示反射的对象应该实施Java语言访问检查。

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

//通过反射创建对象
public class Test07 {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("com.myUtils.reflectUtils.learn.MyUser07");
        //构造一个对象
        MyUser07 user = (MyUser07) c1.newInstance();//实际上调用了无参构造器
        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(Integer.TYPE, String.class);
        user = (MyUser07) constructor.newInstance(5, "张三");
        //通过反射调用方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        setName.invoke(user, "李四");//(对象,参数)
        System.out.println(user.getName());
        //通过反射操作属性
        Field name = c1.getDeclaredField("name");
        name.setAccessible(true);//设置为true就可以获取到私有属性、方法(关闭程序的安全监测)
        name.set(user, "王五");
        System.out.println(name.get(user));
    }
}
class MyUser07{
    private Integer id;
    private String name;

    public MyUser07(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 

性能检测

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

public class Test08 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        /**
         * 普通时17ms
         * 反射耗时3398ms
         * 关闭检测耗时1657ms
         */
        test1();
        test2();
        test3();
    }

    //普通方式
    public static void test1(){
        User08 user08 = new User08();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user08.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通时" + (endTime - startTime) + "ms");
    }
    //反射方式
    public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User08 user08 = new User08();
        Class c1 = user08.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user08, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射耗时" + (endTime - startTime) + "ms");
    }
    //反射方式,关闭检测
    public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User08 user08 = new User08();
        Class c1 = user08.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user08, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("关闭检测耗时" + (endTime - startTime) + "ms");
    }
}
class User08{

    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

反射操作泛型

    Java采用泛型擦除的机制来引入泛型,Java的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。

    为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType集中类型来代表不能被归一到class类中的类型但是又和原始类型齐名的类型。

ParameterizedType:表示一种参数化类型,比如Collection<String>。

GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。

TypeVariable:是各种类型变量的公共父接口。

WildcardType:代表一种通配符类型表达式。

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

//通过反射获取泛型
public class Test09 {
    public void test01(Map<String, User> map, List<Integer> list){
        System.out.println("test01");
    }
    public Map<String, User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //获取方法参数泛型
        Method method = Test09.class.getMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("#"+genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }


            }
        }
        //获取返回值泛型
        Method method2 = Test09.class.getMethod("test02", null);
        Type genericReturnType = method2.getGenericReturnType();//获取返回类型
            System.out.println("#"+genericReturnType);
            if(genericReturnType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
        }
    }
}

反射操作注解

import java.lang.annotation.*;
import java.lang.reflect.Field;


public class Test10 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.myUtils.reflectUtils.learn.Student10");
        //通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得注解的value的值
        TableTest tableTest = (TableTest)c1.getAnnotation(TableTest.class);
        String value = tableTest.value();
        System.out.println(value);//db_student
        //获得类指定的注解
        Field f = c1.getField("name");
        FieldTest annotation = f.getAnnotation(FieldTest.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}


@TableTest("db_student")
class Student10{
    @FieldTest(columnName = "db_id", type = "int", length = 10)
    private Integer id;
    @FieldTest(columnName = "db_name", type = "varchar", length = 20)
    private String name;


    public Student10() {
    }
    public Student10(Integer id, String name) {
        this.id = id;
        this.name = name;
    }


    public Integer getId() {
        return id;
    }


    public void setId(Integer id) {
        this.id = id;
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }
}


//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableTest{
    String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest{
    String columnName();
    String type();
    int length();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了也弱了。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值