反射:在运行期根据当前传入的类型,得到类型(class对象)。也就是说只有在运行期才能知道某种类型。根据这种类型创建class实例,并获取该实例的所有信息。
一、反射使用场景:运行期才能确定对象的类型。
二、常见的类:
Class类:
1、获取class对象的三种方法(以String类为例)
通过类名访问class:
通过类名访问class
Class stringCls1 = String.class;
通过实例访问getClass():
String s = "";
Class stringCls2 = s.getClass();
通过Class类的静态方法forName:
String s = "";
Class stringCls2 = s.getClass();
2、Class类的常见方法
//得到String类的Class对象
Class cls = String.class;
//类名
System.out.println("类的名称:"+cls.getSimpleName());
System.out.println("完全限定名:"+cls.getName());
System.out.println("类的类型名称:"+cls.getTypeName());
//获取继承关系:父类、接口
//父类
Class superCls = cls.getSuperclass();
System.out.println("类的父类:"+superCls.toString());
System.out.println("类的父类的父类:"+superCls.getSuperclass().getName());
//实现的接口
Class[] interfaceCls = cls.getInterfaces();
System.out.println("当前类实现的接口:");
for(Class clss : interfaceCls) {
System.out.println(clss);
}
//包
Package pck = cls.getPackage();
//判断该类的包是否为空
if(pck != null) {
System.out.println("类所在包的名称:"+pck.getName());
}
//判断类型
System.out.println("是否为接口:"+cls.isInterface());
System.out.println("是否为数组:"+cls.isArray());
System.out.println("是否为枚举:"+cls.isEnum());
3、创建某个类的对象
//通过Class类的静态方法forName
//得到该类的Class对象
Class cls = Class.forName(String);
//创建对象,
//newInstance():调用无参构造方法
Object obj = cls.newInstance();
System.out.println(obj);
Constructor类:可以调用任何类型的构造方法
1、Constructor类的getConstructors()方法能获取所有定义的构造方法,但获取的构造方法是public公有的。
public class Demo05 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//得到Exemple类的Class对象
Class cls = Exemple.class;
//调用无参构造方法创建Exemple类型的对象
Exemple ex1 = (Exemple)cls.newInstance();
System.out.println(ex1);
//获取所有的构造方法
//获取所有public公有的构造方法
Constructor[] constructArray = cls.getConstructors();
System.out.println("所有的构造方法:");
for(Constructor constructor1 : constructArray) {
System.out.println(constructor1);
}
System.out.println("-----------分隔符------------------");
//调用有参构造方法创建Exemple类型的对象
//1.获取指定参数类型的构造方法
//无参构造
Constructor constructor2 = cls.getConstructor();
Exemple ex2 = (Exemple)constructor2.newInstance();
System.out.println("无参构造:"+ex2);
//有一个参数的构造方法,传入参数类型
Constructor constructor3 = cls.getConstructor(int.class);
Exemple ex3 = (Exemple)constructor3.newInstance(512);
System.out.println("有1个参数的构造方法:"+ex3);
//有2个参数的构造方法
Constructor constructor4 = cls.getConstructor(int.class,double.class);
System.out.println(constructor4);
//执行构造方法,创建Exemple类型的对象
Exemple ex4 = (Exemple)constructor4.newInstance(1024,3.1415926);
System.out.println("有2个参数的构造方法:"+ex3);
}
}
class Exemple{
public Exemple(){
System.out.println("Exemple类的无参构造方法!");
}
public Exemple(int a){
System.out.printf("Exemple类的有参构造方法,a=%d\n",a);
}
public Exemple(int a,double b){
System.out.printf("Exemple类的有参构造方法,a=%d,b=%f\n",a,b);
}
}
2、Constructor类的getDeclaredConstructors()方法能获取所有定义的构造方法(任意修饰符修饰的)。
public class Demo06 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//得到Exemple类的Class对象
Class cls = Exempl.class;
//获取所有定义的构造方法(任意修饰符修饰)
Constructor[] constructArray = cls.getDeclaredConstructors();
System.out.println("所有定义的构造方法");
for(Constructor constructor : constructArray) {
System.out.println(constructor);
}
//获取一个带一个参数的私有构造方法
Constructor privateConstruct = cls.getDeclaredConstructor(String.class);
//调用私有的构造方法
privateConstruct.setAccessible(true);
Exempl ex = (Exempl)privateConstruct.newInstance("just");
System.out.println(ex);
}
}
class Exempl{
private Exempl(String s){
System.out.printf("Exemple类的有参构造方法,s=%s\n",s);
}
protected Exempl(Float f){
System.out.printf("Exemple类的有参构造方法,f=%d\n",f);
}
public Exempl(){
System.out.println("Exemple类的无参构造方法!");
}
public Exempl(int a){
System.out.printf("Exemple类的有参构造方法,a=%d\n",a);
}
public Exempl(int a,double b){
System.out.printf("Exemple类的有参构造方法,a=%d,b=%f\n",a,b);
}
}
三、判断继承关系
instanceof 运算符:判断“引用”和“类型”之间的关系
//instanceof 运算符:判断“引用”和“类型”之间的关系
Object obj = Integer.valueOf(9876);
//是否为Integer类型
// “引用” “类型”
System.out.println("是否为Integer?"+(obj instanceof Integer));//true
//是否为Double类型
System.out.println("是否为Double?"+(obj instanceof Double));//false
//Integer类继承自Number抽象类,实现Comparable接口
System.out.println("是否为Number?"+(obj instanceof Number));//true
System.out.println("是否为Comparable接口?"+(obj instanceof Comparable));//true
isAssignableFrom()方法:判断“类型”和“类型”之间的关系
//Integer类和Integer类之间是否存在可以赋值的关系
System.out.println("Integer <= Integer:"+Integer.class.isAssignableFrom(Integer.class));//true
//相当于:Number n = Integer.valueOf();
System.out.println("Number <= Integer:"+Number.class.isAssignableFrom(Integer.class));//true
//相当于:Integer i = Number.valueOf();
System.out.println("Integer <= Number:"+Integer.class.isAssignableFrom(Number.class));//false
System.out.println("Double <= Integer:"+Double.class.isAssignableFrom(Integer.class));//false
//一个接口的引用可以指向实现类的对象
System.out.println("Comparable <= Integer:"+Comparable.class.isAssignableFrom(Integer.class));//true
四、访问成员变量(字段):每个字段都会被封装成一个Field对象
public class Demo03 {
public static void main(String[] args) {
//得到Book类的Class对象
Class cls = Book.class;
//所有public访问修饰符定义的字段(包含父类)
//Field[] fields = cls.getFields();
//所有定义的字段(仅包含子类)
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
//返回值为int 1:public, 2:private
System.out.println("成员变量访问修饰符(int):"+field.getModifiers());
System.out.println("成员变量访问修饰符:"+Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:"+field.getType());
System.out.println("成员变量名称):"+field.getName());
System.out.println();
}
}
}
class Book{
private int stack;
public String bookName;
private double sale;
public int getStack() {
return stack;
}
public void setStack(int stack) {
this.stack = stack;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public double getSale() {
return sale;
}
public void setSale(double sale) {
this.sale = sale;
}
@Override
public String toString() {
return "Book [stack=" + stack + ", bookName=" + bookName + ", sale=" + sale + "]";
}
public void dosth(int a,double b,String c) {
}
}
五、使用反射的方式,完成成员变量保存值(set方法)
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
//运行期
//使用反射的方式,完成成员变量保存值
//获取Class类型对象
Class cls = Book.class;
//通过反射创建Book类的对象
Object obj = cls.newInstance();
//public字段
//按照字段名称,获取指定字段
Field field1 = cls.getDeclaredField("bookName");
//set()方法:给成员变量保存值
//参数1:目标Book对象
//参数2:存入成员变量的值
field1.set(obj, "小王子");
System.out.println(obj);
//private字段
//按照字段名称,获取指定字段
Field field2 = cls.getDeclaredField("sale");
//设置允许访问私有成员变量
field2.setAccessible(true);
//set()方法:给成员变量保存值
//参数1:目标Book对象
//参数2:存入成员变量的值
field2.set(obj, 0.8);
System.out.println(obj);
}
六、访问对象中的成员变量(字段)值(get方法)
public class Demo05 {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
//创建Book类的对象
Book book = new Book();
//赋值
book.setBookName("唐诗三百首");
book.setStack(300);
book.setSale(0.8);
//调用静态方法,并传入对象
printInfo(book);
}
public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
//获取Class类型对象
Class cls = obj.getClass();
//获取所有的成员变量
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println("成员变量名称:"+field.getName());
//判断是否可以访问(private)
if(!field.isAccessible()) {
//设置私有成员变量允许访问
field.setAccessible(true);
}
//get()方法:访问成员变量值,传入目标对象
System.out.println("成员变量内容:"+field.get(obj));
}
}
}
七、获取调用方法(getMethod()方法)
public class Demo06 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//获取Class对象
Class cls = Base.class;
//创建Base类的对象
Object obj = cls.newInstance();
//按照方法名称和“参数类型”获取Method方法对象
//create()
//Method method = cls.getMethod("create");
//create(int x)
Method method = cls.getMethod("create", int.class);
//Method对象的invoke()作用:以反射的方式执行create()方法
//参数1:目标对象(具体给哪个对象执行create方法)
//参数2:参数值
int r = (int)method.invoke(obj,5);
System.out.println(r);
}
}
class Base{
public int create() {
return 1;
}
public int create(int x) {
return x;
}
}
八、以反射方式调用静态方法
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//计算以10为底的对数
//获取某个指定数字的位数
System.out.println((int)Math.log10(123456) + 1);
//反射调用
//得到Math类的Class对象
Class cls = Math.class;
//按照方法名称和“参数类型”获取Method方法对象
Method methodLog10 = cls.getMethod("log10", double.class);
//以反射的方式执行methodLog10()方法
int size = Double.valueOf((double)methodLog10.invoke(null, 123456)).intValue() + 1;
System.out.println(size);
}
九、多态:目标对象决定了调用归属
Person类定义了hello()方法,其子类也重写了hello()方法,那么从Person实例中获取hello()方法,作用于Student实例,调用的是父类的hello方法还是子类的hello方法?运行结果是:调用了子类的hello方法。所以使用反射调用方法时,仍然遵循多态原理,调用实例的方法。
public class Demo08 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//获取Person的hello方法
Method h = Person.class.getMethod("hello");
//对Student实例调用hello方法
h.invoke(new Student());
}
}
class Person{
public void hello(){
System.out.println("Person hello!");
}
}
class Student extends Person{
public void hello() {
System.out.println("Student hello!");
}
}