反射

RTTI


 运行时类型识别(RTTI, Run-Time Type Identification)是Java中的机制,在Java运行时,RTTI维护类的相关信息。
 多态是基于RTTI实现的。RTTI的功能主要是由Class类实现的。

 Java中每个对象都有相应的Class对象,因此,我们随时能通过Class对象知道某个对象“真正”所属的类。无论我们对引用进行怎样的类型转换,对象本身所对应的Class对象都是同一个。当我们通过某个对象引用调用方法时,Java总能找到正确的Class类中所定义的方法,并执行该Class类中的代码。由于Class对象的存在,Java不会因为类型的向上转换而迷失。这就是(运行时)多态的原理。

java.lang.Class


Class类,保存着对象的运行时类型信息。类和接口都有。

源码
//包含与类有关的信息
public final class Class<T> implements java.io.Serializable, GenericDeclaration,Type,
	AnnotatedElement {}
获取一个类的Class对象

法1:public static Class<?> forName(String className){} // java.lang.Class

//要放在try...catch语句块中
Class c1 = Class.forName("类的全限定名");
Class<?> c1=Class.forName("类的全限定名");

法2:public final native Class<?> getClass(); // java.lang.Object

//返回 用来表示 该对象的实际类型的 Class引用
Class c2 = 类的对象.getClass();
Class<?> c2 = 类的某对象.getClass();
Class<? extends 该类> = 类的某对象.getClass();

法3:类字面常量

//编译期就会受到检查,无需try块
//类不存在也就没有.class项
Class c3 = 类名.class;
Class<?> c3 = 类名.class;
Class<类名> c3 = 类名.class;

法1与法3的区别:用法3创建对Class对象的引用时还没有进行类加载过程中的初始化阶段;而调用Class.forName,Class对象已经自动完成了加载过程的初始化阶段。而只有触发类初始化条件时,法3中的Class对象才会开始进行初始化阶段的初始化操作。
而法2需要导入类的包。

反射


在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

  1. 以下方法的输出中:参数,域,方法名,都为全限定名。
  2. 使用private的成员:先获取private成员,再设置访问标志,最后调用。

创建实例


法1:一般不用

利用Class类型的方法: public T newInstance()(@Deprecated(since=“9”))
该方法只能利用默认构造函数创建对象。

Class<?> c1= COM.class;
Object obj = c1.newInstance();
法2

利用Constructor类型。

判断某对象是否为某个类的实例


public native boolean isInstance(Object obj);

public class COM { 
	public static void main(String[] args) throws Exception {	
		 Class<?> c1 = String.class;
		 String s1 = new String("hello");
		 String s2 = new String("world");
		 String s3 = new String("helloworld");
		 boolean b1 = c1.isInstance(s1);
		 boolean b2 = c1.isInstance(s2);
		 boolean b3 = c1.isInstance(s3);
		 System.out.println(""+b1+b2+b3);	  
	}
}
Output:
truetruetrue

java.lang.reflect.Constructor


用于描述类的构造器。默认构造器也会被获取到。

相关方法介绍:
public Constructor<?>[] getConstructors():获取所有public构造方法
public Constructor<?>[] getDeclaredConstructors():获取所有的构造方法(包括私有、默认、受保护、公有)
public Constructor< T> getConstructor(Class<?>… parameterTypes):获取单个的public构造方法:
public Constructor< T> getDeclaredConstructor(Class<?>… parameterTypes):获取某个构造方法(包括私有、默认、受保护、公有)
public T newInstance(Object … initargs):调用构造方法,创建一个该类的对象,并用指定的参数进行初始化

Class<?> c1 = Class.forName("test.Student");
Constructor[] conArray = c1.getConstructors();
conArray = c1.getDeclaredConstructors();

Constructor con = c1.getConstructor(cahr.class); //获取参数为char的public构造方法
Constructor con = c1.getConstructor(null);  //获取无参的public构造方法,null可以不写
Object obj = con.newInstance();  //调用构造方法
Student stu = (Student)obj;  //转型

con = c1.getDeclaredConstructor(int.class);  //获取参数为int的私有构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)  //在创建对象前,必须设置的一项
obj = con.newInstance(56);

Output:
public test.Student(java.lang.String,int)

java.lang.reflect.Field


用于描述类的成员变量。

相关方法介绍:
public Field[] getFields():获取所有的public字段
public Field[] getDeclaredFields():获取所有字段,(包括私有、默认、受保护、公有)
public Field getField(String name):获取某个public字段
public Field getDeclaredField(String name):获取某个字段(包括私有、默认、受保护、公有)
public void set(Object obj, Object value):设置字段的值。要设置的字段所在的对象,要为字段设置的值)
(obj=null时是在设置类的静态域)

public String name;
private String phoneNum;

Class<?> c1 = Class.forName("test.Student");
Field[] fieldArray = c1.getFields();
fieldArray = c1.getDeclaredFields();

Field f = c1.getField("name");
Object obj = c1.getConstructor().newInstance();//产生Student对象,Student stu = new Student()
f.set(obj, "hi");//为Student对象中的name属性赋值,stu.name = "hi"
Student stu = (Student)obj;

f = c1.getDeclaredField("phoneNum");
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, "110");

Output:
public java.lang.String test.Student.name

java.lang.reflect.Method


用于描述类的方法。

相关方法介绍:
public Method[] getMethods():获取所有public方法,(包含了所有父类的public方法也包含Object类)
public Method[] getDeclaredMethods():仅获取本类或本接口的成员方法(包括私有、默认、受保护、公有)
public Method getMethod(String name, Class<?>… parameterTypes):获取单个public方法,包括父类的。(方法名,形参的Class类型对象)
public Method getDeclaredMethod(String name, Class<?>… parameterTypes):仅获取本类或本接口的单个方法。(包括私有、默认、受保护、公有)
public Object invoke(Object obj, Object… args):调用某方法。(要调用方法的对象,调用方式时所传递的实参)
(obj=null是在调用静态方法)

Class c1 = Class.forName("test.Student");
Method[] methodArray = c1.getMethods();
methodArray = c1.getDeclaredMethods();

Method m = c1.getMethod("show1", String.class);
Object obj = c1.getConstructor().newInstance();
m.invoke(obj, "hi");

m = c1.getDeclaredMethod("show4", int.class);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);

Output:
private java.lang.String test.Student.show4(int)

main方法的反射


Class c1 = Class.forName("test.Student");
Method m1 = c1.getMethod("main", String[].class);
m1.invoke(null, (Object)new String[]{"d","d","x"}); //这里相当于传了个new String[1]给invoke
m1.invoke(null, new Object[] {new String[] {"d","d","x"}});

通过反射越过泛型检查


泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的。

import java.lang.reflect.Method;
import java.util.ArrayList;
public class COM { 
	public static void main(String[] args) throws Exception {	
		ArrayList<String> strList = new ArrayList<>();
		strList.add("aaa");
		strList.add("bbb");
		Class c1 = strList.getClass(); 
		Method m = c1.getMethod("add", Object.class);  //由于泛型擦除,方法的参数类型变成了Object
		m.invoke(strList, 100);
		for(Object obj : strList){
			System.out.println(obj);
		}
	}
}
Output:
aaa
bbb
100

利用反射创建数组–java.lang.reflect.Array


数组可以赋值给一个Object Reference。
相关方法介绍:
public static Object newInstance(Class<?> componentType, int length);
public static native void set(Object array, int index, Object value);
public static native Object get(Object array, int index);

import java.lang.reflect.Array;
public class COM { 
	public static void main(String[] args) throws Exception {	
		Class<?> c1= Class.forName("java.lang.String");
        Object array = Array.newInstance(c1,25);
        //往数组里添加内容  
        Array.set(array,0,"1");
        Array.set(array,1,"2");
        Array.set(array,2,"3");
        Array.set(array,3,"4");
        Array.set(array,4,"5");
        //获取某一项的内容
        System.out.println(Array.get(array,3));
	}
}
Output:
4

反射缺点


反射会额外消耗一定的系统资源,所以如果不需要动态地创建一个对象,那么就不需要用反射。
反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值