JavaSE基础:反射

反射概述

1.反射的理解和好处
2.Class类的理解和特点
3.通过反射获取类中的成员【了解】
4.通过反射创建对象【掌握】
5.通过反射调用类中的成员【掌握】
6.通过反射观察泛型的本质

1.反射的理解和好处

反射的理解

正常的程序:
类——>对象——>调用里面的成员
反射:

	对象——>类——>调用里面的成员

反射的好处

案例1:

张三和李四共同完成一个项目

张三:设计类:Music类(√)、Word类(×)

李四:创建对象,并调用里面的方法

public void method(){
	Music m = new Music();
	m.work();

	Word w = new Word();//编译错误
	w.work();
}
//使用反射
public void method(Object o){
	Object obj = o.getClass().newInstance();
	o.getClass().getMethod("work").invoke();
}

静态加载:编译期加载,也就是说编译时加载所有类,如果该类不存在,则直接报编译错误,所以依赖性太强。

动态加载:运行期加载,也就是说运行时加载用到的类,降低了依赖性

案例2:

method("john");
public<T> void method(T t){
	t.getClass();
}

Class类的理解和特点 ★

理解

问:自定义的类,是一个对象吗?

答:是

问:自定义的类,是哪个类型的对象?

答:java.lang.Class类

There is a class named Class;
class Person{
	String name;
	public void method(){}
}
class Student{
}
class Book{
}

class MyClass{
	String className;
	public String getName(){}
	Field[] fields;
	Method[] methods;
}

问:Class类的对象如何创建

答:不是new出来的,而是系统自动创建的,我们可以通过一定的方式获取对象的引用

特点

1.Class本身也是一个类
2.Class 对象只能由系统建立对象
3.一个类在 JVM 中只会有一个Class实例 
4.一个Class对象对应的是一个加载到JVM中的一个.class文件
5.每个类的实例都会记得自己是由哪个 Class 实例所生成
6.通过Class可以完整地得到一个类中的完整结构 

反射详解

一、理解

反射:对象————>类————>调用成员

示例:

public<T> T method(T t){
	System.out.println(t.getClass().getName());
	Class c = t.getClass();
}

二、好处
反射属于java实现动态语言的关键,也就是通过反射使用类相当于动态加载。

静态加载:编译期加载,编译时加载所有的类,如果没有则报错,依赖性太强
动态加载:运行期加载,运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性

三、关于类的 加载

1、类的加载时机

①当创建对象时(new)
②当子类被加载时
③调用类中的静态成员时
④通过反射

Class.forName("java.lang.String");

2、了解类的加载过程
①装载
通过类的加载器将字节码文件装载到内存中,从而生成一个Class类的对象,保存信息
②连接
将字节码文件中的二进制合并到jre,进行校验
③初始化
将类中需要初始化的成员进行初始化,一般主要是指静态成员

3、了解类的加载器
功能:负责装载字节码文件到内存中,从而生成Class类的对象
分类:

启动类加载器
	引导类加载器
	扩展类加载器
	系统类加载器
自定义类加载器

装载的机制:
先从下往上检测是否被加载过,如果都没有,则从上往下依次判断是否能被加载

Class类的特点

一、说明
Class 类属于java.lang包的一个类型,负责解剖某个具体类型的内部结构信息

二、特点
1、本身属于一种类型,继承Object类
2、Class类的对象不是new出来的,而是系统创建出来的
3、对于某个具体类的Class类对象,在内存中只有一份,因为类只加载一次
4、某个具体类的对象都能记得它所属的类型,也就是可以获取Class类的对象
5.通过Class类可以完整的得到某个类的结构信息
6、Class类的对象存放在方法区,普通类的对象存放在堆中

三、获取Class类对象
1.通过全类名字符串来获取

Class c = Class.forName(全类名);

2.通过对象获取

Class c = 对象.getClass();

3.通过类获取

Class c = 类名.class;

4.通过类加载器来获取【了解】

Class c = classLoader.loadClass("全类名");

通过反射获取类的结构

一、获取所有属性
1.通过调用方法获取所有属性

Field[] fields = c.getFields();//获取所有public修饰的属性,包含本类以及从父类继承来的,不限于直接父类
Field[] fields = c.getDeclaredFields();//获取本类中定义的所有的属性,不问修饰符

2、获取属性,获取属性信息

for(Field field:fields){
	//修饰符
	String modifier = Modifier.toString(field.getModifiers());
	//类型
	String type = field.getType().getSimpleName();
	//名
	String name = field.getName();
}

二、获取所有方法
1.通过调用方法获取所有属性

Method[] methods = c.getMethods();//获取所有public修饰的方法,包含本类以及从父类继承来的,不限于直接父类
Method[] methods = c.getDeclaredMethods();;//获取本类中定义的所有的方法,不问修饰符

2、遍历方法,获取每个方法信息

for(Method method:methods){
	//修饰符
	String modifier = Modifier.toString(method.getModifiers());
	//类型
	String type = method.getReturnType().getSimpleName();
	//名
	String name = method.getName();
	//参数
	Class[] paraTypes = method.getParameterTypes();
}

三、获取所有构造
1.通过调用方法获取所有属性

Constructor[] constructors = c.getConstructors();//获取所有public修饰的构造器,包含本类以及从父类继承来的,不限于直接父类
Constructor[] constructors = c.getDeclaredConstructors();;//获取本类中定义的所有的构造器,不问修饰符

2、遍历构造器,获取每个构造器信息

for(Constructor con:constructors){
	//修饰符
	String modifier = Modifier.toString(con.getModifiers());
	//名
	String name = con.getName();
	//参数
	Class[] paraTypes = con.getParameterTypes();
}

四、获取包

Package package=c.getPackage();

五、获取父类

Class parent = c.getSuperClass();

六、获取接口

Class[] interfaces = c.getInterfaces();

七、获取父类或接口泛型

Type type = c.getGenericSuperClass();
ParameterizedType pt = (ParameterizedType)type;
Type[] genericTypes =pt.getActualArumentTypes();//泛型部分

八、获取注解
提示:只能获取运行策略为 RUNTIME的注解

Annotation[] anns = c.getAnnotations();
for(Annotation a:anns){
	Type t = a.annotationType();
}

通过反射创建类对象

一.获取Class类对象

Class c = Student.class;

二、通过反射创建对象

方式一:调用无参构造器【推荐】

Object o = c.newInstance();

方式二:调用有参构造

Constructor con = c.getDeclaredConstructor(String.class,int.class);
con.setAccessible(true);
Object o2 = con.newInstance("john",12);

通过反射访问类成员

一、访问特定属性

/*
参数1:具体的对象
参数2:属性名
参数3:要赋的值

*/
	public void setFieldValue(Object o,String name,Object value){
		//1.获取Field对象
		Field field=o.getClass().getDeclaredField(name);
		//2.暴破
		field.setAccessible(true);
		//3.赋值
		field.set(o,value);
	}
/*
	参数1:具体的对象
	参数2:属性名
*/

public Object returnFieldValue(Object o,String name){
	//1.获取Field对象
	Field field=o.getClass().getDeclaredField(name);
	//2.暴破
	field.setAccessible(true);
	return field.get(o);
}

二、访问特定方法

/*
	参数1:对象
	参数2:要调用的方法
	参数3:实参列表
*/
public Object myInvoke(Object o,Method method,Object...objs){
method.setAccessible(true);
	return method.invoke(o,objs);

}

示例:
1.获取Class类的对象

Class c = 对象.getClass();

2.获取指定的方法

Method method = c.getDeclaredMethod(方法名,XX.class);
method.setAccessible(true);

3.调用该方法

method.invoke(对象,实参列表);

通过反射观察泛型本质

面试题1:

List<Integer> list1 = new ArrayList<>();
List list2 = new ArrayList<>();

syso(list1.getClass()==list2.getClass());true

面试题2:通过反射调用list1的add方法

Object o = list1.getClass().getMethod("add",Object.class).invoke(list1,12345);

图解Java反射

Java Reflection
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制提供的功能

在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理

Java反射机制研究及应用

反射相关的主要API:

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
… …

Class 类

在Object类中定义了以下的方法,此方法将被所有子类继承:

●  public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
在这里插入图片描述
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

Class本身也是一个类
Class 对象只能由系统建立对象
一个类在 JVM 中只会有一个Class实例 
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个 Class 实例所生成
通过Class可以完整地得到一个类中的完整结构 

Class类的常用方法

在这里插入图片描述

反射的应用举例

String str = “test4.Person”;
Class clazz = Class.forName(str);
Object obj = clazz.newInstance();
Field field = clazz.getField(“name”);
field.set(obj, “Peter”);
Object obj2 = field.get(obj);
System.out.println(obj2);

实例化Class类对象(四种方法)

1)前提:若已知具体的类,通过类的class属性获取,该方法
最为安全可靠,程序性能最高
实例:

Class clazz = String.class;

2)前提:已知某个类的实例,调用该实例的getClass()方法获
取Class对象
实例:

Class clazz = “www.atguigu.com”.getClass();

3)前提:已知一个类的全类名,且该类在类路径下,可通过
Class类的静态方法forName()获取,可能抛出ClassNotFoundException
实例:

Class clazz = Class.forName(“java.lang.String”);

4)其他方式(不做要求)

ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);

类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
在这里插入图片描述
在这里插入图片描述

ClassLoader

类加载器是用来把类(class)装载进内存的。JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:
在这里插入图片描述

//1.获取一个系统类加载器
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
//2.获取系统类加载器的父类加载器,即扩展类加载器
classloader = classloader.getParent();
System.out.println(classloader);
//3.获取扩展类加载器的父类加载器,即引导类加载器
classloader = classloader.getParent();
System.out.println(classloader);
//4.测试当前类由哪个类加载器进行加载
classloader = 
Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
//5.测试JDK提供的Object类由哪个类加载器加载
classloader = 
Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
//*6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);

创建运行时类的对象

有了Class对象,能做什么?
创建类的对象:调用Class对象的newInstance()方法
要 求:
1)类必须有一个无参数的构造器。
2)类的构造器的访问权限需要足够。

难道没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。步骤如下:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象。
在这里插入图片描述
以上是反射机制应用最多的地方。

//1.根据全类名获取对应的Class对象
String name = “atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) 	con.newInstance("Peter",20);
System.out.println(p2);

通过反射获取运行时类的完整结构

Field、Method、Constructor、Superclass、Interface、Annotation

实现的全部接口
所继承的父类
全部的构造器
全部的方法
全部的Field

使用反射可以取得:
1.实现的全部接口

public Class<?>[] getInterfaces()   

确定此对象所表示的类或接口实现的接口。

2.所继承的父类

public Class<? Super T> getSuperclass()

返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
3.全部的构造器

public Constructor<T>[] getConstructors()

返回此 Class 对象所表示的类的所有public构造方法。

public Constructor<T>[] getDeclaredConstructors()

返回此 Class 对象表示的类声明的所有构造方法。

Constructor类中:

取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();

4.全部的方法

public Method[] getDeclaredMethods()

返回此Class对象所表示的类或接口的全部方法

public Method[] getMethods()  

返回此Class对象所表示的类或接口的public的方法

Method类中:

public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
public Class<?>[] getExceptionTypes()取得异常信息

5.全部的Field

public Field[] getFields() 

返回此Class对象所表示的类或接口的public的Field。

public Field[] getDeclaredFields() 

返回此Class对象所表示的类或接口的全部Field。

Field方法中:

public int getModifiers()  以整数形式返回此Field的修饰符
public Class<?> getType()  得到Field的属性类型
public String getName()  返回Field的名称。

6.Annotation相关

get Annotation(Class<T> annotationClass) 
getDeclaredAnnotations() 

7.泛型相关

获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()

8.类所在的包 Package getPackage()

小 结:

1.在实际的操作中,取得类的信息的操作代码,并不会经常开发。
2.一定要熟悉java.lang.reflect包的作用,反射机制。
3.如何取得属性、方法、构造器的名称,修饰符等。

通过反射调用类中的指定方法、指定属性

1.调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
在这里插入图片描述

Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
2.调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。

public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

在Field中:

public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。

public void setAccessible(true)访问私有属性时,让这个属性可见。 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值