需求引出
1)根据配置文件指定信息,创建对象并调用方法;
2)在通过外部文件配置,不修改源码的条件下,来控制程序,符合设计模式的开闭原则(OCP,不修改源码并且扩张功能)。
package com.pero.reflection.question;
import com.pero.Cat;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author Pero
* @version 1.0
*/
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//根据配置文件指定信息,创建对象并调用方法;
//第一种:
//传统方式,直接通过new语句创建对象,然后调用方法
Cat cat = new Cat();
cat.hi();
//第二种:
//通过获取配置文件中的类相关信息,获取类途径
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
//(key=value)通过配置文件提供的key来获取类的全路径(其实是类名),和方法(方法名)
String classFullPath = properties.get("classFullPath").toString();
String methodName = properties.get("method").toString();
//创建对象
//new classFullPath(),错误!classFullPath是String类型的其内容只是一个类名
//new com.pero.Cat(),完成后返回Cat cat1 = new Cat();
//使用反射机制创建对象
//(1)先加载类,返回Class类对象
Class<?> aClass = Class.forName(classFullPath);
//(2)通过Class类对象aClass获取加载的类com.pero.Cat对象实例【newInstance()】
//对象cat编译类型为Object,运行类型是Cat
//并不清楚Cat类中的属性、方法等
Object o = aClass.newInstance();
System.out.println(o.getClass());
//通过aClass获取加载类com.pero.Cat的MethodName"hi"的方法对象【getMethod(methodName)】
//在反射中可以把方法视为对象
Method method = aClass.getMethod(methodName);
//通过method调用方法,通过方法对象来实现调用方法【invoke()】
//传统办法:对象.方法();反射机制实现办法:方法.invoke(对象)
method.invoke(o);
}
}
package com.pero;
/**
* @author Pero
* @version 1.0
*/
public class Cat {
private String name = "斯文克斯";
public void hi() {
System.out.println("hi" + name);
}
public void cry() {
System.out.println(name + "喵喵叫");
}
}
//在包下创建普通文件re.properties
classFullPath=com.pero.Cat
method=cry
反射机制
1)反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等),并且能够操作对象的属性及方法。反射在设计模式和框架底层都会用到;
2)加载完类后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。
Java程序在计算机中有三个阶段 | ||||
代码阶段/编译阶段 | Class类阶段(加载阶段) | Runtime运行阶段 | ||
class Cat{ private String name; public Cat(){} public void hi(){} } //Cat.java文件 | 方法区: Cat类的字节码二进制数据/元数据 | 得到Class对象后 1)创建对象 2)调用对象方法 3)操作属性等等 | ||
↓ | ||||
javac ↓ 编译 | Class类对象/在堆中 成员变量 Filed[] fileds 构造器 Constructor[] cons 成员方法 Method[] ms | → | Cat cat = new Cat(); cat.hi(); | |
字节码构成,包含信息: private String name public Cat(){} public hi(){} //Cat.class字节码文件 | → 类加载器 ClassLoader (体现反射) | ← | Cat对象/在堆中 该对象知道其属于的Class对象是谁 |
3)Java反射机制可以完成:①在运行时判断任意一个对象所属的类;②在运行时构造任意一个类的对象;③在运行时的到任意一个类所具有的成员变量和方法;④在运行时调用任意一个对象的成员变量和方法;⑤生成动态代理。
4)反射相关的主要类:①java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象;java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法;③java.lang.reflect.Filed:代表类的成员变量,Filed对象表示某个类的成员变量;④java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示某个类的构造器。
package com.pero.reflection;
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;
/**
* @author Pero
* @version 1.0
*/
public class Reflection_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classFullPath = properties.get("classFullPath").toString();
String methodName = properties.get("method").toString();
Class<?> aClass = Class.forName(classFullPath);
Object cat = aClass.newInstance();
System.out.println(cat.getClass());
Method method = aClass.getMethod(methodName);
method.invoke(cat);
//获取类成员变量,Filed对象表示某类的成员变量
Field ageFiled = aClass.getField("age");//不能直接得到私有属性
//传统写法:对象.成员变量;反射写法:成员变量对象.get(对象)
Object age = ageFiled.get(cat);
System.out.println(age);
//获取类的构造方法,Constructor对象表示类的构造器
//()中可以指定构造器参数类型
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);
Constructor<?> constructor1 = aClass.getConstructor(
String.class/*形参String类型的class对象*/);
System.out.println(constructor1);
}
}
package com.pero;
/**
* @author Pero
* @version 1.0
*/
public class Cat {
private String name = "斯文克斯";
public int age = 1;
public Cat(){}
public Cat(String name){
this.name = name;
}
public void hi() {
System.out.println("hi" + name);
}
public void cry() {
System.out.println(name + "喵喵叫");
}
}
反射的优缺点
1)优点:可以动态的创建和使用对象(框架底层核心),使用灵活,没有反射机制框架技术就失去了底层支撑;
2)缺点:使用反射基本是解释执行,对执行速度有影响。可以通过反射访问私有属性并可以做修改,安全性降低。
反射调用优化
1)Method和Filed、Constructor对象都有setAccessible()方法;
2)setAccessible作用是启动和禁用访问安全检查的开关;
3)参数值为true表示反射的对象在使用时取消访问安全检查,提高反射的效率;参数值为false表示反射的对象执行访问安全检查。
反射的优缺点及优化测试代码
package com.pero.reflection;
import com.pero.Cat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Pero
* @version 1.0
*/
public class Reflection02 {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
m1();
m2();
m3();
}
//普通方式调用对象方法
public static void m1() {
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 1; i <= 900000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("普通方式调用对象方法耗时:" + (end - start));
}
//反射调用对象方法
public static void m2() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> aClass = Class.forName("com.pero.Cat");
Object o = aClass.newInstance();
Method hi = aClass.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 1; i < 900000; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射调用对象方法耗时:" + (end - start));
}
//反射调用对象方法优化
public static void m3() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> aClass = Class.forName("com.pero.Cat");
Object o = aClass.newInstance();
Method hi = aClass.getMethod("hi");
hi.setAccessible(true); //参数值为true表示反射的对象在使用时取消访问安全检查
long start = System.currentTimeMillis();
for (int i = 1; i < 900000; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射调用对象方法优化后耗时:" + (end - start));
}
}
普通方式调用对象方法耗时:63
反射调用对象方法耗时:234
反射调用对象方法优化后耗时:203
Class类
基本介绍
1)Class也是类,同样也继承了Object类;
2)Class类对象不是new出来的,而是系统创建的;
3)对于某个类的Class类对象,在内存中只有一份,因为类只加载一次;
4)每个类的实例都会记住自己是由哪个Class实例所生成;
5)通过Class对象可以完整地得到一个类的完整结构,通过一系列API;
6)Class对象是存放在堆中的;
7)类的字节码二进制数据是放在方法区的,有的地方也称为类的元数据(包含方法代码、变量名、方法名、访问权限等)。
测试代码
package com.pero.reflection;
import com.pero.Cat;
/**
* @author Pero
* @version 1.0
*/
public class Class01 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//1)Class也是类,同样也继承了Object类;
//2)Class类对象不是new出来的,而是系统创建的;
//普通方式生成类对象
//Cat cat = new Cat();
//反射方式生成类对象
//类只会加载一次,在追反射生成类对象的过程中要将new Cat注销,否则无法追到类加载阶段
Class<?> aClass01 = Class.forName("com.pero.Cat");
//3)对于某个类的Class类对象,在内存中只有一份,因为类只加载一次;
Class<?> aClass02 = Class.forName("com.pero.Cat");
//aClass01的hashCode值与aClass02的hashCode值相同,表示两个类加载对象是同一个
System.out.println(aClass01.hashCode());
System.out.println(aClass02.hashCode());
Class<?> aClass = Class.forName("com.pero.Dog");
//aClass的hashCode值与前两个不同,Dog类在类加载时生成新的Class对象
System.out.println(aClass.hashCode());
//4)每个类的实例都会记住自己是由哪个Class实例所生成;
}
}
普通方式类加载源码
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
反射方式类加载 源码
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
static ClassLoader getClassLoader(Class<?> caller) {
// This can be null if the VM is requesting it
if (caller == null) {
return null;
}
// Circumvent security check since this is package-private
return caller.getClassLoader0();
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
Class类常用方法
方法名 | 功能说明 |
static Class forName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型等)名称 |
Class[] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperClass() | 返回此Class对象所表示的实体的超类的Class |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Field getDeclaredFields() | 返回Field对象的一个数组 |
Method getMethod(String name, Class...paramTypes) | 返回一个Method对象,对此对象的形参类型为paramType |
测试代码
package com.pero.reflection;
import com.pero.Car;
import java.lang.reflect.Field;
/**
* Class类的常用方法
* @author Pero
* @version 1.0
*/
public class Class02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//类的全路径
String classPath = "com.pero.Car";
//获取Car类对应的Class对象,<?>表示不确定的类型
Class<?> aClass = Class.forName(classPath);
System.out.println(aClass); //这里输出的是目标类的Class对象:com.pero.Car
System.out.println(aClass.getClass()); //这里输出的是aClass的运行类型:java.lang.Class
//获取包名
System.out.println(aClass.getPackage().getName());
//获取全类名
System.out.println(aClass.getName());
//通过aClass创建对象实例
Object o = aClass.newInstance();
Car car = (Car)aClass.newInstance(); //可以转成Car对象(意义不大)
System.out.println(car); //car.toString();
//通过反射获取属性
/*
* public java.lang.String com.pero.Car.brand
* private int com.pero.Car.price
* public java.lang.String com.pero.Car.color
*/
//获取全部属性(字段)包括私有的
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
//获取brand信息
Field brand = aClass.getField("brand");
System.out.println(brand.get(o));
//获取私有属性
Field price = aClass.getDeclaredField("price");
price.setAccessible(true); //越过安全检查
System.out.println(price.get(o));
//通过反射给公有属性赋值
brand.set(o,"路虎");
System.out.println(brand.get(o));
//给私有属性赋值(安全性降低)
price.set(o,1800000);
System.out.println(price.get(o));
//获取全部属性(字段),不包括私有属性
Field[] fields = aClass.getFields();
for (Field field :fields) {
System.out.println(field);
}
}
}
获取Class类对象的方式
1)前提:已知一个类的全名,且该类在类的路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException;实例:Class aClass = Class.foName("java.lang.Cat");
应用场景:多用于配置文件读取类全路径,加载类。
2)前提:若已知具体的类,通过类的class获取,该方式最为可靠安全,程序性能最高;实例:Class aClass = Cat.class;
应用场景:多用于参数传递,比如通过反射获取对应构造器对象。
3)前提:已知某个类的实例,调用实例的getClass()方法获取Class对象;实例:Class aClass = 对象.getClass();
应用场景:通过创建好的对象,获取Class对象。
4)其他方式:
ClassLoader classLoader = 对象.getClass().getClassLoader();
Class aClass = classLoader.loadClass("类的全类名");
5)基本数据类型(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类型对象
Class aClass = 基本数据类型.class;
6)基本数据类型对应的包装类,可以通过.TYPE得到Class类对象
Class aClass = 包装类.TYPE;
测试代码
package com.pero.reflection.class_;
import com.pero.Car;
/**
* 获取Class对象的方式
* @author Pero
* @version 1.0
*/
public class GetClass_ {
public static void main(String[] args) throws ClassNotFoundException {
//通过读取配置文件得到类的全路径,且类在该路径之下(编译阶段)
String classPath = "com.pero.Car";
Class<?> aClass = Class.forName(classPath);
System.out.println(aClass);
//处在类加载,通过类名.class获取,用于参数传递(类加载阶段)
Class<Car> carClass = Car.class;
System.out.println(carClass);
//运行类型其实就是该类在类加载阶段的Class类对象(运行阶段)
Car car = new Car();
Class<? extends Car> aClass1 = car.getClass();
System.out.println(aClass1);
//通过类加载器[类加载器有四种]获取类的Class对象(通过类加载器获取)
//①得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//②通过类加载器得到Class对象【classLoader.loadClass(classPath)】
Class<?> aClass2 = classLoader.loadClass(classPath);
System.out.println(aClass2);
//Class类对象的hashCode
System.out.println(aClass.hashCode());
System.out.println(carClass.hashCode());
System.out.println(aClass1.hashCode());
System.out.println(aClass2.hashCode());
//基本数据类型(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类型对象
Class<Integer> integerClass = int.class;
System.out.println(integerClass);
System.out.println(integerClass.hashCode());
//基本数据类型对应的包装类,可以通过.TYPE得到Class类对象
Class<Integer> type = Integer.TYPE;
System.out.println(type);
System.out.println(type.hashCode());
}
}
有Class对象的类型
1)外部类、成员内部类、静态内部类、局部内部类、匿名内部类;
2)interface:接口;
3)数组;
4)enum:枚举;
5)annotation:注解;
6)基本数据类型;
7)void。
测试代码
package com.pero.reflection.class_;
import java.io.Serializable;
/**
* 有Class类对象的类型
* @author Pero
* @version 1.0
*/
public class AllTypeClass {
public static void main(String[] args) {
//外部类
Class<String> stringClass = String.class;
System.out.println(stringClass);
//接口
Class<Serializable> serializableClass = Serializable.class;
System.out.println(serializableClass);
//数组
Class<int[]> aClass = int[].class;
System.out.println(aClass);
//二维数组
Class<Integer[][]> aClass1 = Integer[][].class;
System.out.println(aClass1);
//注解
Class<Deprecated> deprecatedClass = Deprecated.class;
System.out.println(deprecatedClass);
//枚举
Class<Thread.State> stateClass = Thread.State.class;
System.out.println(stateClass);
//基本数据类型
Class<Long> longClass = long.class;
System.out.println(longClass);
//void
Class<Void> voidClass = void.class;
System.out.println(voidClass);
//Class类的Class类对象
Class<Class> classClass = Class.class;
System.out.println(classClass);
}
}
类加载
基本介绍
反射机制是Java实现动态语言的关键,通过反射实现类的动态加载
1)静态加载:编译时加载相关的类,如果没有,编译时则报错,依赖性太强;
2)动态加载:运行时加载需要的类,如果运行时不用该类即使该类不存在也不报错,降低了依赖性。
类加载时机
1)当创建对象时(new);(静态加载)
2)当子类被加载时,父类也被加载;(静态加载)
3)调用类中的静态成员时;(静态加载)
4)通过反射。(动态加载)
类加载过程
类加载的三个阶段 | ||||||
加载阶段:JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至是网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象。 ↓ | 连接阶段 Linking (包含验证、准备、解析) | ← 连接阶段:将类的二进制数据合并到JRE中。 初始化阶段:1.到初始化阶段才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程;2.<clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并; ↓ | ||||
验证 verification 底层源码生成SecurityManager对象,对文件进行安全的校验。1.目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机要求,并且不会危害虚拟机自身安全;2.包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证;3.可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机加载的时间。 | ||||||
⇩ | ||||||
字节码文件 com.Cat.class | java运行 ⇨ | 加载阶段 Loading | ⇨ | 准备 Preparation JVM会在该阶段对静态变量分配内存并默认初始化(对应数据类型的默认初始值,如0、0L、null、false等),实例属性在此阶段不会分配内存,常量在此阶段直接赋值不再改变。 | ⇨ | 初始化阶段 initializtion 执行在类中定义的代码,显示和指定初始化。 |
javac ⇧ 编译 | ↑ “转化为二进制字节流加载到内存中”这句话意思是:JVM把某个类的字节码二进制数据加载到方法区,同时在堆区生成一个Class对象。 | ⇩ | ↑ 3.JVM会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行完<clinit>()方法。 | |||
Java源码 com.Cat.java | 解析 Resolution JVM将常量池内的符号引用替换为直接引用(内存地址的引用)。 | |||||
类加载后内存布局情况 | ||||||
方法区: 类的字节码(二进制数据),也就是原数据,比如类中定义的方法、访问权限变量等。 | ⇨ 引用 | 堆区: 类的Class对象(生成字节码对应的的数据结构,数据的访问入口)。 |
通过反射获取类的结构信息
第一组:java.lang.Class类 | |
getName | 获取类全名 |
getSimpleName | 获取简单类名 |
getFields | 获取所有public修饰的属性,包含本类以及父类的 |
getDeclaredFields | 获取本类中所有属性 |
getMethods | 获取所有public修饰的方法,包含本类以及父类的 |
getDeclaredMethods | 获取本类中所有方法 |
getConstructors | 获取本类public修饰的构造器 |
getDeclaredConstructors | 获取本类中所有构造器 |
getPackage | 以Package形式返回包信息 |
getSuperClass | 以Class形式返回父类信息 |
getInterfaces | 以Class[]形式返回接口信息 |
getAnnotations | 以Annotation[]形式返回注解信息 |
第二组:java.lang.reflect.Field | |
getModifiers | 以int形式返回修饰符【0表示默认修饰符,1表示public,2表示private,4表示protected,8表示static,16表示final】 |
getType | 以Class形式返回类型 |
getName | 返回属性名 |
第三组:java.lang.reflect.Method | |
getModifiers | 以int形式返回修饰符【0表示默认修饰符,1表示public,2表示private,4表示protected,8表示static,16表示final】 |
getReturnType | 以Class形式返回类型 |
getName | 返回方法名 |
getParameterTypes | 以Class[]返回参数类型数组 |
第四组:java.lang.reflect.Constructor | |
getModifiers | 以int形式返回修饰符 |
getName | 返回构造器名(全类名) |
getParameterTypes | 以Class[]返回参数类型数组 |
package com.pero.reflection;
import org.junit.jupiter.api.Test;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Observer;
/**
* @author Pero
* @version 1.0
*/
public class ReflectionUtils {
public static void main(String[] args) {
}
//第一组方法API
@Test
public void api_01() throws ClassNotFoundException {
//得到Class对象
Class<?> personClass = Class.forName("com.pero.reflection.Person");
//获取类全名
System.out.println(personClass.getName());
//获取简单类名
System.out.println(personClass.getSimpleName());
//获取本类及父类public的属性
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println("本类及父类public的属性:" + field.getName());
}
//获取本类中的所有属性
Field[] declaredFields = personClass.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println("本类中的所有属性:" + field.getName());
}
//获取本类及父类(父类的父类等)的public方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println("本类及父类(父类的父类等)的public方法:" + method.getName());
}
//获取本类的所有方法
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println("获取本类的所有方法:" + method.getName());
}
//获取本类及父类的构造器
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("获取本类及父类的构造器:" + constructor.getName());
}
//获取本类全部构造器
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println("获取本类全部构造器:" + constructor.getName());
}
//获取包信息
System.out.println(personClass.getPackage());
//以Class形式返回父类信息
System.out.println(personClass.getSuperclass());
//以Class[]形式返回本类实现接口信息
Class<?>[] interfaces = personClass.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println("以Class形式返回实现接口信息:" + anInterface);
}
//以Annotation[]形式返回注解信息
Annotation[] annotations = personClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解信息:" + annotation);
}
}
@Test
public void api_02() throws ClassNotFoundException {
Class<?> personClass = Class.forName("com.pero.reflection.Person");
//获取本类中的所有属性
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
//以int形式返回修饰符【0表示默认修饰符,1表示public,2表示private,
// 4表示protected,8表示static,16表示final】,组合的话就进行int加法
System.out.println("本类中的所有属性:" + declaredField.getName() +
"该属性的修饰符:" + declaredField.getModifiers() +
"该属性的类型:" + declaredField.getType());
}
//获取本类的所有方法
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println("获取本类的所有方法:" + method.getName() +
"获取该方法的修饰符:" + method.getModifiers() +
"获取该方法的返回类型:" + method.getReturnType());
//输出当前方法的形参数组情况
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该方法的形参类型:" + parameterType);
}
}
//获取本类全部构造器
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println("获取本类全部构造器:" + constructor.getName());
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println("该构造器的形参类型:" + parameterType);
}
}
}
}
interface IA {
}
interface IB {
}
class Animal implements IA {
public String name;
protected int age;
String job;
private double salary;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public Animal(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
public Animal(String name, int age, String job, double salary) {
this.name = name;
this.age = age;
this.job = job;
this.salary = salary;
}
public void m1() {
}
protected void m2() {
}
void m3() {
}
private void m4() {
}
}
@Deprecated
class Person extends Animal implements IB {
public String name;
protected int age;
String job;
private double salary;
public Person() {
}
public Person(String name) {
this.name = name;
}
private Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
private Person(String name, int age, String job, double salary) {
this.name = name;
this.age = age;
this.job = job;
this.salary = salary;
}
public void m1(String name, int age, double salary) {
}
protected String m5() {
return null;
}
void m3() {
}
private void m4() {
}
}
通过反射创建对象
1)方式一:调用类中的public修饰的无参构造器;
2)方式二:调用类中的指定构造器;
3)Class类相关方法:
①newInstance:调用类中的无参构造器,获取对应类的对象;
②getConstructor(Class...class):根据参数列表,获取对应的public构造器对象;
③getDecalaredConstructor(Class...class):根据参数列表,获取对应的所有构造器对象。
4)Constructor类相关方法:
①setAccessible:爆破;
②newInstance(Object...obj):调用构造器。
package com.pero.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author Pero
* @version 1.0
*/
public class ReflectionCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//获取Class对象
Class<?> aClass = Class.forName("com.pero.reflection.User");
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
}
//通过public无参构造器创建实例
Object o = aClass.newInstance();
System.out.println(o);
//通过public有参构造器创建实例
//①获取构造器传入形参(类型.class)
Constructor<?> constructor = aClass.getConstructor(String.class);
//②用构造器newInstance(/*添加实参(参数类型与形参一致)*/)
Object jake = constructor.newInstance("jake");
System.out.println(jake);
//通过private有参构造器创建实例(不能用getConstructor获取私有的构造器,
// 要用getDeclaredConstructor)
Constructor<?> declaredConstructor =
aClass.getDeclaredConstructor(String.class,int.class); //必须与构造器形参顺序一致
//对于私有构造器创建对象前要使用爆破功能
declaredConstructor.setAccessible(true);//爆破,使用反射可以访问private构造器
Object lucy = declaredConstructor.newInstance("lucy",28);//必须与构造器形参顺序一致
System.out.println(lucy);
}
}
class User{
private String name = "pero";
private int age = 20;
public User(){}
public User(String name){
this.name = name;
}
private User(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
通过反射访问类中的属性
1)根据属性名获取Field对象;
2)爆破(setAccessible(true));
3)访问(set(object,value)、get(object));
4)如果是静态属性,则set和get中的参数object,可以写为bull。
package com.pero.reflection;
import java.lang.reflect.Field;
/**
* @author Pero
* @version 1.0
*/
public class ReflectionAccessibleProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//得到Class对象
Class<?> studentClass =
Class.forName("com.pero.reflection.Student");
//创建对象(无参构造器)
Object o = studentClass.newInstance();
//得到age对象
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
Field age = studentClass.getField("age");
age.set(o,20);
System.out.println(age.get(o));
Field name = studentClass.getDeclaredField("name");
name.setAccessible(true); //修改私有属性前先爆破
name.set(null,"jake"); //静态属性对象填写null
System.out.println(name.get(null)); //静态属性对象填写null
System.out.println(o);
}
}
class Student{
private static String name;
public int age;
@Override
public String toString() {
return "Student{" +
"age=" + age + "name=" + name +
'}';
}
}
通过反射访问类中的方法
1)根据方法名和参数列表获取Method方法对象:
Method m = aClass.getDeclaredMethod(方法名,XX.class);
2)获取对象:Object o = aClass.newInstance();
3)爆破:m.setAccessible(true);
4)访问:Object returnValue = m.invock(o,实参列表);
5)如果是静态方法,则invoke的参数object可以修改为null 。
package com.pero.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Pero
* @version 1.0
*/
public class ReflectionAccessibleMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
//得到Class对象
Class<?> aClass =
Class.forName("com.pero.reflection.Boss");
//创建对象
Object o = aClass.newInstance();
//调用public方法
Method hi = aClass.getMethod("hi",String.class);//有形参一定要标记形参
hi.invoke(o,"pero");
//调用私有方法
Method say = aClass.getDeclaredMethod(
"say",String.class,int.class,char.class);
say.setAccessible(true); //访问私有方法必须爆破
System.out.println(say.invoke(o,"lucy",16,'g'));
//静态方法特有调用
System.out.println(say.invoke(null,"jack",18,'b'));
//方法有返回值同意都是返回Object接收,运行类型和方法定义的返回类型一致
Object invoke = say.invoke(o, "tom", 22, 'b');
}
}
class Boss {
private static String name;
public int age;
public Boss() {
}
private static String say(String s, int n, char c) {
return s + " " + n + " " + c;
}
public void hi(String s) {
System.out.println("hi" + s);
}
}