类加载、类加载器、反射

1. 类加载

若程序使用的类未加载到内存中,则系统会通过加载、连接、初始化三步实现对这个类的加载。

1.1 加载

将class文件读入内存,并为之建立一个Class对象

任何类被使用时都会有一个Class对象。

1.2 连接

1.2.1 验证

是否有准确的内部结构,并和其他类协调一致

1.2.2 准备

负责为类的静态成员分配内存,并设置默认初始化

1.2.3 解析

将类的二进制数据中的符号引用替换为直接引用。

1.3 初始化

1.3.1 类的初始化时机

  • 创建类的实例
  • 类的静态变量,或者为静态变量赋值
  • 类的静态方法
  • 使用反射方法强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

2. 类加载器

负责将.class文件加载到内存中,并为之生成对应的Class对象。

组成:

2.1 Bootstrap ClassLoader

根类加载器,负责Java核心类的加载。

在JDK中JRE的lid目录下的rt.jar文件中。

2.2 Extension ClassLoader

扩展类加载器

负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

2.3 System ClassLoader

系统类加载器

负责在JVM启动时加载在java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

3. 反射

概览:

每个类都有一个 Class 对象,包含了与类有关的信息。

当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。

类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class 对象。

反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
  • Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
  • Constructor :可以用 Constructor 创建新的对象。

Java反射机制是在运行状态中

对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制

3.1 Class类

Class类的实例表示正在运行的 Java 应用程序中的类和接口.

  • 枚举是一种类,注释是一种接口。
  • 每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。
  • 基本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为Class对象。

没有公共构造方法

Class对象是在加载类时由JVM通过调用类加载器中的defineClass()方法自动创建的。

使用Class对象来显示对象的类名:

void printClassName(Object obj) {
    System.out.println("The class of " + obj +" is " + obj.getClass().getName());
}

获取Class对象的3种方式:

1、Object类中的getObject()方法:

Person p=new Person();
Class c=p.getObject();

2、由类名.class获取到字节码文件对象(任意数据类型都有一个class静态属性)

Class c1=Person.class;

3、由Class类方法(将类名作为字符串传给Class类中的静态方法forName())

Class c3=Class.forName("Person");

总结:

1、2必须明确Person类型

3指定这种类型的字符串,扩展更强,不需知道类,只需提供字符串,按照配置文件加载即可。

使用3需在方发出声明throws ClassNotFoundException

3.2 通过反射获取构造方法并使用

在反射机制中,把类的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中构造方法用Constructor类表示。

3.2.1 Constructor类

java.reflect.Constructor;

它提供关于类的单个构造方法的信息以及对它的访问权限。

方法:

T newInstance(Object...initargs):使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

可用Class类中提供的方法获取构造函数。

public Constructor<T> getConstructor(Class<?>... parameterTypes):返回一个Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):返回一个Constructor对象,该对象反映此Class对象所表示的类或接口的指定构造方法。

public Constructor<T>[ ]  getConstructors():返回一个包含某些Constructor对象的数组,这些对象反映此Class对象所表示的类的所有公共构造方法。

public Constructor<T>[ ] getDeclaredConstructors():返回Constructor对象的一个数组,这些对象反映此Class对象表示的类声明的所有构造方法。

需加:NoSuchMethodExceotion、SecurityException

Constructor cons=c.getConstructor(String.class);
  • 若用getConstructors(Class<?>... parameterTypes)获取private,会有——java.lang.NoSuchMethodException
  • 若用getDeclaredConstructor()获取public方法,无提示,可正常运行,因为这个方法可获取所有构造方法

3.3 通过反射获取构造方法并创建对象

步骤:

  1. 获取到Class对象
  2. 获取指定的构造方法
  3. 通过构造方法类Constructor中的方法创建对象
Class c=Class.forName("Person");
Constructor cons=c.getConstructor(String.class,int.class,String.class);
Object ob=cons.newInstance("Zhang",23,"China");

3.4 使用反射获取私有构造方法,创建对象

3.4.1 AccessibleObject类

直接已知子类:

Constructor, Field, Method

它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

  • 对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

常用方法

public void setAccessible(boolean flag) throws SecurityException;将此对象的 accessible 标志设置为指示的布尔值。

  • 参数为true,反射的对象在使用时应取消Java语言的访问检查
  • 参数为false,应实施检查

步骤:

  1. 获取到Class对象
  2. 获取指定的构造方法
  3. 通过暴力访问,由setAccessible(boolean flag)方法
  4. 通过构造方法类Constructor中的方法,public t newInstance(Object...initargs)创建对象
Class c=Class.forName("Person");
Constructor cons=c.getConstructor(String.class,int.class,String.class);
cons.setAccessible(true);
Object ob=cons.newInstance("Zhang",23,"China");

3.5 由反射获取成员变量并使用

反射机制中,类中的成员变量用Field类表示

Field提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。 

 

public Field getField(String name): 返回一个 Field对象,它反映此Class对象所表示的类或接口的指定公共成员字段。

public Field getDeclaredField(String name):返回一个Field对象,该对象反映此Class对象所表示的类或接口的指定已声明字段。

public Field[ ] getFields():返回一个包含某些 Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段。

public Field[ ] getDeclaredFields():返回Field对象的一个数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段。

Field ageField=c.getField("age");

3.5 由反射创建对象获取指定的成员变量,进行赋值与取值操作

步骤:

  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的成员变量(若为私有变量,通过setAccessible(boolean flag)暴力访问)
  5. 通过方法,给指定对象的指定成员变量赋值截取值

public void set(Object obj,Object value): 将指定对象变量上此Field对象表示的字段设置为指定的新值。

public Object get(Object obj):返回指定对象上此Field表示的字段的值。

Class c=Class.forName("Person");

Constructor cons=c.getConstructor(String.class);

Object obj=cons.newInstance("小明");

Field nameF=c.getField("name");//用于后面获得用此Field对象表示的成员变量的值
Field addressF=c.getField("address");

addressF.setAccessible(true);

addressF.set(obj,China);

System.out.println("name"+=nameF.get(obj));

3.6 由反射获取成员方法并使用

反射机制中,类中的成员方法使用类Method表示

它提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 

public Object invoke(Object obj,Object...args):对带有指定参数(args)的指定对象(obj)调用由此Method对象表示的底层方法。

返回方法的方法:

public Method getMethod(String name,Class<?> ...parameterTypes): 返回一个Method对象,它反映此Class对象所表示的类或接口的指定公共成员方法。

public Method getDeclaredMethod(Stirng name,Class<?>...parameterTypes): 返回一个Method对象,该对象反映此Class对象所表示的类或接口的指定已声明方法。

public Method[ ] getMethods():返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共成员方法。

public Method[ ] getDeclaredMethods(): 返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

3.7 通过反射创建对象,调用指定方法

步骤:

  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的方法
  5. ---开启暴力访问
  6. 执行找到的方法
Class c=Class.forName("Person");

Constructor cons=c.getConstructor(String.class);

Object obj=cons.newInstance("小明");

Method me=c.getMethod("method1",String.class);

Object result=me.invoke(obj,"CSDN");

System.out.println("result="+result);

3.8 泛型的擦除

程序编译后产生的.class文件,是没有泛型约束的

可通过反射,向有泛型约束的集合中,添加任意类型的元素。

ArrayList<Integer> list=new ArrayList<Integer>();

list.add(new Integer(30));

Class c=Class.forName("java.util.ArrayList");

Method addM=c.getMethod("add",Object.class);

addM.invoke(list,"haha");

System.out.println(list);

3.9 反射配置文件

通过反射配置文件,运行配置文件中指定的类的对应方法

读取Properties.txt文件中的数据,通过反射技术,完成Person对象的创建

Properties.txt文件内容:

className=packageName.Person

methodName=method5

步骤:

  1. 通过Properties集合从文件中读取数据;
  2. 读取文件中的数据到集合
  3. 获取键对应值
Properties prop=new Properties();

prop.load(new FileInputStream("Properties.txt"));

String className=prop.getProperty("className");
String methosName=prop.getProperty("methodName");

Class c=Class.forName("Person");

Constructor cons=c.getDeclaredConstructor(String.class,int.class,String.class);
Object obj=con.newInstance("小明",20,"中国");

Method me=c.getDeclaredMethod(methodName,null);

me.setAccessible(true);
m.invoke(obj,null);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值