一起来学反射

反射学习笔记

🏠个人主页:不会写代码的满满

🧑个人简介:大家好,我是满满,一个想要与大家共同进步的男人😉😉

目前状况🎉:开学即将大三,目标就是半年内找到一份实习工作👏👏

💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,我亲爱的大佬😘

正文开始 ----------

1. Java反射机制概述

1.1 反射机制的概述

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

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

1.1.1 动态语言 vs 静态语言

1、动态语言

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

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

2. 静态语言

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

Java不是动态语言,但Java可以称之为"准动态语言"。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。

Java的动态性让编程的时候更加灵活!

1.1.2 反射的应用场景

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

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

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

 在运行时获取泛型信息

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

 在运行时处理注解

 生成动态代理

2. 理解Class类和获取Class类

2.1 理解Class类

在Object类中定义了以下的方法,此方法将被所有子类继承:

● public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,

即:可以通过对象反射求出类的名称。

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

 Class本身也是一个类

 Class 对象只能由系统建立对象

 一个加载的类在 JVM 中只会有一个Class实例

 一个Class对象对应的是一个加载到JVM中的一个.class文件

 每个类的实例都会记得自己是由哪个 Class 实例所生成

 通过Class可以完整地得到一个类中的所有被加载的结构

 Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

2.2 Class类的常用方法

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

2.3 反射的应用举例

• String str = "test4.Person";

• Class clazz = Class.forName(str);

• Object obj = clazz.newInstance();

• Field field = clazz.getField("name");

• field.set(obj, "Peter");

• Object name = field.get(obj);

• System.out.println(name);

test4.Person是test4包下的Person类

2.4 获取Class类的实例(四种方法)

1)**前提:**若已知具体的类,通过类的class属性获取,该方法最为安全可靠,

程序性能最高

Class clazz = String.class;

2)**前提:**已知某个类的实例,调用该实例的getClass()方法获取Class对象

Class clazz = "www.manman.com".getClass();

3)**前提:**已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

Class clazz = Class.forName("java.lang.String");

4)其他方式

ClassLoader cl = this.getClass().getClassLoader();

Class clazz4 = cl.loadClass("类的全类名");

2.5 哪些类型可以有Class对象 ?

代码实现

java 复制代码
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);

3. 类的加载与ClassLoader的理解

3.1 类加载器

很多开发人员都遇到过java.lang.ClassNotFoundException或java.lang.NoClassDefError,想要更好的解决这类问题,或者在一些特殊的应用场景,比如需要支持类的动态加载或需要对编译后的字节码文件进行加密解密操作,那么需要你自定义类加载器,因此了解类加载器及其类加载机制也就成了每一个Java开发人员的必备技能之一。

1、类加载器分为:

(1)引导类加载器(Bootstrap Classloader)又称为根类加载器

bash 复制代码
它负责加载jre/rt.jar核心库
它本身不是Java代码实现的,也不是ClassLoader的子类,获取它的对象时往往返回null

(2)扩展类加载器(Extension ClassLoader)

markdown 复制代码
它负责加载jre/lib/ext扩展库
它是ClassLoader的子类

(3)应用程序类加载器(Application Classloader)

复制代码
它负责加载项目的classpath路径下的类
它是ClassLoader的子类

(4)自定义类加载器

复制代码
当你的程序需要加载"特定"目录下的类,可以自定义类加载器;
当你的程序的字节码文件需要加密时,那么往往会提供一个自定义类加载器对其进行解码
后面会见到的自定义类加载器:tomcat中

2、Java系统类加载器的双亲委托模式

简单描述:

vbnet 复制代码
下一级的类加载器,如果接到任务时,会先搜索是否加载过,如果没有,会先把任务往上传,如果都没有加载过,一直到根加载器,如果根加载器在它负责的路径下没有找到,会往回传,如果一路回传到最后一级都没有找到,那么会报ClassNotFoundException或NoClassDefError,如果在某一级找到了,就直接返回Class对象。

应用程序类加载器 把 扩展类加载器视为父加载器,

扩展类加载器 把 引导类加载器视为父加载器。

不是继承关系,是组合的方式实现的。

3.1.1 查看某个类的类加载器对象

(1)获取默认的系统类加载器

java 复制代码
ClassLoader ClassLoader.getSystemClassLoader()

(2)查看某个类是哪个类加载器加载的

java 复制代码
ClassLoader Class对象.getClassLoader()
//如果是根加载器加载的类,则会得到null

(3)获取某个类加载器的父加载器

java 复制代码
ClassLoader ClassLoader对象.getParent()

示例代码:

java 复制代码
package com.manman.loader;

import org.junit.Test;

public class TestClassLoader {
    @Test
    public void test01(){
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("systemClassLoader = " + systemClassLoader);
    }

    @Test
    public void test02()throws Exception{
        ClassLoader c1 = String.class.getClassLoader();
        System.out.println("加载String类的类加载器:" + c1);

        ClassLoader c2 = Class.forName("sun.util.resources.cldr.zh.TimeZoneNames_zh").getClassLoader();
        System.out.println("加载sun.util.resources.cldr.zh.TimeZoneNames_zh类的类加载器:" + c2);

        ClassLoader c3 = TestClassLoader.class.getClassLoader();
        System.out.println("加载当前类的类加载器:" + c3);
    }

    @Test
    public void test03(){
        ClassLoader c1 = TestClassLoader.class.getClassLoader();
        System.out.println("加载当前类的类加载器c1=" + c1);

        ClassLoader c2 = c1.getParent();
        System.out.println("c1.parent = " + c2);

        ClassLoader c3 = c2.getParent();
        System.out.println("c2.parent = " + c3);

    }
}

3.2 类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化

代码演示

java 复制代码
public class ClassLoadingTest {
public static void main(String[] args) {
System.out.println(A.m);
} }
class A {
static { m = 300;
}
static int m = 100;
}
//第二步:链接结束后m=0
//第三步:初始化后,m的值由<clinit>()方法执行决定
// 这个A的类构造器<clinit>()方法由类变量的赋值和静态代码块中的语句按照顺序合并
产生,类似于
// <clinit>(){
// m = 300;
// m = 100;
// }

3.3 什么时候会进行类初始化 ?

java 复制代码
public class ClassLoadingTest {
public static void main(String[] args) {
// 主动引用:一定会导致A和Father的初始化
// A a = new A();
// System.out.println(A.m);
// Class.forName("com.atguigu.java2.A");
// 被动引用
A[] array = new A[5];//不会导致A和Father的
初始化
// System.out.println(A.b);//只会初始化
Father
// System.out.println(A.M);//不会导致A和
Father的初始化
}
static {
System.out.println("main所在的类");
} }
class Father {
static int b = 2;
static {
System.out.println("父类被加载");
} }
class A extends Father {
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
  • 类加载的作用 : 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类java.lang.Class对象,作为方法区中类数据的访问入口。
  • 类缓存 :标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象

3.4 ClassLoader的理解

类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。

4. 创建运行类的对象

4.1 有了Class对象,可以做什么

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

要求:

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

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

难道没有无参的构造器就不能创建对象了吗?

不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

步骤如下:

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

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

3)通过Constructor实例化对象。

以上是反射机制运用最多的地方

java 复制代码
//1.根据全类名获取对应的Class对象
String name = "com.manman.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);

5. 获取运行类的完整实例

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

Fileds、Menthod、Constructor、Superclass、Interface、Annotation

  • 实现的全部接口
  • 所继承的父类
  • 全部的构造器
  • 全部的方法
  • 全部的Field

使用反射可以取得:

1.实现的全部接口

  • public Class<?>[] getInterfaces()

确定此对象所表示的类或接口实现的接口。

2.所继承的父类

  • public Class<? Super T> getSuperclass()

返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的

Class。

3.全部的构造器

  • public Constructor[] getConstructors()

返回此 Class 对象所表示的类的所有public构造方法。

  • public Constructor[] getDeclaredConstructors()返回此 Class 对象表示的类声明的所有构造方法。

Constructor类中:

  • 取得修饰符: public int getModifiers();
  • 取得方法名称: public String getName();
  • 取得参数的类型:public Class<?>[] getParameterTypes();

4.全部的方法

  • public Method[] getDeclaredMethods()

返回此Class对象所表示的类或接口的全部方法

  • public Method[] getMethods()

返回此Class对象所表示的类或接口的public的方法

  • Method类中:
  • public Class<?> getReturnType()取得全部的返回值
  • public Class<?>[] getParameterTypes()取得全部的参数
  • public int getModifiers()取得修饰符
  • public Class<?>[] getExceptionTypes()取得异常信息

5.全部的Field

  • public Field[] getFields()

返回此Class对象所表示的类或接口的public的Field。

  • public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口的全部Field。

Field方法中:

  • public int getModifiers() 以整数形式返回此Field的修饰符
  • public Class<?> getType() 得到Field的属性类型
  • public String getName() 返回Field的名称。

6.Annotation相关

  • get Annotation(Class annotationClass)
  • getDeclaredAnnotations()

7.泛型相关

获取父类泛型类型:Type getGenericSuperclass()

泛型类型:ParameterizedType

获取实际的泛型类型参数数组:getActualTypeArguments()

8.类所在的包

Package getPackage()

6. 调用运行时类的的指定结构

6.1 调用指定方法

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

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

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

Object invoke(Object obj, Object ... args)

说明:

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

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

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

4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用

方法对象的setAccessible(true)方法,将可访问private的方法。

6.2 调用指定属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。

  • public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
  • public Field getDeclaredField(String name)返回此Class对象表示的类或接口的

指定的Field。

在Filed方法中 :

  • public Object get(Object obj) 取得指定对象obj上此Field的属性内容
  • public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

6.3 关于setAccessible方法的使用

  • Method和Filed、Construtor对象都有setAccessible()方法。

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

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

    • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
    • 使得原本无法访问的私有成员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查

7. 反射的应用 : 动态代理

7.2 初始代理模式

  • 代理设计模式的原理:

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理,特征是代理类和目标

对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。


动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

  • 动态代理使用场合:

    • 调试
    • 远程方法调用
  • 动态代理相比于静态代理的优点:

抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

7.2 Java动态代理相关的API

  • Proxy : 专门完成代理的操作类,是所有动态代理的父类。通过此类可以为一个或多个接口动态生成实现类。
  • 提供用于创建动态代理类和动态代理的静态方法。

7.3 动态代理的相关步骤

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。

2.创建被代理的类以及接口

3.通过Proxy的静态方法

newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理。

java 复制代码
RealSubject target = new RealSubject();
// Create a proxy to wrap the original implementation
DebugProxy proxy = new DebugProxy(target);
// Get a reference to the proxy through the Subject interface
Subject sub = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),new Class[] { Subject.class }, proxy);

4.通过 Subject代理调用RealSubject实现类的方法

java 复制代码
String info = sub.say("Peter", 24);
System.out.println(info);

7.4 动态代理与AOP(Aspect Orient Progranmming)

改进后的说明:

代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法

代码示例 :

java 复制代码
public interface Dog{
    void info();
    void run();
}
public class HuntingDog implements Dog{
public void info(){
	System.out.println("我是一只猎狗");
}
public void run(){
	System.out.println("我奔跑迅速");
	}
}
java 复制代码
public class DogUtil{
public void method1(){
	System.out.println("=====模拟通用方法一=====");
}
public void method2(){
	System.out.println("=====模拟通用方法二=====");
	} 
}
java 复制代码
public class MyInvocationHandler implements InvocationHandler{
// 需要被代理的对象
private Object target;
public void setTarget(Object target){
this.target = target;}
// 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Exception{
DogUtil du = new DogUtil();
// 执行DogUtil对象中的method1。
du.method1();
// 以target作为主调来执行method方法
Object result = method.invoke(target , args);
// 执行DogUtil对象中的method2。
du.method2();
return result;}}
java 复制代码
public class MyProxyFactory{
// 为指定target生成动态代理对象
public static Object getProxy(Object target)
throws Exception{
// 创建一个MyInvokationHandler对象
MyInvokationHandler handler = 
new MyInvokationHandler();
// 为MyInvokationHandler设置target对象
handler.setTarget(target);
// 创建、并返回一个动态代理对象
return 
Proxy.newProxyInstance(target.getClass().getClassLoader()
, target.getClass().getInterfaces() , handler);
} }
java 复制代码
public class Test{
public static void main(String[] args) 
throws Exception{
// 创建一个原始的HuntingDog对象,作为target
Dog target = new HuntingDog();
// 以指定的target来创建动态代理
Dog dog = (Dog)MyProxyFactory.getProxy(target);
dog.info();
dog.run();
	} 
}

8.反射的基本应用

8.1 获取类型的详细信息

可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)

示例代码获取常规信息:

java 复制代码
package com.atguigu.reflect;

package com.atguigu.reflect;

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

public class TestClassInfo {
    public static void main(String[] args) throws Exception {
        //1、先得到某个类型的Class对象
        Class clazz = String.class;
        //比喻clazz好比是镜子中的影子

        //2、获取类信息
        //(1)获取包对象,即所有java的包,都是Package的对象
        Package pkg = clazz.getPackage();
        System.out.println("包名:" + pkg.getName());

        //(2)获取修饰符
        //其实修饰符是Modifier,里面有很多常量值
        /*
         * 0x是十六进制
         * PUBLIC           = 0x00000001;  1    1
         * PRIVATE          = 0x00000002;  2	10
         * PROTECTED        = 0x00000004;  4	100
         * STATIC           = 0x00000008;  8	1000
         * FINAL            = 0x00000010;  16	10000
         * ...
         *
         * 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0
         *
         * mod = 17          0x00000011
         * if ((mod & PUBLIC) != 0)  说明修饰符中有public
         * if ((mod & FINAL) != 0)   说明修饰符中有final
         */
        int mod = clazz.getModifiers();
        System.out.println("类的修饰符有:" + Modifier.toString(mod));

        //(3)类型名
        String name = clazz.getName();
        System.out.println("类名:" + name);

        //(4)父类,父类也有父类对应的Class对象
        Class superclass = clazz.getSuperclass();
        System.out.println("父类:" + superclass);

        //(5)父接口们
        System.out.println("父接口们:");
        Class[] interfaces = clazz.getInterfaces();
        for (Class iter : interfaces) {
            System.out.println(iter);
        }

        //(6)类的属性,  你声明的一个属性,它是Field的对象
/*		Field clazz.getField(name)  根据属性名获取一个属性对象,但是只能得到公共的
		Field[] clazz.getFields();  获取所有公共的属性
		Field clazz.getDeclaredField(name)  根据属性名获取一个属性对象,可以获取已声明的
		Field[] clazz.getDeclaredFields()	获取所有已声明的属性
		*/
//        Field valueField = clazz.getDeclaredField("value");
//		System.out.println("valueField = " +valueField);

        System.out.println("------------------------------");
        System.out.println("成员如下:");
        System.out.println("属性有:");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            //修饰符、数据类型、属性名
            int modifiers = field.getModifiers();
            System.out.println("属性的修饰符:" + Modifier.toString(modifiers));

            String name2 = field.getName();
            System.out.println("属性名:" + name2);

            Class<?> type = field.getType();
            System.out.println("属性的数据类型:" + type);
        }
        System.out.println("-------------------------");
        //(7)构造器们
        System.out.println("构造器列表:");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (int i=0; i<constructors.length; i++) {
            Constructor constructor = constructors[i];
            System.out.println("第" + (i+1) +"个构造器:");
            //修饰符、构造器名称、构造器形参列表  、抛出异常列表
            int modifiers = constructor.getModifiers();
            System.out.println("构造器的修饰符:" + Modifier.toString(modifiers));

            String name2 = constructor.getName();
            System.out.println("构造器名:" + name2);

            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }

            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = constructor.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }
        System.out.println("---------------------------------");
        //(8)方法们
        System.out.println("方法列表:");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i=0; i<declaredMethods.length; i++) {
            Method method = declaredMethods[i];
            System.out.println("第" + (i+1) +"个方法:");
            //修饰符、返回值类型、方法名、形参列表 、异常列表
            int modifiers = method.getModifiers();
            System.out.println("方法的修饰符:" + Modifier.toString(modifiers));

            Class<?> returnType = method.getReturnType();
            System.out.println("返回值类型:" + returnType);

            String name2 = method.getName();
            System.out.println("方法名:" + name2);

            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }

            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = method.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }

    }
}

8.2 创建任意引用类型的对象

两种方式:

1、直接通过Class对象来实例化(要求必须有公共的无参构造)

2、通过获取构造器对象来进行实例化

方式一的步骤:

(1)获取该类型的Class对象(2)创建对象

方式二的步骤:

(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象

如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)

示例代码:

java 复制代码
package com.atguigu.reflect;

import org.junit.Test;

import java.lang.reflect.Constructor;

public class TestCreateObject {
    @Test
    public void test1() throws Exception{
//        AtGuiguClass obj = new AtGuiguClass();//编译期间无法创建

        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguClass");
        //clazz代表com.atguigu.ext.demo.AtGuiguClass类型
        //clazz.newInstance()创建的就是AtGuiguClass的对象
        Object obj = clazz.newInstance();
        System.out.println(obj);
    }

    @Test
    public void test2()throws Exception{
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
        //java.lang.InstantiationException: com.atguigu.ext.demo.AtGuiguDemo
        //Caused by: java.lang.NoSuchMethodException: com.atguigu.ext.demo.AtGuiguDemo.<init>()
        //即说明AtGuiguDemo没有无参构造,就没有无参实例初始化方法<init>
        Object stu = clazz.newInstance();
        System.out.println(stu);
    }

    @Test
    public void test3()throws Exception{
        //(1)获取Class对象
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
        /*
         * 获取AtGuiguDemo类型中的有参构造
         * 如果构造器有多个,我们通常是根据形参【类型】列表来获取指定的一个构造器的
         * 例如:public AtGuiguDemo(String title, int num)
         */
        //(2)获取构造器对象
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class);

        //(3)创建实例对象
        // T newInstance(Object... initargs)  这个Object...是在创建对象时,给有参构造的实参列表
        Object obj = constructor.newInstance("尚硅谷",2022);
        System.out.println(obj);
    }
}

8.3 操作任意类型的属性

(1)获取该类型的Class对象

Class clazz = Class.forName("包.类名");

(2)获取属性对象

Field field = clazz.getDeclaredField("属性名");

(3)如果属性的权限修饰符不是public,那么需要设置属性可访问

field.setAccessible(true);

(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象

Object obj = clazz.newInstance(); //有公共的无参构造

Object obj = 构造器对象.newInstance(实参...);//通过特定构造器对象创建实例对象

(4)设置属性值

field.set(obj,"属性值");

如果操作静态变量,那么实例对象可以省略,用null表示

(5)获取属性值

Object value = field.get(obj);

如果操作静态变量,那么实例对象可以省略,用null表示

示例代码:

java 复制代码
package com.atguigu.reflect;

public class Student {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
java 复制代码
package com.atguigu.reflect;

import java.lang.reflect.Field;

public class TestField {
    public static void main(String[] args)throws Exception {
        //1、获取Student的Class对象
        Class clazz = Class.forName("com.atguigu.reflect.Student");

        //2、获取属性对象,例如:id属性
        Field idField = clazz.getDeclaredField("id");

        //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
        idField.setAccessible(true);

        //4、创建实例对象,即,创建Student对象
        Object stu = clazz.newInstance();

        //5、获取属性值
        /*
         * 以前:int 变量= 学生对象.getId()
         * 现在:Object id属性对象.get(学生对象)
         */
        Object value = idField.get(stu);
        System.out.println("id = "+ value);

        //6、设置属性值
        /*
         * 以前:学生对象.setId(值)
         * 现在:id属性对象.set(学生对象,值)
         */
        idField.set(stu, 2);

        value = idField.get(stu);
        System.out.println("id = "+ value);
    }
}

8.4 调用任意类型的方法

(1)获取该类型的Class对象

Class clazz = Class.forName("包.类名");

(2)获取方法对象

Method method = clazz.getDeclaredMethod("方法名",方法的形参类型列表);

(3)创建实例对象

Object obj = clazz.newInstance();

(4)调用方法

Object result = method.invoke(obj, 方法的实参值列表);

如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)

如果方法是静态方法,实例对象也可以省略,用null代替

示例代码:

java 复制代码
package com.atguigu.reflect;

import org.junit.Test;

import java.lang.reflect.Method;

public class TestMethod {
    @Test
    public void test()throws Exception {
        // 1、获取Student的Class对象
        Class<?> clazz = Class.forName("com.atguigu.reflect.Student");

        //2、获取方法对象
        /*
         * 在一个类中,唯一定位到一个方法,需要:(1)方法名(2)形参列表,因为方法可能重载
         *
         * 例如:void setName(String name)
         */
        Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);

        //3、创建实例对象
        Object stu = clazz.newInstance();

        //4、调用方法
        /*
         * 以前:学生对象.setName(值)
         * 现在:方法对象.invoke(学生对象,值)
         */
        Object setNameMethodReturnValue = setNameMethod.invoke(stu, "张三");

        System.out.println("stu = " + stu);
        //setName方法返回值类型void,没有返回值,所以setNameMethodReturnValue为null
        System.out.println("setNameMethodReturnValue = " + setNameMethodReturnValue);

        Method getNameMethod = clazz.getDeclaredMethod("getName");
        Object getNameMethodReturnValue = getNameMethod.invoke(stu);
        //getName方法返回值类型String,有返回值,getNameMethod.invoke的返回值就是getName方法的返回值
        System.out.println("getNameMethodReturnValue = " + getNameMethodReturnValue);//张三
    }

    @Test
    public void test02()throws Exception{
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguClass");
        Method printInfoMethod = clazz.getMethod("printInfo", String.class);
        //printInfo方法是静态方法
        printInfoMethod.invoke(null,"尚硅谷");
    }
}
相关推荐
xcLeigh38 分钟前
HTML5好看的水果蔬菜在线商城网站源码系列模板8
java·前端·html5
Alsn861 小时前
11.Spring Boot 3.1.5 中使用 SpringDoc OpenAPI(替代 Swagger)生成 API 文档
java·spring boot·后端
liyongjun63161 小时前
Java List分页工具
java·后端
猎人everest2 小时前
Spring Boot集成Spring Cloud 2024(不使用Feign)
java·spring boot·spring cloud
茂桑2 小时前
日常开发小Tips:后端返回带颜色的字段给前端
java·状态模式
佩奇的技术笔记2 小时前
Java学习手册:Spring 中常用的注解
java·spring
一键三联啊2 小时前
GC的查看
java·jvm·python
howard20052 小时前
项目三 - 任务2:创建笔记本电脑类(一爹多叔)
java·接口·继承·抽象类
药尘师2 小时前
低版的spring boot 1.X接入knife4j
java·spring boot·后端
淋过很多场雨3 小时前
现代c++获取linux所有的网络接口名称
java·linux·c++