1.定义
Reflections(反射)是动态语言(运行时代码可以根据某些条件改变自身的结构)的关键。加载完类之后,在堆内存的方法区当中就产生了一个Class类型的对象。这个对象包含了完整类的结构信息。我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,我们形象的称为反射。
2反射相关的主要API
1.java.lang.Class 代表一个类
2.java.lang.reflect.Method 代表类的方法
3.java.lang.reflect.Filed 代表类的成员变量
4.java.lang.reflect.Constructor 代表类的构造器
3 例子
反射到底有什么用呢?下面举一个例子
先造一个Person类,设置name为私有属性,设置私有方法showNation()
public class Person {
private String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name = name;
}
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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void show(){
System.out.println("你好!");
}
private String showNation(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
}
正常调用
@Test
public void test(){
Person p1=new Person("Tom",12);
p1.age=10;
System.out.println(p1.toString());
p1.show();
}
显然,除非设置了set get方法,不然不能直接访问私有属性和方法。
而反射就可以调用对象的私有属性和方法,比如下面这段代码。
@Test
//反射
public void test1() throws Exception{
Class ss=Person.class;
//通过反射,创建Person类的对象
Constructor con=ss.getConstructor(String.class,int.class);
Object object = con.newInstance("tom", 12);
Person p1= (Person)object;
System.out.println(p1.toString());
//通过反射,调用对象指定的属性
Field age = ss.getDeclaredField("age");
age.set(p1,10);
System.out.println(p1.toString());
//通过反射,调用对象指定的方法
Method show = ss.getDeclaredMethod("show");
show.invoke(p1);
//通过反射,调用类私有的结构。比如私有的构造器,方法和属性
Constructor con1 = ss.getDeclaredConstructor(String.class);
con1.setAccessible(true);
Person p2 = (Person)con1.newInstance("jerry");
System.out.println(p2.toString());
//调用私有的属性
Field name = ss.getDeclaredField("name");
name.setAccessible(true);
name.set(p2,"jackMa");
System.out.println(p2);
//调用私有的方法
Method showNation = ss.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation=(String)showNation.invoke(p2,"中国");
System.out.println(nation);
}
3.反射和封装矛盾?
从上述例子我们知道,反射是可以调用对象的私有发方法和私有构造器的,那这样岂不是和我们学的封装的思想相互矛盾了吗?
封装,是将具体的实现细节隐藏,而把功能作为整体提供给类的外部使用,也就是说,公有方法能够完成类所具有的功能。当别人使用这个类时,如果通过反射直接调用私有方法,可能根本实现不了类的功能,甚至可能会出错,因此通过反射调用私有方法可以说是没有任何用处的,开发人员没有必要故意去破坏封装好的类。从这点上看,封装性并没有被破坏。(摘自百度问答)
个人理解是:封装性是建议性,反射是可不可行性。也就是说在面对私有方法和私有变量,封装是一个很好建议,但它不妨碍通过反射来实现私有变量和方法的可行性。
4 Class类的理解
那么反射是怎么实现的呢?
首先我们需要知道类的加载过程:程序经过javac.exe文件后生成一个或多个.class结尾的字节码文件。接着使用java.exe对字节码文件解释运行,即字节码文件加载到内存中,这就是类的加载的过程。加载到内存中的类,称为运行时类,此运行时类,就作为Class的一个实例。(这里只是简述下过程)
不仅仅是类,接口,基本数据类型,String,数组,方法都可以是Class实例,在数组中只要数组的类型和维度一样,就同是一个Class。
我们可以通过以下方法来获得这个实例。
以Person类为例
- Class ss=Person.class;
- Person p1=new Person();
Class ss=p1.getclass(); - Class ss=Class.forName(“xxx.Person”);//这里xxx要指定具体的jar包名
- 使用类加载器:ClassLoder
5反射的一些操作
5.1通过反射创建运行时类的对象
public class NewInstanceTest {
@Test
public void test1() throws Exception {
Class ss=Class.forName("Person");
/*
*newInstance():调用此方法,创建对应的运行时类的空参构造器
* 要想正常的创建运行时类的对象必须满足
* 1.运行时类必须提供空参狗在其
*2.访问权限不能为private
*/
Person p =(Person) ss.newInstance();
System.out.println(p);
}
}
public void test2() throws Exception {
String classPath="";
int num=new Random().nextInt(3);
switch (num){
case 0:
classPath="java.util.Date";
break;
case 1:
classPath="java.lang.Object";
break;
case 2:
classPath="Person";
break;
}
Object obj=getInstance(classPath);
System.out.println(obj);
}
/*
* 创建一个指定类的对象。
* */
public Object getInstance(String classpath) throws Exception{
Class aa=Class.forName(classpath);
return aa.newInstance();
}
5.2通过反射获取类的属性
1.先写一个Dog类,用来测试(注解和父类代码省略)
@MyAnnotation
public class Dog extends Creature<String>implements Comparable,Myinterface {
private String name;
int age;
public int id;
public Dog() {
}
@MyAnnotation(value = "小白")
private Dog(String name) {
this.name = name;
}
Dog(String name, int age) {
this.name = name;
this.age = age;
}
@MyAnnotation(value = "阿拉斯加")
private String showVariety(String varity){
System.out.println("这是一只"+varity);
return varity;
}
@MyAnnotation(value = "滑雪")
public String displayLike(String interest){
return interest;
}
@Override
public void info() {
System.out.println("小狗汪汪叫");
}
@Override
public int compareTo(Object o) {
return 0;
}
}
2.测试反射
public class FieldTest {
@Test
public void test(){
Class dd=Dog.class;
//获取属性结构
Field[] fields = dd.getFields();
for(Field f:fields){
System.out.println(f);
}
System.out.println("***************");
//用getDeclarFields获取当前运行时类的所有属性,不包含父类声明的属性
Field[] declaredFields = dd.getDeclaredFields();
for(Field f:declaredFields){
//获取属性的权限修饰符
int modifier=f.getModifiers();
System.out.println(Modifier.toString(modifier));
//获取属性的的数据类型
Class type=f.getType();
System.out.println(type.getName()+"\t");
//获取属性的变量名
String name=f.getName();
System.out.println(name);
System.out.println("..................");
}
}
}
输出结果:
public int Dog.id
public double Creature.weight
***************
private
java.lang.String
name
..................
int
age
..................
public
int
id
..................
5.3通过反射获取运行时类的方法结构
@Test
public void test1(){
Class dd=Dog.class;
Method[] methods = dd.getMethods();
for(Method m:methods){
System.out.println(m);
}
System.out.println("*******************");
//获取当前运行时类的所有方法,不包含父类声明的方法
Method[] declaredFields = dd.getDeclaredMethods();
for(Method m:declaredFields){
//获取注解
Annotation[] annotations = m.getAnnotations();
for(Annotation a:annotations){
System.out.println(a);
}
//获取方法名和形参列表
System.out.print(m.getName());
System.out.print("(");
//获取形参列表
Class[] parameterTypes = m.getParameterTypes();
//先判断一下
if(!(parameterTypes==null||parameterTypes.length==0)){
for(int i=0;i<parameterTypes.length;i++){
System.out.print(parameterTypes[i].getName()+" args_"+i);
}
}
System.out.print(")");
//获取权限修饰符
System.out.print(Modifier.toString(m.getModifiers())+"\t");
//获取返回值类型
System.out.println(m.getReturnType().getName()+"\t");
System.out.println();
//抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(!(exceptionTypes!=null||exceptionTypes.length==0)){
for(int i=0;i<exceptionTypes.length;i++){
System.out.print(exceptionTypes[i].getName());
}
}
System.out.println("............");
}
}
5.4通过反射获取运行时类的父类,接口等…
和上面操作类似,这些不常用,如有需要查文档即可。
本文深入讲解Java反射机制的概念、核心API及其应用场景,并通过实例演示如何使用反射调用私有属性和方法,最后探讨了反射与封装的关系。
991

被折叠的 条评论
为什么被折叠?



