文章目录
- 一、类加载器
-
- 1.三种类加载器
- 2.类加载器的内容
- 3.类加载器的双亲委派模型
- 二、反射
-
- 1.反射的定义
- 2.反射的基本应用
-
- (1)获取Class类的对象(即得到相应类的字节码)
- (2)关于构造方法的反射
- (3)关于成员变量的反射
- (4)关于成员方法的反射
一、类加载器
两个类是同一个类 = 两个类用的类加载器相同 + 类本身的全限定名相同
1.三种类加载器
-
Bootstrap ClassLoader 根类加载器(引导类加载器)
引导类加载器(由C++语言编写)由操作系统的本地代码来实现,这个类加载器没有父类加载器。之所以会出现引导类加载器,是因为其他两个类加载器也需要类加载器来加载。它负责Java核心类的加载,比如System,String等。
-
Extension ClassLoader 扩展类加载器
扩展类加载器(由Java语言编写)主要负责扩展路径下的代码,负责JRE的扩展目录中jar包的加载。它的父类加载器是根类加载器。
-
System ClassLoader 系统类加载器
系统类加载器(由Java语言编写)也称为应用类加载器(APPClassLoader),这两个是等价的。负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。它的父类加载器是扩展类加载器。
- 用户自定义类加载器
三种类加载器的关系图如下:
2.类加载器的内容
- 除了根类加载器以外,其他的类加载器都有且只有一个父类加载器。
- 当生成一个自定义的类加载器实例时,如果没有指定它的父加载器,那么系统类加载器将成为该类加载器的父类加载器。
-
类加载器只负载它的“祖先”无法加载的类。(双亲委派模型)
比如,若系统类加载器需要加载一个类,它首先委托它的父类加载器(扩展类加载器),然后扩展类加载器再委托它的父类加载器(根类加载器),如果父类加载器不能加载此类,那么子类加载器就会在自己的库中查找这个类,这样也使得安全性大大提升。
3.类加载器的双亲委派模型
(1)定义
在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为null时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。
(2)双亲委派模型的优点
- 双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类)。
- 保证了 Java 的核心 API 不被篡改。
二、反射
1.反射的定义
- 反射就是把Java类中的各种成分(成员变量,成员方法,构造器等)映射成相应的Java类。
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 反射一般会涉及如下类:Class(表示一个类的类)、Field(表示属性的类)、Method(表示方法的类)和Constructor(表示类的构造器的类)。
2.反射的基本应用
写在前面,对于IDEA使用反射时,要在File->Settings->Build,Execution,Deployment->Compiler->Java Compiler中的Additional command line parameters: 后面填上 -parameters,点击OK。
(1)获取Class类的对象(即得到相应类的字节码)
- 方法一:getClass()方法
Person p = new Person();
Class c = p.getClass();
- 方法二:通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性)
Class c2 = Person.class;
- 方法三:通过调用Class类中的静态方法forName()方法将类名作为字符串传递给Class类中的静态方法forName即可)
Class c3 = Class.forName("Person");
注: 前两种方法必须明确Person类型,方法三是指定这种类型的字符串就行。(方法三扩展更强,我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了)
首先写好了一个Reflect类,然后利用方法三去获得Class类的对象。
public class Reflect {
public String name;
public int age;
private String address;
public Reflect(){
System.out.println("空参构造方法");
}
public Reflect(String name){
this.name = name;
System.out.println("带有String的构造方法");
}
private Reflect(String name,int age) {
this.name = name;
this.age = age;
System.out.println("带有String,int的私有构造方法");
}
public Reflect(String name ,int age,String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String,int,String的构造方法");
}
public void method1(){
System.out.println("没有返回值没有参数的方法");
}
public void method2(String name){
System.out.println("没有返回值有参数的构造方法"+name);
}
public int method3(){
System.out.println("有返回值,没有参数的构造方法");
return 1;
}
public String method4(String name){
System.out.println("有返回值,有参数的方法");
return "参数为:"+name;
}
public void method5(){
System.out.println("私有方法");
}
public String toString(){
return "Reflect [name=" + name + ", age=" + age + ", address=" + address+ "]";
}
}
public class Demo{
public static void main(String[] args){
Class c = Class.forName("Reflect");
System.out.println(c);
}
}
运行结果:
将ClassNotFoundException异常抛出后,运行结果:
(2)关于构造方法的反射
(2)-1通过反射获取构造方法
- 返回一个构造方法
- public Constructor getConstructor(Class<?>… parameterTypes) 获取由public修饰, 且指定参数类型对应的构造方法。
- public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)。
- 返回多个构造方法
- public Constructor<?>[ ] getConstructors() 获取所有的public 修饰的构造方法。
- public Constructor<?>[ ] getDeclaredConstructors() 获取所有的构造方法(包含私有的)。
举例如下(需要导包):
import java.lang.reflect.Constructor;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c = Class.forName("Reflect");
System.out.println("----------获取所有的构造方法--------------");
Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con : cons){
System.out.println(con);
}
System.out.println("-----------获取一个构造方法(无参)-------------");
Constructor con1 = c.getConstructor(null);
System.out.println(con1);
System.out.println("-----------获取一个构造方法(有参)-------------");
Constructor con2 = c.getConstructor(String.class);
System.out.println(con2);
System.out.println("-----------获取一个全部构造方法(包括私有)(有参)-------------");
Constructor con3 = c.getDeclaredConstructor(String.class,int.class);
System.out.println(con3);
}
}
运行结果:
(2)-2通过反射方式,获取构造方法(public权限),创建对象
- 步骤:
- 获取到Class对象(forName())
- 获取指定的构造方法(利用(2)-1)
-
通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object… initargs)
- 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
Class c = Class.forName("Reflect");
Constructor con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("小东",22,"西安");
System.out.println("obj");
}
}
- 运行结果:获取的哪个构造方法,就用哪个构造方法创建对象,创建对象时用newInstance()
(2)-3通过反射方式,获取私有构造方法,创建对象
- 要想获取私有构造方法,并用私有构造方法来创建对象,首先就要取消java的权限修饰符(public、private等)。AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
- 步骤
- 获取到Class对象
- 获取指定的构造方法
- 暴力访问, 通过setAccessible(boolean flag)方法
-
通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object… initargs)
- 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
Class c = Class.forName("Reflect");
Constructor con = c.getDeclaredConstructor(String.class, int.class);
con.setAccessible(true); //是否取消 Java 语言访问检查:true
Object obj = con.newInstance("小东",22);
System.out.println("obj");
}
}
- 运行结果
(3)关于成员变量的反射
(3)-1通过反射获取成员变量
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
- 返回一个成员变量
- public Field getField(String name) 获取指定的 public修饰的变量。
- public Field getDeclaredField(String name) 获取指定的任意变量。
- 返回多个成员变量
- public Field[] getFields() 获取所有public修饰的变量。
- public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)。
举例如下:
import java.lang.reflect.Field;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException,
NoSuchMethodException, NoSuchFieldException {
Class c = Class.forName("Reflect");
System.out.println("---------获取多个变量(包括私有)--------");
Field[] f = c.getDeclaredFields();
for(Field field : f){
System.out.println(field);
}
System.out.println("---------获取一个变量--------");
Field ageField = c.getField("age");
System.out.println(ageField);
System.out.println("---------获取一个变量(私有)--------");
Field addressField = c.getDeclaredField("address");
System.out.println(addressField);
}
}
运行结果:
(3)-2通过反射获取成员变量,并进行赋值与获取值操作
- 获取成员变量,步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
-
通过方法,给指定对象的指定成员变量赋值或者获取值
5.1public void set(Object obj, Object value)
在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
5.2public Object get(Object obj)
返回指定对象obj中,此 Field 对象表示的成员变量的值
- 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException,
NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c = Class.forName("Reflect");
Constructor con = c.getConstructor(String.class);
Object obj = con.newInstance("小明");
Field nameField = c.getField("name");
Field ageField = c.getField("age");
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true); //取消私有成员变量addressField的Java 语言访问检查
//通过方法,获取指定对象的指定成员变量的值
System.out.println("------------取值--------------");
System.out.println("name = "+ nameField.get(obj));
System.out.println("age = "+ ageField.get(obj));
System.out.println("address = "+ addressField.get(obj));
//赋值
System.out.println("------------赋值--------------");
ageField.set(obj, 23);
addressField.set(obj, "大雁塔");
System.out.println("name = "+ nameField.get(obj));
System.out.println("age = "+ ageField.get(obj));
System.out.println("address = "+ addressField.get(obj));
}
}
- 运行结果:
(4)关于成员方法的反射
(4)-1通过反射获取成员方法
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
- 返回获取一个方法:
-
public Method getMethod(String name, Class<?>… parameterTypes)
获取public 修饰的方法
-
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)
获取任意的方法,包含私有的
(参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型)
-
- 返回获取多个方法:
- public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
- public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
举例如下:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException,
NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c = Class.forName("Reflect");
System.out.println("---------获取多个方法--------------");
Method [] methods = c.getDeclaredMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println("---------获取一个方法--------------");
Method method = c.getMethod("method1", null);
System.out.println(method);
method = c.getMethod("method4", String.class);
System.out.println(method);
System.out.println("---------获取一个方法(私有)--------------");
method = c.getDeclaredMethod("method5", null);
System.out.println(method);
}
}
结果如下:
(4)-2通过反射获取成员方法,使用成员方法
- 获取成员方法,步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的方法
-
执行找到的方法
5.1public Object invoke(Object obj, Object… args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
- 举例如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo{
public static void main (String[] args) throws ClassNotFoundException,
NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c = Class.forName("Reflect");
Constructor con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("小东", 23, "西安");
System.out.println("-----------调用方法------------");
Method m4 = c.getMethod("method4",String.class);
Object result = m4.invoke(obj,"你好呀");
System.out.println("result = " + result);
System.out.println("-----------调用私有方法------------");
Method m5 = c.getDeclaredMethod("method5",null);
m5.setAccessible(true);
m5.invoke(obj,null);
}
}
- 运行结果: