Java反射篇

28 篇文章 0 订阅
26 篇文章 0 订阅
本文详细介绍了Java反射的概念,包括动态语言与静态语言的区别,反射的优缺点,Class类的创建方式,以及如何通过反射获取类信息、执行方法、操作泛型和注解。此外,还探讨了类的加载过程、双亲委派机制以及如何打破这一机制。通过实例展示了反射在性能上的影响,以及如何提高反射效率。
摘要由CSDN通过智能技术生成

Java反射篇

动态语言与静态语言的区别?

动态语言:在运行时代码可以根据某些条件改变自身结构。举例:JavaScript, PHP, Python。

静态语言:运行时结构不可变的语言就是静态语言。举例:Java, C, C++。

说明:由于反射机制的存在,Java可以称为“准动态语言”。

1.反射的定义

Java 反射,就是在运行状态中:
获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等;
获取任意对象的属性,并且能改变对象的属性;
调用任意对象的方法;
判断任意一个对象所属的类;
实例化任意一个类的对象。

2.反射的优缺点

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

缺点:对性能有影响。

3.Class类的创建方式

  1. 通过对象的getClass()。
  2. 类名.class。
  3. Class.forName("")。
package com.zx.reflection;

//测试Class类的创建方式有哪些
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:"+person.name);

        //方式一:通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        //方式二:Class.forName()
        Class c2 = Class.forName("src下的包名.Student");//类似JDBC的加载驱动
        System.out.println(c2.hashCode());

        //方式三:类名.class
        Class c3 = Student.class;
        System.out.println(c3.hashCode());
    }
}

class Person{
    public String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '}';
    }
}

class Student extends Person{
    public Student(){
        this.name = "学生";
    }
}

class Teacher extends Person{
    public Teacher(){
        this.name = "老师";
    }
}

注意:c1, c2, c3的hashCode()都一样,说明加载的类在JVM中只有一个Class实例。

4.Class对象适用的类型

创建形式类型
Object.classObject类
Comparable.class接口
String[].class一维数组
int[][].class二维数组
Override.class注解
ElementType.class枚举
Integer.class基本数据类型
void.classvoid
Class.classClass本身

5.类的加载过程

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

类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。

类的链接:将类的二进制数据合并到JRE中。

类的初始化:JVM负责对类进行初始化。

6.类的初始化的条件

类的主动引用一定会发生类的初始化:

  1. 虚拟机启动,先初始化main方法所在的类
  2. new一个类的对象
  3. 调用类的静态成员和静态方法
  4. 对类进行反射调用
  5. 若父类没被初始化,会先初始化父类

类的被动引用不会发生类的初始化:

  1. 通过子类调父类的静态变量,不会导致子类初始化
  2. 通过数组定义类引用,不会触发此类的初始化
  3. 引用常量不会触发此类的初始化
/**
 * 测试类什么时候初始化
 */
public class Test {
    static {
        System.out.println("Main被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //主动引用 优先级:Main>父类>子类
        //Son son = new Son();

        //反射 Main>父类>子类
        //Class c1 = Class.forName("包名.Son");

        //子类调父类静态变量,子类不会被加载
        //System.out.println(Son.b);

        //只有Main类被加载
        //Son[] arrays = new Son[5];

        //只有Main类被加载,子类中final修饰的常量被调用子类不会被加载
        // System.out.println(Son.M);
    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");
        m = 300;
    }
    static int m = 100;
    static final int M = 1;
}

7.如何获得要加载类的信息

  1. 首先创建一个Class对象,用上面提到的三种方式的一种。假如该对象为c1。
  2. 用c1调方法。
方法说明
c1.getName()获得包名+类名
c1.getSimpleName()获得类名
c1.getFields()获得public修饰的属性
c1.getDeclaredFields()获得所有属性(public,private…)
c1.getDeclaredField("")获得指定属性的value
c1.getMethods()获得本类和父类被public修饰的方法
c1.getDeclaredMethods()获得本类的所有方法包括私有
c1.getMethod(“方法名”,参数类型 )获得指定的方法
c1.newInstance()获取实例(本质是调用了无参构造器)

8.执行效率比较

public class Test{
    public static void main(String[] args) throws Exception{
        test01();
        test02();
        test03();
    }
    public static void test01(){
        User user = new User();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println("普通方法:"+(end-begin)+"ms");
    }

    public static void test02() throws Exception {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);//激活(执行)该方法
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方式:"+(end-begin)+"ms");
    }

    public static void test03() throws Exception {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);//关闭访问安全检查
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);//激活(执行)该方法
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方式(关闭访问安全检查):"+(end-begin)+"ms");
    }
}
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

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

结论:普通方法 > 反射(关闭Java访问安全检查) > 反射

setAccessible(true): 不论是属性还是方法都有该方法,关闭Java访问安全检查,提高反射效率

9.Java内置的三个加载器

  1. 引导类加载器
  2. 扩展类加载器
  3. 应用程序加载器

双亲委派机制:通过以上三个加载器类之间的互相调用来实现的。

双亲委派机制大致内容:
在加载区域类三找要加载的类,优先级为应用程序加载器 > 扩展类加载器 > 引导类加载器,若均没找到,在类路径下三找要加载的类,优先级为引导类加载器 > 扩展类加载器 > 应用程序加载器,若均没找到,则报异常。

优点:可以防止核心API被篡改;避免类被重复加载。
如何打破:通过自定义加载器,重写ClassLoader类中的loadClass方法来打破双亲委派机制。

10.反射操作泛型

//通过反射操作泛型
public class Test {
    public static void main(String[] args) throws Exception {
        Method method = Test.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 = Test.class.getMethod("test02", null);
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
    public void test01(Map<String,User> map, List<User> list){
        System.out.println("test01");
    }

    public Map<String,User> test02(){
        System.out.println("test02");
        return null;
    }
}

11.反射操作注解

//反射操作注解
public class Test {
    public static void main(String[] args) throws Exception{
        Class c1 = Class.forName("包名.Student");

        //通过反射获得注解
        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);

        //获得类指定字段的注解的value的值
        Field f = c1.getDeclaredField("name");
        Filedtest annotation = f.getAnnotation(Filedtest.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }

}


@Tabletest("db_student")
class Student{
    @Filedtest(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Filedtest(columnName = "db_age",type = "int",length = 10)
    private int age;
    @Filedtest(columnName = "db_name",type = "varchar",length = 3)
    private String name;

    public Student() {
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tabletest{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filedtest{
    String columnName();
    String type();
    int length();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值