反射的引入:
Object obj = new Student();
若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:
1.若编译和运行类型都知道,使用 instanceof判断后,强转。
2.编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,这时就必须使用反射了。
3.要是想得到对象真正的类型,就得使用反射。
什么是反射机制?
简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。
反射机制的优点与缺点:
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。
它的缺点是对性能有影响。
使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
Class类和Class类实例
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。
对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?
人 Person
Java类 Class
对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等;
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的;
用类来描述对象,类:描述数据的结构
用元数据来描述Class,MetaData(元数据):描述数据结构的结构;
反射就是得到元数据的行为;
备注:一个类在虚拟机中只有一份字节码;
如何得到各个字节码对应的实例对象?
每个类被加载后,系统会为该类生成对应的Class对象,通过Class对象可以访问到JVM中的这个类,
3种方式:
1、调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);
2、使用Class类的forName(String className)静态方法,className表示全限定名;如String的全限定名:java.lang.String;
3、调用某个对象的getClass()方法。该方法属于Object类;
Class> clz = new Date().getClass();
-
public
class ClassDemo1 {
-
-
public static void main(String[] args) throws Exception {
-
-
//获得Class对象的方法(三种)
-
-
//一:调用属性
-
-
Class
c = String.class;
-
-
System.out.println(c);
//打印结果:class java.lang.String String.class就表示JVM中一份表示String类的字节码
-
-
Class
c2 = String.class;
-
-
System.out.println(c == c2);
//true都是String类的字节码 一个类在虚拟机中只有一份字节码;
-
-
-
-
//二:使用forName()方法
-
-
//Class cla = Class.forName("String");//ERROR,
-
-
Class
cla = (Class)Class.forName(
"java.lang.String");
//必须用上全限定名,否则报错
-
-
System.out.println(c == cla);
//true
-
-
-
-
//三:利用对象调用Object的getClass方法;
-
-
Class c3 =
new String().getClass();
-
-
System.out.println(c == c3);
//ture
-
-
}
-
-
}
我的总结:获取Class对象最常用的是利用属性的方法!
基本的 Java 类型(boolean、byte、char、short、int、long、float 、double)和关键字void通过class属性也表示为 Class 对象;
Class类中boolean isPrimitive() :判定指定的 Class 对象是否表示一个基本类型。
包装类和Void类的静态TYPE字段;
Integer.TYPE == int.class ;
Integer.class == int.class;
数组类型的Class实例对象:
Class
数组的Class对象如何比较是否相等? 数组的维数和数组的类型;
Class类中 boolean isArray() :判定此 Class 对象是否表示一个数组类型。
-
public
class PreClassDemo2 {
-
public static void main(String[] args) {
-
Class> in =
int.class;
-
System.out.println(in);
//int
-
Class> in2 = Integer.class;
-
//包装类都有一个常量TYPE,用来表示其基本数据类型的字节码
-
Class> in3 = Integer.TYPE;
-
System.out.println(in2);
//class java.lang.Integer
-
-
System.out.println(in3);
//int
-
-
System.out.println(in3 == in);
//true 包装类都有一个常量TYPE,用来表示其基本数据类型的字节码,所以这里会相等!
-
-
System.out.println(in3 == in2);
//false
-
-
Class
s = String [].class;
-
-
Class<
int[]> i =
int [].class;
-
-
//System.out.println(i ==s);//编译根本就通过不了,一个是int,一个是String
-
-
}
-
-
//这两个自定义的方法是可以的,一个int,一个Integer//包装类与基本数据类型的字节码是不一样的
-
public void show(int i){}
-
public void show(Integer i){}
-
-
}
import java.lang.reflect.Modifier;
-
class A {
-
-
}
-
-
interface B{
-
-
}
-
-
interface C{
-
-
}
-
public
class BaseDemo3 extends A implements B,C{
-
//内部类
-
public
class C{}
-
public
interface D{}
-
public static void main(String[] args) {
-
//类可以,接口也可以
-
Class
c = BaseDemo3.class;
-
System.out.println(c);
//class junereflect624.BaseDemo3
-
//得到包名
-
System.out.println(c.getPackage());
//package junereflect62;
-
//得到全限定名
-
System.out.println(c.getName());
//junereflect624.BaseDemo3
-
//得到类的简称
-
System.out.println(c.getSimpleName());
//BaseDemo3
-
//得到父类
-
/**
-
* Class super T> getSuperclass() 此处super表示下限
-
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
-
*/
-
System.out.println(c.getSuperclass().getSimpleName());
//A,先获取父类,再获取父类的简称
-
//得到接口
-
System.out.println(c.getInterfaces());
//[Ljava.lang.Class;@1b60280
-
Class[] arr = c.getInterfaces();
-
for (Class cla : arr) {
-
System.out.println(cla);
//interface junereflect624.B interface junereflect624.C
-
}
-
//获得public修饰的类
-
/**
-
* Class>[] getClasses()
-
返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 (如果内部类前面没有加上public的话那么得不到!)
-
*/
-
Class[] cl = c.getClasses();
-
System.out.println(cl.length);
//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)
-
for (Class class1 : cl) {
-
System.out.println(class1);
-
}
-
//获得修饰符
-
int i = c.getModifiers();
-
System.out.println(i);
//常量值1表示public
-
System.out.println(Modifier.toString(i));
//直接打印出public
-
}
-
}
常用方法:
Constructor类用于描述类中的构造方法:
Constructor
返回该Class对象表示类的指定的public构造方法;
Constructor>[] getConstructors()
返回该Class对象表示类的所有public构造方法;
Constructor
返回该Class对象表示类的指定的构造方法,和访问权限无关;
Constructor>[] getDeclaredConstructors()
返回该Class对象表示类的所有构造方法,和访问权限无关;
Method类用于描述类中的方法:
Method getMethod(String name, Class> ... parameterTypes)
返回该Class对象表示类和其父类的指定的public方法;
Method[] getMethods():
返回该Class对象表示类和其父类的所有public方法;
Method getDeclaredMethod(String name, Class>... parameterTypes)
返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;
Method[] getDeclaredMethods(): 获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;
Eg:
import java.lang.reflect.Constructor;
-
class Emp{
-
private String name;
-
private
int age;
-
private Emp() {
-
}
-
Emp(String name){
-
}
-
public Emp(String name,int age){
-
}
-
}
-
public
class ConstructorDemo4 {
-
public static void main(String[] args) throws Exception {
-
//得到所有的构造器(先得到类)
-
Class
c = Emp.class;
-
/**
-
* Constructor>[] getConstructors()
-
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
-
*/
-
Constructor[] con = c.getConstructors();
//前面的修饰符必须是public才可以在这个方法下获取到
-
for (Constructor cons : con) {
-
System.out.println(
"c.getConstructors()"+cons);
//如果上面的某构造器public去掉,则显示不出
-
/**打印
-
public junereflect624.Emp(java.lang.String,int)
-
*/
-
}
-
-
//得到指定的构造器,也是必须public
-
Constructor c1 = c.getConstructor(String.class,
int.class);
-
System.out.println(c1);
//public junereflect624.Emp(java.lang.String,int)
-
-
System.out.println(
"====================================");
-
-
//现在想获得不受public影响的,getDeclaredConstructors(),暴力反射
-
-
con = c.getDeclaredConstructors();
-
for (Constructor cons : con) {
-
System.out.println(
"c.getDeclaredConstructors()=="+cons);
//此时不受修饰符的影响
-
/**打印
-
* public junereflect624.Emp()
-
public junereflect624.Emp(java.lang.String)
-
public junereflect624.Emp(java.lang.String,int)
-
*/
-
}
-
}
-
}
-
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-
class AB{
-
-
protected String name;
-
-
protected String id;
-
-
}
-
@Deprecated
-
-
public
class MethodDemo5 extends AB{
-
void show(){}
-
public void say(){}
-
private
int age;
-
public
char c;
-
private
boolean b;
-
public static void main(String[] args) throws Exception {
-
Class
c = MethodDemo5.class;
-
//获取所有的(包含父类的方法)public修饰的方法
-
Method[] m = c.getMethods();
-
for (Method method : m) {
-
System.out.println(method);
-
}
-
//总结:4个方法,获取全部,获取特定;不受修饰符影响的全部,不受修饰符影响的特定;(前两个都还是受限制的)
-
//获取指定的方法
-
Method me = c.getMethod(
"main", String[].class);
-
System.out.println(
"main "+me);
//main public static void junereflect624.MethodDemo5.main(java.lang.String[]) throws java.lang.Exception
-
//访问所有方法,不受访问权限影响
-
m = c.getDeclaredMethods();
-
for (Method method : m) {
-
System.out.println(
"不受影响的:"+method);
-
}
-
me = c.getDeclaredMethod(
"show");
-
System.out.println(me);
//void junereflect624.MethodDemo.show()
-
me = c.getMethod(
"toString");
-
System.out.println(me);
//public java.lang.String java.lang.Object.toString()
-
/**
-
* Method[] getDeclaredMethods()
-
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法,只可以对当前类有效
-
*/
-
/*me = c.getDeclaredMethod("toString");//ERROR,c.getDeclaredMethod()不能得到继承的方法
-
System.out.println(me);//public java.lang.String java.lang.Object.toString()
-
*/
-
//得到字段
-
Field[] f = c.getFields();
-
for (Field field : f) {
//只得到了public的
-
System.out.println(
"字段"+field);
-
}
-
//特定字段
-
Field fi = c.getField(
"c");
//""里面是名称
-
System.out.println(fi);
//public char junereflect624.MethodDemo.c
-
//得到不受限定名限定的全部字段
-
f = c.getDeclaredFields();
-
for (Field field : f) {
//得到不受修饰符限定的字段,但是只对当前类有效
-
System.out.println(
"全部字段:"+field);
-
/**
-
* 全部字段:private int junereflect624.MethodDemo.age
-
* 全部字段:public char junereflect624.MethodDemo.c
-
* 全部字段:private boolean junereflect624.MethodDemo.b
-
*/
-
}
-
//注释 Annotation
-
Annotation[] a = c.getAnnotations();
-
System.out.println(a.length);
-
for (Annotation annotation : a) {
-
System.out.println(annotation);
-
}
-
//特定注解
-
Deprecated d = c.getAnnotation(Deprecated.class);
-
System.out.println(d);
-
}
-
}
获取当前对象的字段:
-
import java.lang.reflect.Field;
-
class Stu{
-
public String name;
-
public String sex;
-
public
int age;
-
public Stu(String name, String sex, int age) {
-
super();
-
this.name = name;
-
this.sex = sex;
-
this.age = age;
-
}
-
}
-
-
public
class ReflectDemo6 {
-
public static void main(String[] args) throws Exception {
-
Stu s =
new Stu(
"刘昭",
"男",
12);
-
Class
c = Stu.class;
-
Field f = c.getField(
"name");
-
System.out.println(f.get(s));
从哪个对象身上取!此时显示刘昭!
-
// 修改对象的值
-
/**
-
Field f = c.getField("name");
-
f.set(s,"章泽天");
-
System.out.println(f.get(s));//从哪个对象身上取!//此时显示章泽天
-
*/
-
-
}
-
-
}
我的总结:对于方法,字段,构造方法之类用类获取记住四个:获取全部,获取特定,暴力获取全部,暴力获取特定!
创建对象:
1、使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法。
2、使用Class对象获取指定的Constructor对象,再调用Constructor的newInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true;
Eg:
最简单的:
-
class User{
-
//将默认的构造方法私有化的话就不可以再创建对象,两种方法都是这样
-
/*private User(){}*/
-
public String toString() {
-
return
"User对象创建成功!";
-
}
-
}
-
public
class NewInstanceDemo6 {
-
public static void main(String[] args) throws Exception {
-
//传统方式创建对象
-
System.out.println(
new User());
-
//使用反射的方式
-
Class
c = User.class;
-
User u = c.newInstance();(直接newInstance的话必须保证默认的构造方法正常存在,也就是没有被私有化!这是前提条件)
-
System.out.println(u);
-
}
-
}
复杂点的:更强大的第二种:
使用指定构造方法来创建对象:
获取该类的Class对象。
利用Class对象的getConstructor()方法来获取指定的构造方法。
调用Constructor的newInstance()方法创建对象。
AccessibleObject对象的setAccessible(boolean flag)方法,当flag为true的时候,就会忽略访问权限(可访问私有的成员)。
其子类有Field, Method, Constructor;
若要访问对象private的成员?
在调用之前使用setAccessible(true),
Xxx x = getDeclaredXxxx();//才能得到私有的类字段.
总结步骤:
例子
import java.lang.reflect.Constructor;
-
class Per{
-
private String name;
-
private
int age;
-
private Per(){
-
}
-
private Per(String name){
-
}
-
public String toString() {
-
return
"对象!!!";
-
}
-
}
-
public
class NewInstanceDemo7 {
-
public static void main(String[] args) throws Exception {
-
Class
c = Per.class;
-
//System.out.println(c.newInstance());;//证明利用无参的可以
-
先获得需要被调用的构造器(private 修饰的构造方法)
-
Constructor
con = c.getDeclaredConstructor();
//调用默认的,什么都不要写
-
System.out.println(con);
//private junereflect624.Per()
-
/*con = c.getDeclaredConstructor(String.class);获取指定的构造方法
-
System.out.println(con);//private junereflect624.Per(java.lang.String)*/
-
//现在只需要执行这个构造器,
-
/**
-
* T newInstance(Object... initargs)
-
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
-
*/
-
//私有的成员是受保护的,不能直接访问
-
//若要访问私有的成员,得先申请一下
-
con.setAccessible(
true);
//允许访问
-
Per p = con.newInstance();
//成功,通过私有的受保护的构造方法创建了对象
-
System.out.println(
"无参构造方法"+p);
-
con = c.getDeclaredConstructor(String.class);
-
System.out.println(con);
//private junereflect624.Per(java.lang.String);
-
con.setAccessible(
true);
//允许访问
-
p = con.newInstance(
"liuzhao");
//成功,通过私有的受保护的构造方法创建了对象
-
System.out.println(
"String构造方法"+p);
-
}
-
}
备注:对于此时的话,单例模式就不再安全了!反射可破之!!
每个Method的对象对应一个具体的底层方法。获得Method对象后,程序可以使用Method里面的invoke方法来执行该底层方法。
Object invoke(Object obj,Object ... args):obj表示调用底层方法的对象,后面的args表示传递的实际参数。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null,想想为什么?
如果底层方法所需的形参个数为 0,则所提供的 args 数组长度可以为 0 或 null。
不写,null,或 new Object[]{}
若底层方法返回的是数组类型,invoke方法返回的不是底层方法的值,而是底层方法的返回类型;
import java.lang.reflect.Method;
-
class Dept{
-
public String show(String name){
//用反射的方法来调用正常的方法
-
return name+
",您好!";
-
}
-
private void privateshow(){
//用反射来实现对私有化方法的调用
-
System.out.println(
"privateshow");
-
}
-
public static void staticshow(){
-
System.out.println(
"staticshow");
-
}
-
}
-
public
class InvokeDemo9 {
-
public static void main(String[] args) throws Exception {
-
/* 传统方式:
-
String name = new Dept().show("刘昭");
-
System.out.println(name);*/
-
-
/**
-
* Method getMethod(String name, Class>... parameterTypes)
-
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指 定公共成员方法。
-
name - 方法名
-
parameterTypes - 参数列表
-
*/
-
//想要通过反射来调用Dept中的方法
-
Class
c = Dept.class;
-
Method m = c.getMethod(
"show", String.class);
-
Object o = m.invoke(c.newInstance(),
"刘昭");
-
System.out.println(o);
-
-
//私有化的方法
-
m = c.getDeclaredMethod(
"privateshow");
//无参方法
-
m.setAccessible(
true);
-
o = m.invoke(c.newInstance());
-
-
//静态方法的调用
-
m = c.getMethod(
"staticshow");
-
m.invoke(
null);
//staticshow为静态方法,不需创建对象,所以这里会是null
-
}
-
}
打印
刘昭,您好!
privateshow
staticshow
Field提供两组方法操作字段:
xxx getXxx(Object obj):获取obj对象该Field的字段值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,Object get(Object obj);
void setXxx(Object obj,xxx val):将obj对象的该Field字段设置成val值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,void set(Object obj, Object value);
package junereflect624;
//获取字符,并且赋值,然后再取出来(对应的去查看api,比如这个是Field,别的比如Constructor,Method)
步骤:
import java.lang.reflect.Field;
-
class Cat{
-
private
String name;
-
public
int age;
-
private
String color;
-
}
-
public
class FieldDemo12 {
-
public static void main(String[] args) throws Exception {
-
Class
clz = Cat.class;
-
Field[] f = clz.getDeclaredFields();
-
-
for (Field field : f) {
-
System.out.println(field);
-
}
-
-
Field fi = clz.getDeclaredField(
"name");
-
System.out.println(fi);
-
-
System.out.println(fi.getName());
//name
-
-
//核心开始
-
/**
-
* void set(Object obj, Object value)
-
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
-
*/
-
Cat c = clz.newInstance();
-
fi.setAccessible(
true);
-
fi.set(c,
"刘昭");
//赋值成功
-
Object o = fi.get(c);
-
System.out.println(o);
//取出成功
-
-
fi = clz.getDeclaredField(
"age");
-
fi.setAccessible(
true);
-
fi.set(c,
21);
-
int i = fi.getInt(c);
//左边的接受类型已经写成了int,右边的返回类型就也必须是int
-
System.out.println(i);
//获取成功
-
}
-
}
打印
private java.lang.String junereflect624.Cat.name
public int junereflect624.Cat.age
private java.lang.String junereflect624.Cat.color
private java.lang.String junereflect624.Cat.name
name
刘昭
21
通过指定对应的Class对象,程序可以获得该类里面所有的Field,不管该Field使用private 方法public。获得Field对象后都可以使用getType()来获取其类型。
Class> type = f.getType();//获得字段的类型
但此方法只对普通Field有效,若该Field有泛型修饰,则不能准确得到该Field的泛型参数,如Map
为了获得指定Field的泛型类型,我们采用:
Type gType = f.getGenericType();得到泛型类型
然后将Type对象强转为ParameterizedType,其表示增加泛型后的类型
Type getRawType()//返回被泛型限制的类型;
Type[] getActualTypeArguments()//返回泛型参数类型;
利用反射来获取泛型的类型(泛型信息)
步骤:
-
import java.lang.reflect.Field;
-
import java.lang.reflect.ParameterizedType;
-
import java.lang.reflect.Type;
-
import java.util.HashMap;
-
import java.util.Map;
-
public
class GetGenericTypeDemo14 {
-
Map
map =
new HashMap();
-
-
public static void main(String[] args) throws Exception {
-
Class c = GetGenericTypeDemo14.class;
-
Field f = c.getDeclaredField(
"map");
-
System.out.println(f);
-
System.out.println(f.getName());
//map
-
-
// Class> getType() 返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。
-
Class cl = f.getType();
-
System.out.println(
"获得其类型:"+cl);
-
//获得其类型:interface java.util.Map
-
-
/**
-
* Type getGenericType() 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。
-
* Type是Class的接口;
-
*/
-
Type t = f.getGenericType();
//包含泛型的类型
-
System.out.println(t);
-
//java.util.Map
-
-
/**
-
* Type这个类里面没有任何的方法,所以需要调用子类的方法,那么大的类型转到小的类型,需要强转!
-
*/
-
ParameterizedType pt = (ParameterizedType)t;
//强转到其子类
-
/**
-
* Type[] getActualTypeArguments()
-
返回表示此类型实际类型参数的 Type对象的数组。
-
Type getOwnerType()
-
返回 Type 对象,表示此类型是其成员之一的类型。
-
Type getRawType()
-
返回 Type 对象,表示声明此类型的类或接口。
-
*/
-
-
t = pt.getRawType();
//类型的类或接口
-
System.out.println(t);
-
-
Type[] ts = pt.getActualTypeArguments();
-
for (Type type : ts) {
-
System.out.println(type);
-
/**
-
* class java.lang.String
-
class java.lang.Integer
-
*/
-
}
-
}
-
}
打印:
java.util.Map junereflect624.GetGenericTypeDemo14.map
map
获得其类型:interface java.util.Map
java.util.Map
interface java.util.Map
class java.lang.String
class java.lang.Integer
我的总结:多查找api,参考api中方法使用的限制,比如是否静态、返回值类型等。