反射机制的基本使用

一、动态代理的思想分析

在这里插入图片描述

二、反射机制的引出

一个需求引出反射

问题:
1.根据配置文件re.propertes指定信息,创建Cat对象,并调用方法hi
classfullpath=com.dadao.Cat
method=hi

这样的需求在学习框架的时候特别多,即通过外部文件配置在不修改源码的情况下,来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,可以实现扩容功能)

演示:
re.propertes文件 :这是src下的文件

classfullpath=com.dadao.Cat
method=cry

Cat对象:这是com.dadao包下的一个类

package com.dadao;
public class Cat {
    private String name="招财猫";
    public int age=10;
    public Cat(){}
    public Cat(String name){
        this,name=name;
    }
    public void hi(){
        System.out.println("hi "+name);
    }
    public void cry(){
        System.out.println(name+" 喵喵叫");
    }
    public void show(){}//用于后面测试反射机制的优化
}

核心部分:这是com.dadao包下的reflection 包下的一个类

package com.dadao.refletion;
import com.dadao.Cat;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;


public class question {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {


        //根据re.properties文件指定信息,创建Cat对象并调用方法hi

//        传统方法 new对象->调用方法
//        Cat cat = new Cat();
//        cat.hi();  --》cat.cry();  //要修改源码


//        明白反射
//        1.使用Properties 类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath=properties.get("classfullpath").toString();
        String methodName=properties.get("method").toString();
        System.out.println(classfullpath);
        System.out.println(methodName);

//        2.创建对象,传统的方式不行--》反射机制
//        new classfullpath();//classfullpath是一个字符串

//        3.使用反射机制解决
//        (1)加载类
        Class cls = Class.forName(classfullpath);
//        (2)通过cls对象得到加载的类com.dadao.Cat的对象实例
        Object o = cls.newInstance();
//        (3)通过cls 得到你加载的类 com.dadao.Cat 的methodName"hi" 的方法对象
//        即在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
//        通过method1 调用方法:即通过方法对象来实现调用方法
        System.out.println("=====================================");
        method1.invoke(o);  //传统方法  对象.方法(),  反射机制   方法.invoke(对象);
    }
}

运行结果:

com.dadao.Cat
cry
=====================================
招财猫 喵喵叫

三、反射机制

  1. 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法,反射在设计模式和框架的底层都会用到。
  2. 加载完类后,在堆中就产生了一个Class类型的对象,(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到的类的结构。这个Class对象就像一面镜子,透过这面镜子看到类的结构,所以形象地成为“反射”。
    执行流程图
    在这里插入图片描述

三、1、反射相关的主要类

  1. 在java.lang.reflection类中,有以下几个重要的类:
      1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
      1. java.lang.reflect.Method:表示类的方法,Method对象表示某个类的方法
      1. java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
      1. java.lang.reflect.Constructor:代表类的构造器方法,Constructor对象表示构造器

代码演示:(前面部分和上面的代码演示是一样的)

package com.dadao.refletion;
import com.dadao.Cat;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;


public class question {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {


        //根据re.properties文件指定信息,创建Cat对象并调用方法hi

//        传统方法 new对象->调用方法
//        Cat cat = new Cat();
//        cat.hi();  --》cat.cry();  //要修改源码


//        明白反射
//        1.使用Properties 类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath=properties.get("classfullpath").toString();
        String methodName=properties.get("method").toString();
        System.out.println(classfullpath);
        System.out.println(methodName);

//        2.创建对象,传统的方式不行--》反射机制
//        new classfullpath();//classfullpath是一个字符串

//        3.使用反射机制解决
//        (1)加载类
        Class cls = Class.forName(classfullpath);
//        (2)通过cls对象得到加载的类com.dadao.Cat的对象实例
        Object o = cls.newInstance();
//        (3)通过cls 得到你加载的类 com.dadao.Cat 的methodName"hi" 的方法对象
//        即在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
//        通过method1 调用方法:即通过方法对象来实现调用方法
        System.out.println("=====================================");
        method1.invoke(o);  //传统方法  对象.方法(), 反射机制   方法.invoke(对象);


System.out.println("=========================看这里==================================");
//        getField()方法不能得到私有的属性
        Field nameField = cls.getField("age");
        System.out.println(nameField.get(o));

//        构造器中()可以指定构造器参数类型
        Constructor constructor = cls.getConstructor();
        System.out.println(constructor);
//    这里的String.class 就是String 类的Class对象
        Constructor constructor1 = cls.getConstructor(String.class);
        System.out.println(constructor1);
    }
}

运行结果:

com.dadao.Cat
cry
=====================================
招财猫 喵喵叫
10
public com.dadao.Cat()
public com.dadao.Cat(java.lang.String)

反射的优缺点

  1. 优点:可以动态地创建和使用对象(也是框架底层的核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
  2. 缺点:使用反射基本是解释执行,对执行速度有影响。

反射调用的优化

1.Method和Field、Cinstructor对象都有setAccessible()方法
2.setAccessible()方法作用是启动和禁止访问安全检查的开关
3.参数值为true表示 反射的对象在使用时间 取消 访问检查,提高反射的效率。参数为false则表示反射的对象 执行 检查访问

代码演示:

package com.dadao.refletion;

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

public class question02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        m1();
        m2();

    }

    public static void m1() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class cls = Class.forName("com.dadao.Cat");
        Object o = cls.newInstance();
        Method show = cls.getMethod("show");
        long start=System.currentTimeMillis();
        for (int i=0;i<900000000;i++){
            show.invoke(o);
        }
        long end =System.currentTimeMillis();
        System.out.println("m1()耗时"+(end-start));
    }

//    反射机制的优化+关闭访问检查
        public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
                Class cls = Class.forName("com.dadao.Cat");
                Object o = cls.newInstance();
                Method show = cls.getMethod("show");
                show.setAccessible(true); //取消访问检查
                long start=System.currentTimeMillis();
                for (int i=0;i<900000000;i++){
                    show.invoke(o);
                }
                long end =System.currentTimeMillis();
                System.out.println("m2()耗时"+(end-start));
          }
}

运行结果:

m1()耗时1500
m2()耗时843
//注意,这里的时间是会变化的

三、1.1、Class类

基本介绍

  1. Class也是类,因此也继承Object类
  2. Class类不是new出来的,而是由系统创建的
  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
  4. 每个类的实例都可以辨别自己是哪个Class类生成的
  5. 通过Class类可以完整地得到一个类的完整结构,通过一系列的API
  6. Class对象是存放在堆内存上面的
  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据。

代码:

package com.dadao.refletion;

import com.dadao.Cat;

public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {
//        1.传统方法new对象
//        Cat cat = new Cat();
//        源码:
        /*
        public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
        }
         */

//        2.反射方式:
        Class aClass = Class.forName("com.dadao.Cat");
//        源码:
        /*
        public Class<?> loadClass(String name) throws ClassNotFoundException {
             return loadClass(name, false);  //name="com.dadao.Cat";
        }

         */
//        总结:1.传统方法和反射方法创建Class类的方法都是调用了同一个代码的
//             2.如果在测试反射机制的时候,上面的 Cat cat = new Cat();
//               没有被注销,那么就会查找不到ClassLoader下的源码,
//               因为对于某个类的Class对象,在内存中只会执行一次,因为类只加载一次
        /*public Class<?> loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false);  //name="com.dadao.Cat";
         } */
    }
}

三、1.2、Class类的常用方法

在com.dadao包下创建一个新类Car

package com.dadao;

public class Car {
    public String brand="宝马";
    public int price=5000000;
    public String color="白色";

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

用代码来演示方法的使用:

package com.dadao.refletion.class_;

import com.dadao.Car;

import java.lang.reflect.Field;

//演示class类的常用方法
public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        String classAllPath="com.dadao.Car";
//       1. 获取到Car类对应的Class对象
//        <?> 表示不确定的java类型
        Class<?> cls = Class.forName(classAllPath);

        //2.输出cls
        System.out.println(cls);//显示cls对象,是哪个类的Class对象 com.dadao.Car
        System.out.println(cls.getClass());//输出cls的运行类型:java.lang.Class

//        3.得到包名
        System.out.println(cls.getPackage().getName()); //包名

//        4.得到类名
        System.out.println(cls.getName());

//        5.通过cls创建对象实例
        Object car = (Car)cls.newInstance();  //这个就是一个实实在在的Car对象
        System.out.println(car);//car.toString
//        6.通过反射获取属性 brand,如果这个brand属性是私有的,下面一行语句就会有问题
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));  //宝马
//        7.通过反射给属性赋值
        brand.set(car,"奔驰");
        System.out.println(brand.get(car));  //奔驰
//        8.希望得到所有的属性(字段)
        System.out.println("====所有的字段属性====");
        Field[] fields = cls.getFields();
        for (Field f :fields) {
            System.out.println(f.getName()); //属性字段的各个名称
        }

    }
}

运行结果:

class com.dadao.Car
class java.lang.Class
com.dadao
com.dadao.Car
Car{brand='宝马', price=5000000, color='白色'}
宝马
奔驰
====所有的字段属性====
brand
price
color

三、1.3、获取Class类对象的方法

  1. 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException异常,
    实例:Class cls1=Class.forName(“java.lang.Car”)
    *应用场景:*多用于配置文件,读取全路径,加载类
  2. 前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高
    实例:Class cls2=Car.class;
    *应用场景:*多用于参数传值,比如通过反射得到对应的构造器对象
  3. 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
    实例:Class cls3=对象.getClass();// 运行类型(就是我们平时用来获取该方法的类的方法)
  4. 其他方式(类加载器)
    ClassLoader cls=对象.getClass().getClassLoader():
    Class clazz4=cls.loadClass(“类的全类名”);
  5. 基本数据(int,char,boolean,float,double,byte,long,short)按下面的方式得到Class类对象
    Class cls=基本数据类型.class;
  6. 基本数据类型对应的包装类,可以通过.TYPE得到Class对象
    Class cls=包装类.TYPE;

代码演示

package com.dadao.refletion.class_;

import com.dadao.Car;

//演示得到Class对象的方法
public class GetClass {
    public static void main(String[] args) throws ClassNotFoundException {

//        1.Class.forName
        String classAllPath="com.dadao.Car";//通过配置文件获取
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println("cls1="+cls1);

//        2.类名.class ,应用场景:用于参数传递
        Class cls2=Car.class;
        System.out.println("cls2="+cls2);


//       3.对象.getClass() 应用场景:有对象实例
          Car car=new Car();
          Class cls3=car.getClass();
        System.out.println("cls3="+cls3);


//        4.通过类加载器(有(4)种)来获取到类的Class
//          (1)先得到类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
//        (2)通过类加载器得到Class对象
        Class<?> cls4 = classLoader.loadClass(classAllPath);
        System.out.println("cls4="+cls4);

//        cls1,cls2,cls3,cls4是同一个Class
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());

//        5.基本数据类型可以按下面的方式得到Class对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println("integerClass="+integerClass);


//        6.基本数据类型对应的包装类,可以通过.TYPE 得到Class对象
        Class<Integer> type1 = Integer.TYPE;
        Class<Character> type2 = Character.TYPE;
        System.out.println("type1="+type1);

//        下面两个hashCode值是相等的,都是int(都是自动装箱或者拆箱)
        System.out.println(integerClass.hashCode());
        System.out.println(type1.hashCode());


    }
}

运行结果:

cls1=class com.dadao.Car
cls2=class com.dadao.Car
cls3=class com.dadao.Car
cls4=class com.dadao.Car
460141958
460141958
460141958
460141958
integerClass=int
type1=int
1163157884
1163157884

三、1.4、哪些类型有Class对象

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

代码演示:

package com.dadao.refletion.class_;

import java.io.Serializable;

//演示哪些类型有Class
public class AllTypeClass {
    public static void main(String[] args) {

//        外部类
        Class<String> stringClass = String.class;
//        接口
        Class<Serializable> serializableClass = Serializable.class;
//        数组
        Class<Integer[]> aClass = Integer[].class;
//        二维数组(其他类型也可以)
        Class<float[][]> aClass1 = float[][].class;
//        注解
        Class<Deprecated> deprecatedClass = Deprecated.class;
//        枚举
        Class<Thread.State> stateClass = Thread.State.class;
//        基本数据类型
        Class<Long> longClass = long.class;
//        void
        Class<Void> voidClass = void.class;
//        Class自己本身也有(万物皆对象)
        Class<Class> classClass = Class.class;

        System.out.println("stringClass(外部类):"+stringClass);
        System.out.println("serializableClass(接口):"+serializableClass);
        System.out.println("aClass(数组):"+aClass);
        System.out.println("aClass1(二维数组):"+aClass1);
        System.out.println("deprecatedClass(注释):"+deprecatedClass);
        System.out.println("stateClass(枚举):"+stateClass);
        System.out.println("longClass(基本数据类型):"+longClass);
        System.out.println("voidClass(void):"+voidClass);
        System.out.println("classClass(Class本身):"+classClass);


    }
}

运行结果:

stringClass(外部类):class java.lang.String
serializableClass(接口):interface java.io.Serializable
aClass(数组)class [Ljava.lang.Integer;
aClass1(二维数组):class [[F
deprecatedClass(注释):interface java.lang.Deprecated
stateClass(枚举):class java.lang.Thread$State
longClass(基本数据类型):long
voidClass(void):void
classClass(Class本身):class java.lang.Class

三、2、类加载器

三、2.1、类加载

在这里插入图片描述

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

  1. 静态加载:在编译的时侯加载相关的类,如果编译时没有检测到对应的类就会报错,依赖性太强。
  2. 动态加载:在运行的时候加载所需要的类(就是说,当程序运行的时候,并且执行到对应代码的时候,才会加载这个类),如果运行时不用该类,即使不存咋该类也不会报错,因此降低了依赖性。

代码演示: (注意,这里不能使用idea等计较强大的功能的软件,因为编译器直接就给出错误提示了,无法达到演示的效果)

import java.util.*
import java.reflect.*
public class ClassLoad_{
    public static void main(String[] args) throws Exception{
      Scanner scanner =new Scanner(System.in);
      System.out.println("请输入key");
      String key=scanner.next();
      switch(key){
         case '1':
             //这里的dog类就是属于一个类的静态加载
             //虽然我们这里的dog类不一定会用到
             //但是它在编译的时候依旧会加载这个类进行语法的校验,这就是静态加载
             //如果代码里面没有检测到dog类,代码就会报错,所以说依赖性很强。
             Dog dog=new Dog();
             dog.cry();
             break;
         case '2':
         //(反射)这里是动态加载:
         //当我们运行的时候,并且要执行到这段代码的时候,它才会真正加载这个类
             Class cls=Class.forName("Person");
             Object o=cls.newInstance():
             Mrthod m=cls.getMethod("hi");
             m.invoke(o);
             System.out.println("ok");
             break;
         default:
           system.out.println("do nothing");
   }
}
//因为new Dog() 是静态加载,因此必须编写dog类才能编译成功
//Person类是动态加载,所以,即使没有编写Person类也不会报错,可以编译成功,
//只有当动态加载到该类的时并且代码中没有该类的时候才会报错 (就是说这里的key输入了2,用到那段代码的时候)
class Dog(){
  public void cry(){
     System.out.println("小狗汪汪叫");
    }            
}

class Person{
  public void hi(){
    System.out.println("小孩哈哈笑");
   }
}

三、2.2、类加载的时机

  1. 类加载的步骤:

      1. 当创建对象时(new) //静态加载
      1. 当子类被加载时,父类也加载 //静态加载
      1. 调用类中的静态成员 //静态加载
      1. 通过反射 //动态加载
  2. 类加载流程图
    在这里插入图片描述

  3. 图片对应模块的解析

    • 加载Loading方格:将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程是由类加载器完成的。
    • 连接Linking方格:将类的二进制数据合并到JRE中去
      加载和连接都是由JVM机去完成的,而初始化才是我们程序员可以控制的
    • 初始化initialization方格:这里的初始化是类加载,不是new对象!!!
      JVM负责对类的初始化,这里主要是指静态成员(静态成员的初始化和类加载是一起的)
  4. 加载阶段的解析

    • 1、加载阶段: JVM在该阶段的主要目的就是将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
    • 2、连接阶段–验证verification:
      • 1.目的是为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
      • 2.包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证。(如果其中有不符合的情况就会抛出异常)
      • 3.可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间(有些时候,项目非常大,类很多,如果你想考虑到性能的问题,你就可以用这个提高项目的性能,就是说加快类加载的时间)
    • 3、连接阶段–准备
      • JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0,0L,null,false等),这些变量所使用的内存都将在方法区中进行分配
    • 4、连接阶段-解析: 虚拟机将常量池的符号引用替换为直接引用的过程
    • 5、初始化(Initialization):
        1. 到初始化阶段,才是真正执行类中定义的Java程序代码,此阶段执行()方法的过程
        1. ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和**静态代码块中的语句,并进行合并。
        1. 虚拟机会保证一个类的()方法在多线程环境中被正确地(synchronized)加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。(正因为有这个机制,才能保证某个类在内存中,只有一份Class对象)

连接阶段-准备-----代码说明:

class A{
//    属性==成员变量==字段
//    连接阶段--准备   属性是如何处理的
//    1. n1 是实例属性,不是静态变量,因此在这个准备阶段,是不会分配内存的
//    2.n2 是静态变量,会分配内存, 但是n2 是默认初始化 0,而不是20,
//    只有在!初始化阶段!才会真正执行代码,n2才会是20
//    3.n3是 static final 是常量,它和静态变量不一样,因为它一旦赋值就不会再变化,所以n3就会直接赋值 n3=30
    public int n1=10;
    public static int n2=20;
    public static final int n3=30;
}

类加载–初始化阶段-------代码演示:

package com.dadao.refletion.classLoad_;
//演示类加载--初始化阶段
public class classLoad03 {
    public static void main(String[] args) {
//        分析:
//        1.加载B类,并生成B类的class对象
//        2.连接: num=0
//        3.初始化阶段
//           依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
             /*
             clinit(){
                 System.out.println("B的静态代码块被执行");
                 num=30;
                 num=100;
             }

              合并:num=100;
              */
//        new B();//类加载
        System.out.println(B.num);//100,
        // 如果直接使用类的静态属性也会导致类的加载
    }
}

class B{
    static {
        System.out.println("B的静态代码块被执行");
        num=30;
    }
    static int num=100;
    public B(){
        System.out.println("B的构造器被执行");
    }
}

运行结果:
没有new对象的

B的静态代码块被执行
100

new了对象的

B的静态代码块被执行
B的构造器被执行
100

三、2.3、获取类的结构信息

  1. 一、第一组方法API(java.lang.Class类)
      1. getName():获取全类名
      1. getSimpleName()获取简单类名
      1. getFields() : “获取所有public修饰的属性(包含本类以及父类)”
      1. getDeclaredFields() :获取本类所有属性
      1. getMethods():获取所有public修饰的方法,包含本类以及父类(不是局限在直接父类)
      1. getDeclaredMethods():获取本类所有方法
      1. getConstructors():获取本类所有public修饰的构造器
      1. getDeclaredConstructors():获取本类所有的构造器
      1. getPackage()以Package形式返回包的信息
      1. getSuperclass():以Class形式返回信息
      1. getInterfaces():以Class[]形式返回信息
      1. getAnnotations():以Annotation[]形式返回注解信息
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionUtils {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//        演示如何通过反射获取类的结构信息
        //    第一组方法API(java.lang.Class类)

//        得到Class对象
        Class<?> personCls = Class.forName("com.dadao.refletion.Person");
//        getName():获取全类名
        System.out.println("获取全类名="+personCls.getName());
        System.out.println("---------------------------------------------");
//        getSimpleName()获取简单类名
        System.out.println("获取简单类名="+personCls.getSimpleName());
        System.out.println("---------------------------------------------");
//        getFields() : "获取所有public修饰的属性(包含本类以及父类)"
        Field[] fields = personCls.getFields();
        for (Field field :fields) {//增强for
            System.out.println("本类以及父类的属性:"+field.getName());
        }
        System.out.println("---------------------------------------------");
//        getDeclaredFields() :获取本类所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有属性="+declaredField.getName());
        }
        System.out.println("---------------------------------------------");
//        getMethods():获取所有public修饰的方法,包含本类以及父类(不是局限在直接父类)
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的方法="+method.getName());
        }
        System.out.println("---------------------------------------------");
//       getDeclaredMethods():获取本类所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有方法="+declaredMethod.getName());
        }
        System.out.println("---------------------------------------------");
//        getConstructors():获取本类所有public修饰的构造器
        Constructor<?>[] constructors = personCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本类的构造器="+constructor.getName());
        }
        System.out.println("---------------------------------------------");
//        getDeclaredConstructors():获取本类所有的构造器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("本类所有的构造器="+declaredConstructor.getName());
        }
        System.out.println("---------------------------------------------");
//      getPackage()以Package形式返回包的信息
        System.out.println(personCls.getPackage());
        System.out.println("---------------------------------------------");
//     getSuperclass():以Class形式返回信息
        Class<?> superclass = personCls.getSuperclass();
        System.out.println("父类的class对象="+superclass);
        System.out.println("---------------------------------------------");
//      getInterfaces():以Class[]形式返回信息
        Class<?>[] interfaces = personCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("接口信息:="+anInterface);
        }
        System.out.println("---------------------------------------------");
//       getAnnotations():以Annotation[]形式返回注解信息
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息="+annotation);
        }
        System.out.println("---------------------------------------------");
    }

}

class A{
    public String hobby;
    public void hi(){

    }
    public A(){

    }
}

interface IA{}
interface IB{}

@Deprecated
class Person extends A implements IA,IB{
//    属性
    public String name;
    protected int age;
    String job;
    private double sal;
//    构造器
    public Person(){}
    public Person(String name){}
    private Person(String name,int age){

    }

//    方法
    public void m1(){

    }
    protected void m2(){

    }
    private void m3(){

    }
    void m4(){

    }
}

运行结果:

获取全类名=com.dadao.refletion.Person
---------------------------------------------
获取简单类名=Person
---------------------------------------------
本类以及父类的属性:name
本类以及父类的属性:hobby
---------------------------------------------
本类所有属性=name
本类所有属性=age
本类所有属性=job
本类所有属性=sal
---------------------------------------------
本类及父类的方法=m1
本类及父类的方法=hi
本类及父类的方法=wait
本类及父类的方法=wait
本类及父类的方法=wait
本类及父类的方法=equals
本类及父类的方法=toString
本类及父类的方法=hashCode
本类及父类的方法=getClass
本类及父类的方法=notify
本类及父类的方法=notifyAll
---------------------------------------------
本类中所有方法=m3
本类中所有方法=m2
本类中所有方法=m1
本类中所有方法=m4
---------------------------------------------
本类的构造器=com.dadao.refletion.Person
本类的构造器=com.dadao.refletion.Person
---------------------------------------------
本类所有的构造器=com.dadao.refletion.Person
本类所有的构造器=com.dadao.refletion.Person
本类所有的构造器=com.dadao.refletion.Person
---------------------------------------------
package com.dadao.refletion
---------------------------------------------
父类的class对象=class com.dadao.refletion.A
---------------------------------------------
接口信息:=interface com.dadao.refletion.IA
接口信息:=interface com.dadao.refletion.IB
---------------------------------------------
注解信息=@java.lang.Deprecated()
---------------------------------------------

  1. 二、第二组java.lang.reflect.Fied类

      1. getModifiers:以int形式返回修饰符
        【说明:默认修饰符是0,public 是 1, private 是2,protected 是4,static是8,final是16】
      1. getType : 以Class形式返回类型
      1. getName:返回属性名
  2. 三、第三组java.lang.reflect.Method类

      1. getModifiers:以int形式返回修饰符
        【说明:默认修饰符是0,public 是 1, private 是2,protected 是4,static是8,final是16】
      1. getReturnType:以Class形式获取 返回类型
      1. getName:返回方法名
      1. getParameterTypes:以Class[]返回参数类型数组
  3. 四、第四组java.lang.reflect.Constructor类

      1. getModifiers:以int形式返回修饰符
        【说明:默认修饰符是0,public 是 1, private 是2,protected 是4,static是8,final是16】
      1. getName:返回构造器名
      1. getParameterTypes:以Class[]返回参数类型数组
public class ReflectionUtils02 {
    public static void main(String[] args) throws ClassNotFoundException {
//        第二组:
        //        得到Class对象
        Class<?> personCls = Class.forName("com.dadao.refletion.Person2");
        //        getDeclaredFields() :获取本类所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有属性="+declaredField.getName()
            +" 该属性的修饰符值="+declaredField.getModifiers()
            +" 该属性的类型="+declaredField.getType());
        }
        System.out.println("================================================");
//        第三组:
        //       getDeclaredMethods():获取本类所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有方法="+declaredMethod.getName()
            + " 该方法访问修饰符值="+declaredMethod.getModifiers()
            + " 该方法返回类型"+declaredMethod.getReturnType());

            //        输出当前这个方法形参数组情况
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该方法的形参类型="+parameterType);
            }
        }

    }
}
class A2{
    public String hobby;
    public void hi2(){

    }
    public A2(){

    }
}

interface IA2{}
interface IB2{}

@Deprecated
class Person2 extends A2 implements IA2,IB2{
    //    属性
    public String name;
    protected int age;
    String job;
    private double sal;
    //    构造器
    public Person2(){}
    public Person2(String name){}
    private Person2(String name,int age){

    }

    //    方法
    public void m1(String name,int age,double sal){

    }
    protected String m2(){
        return null;
    }
    private void m3(){

    }
    void m4(){

    }
}

运行结果:

本类所有属性=name 该属性的修饰符值=1 该属性的类型=class java.lang.String
本类所有属性=age 该属性的修饰符值=4 该属性的类型=int
本类所有属性=job 该属性的修饰符值=0 该属性的类型=class java.lang.String
本类所有属性=sal 该属性的修饰符值=2 该属性的类型=double
================================================
本类中所有方法=m2 该方法访问修饰符值=4 该方法返回类型class java.lang.String
本类中所有方法=m4 该方法访问修饰符值=0 该方法返回类型void
本类中所有方法=m1 该方法访问修饰符值=1 该方法返回类型void
该方法的形参类型=class java.lang.String
该方法的形参类型=int
该方法的形参类型=double
本类中所有方法=m3 该方法访问修饰符值=2 该方法返回类型void

运行结果:

本类所有属性=name 该属性的修饰符值=1 该属性的类型=class java.lang.String
本类所有属性=age 该属性的修饰符值=4 该属性的类型=int
本类所有属性=job 该属性的修饰符值=0 该属性的类型=class java.lang.String
本类所有属性=sal 该属性的修饰符值=2 该属性的类型=double
================================================
本类中所有方法=m2 该方法访问修饰符值=4 该方法返回类型class java.lang.String
本类中所有方法=m4 该方法访问修饰符值=0 该方法返回类型void
本类中所有方法=m1 该方法访问修饰符值=1 该方法返回类型void
该方法的形参类型=class java.lang.String
该方法的形参类型=int
该方法的形参类型=double
本类中所有方法=m3 该方法访问修饰符值=2 该方法返回类型void

四、通过反射创建对象

  1. 一、方式1:调用类中的public修饰的无参构造器
  2. 二、方式2:调用类中的指定构造器
  3. 三、Class类的相关方法:
    • newInstance:调用类中的无参构造器,获取对应类的对象
    • getConstructor(Class…class):根据参数列表,获取对应的public构造器对象
    • getDeclaredConstructor(Class…class):根据参数列表,获取对应的构造器对象
  4. 四、Constructor类相关方法
    • setAccessible:爆破【暴力破解】,使用反射可以访问private的构造器
    • newInstance(Object…obj):调用构造器

代码演示:

//通过反射机制创建对象
public class ReflectCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//        1.先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.dadao.refletion.User");
//        2.通过public的无参构造器创建实例
        Object o = userClass.newInstance();
        System.out.println(o);
//        3.通过public的有参构造器创建实例

        /*
        constructor对象就是构造器:
        public User(String name){
            this.name=name;
         }
         */
//        1.先得到对应的构造器
        Constructor<?> constructor = userClass.getConstructor(String.class);
//        2.通过构造器来创建实例,并传入实参
        Object daDao = constructor.newInstance("daDao");
        System.out.println("daDao"+daDao);//输出对象自动调用toString方法

//        4.通过非public的有参构造器创建实例
//           -先得到私有的构造器对象
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
//            -创建实例
        constructor1.setAccessible(true);//爆破【暴力破解】,使用反射可以访问private的构造器
        Object bigDao = constructor1.newInstance(100, "大刀");
        System.out.println(bigDao);
    }
}

class User{
    private int age=10;
    private String name="daDao";
    public User(){}

    public User(String name){
        this.name=name;
    }

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

    public String toString(){
        return "User [age="+age +", name="+name+"]";
    }
}

运行结果:

User [age=10, name=daDao]
daDaoUser [age=10, name=daDao]
User [age=100, name=大刀]

四、1、访问属性

  1. 根据属性名获取Field对象
    Field f=Class对象.getDeclaredField(属性名);
  2. 爆破:f.setAccessible(true);//f 是Field
  3. 访问:
    • f.set(o,值);
    • System.out.print(f.get(o));
  4. 如果是静态属性,则set和get中的参数o,可以写成null
public class ReflectAccessProperty {
//    演示反射访问属性
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
    //    1.得到Student类对应的Class对象
    Class<?> stuClass = Class.forName("com.dadao.refletion.Student");
//    2.创建对象
    Object o = stuClass.newInstance(); //o的运行类型就是Student
//    3.使用反射得到age属性对象
    Field age = stuClass.getDeclaredField("age");
    age.set(o,88);//通过反射来操作我们的属性
    System.out.println(o);
    System.out.println(age.get(o));//通过反射获取age属性值

//    4.使用反射操作name 属性
    Field name = stuClass.getDeclaredField("name");
//    对我们的name进行爆破
    name.setAccessible(true);//爆破操作之后,就可以访问private属性
//    name.set(o,"大刀哥");
//    因为name是static属性,因此o也可以写成null
    name.set(null,"大刀哥");
    System.out.println(o);
    System.out.println(name.get(o));//获取属性值
    System.out.println(name.get(null));//获取属性值,要求属性必须是静态的
}

}

class Student{
    public int age;
    private static String name;

    public Student(){

    }

    @Override
    public String toString() {
        return "Student [age="+age+", name="+name+"]";
    }
}

运行结果:

Student [age=88, name=null]
88
Student [age=88, name=大刀哥]
大刀哥
大刀哥

四、2、 访问方法:

  1. 根据方法名和参数列表获取Method方法对象:
    Method m=class.getDeclaredMethod(方法名, XX.class);
  2. 获取对象:
    Object o=class.newInstance():
  3. 爆破:m.setAccessible(true);
  4. 访问:Object returnValue=m.invoke(o,实参列表);
  5. 注意:如果是静态方法,则invoke的参数o,可以写成null;
public class ReflectAccessMethod {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//        演示通过反射调用方法

//      1.得到Boss类对应的Class对象
        Class<?> bossClass = Class.forName("com.dadao.refletion.Boss");
//        2.创建对象
        Object o = bossClass.newInstance();
//        3。调用public的hi方法
//        3.1得到hi对象
        Method hi = bossClass.getDeclaredMethod("hi",String.class);
//        3.2调用
         hi.invoke(o, "大刀哥");

//         调用private static 方法
        Method say = bossClass.getDeclaredMethod("say", int.class, String.class, char.class);
        say.setAccessible(true);//
//        因为say方法是私有的,所以需要爆破
        System.out.println( say.invoke(o, 100, "张三", '男'));
//        因为say方法是static 所以也可以传入null进行查找
        System.out.println(say.invoke(null, 100, "张六", '女'));

//        5.在反射中,如果方法有返回值,统一返回Object,但其他运行类型和方法定义的返回类型一致
        Object reVal = say.invoke(null, 300, "王武", '男');
        System.out.println("reVal 的运行类型="+reVal.getClass());

    }
}

class Boss{
    public int age;
    private static String name;

    public Boss(){

    }
    private static String say(int n,String s,char c){
        return n + " "+ s+" "+c;
    }

    public void hi(String s){
        System.out.println("hi "+s);
    }
}

运行结果:

hi 大刀哥
100 张三 男
100 张六 女
reVal 的运行类型=class java.lang.String

练习:对File类的操作

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        1.利用Class类的forName方法得到file类的class对象
        Class<?> aClass = Class.forName("java.io.File");
//        2.得到所有的构造器
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("File的构造器="+declaredConstructor);
        }
        System.out.println("================================================");
//        3.指定得到public java.io.File(java.lang.String)构造器
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
        String filePath="d:\\MyNew.txt";
        Object o = declaredConstructor.newInstance(filePath);

//        4.得到createNewFile 的方法对象
        Method createNewFile = aClass.getDeclaredMethod("createNewFile");
        createNewFile.invoke(o);
//        o的运行类型就是File
        System.out.println(o.getClass());
        System.out.println("创建文件成功"+filePath);

    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值