java零基础Ⅲ-- 3.反射

连接视频



一、反射机制

一个需求引出反射

请求看下面的问题:

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

classfullpath=com.zzpedu.Cat  #(类全路径)
method=hi

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


快速入门

re.properties配置文件

classfullpath=com.zzpedu.Cat
method=cry
package com.zzpedu;

public class Cat {
    private String name = "招财猫";
    public void hi(){//成员方法
        System.out.println("hi " + name);
    }
    public void cry(){//成员方法
        System.out.println(name + " 喵喵喵叫~~~");
    }
}
/**
 * 反射问题的引入
 */
@SuppressWarnings({"all"})
public class ReflectionQuestion {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, 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=" + classfullpath);
        System.out.println("method=" + methodName);

        //2. 创建对象,传统的方法,行不通 =》 反射机制
        // new classfullpath(); 错误

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

运行效果:

在这里插入图片描述


Java Reflection

1、反射机制允许程序在执行期间借助 Reflection API 获得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能够操作对象的属性及方法。反射在设计模式和框架底层都使用到

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


Java 反射机制原理示意图

在这里插入图片描述


Java反射机制可以完成

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

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

3、在运行时得到任意一个类所具有的成员变量和方法

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

5、生成动态代理


反射相关的注意类

1、java.lang.Class:代表一个类,Class类对象表示某个类加载后在堆中的对象

2、java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法

3、java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量

4、java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器

这些类在 java.lang.reflection

在这里插入图片描述

package com.zzpedu;

public class Cat {
    private String name = "招财猫";
    public int age = 10;
    public Cat(String name){
        this.name = name;
    }
    public Cat(){}
    public void hi(){//成员方法
        //System.out.println("hi " + name);
    }
    public void cry(){//成员方法
        System.out.println(name + " 喵喵喵叫~~~");
    }
}
public class Reflection01 {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();

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

        //java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
        //得到name字段
        // cls.getField("name") 不能得到私有的属性
        Field nameField = cls.getField("age");
        System.out.println(nameField.get(obj));// 传统方法 对象.成员变量,反射: 成员变量对象.get(对象)

        //java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
        Constructor constructor = cls.getConstructor();// ()中可以指定构造器参数类型,返回无参构造器
        System.out.println(constructor);//Cat()

        Constructor constructor2 = cls.getConstructor(String.class);// (String.class)就是String类的Class对象,返回有参构造器
        System.out.println(constructor2);//Cat(java.lang.String)
    }

运行效果:

在这里插入图片描述


反射优点和缺点

1、优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑

2、缺点:使用反射基本是解释执行,对执行速度有影响

public class Reflection02 {
    public static void main(String[] args) throws Exception {
        m1();
        m2();
    }
    //传统方法来调用hi
    public static void m1(){
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("m1()方法 耗时=" + (end-start));

    }


    //反射机制调用hi
    public static void m2() throws Exception {
        Class cls = Class.forName("com.zzpedu.Cat");
        Object obj = cls.newInstance();
        Method hi = cls.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            hi.invoke(obj);// 反射机制调用
        }
        long end = System.currentTimeMillis();
        System.out.println("m2()方法 耗时=" + (end-start));
    }

}

运行效果:

在这里插入图片描述


反射调用优化 - 关闭访问检查

1、MethodFieldConstructor对象都有setAccessible()方法

2、setAccessible()作用是启用和禁用访问检查的开关

3、参数为 true 表示 反射的对象在使用是取消访问检查,提高反射效率。参数值 false 则表示反射的对象执行访问检查

在这里插入图片描述

在这里插入图片描述

public class Reflection02 {
    public static void main(String[] args) throws Exception {
        m1();
        m2();
        m3();
    }
    //传统方法来调用hi
    public static void m1(){
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("m1()方法 耗时=" + (end-start));
    }

    //反射机制调用hi
    public static void m2() throws Exception {
        Class cls = Class.forName("com.zzpedu.Cat");
        Object obj = cls.newInstance();
        Method hi = cls.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            hi.invoke(obj);// 反射机制调用
        }
        long end = System.currentTimeMillis();
        System.out.println("m2()方法 耗时=" + (end-start));
    }
    
    //反射机制调用优化 + 关闭访问检查
    public static void m3() throws Exception {
        Class cls = Class.forName("com.zzpedu.Cat");
        Object obj = cls.newInstance();
        Method hi = cls.getMethod("hi");
        hi.setAccessible(true);//在反射调用时,取消访问检查
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            hi.invoke(obj);// 反射机制调用
        }
        long end = System.currentTimeMillis();
        System.out.println("m3()方法 耗时=" + (end-start));
    }
}

运行效果:

在这里插入图片描述



二、Class类

基本介绍

1、Class也是类,因此也继承Object类

在这里插入图片描述

2、Class类对象不是new出来的,而是系统创建的

在这里插入图片描述

3、对应某个类的Class类对象,在内存中只有一份,因为类加载一次

4、每个类是实例都会记得是由哪个 Class 实例所生成

5、通过 Class对象 可以完整地得到一个类的完整结构,通过一系列API

6、Class对象是存放在 堆 中的

7、类的字节码二进制数据,是放在方法区,有的地方称为类的元数据(包括 方法代码,类变量,方法名,访问权限等等)https://www.zhihu.com/question/38496907

在这里插入图片描述

/**
 * 对Class类特点的梳理
 */
public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //2. Class类对象不是new出来的,而是系统创建的
        //(1) 传统new对象
        /*
        ClassLoader类
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
            }
         */
        //Cat cat = new Cat();
        //(2) 反射方式,如果没有debug到 ClassLoader类 loadClass,原因是,之前的 Cat cat = new Cat(); 已经加载了
        /*
         ClassLoader类,仍然是通过 ClassLoader类加载Cat类的 Class对象
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
            }
         */
        Class cls1 = Class.forName("com.zzpedu.Cat");

        //3. 对应某个类的Class类对象,在内存中只有一份,因为类加载一次
        Class cls2 = Class.forName("com.zzpedu.Cat");
        System.out.println("cls1=" + cls1.hashCode());
        System.out.println("cls2=" + cls2.hashCode());
        Class cls3 = Class.forName("com.zzpedu.Dog");
        System.out.println("cls3=" + cls3.hashCode());
    }
}

运行效果:

在这里插入图片描述


Class类的常用方法:

方法名功能说明
static Class forName(String className)返回指定类名 className 的 Class 对象
Object newInstance()调用缺省构造函数,返回该Class对象的一个实例
String getName()()返回由 类对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String
Class [] getInterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Class getSuperclass()返回表示此Class所表示的实体(类,接口,基本类型或void)的超类 类
Constructor[] getConstructors()返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象
Field[] getDeclaredFields()返回Field对象的一个数组
Method getMethod(String name, Class… paramTypes )返回一个Method对象,此对象的形参类型为paramType

应用实例

package com.zzpedu;

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

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

/**
 * 演示Class常用方法
 */
public class Class02 {
    public static void main(String[] args) throws Exception {
        String classAllPath = "com.zzpedu.Car";
        //1. 获取到Car类的 对应的 Class对象
        // <?> 表示不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //2. 输出 显示cls对象,是哪个类的Class对象 不能强转类
        System.out.println("cls=" + cls);// class com.zzpedu.Car
        //输出cls的运行类型 java.lang.Class
        System.out.println("cls.getClass()=" + cls.getClass());//class java.lang.Class
        //3. 得到包名
        System.out.println("Package=" + cls.getPackage().getName());//com.zzpedu
        //4. 得到全类名
        System.out.println("Name=" + cls.getName());//com.zzpedu.Car
        //5. 通过cls来创建对象实例
        Car car = (Car)cls.newInstance();
        System.out.println("car=" + car);//Car{brand='宝马', price=10000, color='白色'}
        //6. 通过反射获取属性 brand
        Field brand = cls.getField("brand");
        System.out.println("brand=" + brand.get(car));//宝马
        //7. 通过反射给属性赋值
        brand.set(car,"奔驰");
        System.out.println("brand=" + brand.get(car));//奔驰
        System.out.println("========所有属性字段=====");
        //8. 得到所有属性(字段)
        Field[] fields = cls.getFields();
        for(Field f : fields){
            System.out.println(f.getName());//字段名称
        }
    }
}

运行效果:

在这里插入图片描述


获取Class类对象

在这里插入图片描述

1、前提:已知一个类的全路径,并且该类在类路径下,可以通过Class类的静态方法forName()获取,可以抛出 ClassNotFoundException,实例: Class cls1 = Class.forName("java.lang.Cat")
应用场景:多用于配置文件,读取类全路径,加载类

2、前提:若已知具体的类,通过类的class 获取,该方法 最为安全可靠,程序性能最高实例: Class cls2 = Cat.getClass
应用场景:多用于参数传递,比如通过反射得到对应构造器对象

3、前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:Class clazz = 对象.getClass();//运行类型
应用场景:通过创建好的对象,获取Class对象

4、其他方式
ClassLoader cl = 对象.getClass().getClassLoader();
Class clazz = cl.loadClass("类的全类名")

5、基本数据类型(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象

Class cls = 基本数据类型.clss

6、基本数据类型对应的包装类,可以通过 .TYPE 得到Class对象

Class cls = 包装类.TYPE
/**
 * 演示得到Class对象的各种方式(6)
 */
public class GetClass_ {
    public static void main(String[] args) throws Exception {
        //1. Class.forName
        String classAllPath = "com.zzpedu.Car";//通过读取配置文件获取
        Class cls1 = Class.forName(classAllPath);
        System.out.println(cls1);//class com.zzpedu.Car

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

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

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

        //cls1/cls2/cls3/cls4 其实是同一个对象
        System.out.println(cls1.hashCode());//460141958
        System.out.println(cls2.hashCode());//460141958
        System.out.println(cls3.hashCode());//460141958
        System.out.println(cls4.hashCode());//460141958

        //5. 基本数据类型(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println(integerClass);//int
        System.out.println(characterClass);//char
        System.out.println(booleanClass);//boolean

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

        System.out.println(integerClass.hashCode());//1163157884
        System.out.println(type1.hashCode());//1163157884
    }
}

运行效果:

在这里插入图片描述


哪些类型有Class对象

如下类型有Class对象:

1、外部类,成员内部类,静态内部类,局部内部类,匿名内部类

2、interface:接口

3、数组

4、enum:枚举

5、annotation:注解

6、基本数据类型

7、void

应用实例

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

        Class cls1 = String.class;//外部类
        Class cls2 = Serializable.class;//接口
        Class<Integer[]> cls3 = Integer[].class;//数组
        Class<float[][]> cls4 = float[][].class;//二维数组
        Class<Deprecated> cls5 = Deprecated.class;//注解(过时的)

        Class<Thread.State> cls6 = Thread.State.class;//枚举
        Class<Long> cls7 = long.class;//基本数据类型
        Class<Integer> cls8 = Integer.class;//包装类型
        Class<Void> cls9 = void.class;//void数据类型
        Class<Class> cls10 = Class.class;//Class类型

        System.out.println(cls1);//class java.lang.String
        System.out.println(cls2);//interface java.io.Serializable
        System.out.println(cls3);//class [Ljava.lang.Integer;
        System.out.println(cls4);//class [[F
        System.out.println(cls5);//interface java.lang.Deprecated
        System.out.println(cls6);//class java.lang.Thread$State
        System.out.println(cls7);//long
        System.out.println(cls8);//class java.lang.Integer
        System.out.println(cls9);//void
        System.out.println(cls10);//class java.lang.Class

    }
}



三、类加载

基本说明

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

1、静态加载:编译时加载相关的类,如果没有则会报错,依赖性太强

import java.util.Scanner;

public class ClassLoad_ {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请求输入key:");
        String key = scanner.next();
        switch (key){
            case "1":
                Dog dog = new Dog();//静态加载 依赖性很强
                dog.cry();
                break;
            case "2":
                System.out.println("ok");
                break;
            default:
                System.out.println("do nothing...");
        }
    }
}

在这里插入图片描述

2、动态加载:运行时加载需要的类,如果运行时不用改类,即使不存在该类,也不报错,降低了依赖性

import java.lang.reflect.Method;
import java.util.Scanner;

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 = new Dog();//静态加载 依赖性很强
//                dog.cry();
                break;
            case "2":
            	//反射 -> 动态加载
                Class cls = Class.forName("Person");//加载Person[动态加载]
                Object o = cls.newInstance();
                Method m = cls.getMethod("hi");
                m.invoke(o);
                System.out.println("ok");
                break;
            default:
                System.out.println("do nothing...");
        }
    }
}

在这里插入图片描述

在这里插入图片描述

import java.lang.reflect.Method;
import java.util.Scanner;

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 = new Dog();//静态加载 依赖性很强
               dog.cry();
                break;
            case "2":
                //反射 -> 动态加载
                Class cls = Class.forName("Person");//加载Person[动态加载]
                Object o = cls.newInstance();
                Method m = cls.getMethod("hi");
                m.invoke(o);
                System.out.println("ok");
                break;
            default:
                System.out.println("do nothing...");
        }
    }
}
//因为new Dog() 是静态加载,因此必须编写Dog
//Person类是动态加载,所以,没有编写Person也不会报错,只有动态加载该类时,才会报错
class Dog{
    public void cry(){
        System.out.println("小狗汪汪叫~");
    }
}
class Person{
    public void hi(){
        System.out.println("hi~");
    }
}

在这里插入图片描述


类的加载机制

1、当创建对象时(new)【静态加载】

2、当子类被加载时,父类也加载【静态加载】

3、调用类中的静态成员时【静态加载】

4、通过反射【动态加载】

Class.forName("com.zzpwdu.Cat");


类加载过程图

在这里插入图片描述


类加载各阶段完成任务

在这里插入图片描述


加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class对象

在这里插入图片描述


连接阶段 - 验证

1、目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

在这里插入图片描述

2、包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证

在这里插入图片描述

3、可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机加载的时间

在这里插入图片描述


连接阶段 - 准备

1、JVM 会在该阶段第静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如 0,0L,null,false)等。这些变量所使用的内存在方法区中进行分配

在这里插入图片描述

/**
 * 说明一下一个类加载的链接阶段-准备
 */
public class ClassLoad02 {
    public static void main(String[] args) {
    }
}
class A{
    //属性-成员变量-字段
    //分析:类加载的链接阶段-准备,属性是如何处理
    //1. n1 是实例属性,不是静态变量,因此在准备阶段,是不会分配内存
    //2. n2 是静态变量,分配内存 n2 是默认初始化 0,而不是20
    //3. n3 是static final 是常量,它和静态变量不一样,因为一旦赋值就不变 n3 = 30
    public int n1 = 10;
    public static int n2 = 20;
    public static final int n3 = 30;
}

连接阶段 - 解析

1、虚拟机将常量池内的符号引用替换为直接引用的过程。【JVM 内存地址分配】


Initialization(初始化)

1、到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行 <clinit>()方法的过程

2、<clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有 静态变量的赋值动作和静态代码块中的语句,并进行合并。

3、虚拟机会保证一个类的 <clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的 <clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行 <clinit>()方法完毕

/**
 * 演示类加载-初始化阶段
 */
public class ClassLoad03 {
    public static void main(String[] args) throws ClassNotFoundException {
        //分析:
        //1. 加载B类,并生成 B的class对象
        //2. 连接 num = 0
        //3. 初始化阶段
        //   依次自动收集类中的所有 静态变量的赋值动作和静态代码块中的语句,并合并
        /*
              clinit(){
                System.out.println("B 静态代码块被执行");
                num = 300;
                num = 100;
              }
              合并:num = 100
         */
        //4. System.out.println("B构造器被执行");
//        new B();//类加载
//        System.out.println(B.num);//100 如果直接使用类的静态属性,也会导致类加载

        //看看加载类的时候,是有同步机制控制
        /*
         protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
            {
                //正因为有个机制,才能保证某个类在内存中,只有一份Class对象
                synchronized (getClassLoadingLock(name)) {
                    // First, check if the class has already been loaded
                    Class<?> c = findLoadedClass(name);
                    ....
                    }
              }
         */
        B b = new B();
    }
}

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

在这里插入图片描述



四、反射获取类的结构信息

第一组:java.lang.Class类

1、getName:获取全类名

2、getSimpleName:获取简单类名

3、getFields:获取所有public修饰的属性,包含本类以及父类的

4、getDeclaredFields:获取本类中所有属性

5、getMethods:获取所有的public修饰的方法,包含本类以及父类的

6、getDeclaredMethods:获取本类的所有方法

7、getConstructors:获取本类所有public修饰的构造器

8、getDeclaredConstructors:获取本类中所有构造器

9、getPackage:以Package形式返回 包信息

10、getSuperClass:以Class形式返回父类信息

11、getInterfaces:以Class[]形式返回接口信息

12、getAnnotations:以Annotation[] 形式返回注解信息

/**
 * 演示如何通过反射获取类的结构信息
 */
public class ReflectionUtils {
    //第一组
    @Test
    public void api_01() throws Exception {
        //得到Class对象
        Class personCls = Class.forName("com.zzpedu.reflection.Person");
        //1、getName:获取全类名
        System.out.println(personCls.getName());//com.zzpedu.reflection.Person
        //2、getSimpleName:获取简单类名
        System.out.println(personCls.getSimpleName());//Person
        //3、getFields:获取所有public修饰的属性,包含本类以及父类的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {//增强for
            System.out.println("本类以及父类的属性=" + field.getName());
        }
        //4、getDeclaredFields:获取本类中所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {//增强for
            System.out.println("本类中所有属性=" + declaredField.getName());
        }
        //5、getMethods:获取所有的public修饰的方法,包含本类以及父类的
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类以及父类的方法=" + method.getName());
        }
        //6、getDeclaredMethods:获取本类的所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类的所有方法=" + declaredMethod.getName());
        }
        //7、getConstructors:获取本类所有public修饰的构造器
        Constructor[] constructors = personCls.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("本类的构造器=" + constructor.getName());
        }
        //8、getDeclaredConstructors:获取本类中所有构造器
        Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里只是输出名字
        }
        //9、getPackage:以Package形式返回 包信息
        System.out.println(personCls.getPackage());//package com.zzpedu.reflection
        //10、getSuperClass:以Class形式返回父类信息
        System.out.println("父类的class对象=" + personCls.getSuperclass());//class com.zzpedu.reflection.A
        //11、getInterfaces:以Class[]形式返回接口信息
        Class[] interfaces = personCls.getInterfaces();
        for (Class anInterface : interfaces) {
            System.out.println("接口信息=" + anInterface.getName());
        }
        //12、getAnnotations:以Annotation[] 形式返回注解信息
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息=" + annotation);
        }
    }
}
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(){ }
    void m3(){ }
    private void m4(){ }
}

运行效果

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

第二组:java.lang.reflect.Field类

1、getModifiers:以int形式返回修饰符
【说明:默认修饰符 是0,public 是1,private 是2,protected 是4,static 是8,final 是 16】【如果是: public(1) + static(8) = 9

2、getType:以Class形式返回类型

3、getName:返回属性名

@Test
public void api_02() throws Exception {
    //得到Class对象
    Class personCls = Class.forName("com.zzpedu.reflection.Person");
    //4、getDeclaredFields:获取本类中所有属性
    //说明:默认修饰符 是0,public 是1,private 是2,protected 是4,static 是8,final 是 16
    Field[] declaredFields = personCls.getDeclaredFields();
    for (Field declaredField : declaredFields) {//增强for
        System.out.println("本类中所有属性=" + declaredField.getName()
                + " 该属性的修饰符值=" + declaredField.getModifiers()
                + " 该属性的类型=" + declaredField.getType());
    }
}
class Person{
    //属性
    public String name;
    protected static int age;// 4 + 8 =12
    String job;
    private double sal;
}

运行效果:

本类中所有属性=name 该属性的修饰符值=1 该属性的类型=class java.lang.String
本类中所有属性=age 该属性的修饰符值=12 该属性的类型=int
本类中所有属性=job 该属性的修饰符值=0 该属性的类型=class java.lang.String
本类中所有属性=sal 该属性的修饰符值=2 该属性的类型=double

第三组: java.lang.reflect.Method类

1、getModifiers:以int形式返回修饰符
【说明:默认修饰符 是0,public 是1,private 是2,protected 是4,static 是8,final 是 16

2、getReturnType:以Class形式获取 返回类型

3、getName:返回方法名

4、getParameterTypes:以Class[]返回参数的类型数组

@Test
public void api_03() throws Exception {
    //得到Class对象
    Class personCls = Class.forName("com.zzpedu.reflection.Person");
    //6、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 Person{
    //方法
    public void m1(String name,int age,double sal){ }
    protected String m2(){ return ""; }
    void m3(){ }
    private void m4(){ }
}

运行效果:

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

第四组:java.lang.reflect.Constructor类

1、getModifiers:以int形式返回修饰符

2、getName:返回构造器名(全类名)

3、getParameterTypes:以Class[]返回参数类型数组

@Test
public void api_04() throws Exception {
    //得到Class对象
    Class personCls = Class.forName("com.zzpedu.reflection.Person");
    //8、getDeclaredConstructors:获取本类中所有构造器
    Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
    for (Constructor declaredConstructor : declaredConstructors) {
        System.out.println("本类中所有构造器=" + declaredConstructor.getName()
                + " 该类构造器的访问修饰符值=" + declaredConstructor.getModifiers());
        //输出当前这个构造器的形参类型数组
        Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println("该构造器的形参类型=" + parameterType);
        }
    }
}
class Person {
    //构造器
    public Person(){}
    protected Person(String name){}
    //私有构造器
    private Person(String name,int age){}
}

运行效果:

本类中所有构造器=com.zzpedu.reflection.Person 该类构造器的访问修饰符值=2
该构造器的形参类型=class java.lang.String
该构造器的形参类型=int
本类中所有构造器=com.zzpedu.reflection.Person 该类构造器的访问修饰符值=4
该构造器的形参类型=class java.lang.String
本类中所有构造器=com.zzpedu.reflection.Person 该类构造器的访问修饰符值=1



五、通过反射创建对象

1、方式一:调用类中public修饰的无参构造器

2、方式二:调用类中指定的构造器

3、Class类相关方法

  • newInstance:调用类中的无参构造器,获取对应的对象
  • getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
  • getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象

4、Constructor类相关方法

  • setAccessible:爆破
  • newInstance(Object...obj):调用构造器

案例演示

1、测试1:通过反射创建某类的对象,要求该类中必须有public的无参构造
2、测试2:通过调用某个特定构造器的方式,实现创建某类的对象

/**
 * 演示通过反射机制创建实例
 */
public class ReflectionCreateInstance {
    public static void main(String[] args) throws Exception {
        //1. 先获取到User对象的Class对象
        Class<?> userCls = Class.forName("com.zzpedu.reflection.User");
        //2. 通过public的无参构造器创建实例
        Object obj1 = userCls.newInstance();
        System.out.println("obj1=" + obj1);
        //3. 通过public的有参构造器创建实例
        /*
            constructor 对象就是
            public User(String name){//public的有参构造器
                this.name = name;
            }
         */
        //3.1 先得到对应的构造器
        Constructor<?> constructor = userCls.getConstructor(String.class);
        //3.2 创建实例 并传入实参
        Object obj2 = constructor.newInstance("java");
        System.out.println("obj2=" + obj2);
        //4. 通过非public的有参构造器创建实例
        //4.1 先得到private的构造器
        Constructor<?> constructor1 = userCls.getDeclaredConstructor(int.class, String.class);
        //爆破【暴力破解】,使用反射可以访问 private构造器/方法/属性
        constructor1.setAccessible(true);
        //4.2 创建实例 并传入实参
        Object obj3 = constructor1.newInstance(100, "张三丰");
        System.out.println("obj3=" + obj3);
    }
}
class User{//User类
    private int age = 10;
    private String name = "zzp";
    public User(){}//无参构造器 public
    public User(String name){//public的有参构造器
        this.name = name;
    }
    private User(int age,String name){//私有 有参构造器
        this.age = age;
        this.name = name;
    }

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

运行效果:
在这里插入图片描述



六、通过反射访问类中成员

访问属性

1、根据属性名获取Field对象

Field f = clazz对象.getDeclaredField(属性名);

2、爆破:f.setAccessible(true);//f 是 Field

3、访问
f.set(o,值);//o 对象
syso(f.get(o));//o 对象

4、注意:如果是静态属性,则set和get中的参数o,可以写成null

/**
 * 演示反射操作属性
 */
public class ReflectionAccessProperty {
    public static void main(String[] args) throws Exception {
        //1. 先获取到Student对象的Class对象
        Class<?> stuCls = Class.forName("com.zzpedu.reflection.Student");
        //2. 创建对象
        Object o = stuCls.newInstance();//o 的运行类型就是Student
        System.out.println("o=" + o.getClass());
        //3. 使用反射得到age 属性对象
        Field age = stuCls.getField("age");
        age.set(o,88);//通过反射来操作属性
        System.out.println(o);
        System.out.println(age.get(o));//直接返回age属性值

        //4. 使用反射操作name 属性
        Field name = stuCls.getDeclaredField("name");
        //对 私有的 name 进行爆破
        name.setAccessible(true);
        //name.set(o,"zzp");
        // 因为name是static属性,因此 o对象 也可以写成null
        name.set(null,"zzp~");
        System.out.println(o);
        System.out.println(name.get(o));//获取属性值
        System.out.println(name.get(null));//获取属性值,要求name是静态的static
    }
}
class Student{
    public int age;
    private static String name;
    public Student(){}

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

运行效果:
在这里插入图片描述


访问方法

1、根据方法名和参数列表获取Method方法对象:
Method m = clazz.getDeclaredMethod(方法名, XX.class);//得到本类的所有方法

2、获取对象:Object o = clazz.newInstance();

3、爆破:m.setAccessible(true);

4、访问:Object returnValue = m.invoke(o,实参列表);//o 就是对象

5、注意:如果是静态方法,则invoke的参数o,可以成null

/**
 * 演示通过反射调用方法
 */
public class ReflectionAccessMethod {
    public static void main(String[] args) throws Exception {
        //1. 先获取到Boss对象的Class对象
        Class<?> bossCls = Class.forName("com.zzpedu.reflection.Boss");
        //2. 创建对象
        Object o = bossCls.newInstance();//o 的运行类型就是Boss
        //3. 调用public的hi方法
        //Method hi = bossCls.getMethod("hi",String.class);//OK
        //3.1 得到hi方法对象
        Method hi = bossCls.getDeclaredMethod("hi",String.class);
        //3.2 调用hi
        hi.invoke(o,"zzp先生");

        //4. 调用private static方法
        //4.1 得到 say 方法对象
        Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
        //4.2 因为 say 是私有的方法,所以需要爆破
        say.setAccessible(true);
        Object invoke1 = say.invoke(o, 88, "张三", '男');
        System.out.println(invoke1);
        Object invoke2 = say.invoke(null, 24, "小芳", '女');
        System.out.println(invoke2);

        //5. 在反射中,如果方法有返回值,统一返回Object,但是它的运行类型和方法中定义的返回类型一致
        Object invoke3 = say.invoke(null, 50, "韩夫人", '女');
        System.out.println("invoke3的运行类型=" + invoke3.getClass());
    }
}
class Boss{
    private 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);
    }
}

运行效果:

在这里插入图片描述



作业

练习1:

通过反射修改私有成员变量

1、定义PrivateTest类,有私有name属性,并且属性值为 hellokitty
2、提供getName的共有方法
3、创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值,并调用getName()方法打印name属性值

public class Homework01 {
    public static void main(String[] args) throws Exception {
        /**
         * 1、定义PrivateTest类,有私有name属性,并且属性值为 hellokitty
         * 2、提供getName的共有方法
         * 3、创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值,并调用getName()方法打印name属性值
         */
        //1. 得到 PrivateTest类对应的Class对象
        Class<PrivateTest> privateTestClass = PrivateTest.class;
        //2. 创建实例对象
        PrivateTest privateTestObj = privateTestClass.newInstance();
        //3. 得到name属性
        Field name = privateTestClass.getDeclaredField("name");//name属性是私有的
        //4. 爆破 name
        name.setAccessible(true);
        System.out.println("name的属性值=" + name.get(privateTestObj));
        name.set(privateTestObj,"叮当猫");
        //5. 得到getName方法对象
        Method getName = privateTestClass.getMethod("getName");
        //6. 因为getName() 是public,使用直接调用
        Object invoke = getName.invoke(privateTestObj);
        System.out.println("name的属性值=" + invoke);
    }
}
class PrivateTest{
    private String name = "hellokitty";
    //默认无参构造器
    public String getName(){
        return name;
    }
}

运行效果:

在这里插入图片描述


练习2

利用反射和Field完成一下功能
1、利用Class对象的forName()方法得到File类的class对象
2、在控制台打印File类的所有构造器
3、通过newInstance的方法创建File对象,并创建 f:\data\mynew.txt 文件

提示:创建文件的正常写法如下

File file = new File("f:\\aa.txt");
file.createNewFile();
public class Homework02 {
    public static void main(String[] args) throws Exception {
        /**
         * 1、利用Class对象的forName()方法得到File类的class对象
         * 2、在控制台打印File类的所有构造器
         * 3、通过newInstance的方法创建File对象,并创建 f:\data\mynew.txt 文件
         */
        //1. 利用Class对象的forName()方法得到File类的class对象
        Class<?> fileCls = Class.forName("java.io.File");
        //2. 得到所有的构造器
        Constructor<?>[] declaredConstructors = fileCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("File的构造器=" + declaredConstructor);
        }
        //3. 指定得到构造器 public java.io.File(java.lang.String)
        Constructor<?> constructor = fileCls.getDeclaredConstructor(String.class);
        String filePath = "f:\\data\\mynew.txt";
        Object file = constructor.newInstance(filePath);//创建File对象实例 一个String参数构造器 
        //file的运行类型就是File
        System.out.println(file.getClass());
        //4. 得到createNewFile的方法对象
        Method createNewFile = fileCls.getMethod("createNewFile");
        createNewFile.invoke(file);//创建文件 无实参
        System.out.println("创建文件成功=" + filePath);
    }
}

运行效果:
在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值