标签:java gets sse object equal param gem extends 如何
? 反射机制主要在于这个反字.
? 一般情况下,我们使用类的时候都是知道它是个什么类,比如定义了一个Person类,在使用的时候我们已经知道这个Person类是用来做什么的,于是我们就可以直接new一个Person类的实例对象,然后对这个实例对象进行操作,这是常规的操作。
public class Person {
public void say() {
System.out.println("hello java");
}
public static void main(String[] args) {
Person p = new Person();
p.say();
}
}
? 而反射,就是与常规情况不同,在一开始我们并不知道要初始化的类对象是什么,导致我们没法使用new去实例化,但可以使用JDK提供的反射API进行调用,比如如下代码:(相关API下面会提及)
public class Person {
public void say() {
System.out.println("hello java");
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception{
// 获取class类对象
Class<?> cls = Class.forName("Person");
// 获取方法
Method method = cls.getMethod("say");
// 获取构造器
Constructor<?> constructor = cls.getConstructor();
// 创建对象
Object obj = constructor.newInstance();
// 执行对象的方法
method.invoke(obj); // 输出 hello java
}
}
? 以上两种方式效果都是一样的,区别就在第一段代码在还没运行时候就已经确定了要运行的类是Person,而第二段代码是在运行时候,由字符串传递了类名,才知道要执行的类是哪个。
? 所以,Java的反射机制就是指在运行中,才会知道要运行的类是哪一个,而且可以在运行时候获取这个类的所有属性和方法。
理解Class类
首先得清楚,在java中,除了int
等基础类型外,其他类型全都是class
.
class是由JVM在执行过程中动态加载的,每加载一种class,JVM就会为其创建一个Class类型的实例,并关联起来,这里的Class类型,也是class的一种,只是名字和class相似(Java是大小写敏感的)。
第二,Class类的实例内容就是创建的类的类型信息,包括类名、包名、父类、实现的接口、所有方法、所有成员变量等。
比如我们创建了一个Person类,那么java就会生成一个内容是Person的Class类实例,这也意味着如果我们获取了某个类对象的Class实例,我们就可以知道这个Class
实例所对应的class
的所有信息(这就是反射)。
第三,Class类的对象不像普通类一样,是不可以使用new来创建的,它只能由JVM创建,因为这个类并没有public的构造方法。
如何获取Class类对象
有三种方式可以获取Class类对象:
类名.class:通过类名的属性class获取
比如:Class cls = String.class;
Class.forName():通过Class类的静态方法forName获取
用于已知一个类的完整类名(包括包名)
Class cls = Class.forName("java.lang.String");
对象.getClass():通过实例对象的getclass方法获取
用于已知实例对象的情况。
String str = "hello";
Class cls = str.getClass();
注意:因为Class类实例在JVM中是唯一的,所以上面三种方法获取的Class实例都是同一个的。
JVM动态加载
? JVM在执行java程序时候,并不是一次性把所有的class全部加载到内存的,而是在第一次需要用到class时候才会加载,比如下面的例子,执行ReflectTest.java时,用到了main,所以JVM会先把ReflectTest.class
加载到内存,但不会加载Person.class
,当执行到了new Person()
时候,发现需要用到Person类时候,才会去加载Person.class,这就是JVM动态加载的特性。
public class Person {
public void say() {
System.out.println("hello java");
}
}
public class ReflectTest {
public static void main(String[] args) {
Person p = new Person();
p.say();
}
}
? 正是有JVM动态加载的特性,我们才可以在运行时根据条件来加载不同的类
? 现在我们已经知道了怎么获取Class实例了,也知道了我们可以获取class的所有信息,但我们一般是通过这个Class实例来获取成员变量Field、构造器Constructor以及成员方法Method。
? 在Class类实例中,是将类的各个组成部分(成员变量,成员方法,构造器等)封装为其他对象的,所以我们通过Class实例获取到也是一个个对象,每个对象内有各自的方法。
? 下面就看看如何获取这些信息:
获取成员变量Field
Field[] getFields()
:获取所有public修饰的成员变量Field getField(String name)
:获取指定名称的public修饰的成员变量Field getDeclaredFields()
:获取所有的成员变量,不考虑修饰符Field getDeclaredField(String name)
:获取指定名称的成员变量,不考虑修饰符上面说过,Class类实例中式将类的每一部分都封装为对象,那么每一个成员变量,从Class类实例中获取到的也是一个对象Field,要操作成员变量,就是操作这个Field对象的成员,其主要方法有以下几个:
void set(Object obj, Object value)
:设置值Object get(Object obj)
:获取值void setAccessible(boolean flag)
:设置允许访问public class Person {
public int age;
private String name;
public void say() {
System.out.println("hello java");
}
}
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("Person");
Constructor<?> constructor = cls.getConstructor();
Object obj = constructor.newInstance();
// 获取所有public的成员变量
Field[] fields = cls.getFields();
for (Field f : fields) {
System.out.println(f.getName());
} // 输出:age
// 获取指定名字的public成员变量
Field field = cls.getField("age");
field.set(obj, 20);
System.out.println(field.get(obj));// 输出:20
// 获取所有的成员变量,不考虑修饰符
Field[] fields1 = cls.getDeclaredFields();
for(Field f : fields1) {
System.out.println(f.getName());
}// 输出: age name
// 获取指定名字的成员变量,不考虑修饰符,这里操作私有变量name
Field f = cls.getDeclaredField("name");
f.setAccessible(true);
f.set(obj, "hello");
System.out.println(f.get(obj)); // 输出:hello
}
}
注意:
? 在获取private修饰的成员变量时,如果没有加上这一句f.setAccessible(true);是会报一个异常
java.lang.IllegalAccessException: Class ReflectTest can not access a member of class Person with modifiers "private"
,是因为正常情况下,是无法访问Person类的private字段的,而setAccessible(true)
的意思是不管字段是不是public,一律运行访问
获取构造器Constructor
Constructor<?>[] getConstructors()
:获取所有public的构造器Constructor<T> getConstructor(Class<?>... parameterTypes)
:获取指定参数的public的构造器Constructor<?>[] getDeclaredConstructors()
:获取所有的构造器Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
:获取指定参数的构造器,不考虑修饰符注意:这里的参数
Class<?>... parameterTypes
是指可变参数,可以有一个,也可以没有,类型是Class类型的,比如参数为int,则为int.class,就是上述说的三种获取Class类对象的方法。
同样,获取构造器返回的是Constructor对象,也有几个常用操作的方法:
T newInstance(Object... initargs)
::创建一个实例void setAccessible(boolean flag)
:设置允许访问public class Person {
public int age;
private String name;
public Person() {
System.out.println("public无参构造方法");
}
public Person(int age,String name) {
System.out.println("age = " + age + ",name = " + name);
}
}
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("Person");
// 获取所有public修饰的构造方法
Constructor<?>[] constructors = cls.getConstructors();
// 获取无参构造方法
// Constructor<?> c0 = cls.getConstructor();
// c0.newInstance();
cls.newInstance(); // 对于空参的构造方法生成对象的,可以直接通过Class对象的newInstance()生成
// 获取指定参数列表的构造方法
Constructor<?> c = cls.getConstructor(int.class, String.class);
c.newInstance(10, "Apple");
}
}
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法。
获取成员方法Method
Method[] getMethods()
:获取所有public的方法(包括父类)Method getMethod(String name, Class<?>... parameterTypes)
:获取public的方法(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有方法(不包括父类)Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:获取当前类的某个方法(不包括父类)获取方法相对应返回的对象是Method,主要有以下几个方法调用:
Object invoke(Object obj, Object... args)
:执行方法String getName()
:获取方法名void setAccessible(boolean flag)
:设置允许访问父类Person:
public class Person {
public int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public String getName() {
return this.name;
}
}
子类Student:
public class Student extends Person {
private int score;
public Student(int age, String name, int score) {
super(age, name);
this.score = score;
}
public static void staticMethod(){
System.out.println("static Method");
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
private int getAge() {
return this.age;
}
}
反射操作:
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args) throws Exception{
Class<?> cls = Student.class;
// 获取构造器
Constructor<?> con = cls.getConstructor(int.class, String.class);
// 生成实例
Object obj = con.newInstance(20, "zhangsan",90);
// 获取类的所有public方法(包括父类方法)
Method[] methods = cls.getMethods();
for (Method m : methods) {
System.out.print(m.getName() + " ");
} // 输出:getScore setScore staticMethod getName wait wait wait equals toString hashCode getClass notify notifyAll
// 获取子类某个public方法
Method getScoreMethod = cls.getMethod("getScore");
System.out.println(getScoreMethod.invoke(obj)); // 输出: 90
// 获取父类某个public方法
Method getNameMethod = cls.getMethod("getName");
System.out.println(getNameMethod.invoke(obj)); // 输出:zhangsan
// 获取当前类的所有方法
Method[] methods2 = cls.getDeclaredMethods();
for (Method m : methods2) {
System.out.print(m.getName() + " ");
} // 输出:getAge setScore staticMethod getScore
// 获取当前类的指定的方法 私有方法--getAge
Method getAgeMethod = cls.getDeclaredMethod("getAge");
getAgeMethod.setAccessible(true);
System.out.println(getAgeMethod.invoke(obj)); // 输出: 20
// 获取静态方法
Method getStaticMethod = cls.getDeclaredMethod("staticMethod");
getStaticMethod.invoke(null); // 输出: static Method
}
}
注意:如果调用静态方法,是不需要指定实例对象的,所以invoke方法传入的第一个参数设置为null。
? 从以上的几个例子可看出,一般我们使用反射获取一个对象的步骤有以下几步:
获取类的Class对象实例(有三种方式)
比如:Class cls = Student.class
;
使用Class对象实例获取构造方法对象
比如:Constructor constructor = cls.getConstructor();
使用构造器方法对象的newInstance()方法创建对象
比如:Object obj = constructor.newInstance();
进行相关操作,获取成员变量,获取方法,调用方法等。
标签:java gets sse object equal param gem extends 如何
原文地址:https://www.cnblogs.com/LucasBlog/p/12181151.html