PHP 反射机制打印对象
Posted 心随所遇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP 反射机制打印对象相关的知识,希望对你有一定的参考价值。
<?php /** echo Obj::new(\'Redis\'); echo Obj::new(\'Redis\')->method(\'set\')?->isPublic(); */ class Obj extends ReflectionClass public static function std(?array $attrs = null) $std = new \\stdClass(); foreach ($attrs as $key => $value) ($key) ? $std->$value = \'\' : $std->$key = $value; return $std; public static function new($argument) return new static($argument); public function ($name) return $this->getConstant($name); public function property($name) try return $this->getProperty($name); catch (\\ReflectionException $e) return null; public function method($name) try return $this->getMethod($name); catch (\\ReflectionException $e) return null; public function __toString() $items = $this->getReflectionConstants(); foreach ($items as $key => $item) $class[\'const\'][] = (\'const %s = %s;\', $item->getName(), $item->getValue()); $items = $this->getProperties(); foreach ($items as $key => $item) $class[\'prop\'][] = (\'%s %s = "%s"\', (\' \', Reflection::getModifierNames($item->getModifiers())), $item->getName(), $item->getDefaultValue()); $items = $this->getMethods(); foreach ($items as $key => $item) $params = $item->getParameters(); $param = \'\'; if ($params) foreach ($params as $item1) $param .= $item1->() . \' \' . $item1->getName(); if ($item1->isDefaultValueAvailable()) $param .= \'=\' . $item1->getDefaultValue() . \', \'; else $param .= \', \'; $param = ($param, \', \'); $class[\'method\'][] = (\'%s %s(%s )\', (\' \', Reflection::getModifierNames($item->getModifiers())), $item->name, $param); $output = ($class[\'const\'] ?? [], $class[\'prop\'] ?? [], $class[\'method\'] ?? []); return ("\\n", $output);
反射机制(Reflection)
一、Java反射机制概述
1. 准动态语言
-
动态语言
- 可以在运行时,根据某些条件改变自身结构
- Object-C、C#、JavaScript、PHP、Python
-
静态语言
- 运行时结构不可改变
- Java、C、C++
- 利用反射机制可以使Java获得类似动态语言的特性
2. 反射
-
反射机制在执行期间借助ReflectionAPI
- 可以取得任何类的内部信息
- 并能直接操作任意对象的内部属性及方法(包括private修饰的)
-
加载完类后,在堆内存的方法去产生一个Class类型的对象——包含完整的类的结构信息
3. 反射的优缺点
- 优点
- 实现动态创建对象和编译
- 缺点
- 对性能有影响,反射是一种解释操作结构,操作JVM
4. 反射获取对象和new的区别
new——通过类来创建对象
class——通过获取class对象获取类、class对象有且只有一个
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//正常创建对象——通过类创建对象
Test t1 = new Test();
Test t2 = new Test();
t1.getAge();
//new创建的不是同一个对象
System.out.println(t1.hashCode());//460141958
System.out.println(t2.hashCode());//1163157884
//反射获取对象——通过对象获取类
Class c1 = Class.forName("com.shelton.reflection.Test");
Class c2 = Class.forName("com.shelton.reflection.Test");
c1.getName();
//Class对象是唯一的
System.out.println(c1.hashCode());//1956725890
System.out.println(c2.hashCode());//1956725890
}
}
//实体类
class Test {
private String name;
private int age;
private String hobby;
public Test() {
}
public Test(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return super.toString();
}
}
二、理解Class类并获取Class实例
1. 如何获取Class类的实例
-
已知具体的类,通过类的class属性获取(最安全,性能最高)
Class c1 = Person.class;
-
已知某 个类的实例,调用该实例的getClass()方法获取Class对象
Class c1 = person.getClass();
-
已知类的全名且该类在类路径下,静态的forName()方法获取,并抛出异常
Class c1 = Class.forName("demo01.Student");
-
内置基本数据类型可以直接用类名.Type
-
ClassLoader
public class Test02 { public static void main(String[] args) throws ClassNotFoundException { //new创建对象 Person p1 = new Student();//多态 System.out.println(p1.name);//学生 //方式1—— Class c1 = Student.class; System.out.println(c1.hashCode());//460141958 //方式2——通过对象获得 Class c2 = p1.getClass(); System.out.println(c2.hashCode());//460141958 //方式3——通过包名获得 Class c3 = Class.forName("com.shelton.reflection.Student"); System.out.println(c3.hashCode());//460141958 //方式4——基本类型的包状态 Class c4 = Integer.TYPE; System.out.println(c4);//int //获取父类 Class c5 = c1.getSuperclass(); System.out.println(c5);//class com.shelton.reflection.Person } } class Person { String name; } class Student extends Person { public Student() { this.name = "学生"; } } class Teacher extends Person { public Teacher() { this.name = "老师"; } }
2. 哪些类可以有class对象
public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class;//类
Class c2 = Runnable.class;//接口
Class c3 = String[].class;//一维数组
Class c4 = int[][].class;//二维数组
Class c5 = Target.class;//注解
Class c6 = ElementType.class;//枚举类型
Class c7 = Integer.class;//包装类
Class c8 = void.class;//空类型
Class c9 = Class.class;//类
System.out.println(c1);//class java.lang.Object
System.out.println(c2);//interface java.lang.Runnable
System.out.println(c3);//class [Ljava.lang.String;
System.out.println(c4);//class [[I
System.out.println(c5);//interface java.lang.annotation.Target
System.out.println(c6);//class java.lang.annotation.ElementType
System.out.println(c7);//class java.lang.Integer
System.out.println(c8);//void
System.out.println(c9);//class java.lang.Class
//同一个类只有一个class对象
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());//325040804
System.out.println(b.getClass().hashCode());//325040804
}
}
三、类的加载与ClassLoader
- 程序使用某个类的时候
1 类的加载
-
加载
- 生成class对象
-
连接
- 验证:确保加载类的信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(Static)再方法区中分配内存,并设置变量的默认值
- 解析:虚拟机常量池内的符号(常量名)引用替换为直接引用(地址)
-
初始化
-
类构造器
<clinit>()方法
-
先初始化父类
-
虚拟机会保证一个类的
()方法在多线程环境中被正确加锁和同步 public class Test04 { public static void main(String[] args) { //1. Test004 test加载到内存,产生一个Class对象 A test = new A(); System.out.println("调用完类的a值:"+ A.a);//静态变量 } } class A { //2. 链接 链接结束后,a = 0 static { System.out.println("静态代码块初始化"); int a = 300; } static int a = 100;//静态变量 //3. new Test004()构造器初始化前,先执行类构造器clinic(由静态域组成) /* <clinit>(){ System.out.println("静态代码块初始化"); int a = 300; static int a = 100;//静态变量 } */ public A() { System.out.println("A类的无参构造"); } } /* 静态代码块初始化 A类的无参构造 调用完类的a值:100 */
-
2 为什么会发生初始化
-
类的主动引用——一定发生初始化
new和反射一定会初始化
- 虚拟机启动,先初始化main方法所在的类
- 调用静态成员(final常量除外)和静态方法
- java.lang.reflect包方法对类反射调用
- 初始化一个类,先初始化它的父类
-
类的被动引用——不会发生初始化
-
访问静态域时,只有真正声明这个域的类才会被初始化
- 子类引用父类的静态变量,子类不会初始化
-
数组定义类引用
-
引用常量
public class Test07 { static { System.out.println("Main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { //主动引用 //1. new Son son = new Son(); /* Main类被加载 父类被加载 子类被加载 */ //2. 反射 Class c1 = Class.forName("com.shelton.reflection.Son"); /* Main类被加载 父类被加载 子类被加载 */ // 被动引用 // 1.子类调用父类的静态变量、方法,子类不会被加载 System.out.println(Son.b); /* Main类被加载 父类被加载 2 */ //2. 数组 Son[] array = new Son[10]; /* Main类被加载 */ //3. 静态常量 System.out.println(Son.M); /* Main类被加载 1 */ } } class Father { static int b = 2; static { System.out.println("父类被加载"); } } class Son extends Father { static { System.out.println("子类被加载"); m = 300; } static int m = 100; static final int M = 1; }
-
3 类加载器
-
类加载:将class文件字节码加载到内存中,生成唯一的Class对象,作为方法区类数据的访问入口
-
类缓存:类加载器加载了某个类后,会维持加载一段时间(缓存),JVM垃圾回收机制可以回收这些Class对象
- 提高效率
-
类加载器的作用
-
自定义类加载器App--->系统类加载器Syc ---> 扩展类加载器Exc ---引导类加载器Boot(无法获取)
public class Test08 { public static void main(String[] args) throws ClassNotFoundException { //1. 加载器类型 //获取系统类的加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //获取系统类加载器的父类加载器--->扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); //获取扩展类加载器的父类加载器--->根加载器 ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //2. 测试类是哪个加载器加载的 System.out.println("***********测试***********"); //当前类——用户自定义类 ClassLoader classLoader = Class.forName("com.shelton.reflection.Test08").getClassLoader(); System.out.println(classLoader);//APP //Object类——JDK内置类 ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader1);//null——C++写的无法获取 //3. 获得系统类加载器可以加载的路径 System.out.println(System.getProperty("java.class.path")); /* C:Program FilesJavajdk1.8.0_212jrelibcharsets.jar; C:Program FilesJavajdk1.8.0_212jrelibdeploy.jar; C:Program FilesJavajdk1.8.0_212jrelibextaccess-bridge-64.jar; C:Program FilesJavajdk1.8.0_212jrelibextcldrdata.jar; C:Program FilesJavajdk1.8.0_212jrelibextdnsns.jar; C:Program FilesJavajdk1.8.0_212jrelibextjaccess.jar; C:Program FilesJavajdk1.8.0_212jrelibextjfxrt.jar; C:Program FilesJavajdk1.8.0_212jrelibextlocaledata.jar; C:Program FilesJavajdk1.8.0_212jrelibext ashorn.jar; C:Program FilesJavajdk1.8.0_212jrelibextsunec.jar; C:Program FilesJavajdk1.8.0_212jrelibextsunjce_provider.jar; C:Program FilesJavajdk1.8.0_212jrelibextsunmscapi.jar; C:Program FilesJavajdk1.8.0_212jrelibextsunpkcs11.jar; C:Program FilesJavajdk1.8.0_212jrelibextzipfs.jar; C:Program FilesJavajdk1.8.0_212jrelibjavaws.jar; C:Program FilesJavajdk1.8.0_212jrelibjce.jar; C:Program FilesJavajdk1.8.0_212jrelibjfr.jar; C:Program FilesJavajdk1.8.0_212jrelibjfxswt.jar; C:Program FilesJavajdk1.8.0_212jrelibjsse.jar; C:Program FilesJavajdk1.8.0_212jrelibmanagement-agent.jar; C:Program FilesJavajdk1.8.0_212jrelibplugin.jar; C:Program FilesJavajdk1.8.0_212jrelib esources.jar; C:Program FilesJavajdk1.8.0_212jrelib t.jar; D:CodesJAVAJavaSEReflectionoutproductionReflection; C:Program FilesJetBrainsIntelliJ IDEA 2020.1.1libidea_rt.jar */ } }
-
-
双亲委派机制
- 用户自己写了一个String类,但在java.lang.String里有这个String包了
public class String { }
- 如果调用String类里的方法,不会调用这个String类,而是调用java.lang.String
- 双亲委派机制会在创建对象后,先向上找包(App ---> Exc ---Boot),找不到就再使用用户自定义加载器
四、获取运行时类的完整结构
-
通过反射获取运行时类的完整结构
- Field、Method、Constructor、Superclass、Interface、Annotation
-
例如有一个实体类User
public class User { private String name; private int id; private int age; public String hobby; public User(String name, int id, int age) { this.name = name; this.id = id; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name" + name + ‘‘‘ + ", id" + id + ", age" + age + ‘}‘; } }
1 获得类的名字
-
getName()
-
getSimpleName()
User user = new User(); Class c2 = user.getClass(); //获得类的名字——通过对象 System.out.println(c2.getName()); //包名+类名 System.out.println(c2.getSimpleName()); //类名
2 获得类的属性
-
getFields()
-
getDeclaredFields()
Class c1 = Class.forName("com.shelton.reflection.User"); //获得类的属性 //只能找到public属性 Field[] fields = c1.getFields(); for (Field field : fields) { System.out.println(field); } //找到全部的属性 Field[] declaredFields = c1.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } //获得指定属性 Field name = c1.getDeclaredField("name"); System.out.println(name); /* ============getFields()============ public java.lang.String com.shelton.reflection.User.hobby ============getDeclaredFields()============ private java.lang.String com.shelton.reflection.User.name private int com.shelton.reflection.User.id private int com.shelton.reflection.User.age public java.lang.String com.shelton.reflection.User.hobby ============getDeclaredField("name")============ private java.lang.String com.shelton.reflection.User.name */
3 获得类的方法
-
getMethods()
-
getDeclaredMethods()
Class c1 = Class.forName("com.shelton.reflection.User"); //获得本类及其父类的所有方法 Method[] methods = c1.getMethods(); for (Method method : methods) { System.out.println(method); } //获得本类的所有方法 Method[] declaredMethods = c1.getDeclaredMethods(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } //获得指定方法 //需要这个参数,因为方法有重载,方法名是一样的 Method getName = c1.getMethod("getName", null); Method setName = c1.getMethod("setName", String.class); System.out.println(getName); System.out.println(setName); /* ============getMethods()============ public java.lang.String com.shelton.reflection.User.toString() public java.lang.String com.shelton.reflection.User.getName() public int com.shelton.reflection.User.getId() public void com.shelton.reflection.User.setName(java.lang.String) public int com.shelton.reflection.User.getAge() public void com.shelton.reflection.User.setId(int) public void com.shelton.reflection.User.setAge(int) public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ============getDeclaredMethods()============ private java.lang.String com.shelton.reflection.User.name private int com.shelton.reflection.User.id private int com.shelton.reflection.User.age public java.lang.String com.shelton.reflection.User.hobby ============getMethod("getName", null)============ public java.lang.String com.shelton.reflection.User.getName() public void com.shelton.reflection.User.setName(java.lang.String) */
4 获得类的构造器
-
getConstructors()
-
getDeclaredConstructors()
//获得本类及父类的构造器 Constructor[] constructors = c1.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } //获得本类的构造器 Constructor[] declaredConstructors = c1.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } //获得指定的构造器 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); System.out.println(declaredConstructor); /* ============getConstructors()============ public com.shelton.reflection.User() public com.shelton.reflection.User(java.lang.String,int,int) ============getDeclaredConstructors============ public com.shelton.reflection.User() public com.shelton.reflection.User(java.lang.String,int,int) ============getDeclaredConstructor============ public com.shelton.reflection.User(java.lang.String,int,int) */
五、有了class对象,能做什么?
1 通过反射获取类构造器创建对象
-
newInstance()方法
-
类必须有无参构造
-
类的构造器访问权限必须足够
Class c1 = Class.forName("com.shelton.reflection.User"); //本质是调用无参构造创建对象,因此删除User的无参构造后会报错 User user = (User)c1.newInstance(); System.out.println(user);//User{name123‘, id1, age1}
-
也可以使用有参构造创建user对象
Class c1 = Class.forName("com.shelton.reflection.User"); //调用User的有参构造器使用 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); //创建User对象 User user2 = (User)declaredConstructor.newInstance("shelton", 1, 20); System.out.println(user2);//User{nameshelton‘, id1, age20}
-
2 通过反射获取一个方法
Class c1 = Class.forName("com.shelton.reflection.User");
User user3 = (User)c1.newInstance();
//获取set方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//使用方法
//invoke(对象,方法的值)
setName.invoke(user3,"哈哈");
System.out.println(user3);//User{name哈哈‘, id0, age0}
3 通过反射操作属性
如果是private属性,则要设置accessible为true
Class c1 = Class.forName("com.shelton.reflection.User");
//创建user对象
User user4 = (User)c1.newInstance();
//获取name属性
Field name = c1.getDeclaredField("name");
//关闭安全检测——因为name属性是private,不能直接操作
name.setAccessible(true);
//赋值
name.set(user4,"嘿嘿");
System.out.println(user4.getName());//嘿嘿
4 通过accessible来提高效率
public class Test11 {
//普通方式
public static void Test1 () {
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1_000_000_000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式运行了:"+(endTime-startTime)+"ms");
}
//反射方式
public static void Test2 () throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class c1 = Class.forName("com.shelton.reflection.User");
User user = (User)c1.newInstance();
Method getName = c1.getDeclaredMethod("getName",null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1_000_000_000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式运行了:"+(endTime-startTime)+"ms");
}
//反射方式——关闭检测
public static void Test3 () throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class c1 = Class.forName("com.shelton.reflection.User");
User user = (User)c1.newInstance();
Method getName = c1.getDeclaredMethod("getName",null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1_000_000_000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射关闭检测方式运行了:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Test1();
Test2();
Test3();
}
}
/*
普通方式运行了:5ms
反射方式运行了:2837ms
反射关闭检测方式运行了:2049ms
*/
5 反射操作泛型
//通过反射获取泛型
public class Test12 {
public void method01(Map<String,User> map, List<User> list) {
System.out.println("method01");
}
public Map<String,User> method02() {
System.out.println("method02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
//获取泛型信息
//1. 获取方法,输入参数,然后生成一个对象
Method method01 = Test12.class.getDeclaredMethod("method01", Map.class, List.class);
//2. 获取泛型类型,返回一个Type类型的数组,生成对象
Type[] genericParameterTypes = method01.getGenericParameterTypes();
//3. 打印输出这个Type类型数组
for (Type genericParameterType : genericParameterTypes) {
System.out.println("泛型信息:"+genericParameterType);
//4. 获取泛型的参数
//判断泛型的参数类型 是否为 结构化参数类型
if (genericParameterType instanceof ParameterizedType) {
//是则强转成 结构化参数类型,调用getActualTypeArguments()方法获取参数
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(" 参数:"+actualTypeArgument);
}
}
}
}
}
/* 泛型信息:java.util.Map<java.lang.String, com.shelton.reflection.User>
参数:class java.lang.String
参数:class com.shelton.reflection.User
泛型信息:java.util.List<com.shelton.reflection.User>
参数:class com.shelton.reflection.User
*/
6 通过反射操作注解
ORM——object relationship mapping
通过反射操作注解
-
创建注解(类、属性)
//注解——类 @Target(value = ElementType.TYPE)//作用于类 @Retention(value = RetentionPolicy.RUNTIME)//运行时有效 @interface MyClass { String value() default ""; } //注解——属性 @Target(value = ElementType.FIELD)//作用于属性 @Retention(RetentionPolicy.RUNTIME) @interface Myfield { String columnName() default "null";//字段名 String type() default "null"; //类型 int length() default 0;//长度 }
-
创建一个实体类Student
//实体类 @MyClass("this is a boy") class People { @Myfield(columnName = "boy_id",type = "int",length = 10) private int id; @Myfield(columnName = "boy_age",type = "int",length = 10) private int age; @Myfield(columnName = "boy_name",type = "varchar",length = 3) private String name; public People() { } public People(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Man{" + "id=" + id + ", age=" + age + ", name=‘" + name + ‘‘‘ + ‘}‘; } }
-
通过反射操作注解
public class test13 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { //1. 获取类的注解信息 //获取Man类的class对象 Class c1 = Class.forName("com.shelton.reflection.People"); //通过class对象调用 getAnnotations()方法,获取注解,创建对象 Annotation[] annotations = c1.getAnnotations(); //遍历打印输出 for (Annotation annotation : annotations) { System.out.println(annotation); //@com.shelton.reflection.MyClass(value=this is a boy) } //2. 获取类注解,value的值 //强转成MyClass类型,获取value参数 MyClass annotation = (MyClass)c1.getAnnotation(MyClass.class); System.out.println(annotation.value());//this is a boy //3. 获取指定属性的注解信息 Field name = c1.getDeclaredField("name"); Myfield annotation1 = name.getAnnotation(Myfield.class); System.out.println(annotation1.columnName());//boy_name System.out.println(annotation1.type());//varchar System.out.println(annotation1.length());//3 } }
以上是关于PHP 反射机制打印对象的主要内容,如果未能解决你的问题,请参考以下文章