反射学习笔记
反射学习笔记
反射是在程序运行过程中分析类的一种能力。java反射机制在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
获取Class对象
java文件编译后生成的class文件(字节码文件)是唯一的,所有的对象都是通过class文件来创建。一个java文件对应一个字节码文件,一个字节码文件(后缀名为class的文件)对应一个Class对象。
有三种方式可以获取到一个类的Class对象,通过使用Class对象,可以获取到类的相关信息(例如类的构造器、方法和属性)。Class本身也是一个类型,Class提供了一系列的方法来获取类的相关信息。
所有的引用数据类型都具备一个class属性,所有的对象都有一个getClass()方法,对于基本数据类型也都有一个class属性(例如 int.class)。
方式一:
所有对象都可以调用继承自Object类的getClass()方法,该方法返回对象对应的Class对象。
代码演示:
//p代表一个Person类的对象
Person p = new Person();
//调用对象p的getClass方法,获取到一个Class对象,用cls接收
Class cls = p.getClass();
同一个类的对象,对应的Class对象是一致的:
public static void main(String[] args) {
//获取p1的class对象
Person p1 = new Person();
Class cls1 = p1.getClass();
//获取p2的class对象
Person p2 = new Person();
Class cls2 = p2.getClass();
//同一个类只有一个class文件,class文件所对应的类型就是Class
System.out.println(cls1 == cls2);
}
/*程序运行结果
true
*/
方式二:
每一个类都具有一个.class静态属性,用对象调用class属性获取到类的Class对象
public static void main(String[] args) {
//类名.class 得到一个Class对象
Class cls1 = Person.class;
Class cls2 = Person.class;
//比较两次获得的Class对象是否相同
System.out.println(cls1 == cls2);
}
/*程序运行结果
true
*/
方式三:
前两种方式都是在已知类名的情况下获得Class对象,使用的前提是类中有对应的java文件,并不常用。
通常情况下在使用第三方的jar包时,能知道的仅仅是通过build path添加到功能的class文件的路径及名称。
这时候需要用到Class类中的静态方法forName()
Class.forName()返回的是一个Class对象。
Class.forName()的作用是使JVM查找指定路径类的class文件,然后将class文件读入内存,为该类生成一个class对象作为访问类型信息的入口。
public static void main(String[] args) throws ClassNotFoundException {
//获取路径下的Person.class文件,返回一个该class文件对应的Class对象,如果找不到Person.class文件,则会出现ClassNotFoundException,因此要抛出异常
Class cls1 = Class.forName("cn.study.reflex.Person");
//使用前两种方式获取Person的Class对象
Person p = new Person();
Class cls2 = p.getClass();
Class cls3 = Person.class;
//测试获取到的Class对象是否和另外两个方式获取到的一样
System.out.println(cls1 == cls2 );
System.out.println(cls1 == cls3);
}
/*程序运行结果
true
true
*/
通过反射获取类的构造器
1.获取构造器:
1).批量获取构造器:
public Constructor[] getConstructors():获取所有"公有的"构造器。
public Constructor[] getDeclaredConstructors():获取所有的构造器(包括私有、受保护、默认、公有)
2).单个获取构造器:
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造器。
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造器"可以是私有的,或受保护、默认、公有。
2.调用构造器:
Constructor–>newInstance(Object… initargs):实例化一个对象。
获取单个构造器
用到的方法:
getConstructor() :获取单个的"公有的"构造器,获取到的具体某个构造器由传入的参数决定,参数是类型的Class对象,如果没有与参数匹配的构造器,则会出现异常,要捕获或抛出异常。
newInstance():实例化一个对象。
getDeclaredConstructor():特性和上面的getConstructor() 方法差不多,区别在于获取的构造器可以是私有的,受保护的、默认的、公有的。
代码演示:
/**
* 一个用于测试的类
*/
public class Person {
private int age;
private String name;
//公有无参构造器
public Person() {
System.out.println("公有无参构造器执行");
}
//公有带参构造器
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
System.out.println("公有带参构造器执行--age:"+age+",name:"+name);
}
//私有的构造器
private Person(int age) {
this.age = age;
System.out.println("私有构造器执行--age:"+age);
}
//受保护的构造器
protected Person(String name) {
this.name = name;
System.out.println("受保护构造器执行--name:"+name);
}
}
/**
* 获取单个构造器演示
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//获取指定路径的Person.class文件对应的Class对象
Class cls = Class.forName("cn.yunhe.study.Person");
//没有参数所以获取的是类中的无参公有构造器,获取到的构造器由Constructor类型变量的接收
Constructor con1 = cls.getConstructor();
//该语句获取的是类的带参公有构造器,这里的异常处理是向上抛出异常
Constructor con2 = cls.getConstructor(int.class,String.class);
//获取"私有的"构造器
Constructor con3 = cls.getDeclaredConstructor(int.class);
//获取"受保护的"构造器
Constructor con4 = cls.getDeclaredConstructor(String.class);
//newInstance()方法可以调用构造器实例化一个对象,以下是通过不同的构造器创建对象
Person p1 = (Person) con1.newInstance();
Person p2 = (Person) con2.newInstance(12,"李四");
System.out.println(con3);//私有的构造器
Person p4 = (Person) con4.newInstance("王五");
}
/*程序运行结果
公有无参构造器执行
公有带参构造器执行--age:12,name:李四
private cn.study.reflex.Person(int)
受保护构造器执行--name:王五
*/
批量获取构造器
用到的方法:
public Constructor[] getConstructors():获取所有"公有的"构造器,返回一个所有公有构造器组成的Constructor类型的数组。
public Constructor[] getDeclaredConstructors():获取一个所有的构造器(包括私有、受保护、默认、公有) 组成的Constructor类型的数组。
代码演示:
/**
* 批量获取构造器演示
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//获取指定路径的Person.class文件对应的Class对象
Class cls = Class.forName("cn.study.reflex.Person");
//获取所有的公有构造器
Constructor[] con1 = cls.getConstructors();
//获取所有的构造器
Constructor[] con2 = cls.getDeclaredConstructors();
//遍历公有构造器的数组
for(Constructor con : con1) {
System.out.println(con);
}
System.out.println("---------------------------");
//遍历所有构造器的数组
for(Constructor con : con2) {
System.out.println(con);
}
}
/*程序运行结果
public cn.study.reflex.Person(int,java.lang.String)
public cn.study.reflex.Person()
---------------------------
protected cn.study.reflex.Person(java.lang.String)
private cn.study.reflex.Person(int)
public cn.study.reflex.Person(int,java.lang.String)
public cn.study.reflex.Person()
*/
通过反射获取类中方法
获取类中方法需要用到的:
1.获取方法
1)批量获取:
public Method[] getMethods()
获取所有"公有方法"(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods()
获取所有的成员方法,包括私有的(不包括继承的)
2)单个获取:
public Method getMethod(String name,Class<?>… parameterTypes)
获得该类某个公有的方法。参数: “name” 是方法名,"Class … " 是方法形参的Class类型对象。
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)
获得该类某个方法
2.调用方法:
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;
部分代码演示:
/**
* 一个用于测试的类
*/
public class Person {
private int age;
private String name;
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;
}
private void pm() {
System.out.println("私有方法");
}
}
/**
* 批量获取类中方法演示
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class cls = Class.forName("cn.study.reflex.Person");
//参数一:方法名 参数二:参数类型
Method method1 = cls.getMethod("setAge", int.class);
Method method2 = cls.getDeclaredMethod("pm");
Method[] method3 = cls. getDeclaredMethods();
System.out.println(method1);
System.out.println(method2);
System.out.println("----------------");
//遍历获取到的类中的所有成员方法
for(Method m :method3) {
System.out.println(m);
}
}
/*程序运行结果
public void cn.study.reflex.Person.setAge(int)
private void cn.study.reflex.Person.pm()
----------------
public java.lang.String cn.study.reflex.Person.getName()
public void cn.study.reflex.Person.setName(java.lang.String)
private void cn.study.reflex.Person.pm()
public void cn.study.reflex.Person.setAge(int)
public int cn.study.reflex.Person.getAge()
*/
通过反射获得类中属性
1.获取类中属性
1)批量获取
Field[] getFields()
获得所有公有的属性对象
Field[] getDeclaredFields()
获得所有属性对象,包括:私有、受保护、默认、公有;
2)单个获取
public Field getField(String fieldName)
获得某个公有的属性对象;
public Field getDeclaredField(String fieldName)
获得某个属性对象(可以是私有的)
2.设置变量的值:
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的变量所在的对象;
2.value:要为变量设置的值;
部分代码演示:
/**
* 一个用于测试的类
*/
public class Person {
private int age;
private String name;
public double a;
}
/**
* 批量获取类中属性演示
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class cls = Class.forName("cn.study.reflex.Person");
Field[] f1 = cls.getFields();
Field[] f2 = cls.getDeclaredFields();
for(Field f :f1) {
System.out.println(f);
}
System.out.println("------------");
for(Field f :f2) {
System.out.println(f);
}
}
/*程序运行
public double cn.study.reflex.Person.a
------------
private int cn.study.reflex.Person.age
private java.lang.String cn.study.reflex.Person.name
public double cn.study.reflex.Person.a
*/
上一篇: Java面试宝典-2