Java反射学习总结,让学习 so easy!

目录(文章略长!选择阅读😀)

前言:

基础API

Field类(类的属性(字段)类)

Method类(类的成员方法类)

Constructor类 (类的构造器类)

从Class类开始

通过反射获取一个类的父类和接口

获取类的成员属性

通过反射操作对象属性,包括private属性

获取类的成员方法

使用类的成员方法

获取类的构造器

通过类的构造器创建对象

通过反射无视泛型🙈

通过反射修改数组的长度🚨(此处有坑)


前言:

反射的优点:

  • 能够在运行时动态的获取类的实例,大大提高了系统的灵活性。
  • 与Java动态编译相结合,可以实现强大的功能

反射的缺点:

  • 使用反射会降低程序的性能
  • 反射会降低程序的安全性
  • 破坏了类的封装性,可以通过反射获取这个类的私有方法和属性

基础API

Field类(类的属性(字段)类)

常用方法:

String toGenericString()使用通用形式描述此字段
String getName()获取此字段的名字
int getModifiers()返回一个整数,然后使用Modifier.toString(int mod)解码修饰符。 
Class<?> getType()返回此字段的数据类型
public void setAccessible(boolean flag)设施该类private字段是否可以被操作
Object get(Object o)在指定的字段上获取此字段的值
void set(Object o,Object value)在指定的对象上设置此字段的值

Method类(类的成员方法类)

String toGenericString()使用通用形式描述此方法
String getName()获取此方法的名字
int getModifiers()返回一个整数,然后使用Modifier.toString(int mod)解码修饰符。 
Class<?> getReturnType()获取函数返回值数据类型
public void setAccessible(boolean flag)设置此类的private 方法是否可以被操作
Class<?>[]  getParameterType()返回该方法的所有参数
int getParameterCount()获取此函数参数的数量
Objcet invoke(Object obj,Object...args)在指定对象上调用此方法,并传入指定的参数

Constructor类 (类的构造器类)

String toGenericString()使用通用形式描述此构造器
String getName()获取此构造器的名字,即与类同名
int getModifiers()返回一个整数,然后使用Modifier.toString(int mod)解码修饰符。 
public void setAccessible(boolean flag)如果构造器是私有的,则设置它的访问性
Class<?>[]  getParameterType()返回该构造器的所有参数
int getParameterCount()获取此构造器参数的数量
T newInstance(Object....initargs)使用此构造器实例化对象,可传入参数

 

从Class类开始

(class是Java的关键字,Class是Java的一个类,开头大写别搞混了)Class类是Java的反射中心,官方API说明如下:

  • public final class Class<T>
    extends Object
    implements Serializable, GenericDeclaration, Type, AnnotatedElement

final 说明此类不可被继承,Class的常用方法如下:

Static Class<?> forName(String className)返回与类名或接口名关联的类对象
String getName()返回此类对象所指的完整类名
Class<?> getSuperclass()获取此类对象 的父类 类对象
Class<?>[] getInterfaces()获取此类对象 的所有实现接口,并返回一个接口数组

Field[] getDeclareFields()  可传入字段名,返回相应的字段

返回此类的属性(包括private),不包括继承来的属性
 Method[]  getDeclaredMethods()  可传入方法名可参数 (String,Class...),返回该方法返回此类的方法(包括private),不包括继承来的方法
Constructor[] getConstructor() 可传入参数(Class....praameterType)获取指定的构造器返回此类的构造器(包括private),不包括继承来的方法
Field[] getFields()   可传入字段名,返回相应的字段返回本类及父类中所有的public 属性
 Method[] getMethods()  可传入方法名和参数 (String,Class...),返回该方法返回本类及父类中所有的public 方法
Constructor[] getConstructor() 可传入参数(Class....praameterType)获取指定的构造器因为构造器不是类的成员,不会被继承,故该方法只能返回本类public 修饰的构造器
Class getComponentType()返回数组元素的数据类型

通俗的讲,Class类的一个实例化对象(即“类对象”)指的就是一个Java类(包括接口,枚举),例如String.class的类型是一个Class<String>,而使用从Object那继承的getClass()方法,也可以返回本对象的Class对象,如下代码演示:

 public static void main(String[] args) {
         /**
         实例化Class类的对象
         */
        Class<?> class1 = null;
        Class<?> class2 = null;
        Class<?> class3 = null;

        class1 = Class.forName("java.util.Date");
        class2 = new Date().getClass();
        class3 = Date.class;
        System.out.println(class2.getName());
        System.out.println(class3.getName());
        System.out.println(class1.getName());
    }

运行结果:(注意:foName()方法中的类名要写它的完整类名

 

通过反射获取一个类的父类和接口

此处我们拿一个已知类  ArrayList 来测试,它的结构如下:

代码示例:

public static void main(String[] args){
        /**
         通过反射获取一个类的父类和接口
         */
        Class<?> class1=ArrayList.class;
        //获取本类的父类
        Class<?> superclass=class1.getSuperclass();
        System.out.println("本类的父类为"+superclass.getName());
        //获取本类的所有接口
        Class<?> myinterface[] =class1.getInterfaces();
        System.out.println("本类的接口有:");
        for (Class<?> c : myinterface) {
            System.out.println(c.getName());
        }
}

运行结果:

获取类的成员属性

示例代码:

import java.awt.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
反射获取类的属性
 */
public class Test {
    private Class<Dog> dogClass;
    public Test(){
        dogClass=Dog.class;
        System.out.println("获取本类的所有属性,包括private修饰");
        Field[] fields=dogClass.getDeclaredFields();
        print(fields);
        System.out.println("本类及父类中所有的public 属性");
        Field[] fields1=dogClass.getFields();
        print(fields1);
    }
    public void print(Field[] fields){
        for(Field field:fields){
            String fieldModifiers= Modifier.toString(field.getModifiers());//获取属性修饰符
            Class<?> fieldType=field.getType();//获取属性类型
            String fieldName=field.getName();//获取属性名
            System.out.println(fieldModifiers+" "+fieldType+" "+fieldName+";");
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
    }
}
interface Eat{
    int number=100;
}
class Animal {  //动物类
    public Color color;//颜色
    private int weight; //重量
}
class Dog extends Animal implements Eat{
    public String home;//狗窝
    private String name;//狗名
}

运行结果:

观察这个结果,会发现,因为private声明的类成员不会被继承,所以 反射不可以在本类中获取到父类的私有成员,但这个问题也很好解决,既然本类获取不到,那我们完全可以先获取父类,然后在操作父类获取此属性,反射来了拦都拦不住!😀(像极了生气的女朋友)

通过反射操作对象属性,包括private属性

import java.awt.*;
import java.lang.reflect.Field;
/*
反射获取类的属性
 */
public class Test {
    private Class<Dog> dogClass;
    public Test(){
        dogClass=Dog.class;
        try {
            Field field=dogClass.getDeclaredField("name");
            field.setAccessible(true);//设置此私有属性可操作
            System.out.println(field.toGenericString());//属性描述
            Dog dog=new Dog("我的家","小虎");
            System.out.println(field.get(dog));//获取该对象的该字段
            field.set(dog,"阿狗");
            System.out.println(dog.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
    }
}
class Dog{
    public String home;//狗窝
    private String name;//狗名
    public Dog(String home, String name) {
        this.home = home;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "home='" + home + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

运行结果:

 

获取类的成员方法

与获取类的成员属性相似,(如需获取父类的私有成员方法,小技巧同上)代码如下:

import java.lang.reflect.Method;
/*
获取类的成员方法
 */
public class Test {
    private Class<Dog> dogClass;
    public Test(){
        dogClass=Dog.class;
        System.out.println("本类所有方法:");
        Method[] methods=dogClass.getDeclaredMethods();
        for (Method method:methods){
            System.out.println(method.toGenericString());
        }
        System.out.println("本类及其超类的所有public方法");
        Method[] methods2=dogClass.getMethods();
        for (Method method:methods2){
            System.out.println(method.toGenericString());
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
    }
}
class Dog{
    public String home;//狗窝
    private String name;//狗名
    public Dog(String home, String name) {
        this.home = home;
        this.name = name;
    }
    private void check(){
        System.out.println("hello world");
    }
    public boolean print(){
        return true;
    }
}

运行结果:

 

使用类的成员方法

代码示例:

import java.lang.reflect.Method;
/*
获取类的成员方法
 */
public class Test {
    private Class<Dog> dogClass;
    public Test(){
        dogClass=Dog.class;
        try {
            //如果要调用的成员方法有参数,则需要说明参数,防止重载的混淆
            Method method=dogClass.getDeclaredMethod("print",String.class,String.class);
            method.setAccessible(true);//设置private方法可访问
            Dog dog=new Dog("我家","小虎");
            method.invoke(dog,"这是","的狗窝");//在调用时要声明参数
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
    }
}
class Dog{
    public String home;//狗窝
    private String name;//狗名
    public Dog(String home, String name) {
        this.home = home;
        this.name = name;
    }
    @Override
    public String toString() {
        return (home+name);
    }
    private void print(String string1,String string2){
        System.out.println(string1+toString()+string2);
    }
}

运行结果:

获取类的构造器

代码示例:

import java.lang.reflect.Constructor;
/*
获取类的成员方法
 */
public class Test {
    private Class<Dog> dogClass;
    public Test(){
        dogClass=Dog.class;
        try {
            System.out.println("getDeclaredConstructors()获取本类所有的构造器:");
            Constructor[] constructors=dogClass.getDeclaredConstructors();
            for(Constructor constructor1:constructors){
                System.out.println(constructor1.toGenericString());
            }
            System.out.println("getConstructors()获取本类的所有共有构造器:");
            Constructor[] constructors1=dogClass.getConstructors();
            for(Constructor constructor:constructors1){
                System.out.println(constructor.toGenericString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
    }
}
class Animal{
    public Animal(){
        System.out.println("hello world");
    }
}
class Dog extends Animal{
    public String home;//狗窝
    private String name="小虎";//狗名
    public Dog(String home, String name) {
        this.home = home;
        this.name = name;
    }
    private Dog(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "home='" + home + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

运行结果:

 

通过类的构造器创建对象

代码示例:(下面代码调用的类的私有构造器来构造对象,所以可以说是破解了单例模式里的限制了,无敌)

import java.lang.reflect.Constructor;
/*
通过私有构造器构造对象
 */
public class Test {
    private Class<Dog> dogClass;
    public Test(){
        dogClass=Dog.class;
        try {
            Constructor constructor=dogClass.getDeclaredConstructor(String.class);
            constructor.setAccessible(true);
            Dog dog= (Dog) constructor.newInstance("我家的");
            System.out.println(dog.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Test test=new Test();
    }
}
class Animal{
    public Animal(){  //在创建子类对象的时候,父类的无参构造也会被调用
        System.out.println("hello world");
    }
}
class Dog extends Animal{
    public String home;//狗窝
    private String name="小虎";//狗名
    public Dog(String home, String name) {
        this.home = home;
        this.name = name;
    }
    private Dog(String home) {
        this.home=home;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "home='" + home + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

运行结果:

 

通过反射无视泛型🙈

泛型在使用使类的定义更加灵活,但有时因为需要,我们不得不跳过泛型的限制,而反射就可以做到这一点,原理如下:

因为Java泛型只在编译时生效,这个时候可以使用反射获取需要泛型的方法,然后使用它,跳过编译阶段

示例代码:

public static void main(String[] args) {
        ArrayList<Integer> arrayList=new ArrayList<>();//定义一个泛型为Integer的集合,说明里面只能存储Integer类型及其子类
        arrayList.add(12);//存入一个Integer值
        Class<?> listClass=arrayList.getClass();//获取ArrayList类对象
        try {
            Method method=listClass.getMethod("add",Object.class);//获取add(Object o)方法
            method.invoke(arrayList,"hello world");//向集合中插入String类型的数据
            method.invoke(arrayList,new Date());//插入Date类型的数据
            //以下三种获取元素的方法皆可,不可使用Iterable的forEach()方法
            System.out.println(arrayList);
            Iterator iterator=arrayList.iterator();
            while (iterator.hasNext()){
                System.out.print(iterator.next().toString()+"---");
            }
            System.out.println("\n"+arrayList.get(2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行结果:

 

通过反射修改数组的长度🚨(此处有坑)

没错,反射又在挑战我们的基础认知,此处不要慌!默念:我们现在看到的都是JVM上的表象,在内存里存储的数组长度不能改变这仍然是真理!现在我们就来谈谈我们可以使用的表象:

首先看如下代码:

int[] temp = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(temp instanceof Object);

没错它的数据结果是true 查阅资料,在权威书籍上发现这句话:

每个Java数组都与一个Class对象关联,并于其他具有相同元素类型的数据共享该对象,尽管数据类型不是类,但是每个数据的Class对象起到的作用看起来像是:

  • 每个数组类型的直接超类都是Object
  • 每个数组类型都实现了Cloneable接口和 java.io.Serializable接口

在细品JavaAPI中对Class的描述:

Class类的类表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注释是一种界面。 每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。 原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象

😏嘿嘿!看来Class对像对Java中的原始类型,数组,void,引用类型是通吃!那意味可以通过getClass()方法来获取与数组相关联的Class对象,那么反射他来了,他带着六亲不认的步伐来了!

public class Test {
    public static void main(String[] args) {
        int[] ai={1,2,3,4,5,6,6};
        print(ai);
        Class<?> aiClass=ai.getClass();//获取Class对象
        Class<?> aiClassNumber=aiClass.getComponentType();//获取数组的元素的Class对象,此处为int(也可被Class对象表示)
        int[] newInstance= (int[]) Array.newInstance(aiClassNumber,12);//新建一个元素类型为int,长度为12的数组
        System.arraycopy(ai,0,newInstance,0,ai.length); //copy原数组的数据
        print(newInstance);
    }
    public static void print(int[] ai){//输出数组
        System.out.println("数组长度:"+ai.length);
        for(int i=0;i<ai.length;i++){
            System.out.print(ai[i]+"  ");
        }
        System.out.println();
    }
}

结果:

哈哈哈!看完代码就明白了,这就是简单的新建数组,再赋值数据,在内存空间里的数组长度仍然变不了,就连反射也没办法!虽然完全可以使用其他的代码替代这一操作,但这足以让你理解数组与反射的关系,默念真理🙆

 

 

                                                                    暂时完毕!谢谢观看,觉得🆗点个赞呗!😀

                                                                                      

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值