JAVA之反射机制
反射的JAVA的一个重要的机制
静态语言:在编译期间检查类型,所有的类型检查发生在编译时。变量的类型在编译时是已知的,不会随着程序运行时期的变化而变化。典型的静态语言包括 Java、C#、C++等。
动态语言:在运行时检查类型,类型检查发生在运行时。变量的类型可以在运行时随程序的执行而改变。典型的动态语言包括 Python、JavaScript、Ruby等。
虽然java是一个静态语言,但是反射机制让java可以成为一个准动态语言。
正常方式实例化对象的过程:导入包类->new关键字实例化对象->获取实例化对象
反射方式:实例化对象->getClass()方法->得到完整的包类
可以说反射是正常的逆过程,但是因为是逆推导过程,运行速度要比正常的慢不少
以下是java中与反射有关的API
- java.lang.Class:类
- java.lang.reflect.Field:类的字段
- java.lang.reflect.Method:类的方法
- java.lang.reflect.Constructor:类的构造器
- java.lang.reflect.Modifier:类的修饰符
反射初体验
//Student
package com.std.www.javaReflection;
public class Student {
private int id;
private String name;
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
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;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//ReflectionTest
package com.std.www.javaReflection;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
Class c1=Class.forName("com.std.www.javaReflection.Student");
System.out.println(c1);
Class c2=Class.forName("com.std.www.javaReflection.Student");
Class c3=Class.forName("com.std.www.javaReflection.Student");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
从上面可以发现,一个类在内存加载中只会有一个Class对象,并且整个类的结构都会保存到该Class对象中
Class类介绍
方法:
-
forName(String className)
:静态方法,根据类的完全限定名(包括包名)返回对应的 Class 对象。 -
newInstance()
:通过默认构造函数创建类的新实例(已经过时)。 -
getDeclaredFields()
:获取类中所有字段的数组,包括公共、受保护、默认(包)访问和私有字段,但不包括继承的字段。 -
getDeclaredMethods()
:获取类中所有方法的数组,与getDeclaredFields
类似,包括公共、受保护、默认(包)访问和私有方法,但不包括继承的方法。 -
getDeclaredConstructors()
:获取类中声明的所有构造函数。 -
getMethod(String name, Class<?>... parameterTypes)
:获取类中指定方法名和参数类型的公共方法。 -
getField(String name)
:获取类中指定名称的公共字段。
属性:
-
static final Class<?> TYPE
:Class
对象表示的类的基本类型,如果该类不是基本类型,则返回null
。 -
ClassLoader getClassLoader()
:获取加载这个类的类加载器。 -
String getName()
:获取类的名称。 -
Class<?>[] getInterfaces()
:获取该类实现的接口列表。 -
Class<? super T> getSuperclass()
:获取该类的父类。
得到Class类的几种方法
package com.std.www.javaReflection;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
Student stu=new Student();
//通过对象直接获取
Class c1=stu.getClass();
//forName获取
Class c2=Class.forName("com.std.www.javaReflection.Student");
//类名获取
Class c3=Student.class;
//包装类独有的获取方法
Class c4=Integer.TYPE;
//获取父类的Class类
Class c5=c1.getSuperclass();
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
}
}
哪些类有Class对象
- 外部类,成员类,局部内部类,匿名内部类
- 接口
- 数组
- 枚举
- 注解
- 基本数据类型
- void
类加载器
类加载器(Class Loader)是 Java 运行时环境的一部分,它负责加载 Java 类文件并将其转换为在 JVM 中运行的 Java 类。类加载器的主要任务是在运行时查找和加载类文件。它按需加载类文件,使得在程序运行时可以动态加载类。
类加载器的主要特点如下:
- 加载类:负责将类文件的二进制数据加载到内存中。
- 检查类的唯一性:确保加载的类在 JVM 中是唯一的,即对于同一个类文件,不会被加载多次。
- 类的链接:在加载类时,进行验证、准备、解析等步骤,最终生成可执行代码。
- 加载顺序:类加载器采用双亲委派模型,按照一定的顺序加载类,首先交由父类加载器加载,如果父类加载器无法加载,则由当前类加载器加载。
在 Java 中,有如下几种类加载器:
- 引导类加载器(Bootstrap Class Loader):负责加载 Java 核心库,是最顶层的类加载器。
- 扩展类加载器(Extension Class Loader):负责加载 Java 的扩展库,例如我们自定义的lib目录下的jar包。
- 系统类加载器(System Class Loader):也称为应用类加载器,负责加载应用程序的类。
我们也可以通过扩展自定义类加载器
package com.std.www.javaReflection;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统加载类可以加载类的路径
String loaders=System.getProperty("java.class.path");
String[] str=loaders.split(";");
for (String s : str) {
System.out.println(s);
}
}
}
如果类不在上面的路径中就无法加载
反射获取类的结构
package com.std.www.javaReflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1=Class.forName("com.std.www.javaReflection.Student");
//获取类的公有属性
System.out.println("==================类的公有属性==================");
Field[] fields = c1.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取类的全部属性
System.out.println("==================类的全部属性==================");
fields=c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//获取指定类的属性
System.out.println("==================指定类的属性==================");
Field field=c1.getDeclaredField("name");
System.out.println(field);
System.out.println("==================类的公共方法==================");
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("==================类的全部方法==================");
methods=c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("==================类的指定方法=================");
Method method = c1.getMethod("setName", String.class);//这里为参数的类型,因为可能存在参数重载
System.out.println(method);
System.out.println("==================类的公共构造器=================");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("==================类的指定构造器=================");
constructors=c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("==================类的指定构造器=================");
System.out.println(c1.getConstructor(null));
System.out.println(c1.getConstructor(int.class,String.class,int.class));
}
}
Class对象的用法
package com.std.www.javaReflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//通过反射获取Class对象
Class c=Class.forName("com.std.www.javaReflection.Student");
//获取构造器
Constructor con = c.getConstructor(int.class, String.class, int.class);
//通过构造器创建对象
Object stu = con.newInstance(2,"张三",20);
System.out.println(stu);
//通过反射获取方法并调用
Method setName = c.getDeclaredMethod("setName", String.class);
//方法.invoke(对象,方法实参),激活,把获取到的方法和实际的对象进行绑定调用
setName.invoke(stu,"王五");
System.out.println(stu);
//通过反射获取属性
Field name=c.getDeclaredField("name");
name.setAccessible(true);
name.set(stu,"程序员");
System.out.println(stu);
Field age=c.getDeclaredField("age");
age.set(stu,40);
}
}
这里我们可以发现私有属性的方法和值无法直接调用,但是可以通过关闭其的属性检测来进行更改,这里设置为可以访问
这就是反射的基本操作