Java反射学习总结,让学习 so easy!
目录(文章略长!选择阅读????)前言:基础APIField类(类的属性(字段)类)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();
}
}
结果:
哈哈哈!看完代码就明白了,这就是简单的新建数组,再赋值数据,在内存空间里的数组长度仍然变不了,就连反射也没办法!虽然完全可以使用其他的代码替代这一操作,但这足以让你理解数组与反射的关系,默念真理🙆
暂时完毕!谢谢观看,觉得🆗点个赞呗!😀
更多推荐
所有评论(0)