类加载器

  • 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

类的加载

  • 加载 就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象
  • 连接 验证是否有正确的内部结构,并和其他类协调一致。准备负责为类的静态成员分配内存,并设置默认初始化值。解析将类的二进制数据中的符号引用替换为直接引用
  • 初始化 就是我们以前讲过的初始化步骤(new 对象)
  • 注:简单的说就是:把.class文件加载到内存里,并把这个.class文件封装成一个Class类型的对象。

类的加载时机

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

反射

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

  • 条件:运行状态
  • 已知:一个类或一个对象(根本是已知.class文件)
  • 结果:得到这个类或对象的所有方法和属性

Class类

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

Modifier and Type Method and Description
<U> 类<? extends U> asSubclass(类<U> clazz)
这个 对象来表示由指定的类对象表示的类的子类。
T cast(Object obj)
施放一个目的是通过本表示的类或接口 对象。
boolean desiredAssertionStatus()
如果要在调用此方法时初始化该类,则返回将分配给此类的断言状态。
static 类<?> forName(String className)
返回与给定字符串名称的类或接口相关联的 对象。
static 类<?> forName(String name, boolean initialize, ClassLoader loader)
使用给定的类加载器返回与给定字符串名称的类或接口相关联的 对象。
AnnotatedType[] getAnnotatedInterfaces()
返回一个 AnnotatedType对象的数组, AnnotatedType使用类型指定由此 AnnotatedType对象表示的实体的超级
AnnotatedType getAnnotatedSuperclass()
返回一个 AnnotatedType对象,该对象表示使用类型来指定由此 对象表示的实体的 类。
<A extends Annotation>
A
getAnnotation(类<A> annotationClass)
返回该元素的,如果这样的注释 否则返回null指定类型的注释。
Annotation[] getAnnotations()
返回此元素上 存在的注释。
<A extends Annotation>
A[]
getAnnotationsByType(类<A> annotationClass)
返回与此元素相关 联的注释
String getCanonicalName()
返回由Java语言规范定义的基础类的规范名称。
类<?>[] getClasses()
返回包含一个数组 表示所有的公共类和由此表示的类的成员接口的对象 对象。
ClassLoader getClassLoader()
返回类的类加载器。
类<?> getComponentType()
返回 数组的组件类型的Class。
Constructor<T> getConstructor(类<?>... parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 函数。
Constructor<?>[] getConstructors()
返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 对象。
<A extends Annotation>
A
getDeclaredAnnotation(类<A> annotationClass)
如果这样的注释 直接存在 ,则返回指定类型的元素注释,否则返回null。
Annotation[] getDeclaredAnnotations()
返回 直接存在于此元素上的注释。
<A extends Annotation>
A[]
getDeclaredAnnotationsByType(类<A> annotationClass)
如果此类注释 直接存在间接存在,则返回该元素的注释(指定类型)。
类<?>[] getDeclaredClasses()
返回一个反映所有被这个 对象表示的类的成员声明的类和 对象的数组。
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 函数。
Constructor<?>[] getDeclaredConstructors()
返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组
Field getDeclaredField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 对象。
Field[] getDeclaredFields()
返回的数组 Field对象反映此表示的类或接口声明的所有字段 对象。
方法 getDeclaredMethod(String name, 类<?>... parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 对象。
方法[] getDeclaredMethods()
返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
类<?> getDeclaringClass()
如果由此 对象表示的类或接口是另一个类的成员,则返回表示其声明的类的 对象。
类<?> getEnclosingClass()
返回底层类的即时封闭类。
Constructor<?> getEnclosingConstructor()
如果此对象表示构造函数中的本地或匿名类,则返回表示底层类的立即封闭构造函数的Constructor对象。
方法 getEnclosingMethod()
如果此对象表示方法中的本地或匿名类,则返回表示基础类的即时封闭方法的方法对象。
T[] getEnumConstants()
返回此枚举类的元素,如果此Class对象不表示枚举类型,则返回null。
Field getField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 对象。
Field[] getFields()
返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 对象。
Type[] getGenericInterfaces()
返回 Type表示通过由该对象所表示的类或接口直接实现的接口秒。
Type getGenericSuperclass()
返回 Type表示此所表示的实体(类,接口,基本类型或void)的直接超类
类<?>[] getInterfaces()
确定由该对象表示的类或接口实现的接口。
方法 getMethod(String name, 类<?>... parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 对象。
方法[] getMethods()
返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 对象,包括那些由类或接口和那些从超类和超接口继承的声明。
int getModifiers()
返回此类或接口的Java语言修饰符,以整数编码。
String getName()
返回由 对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String
软件包 getPackage()
获取此类的包。
ProtectionDomain getProtectionDomain()
返回 ProtectionDomain
URL getResource(String name)
查找具有给定名称的资源。
InputStream getResourceAsStream(String name)
查找具有给定名称的资源。
Object[] getSigners()
获得这个类的签名者。
String getSimpleName()
返回源代码中给出的基础类的简单名称。
类<? super T> getSuperclass()
返回 表示此所表示的实体(类,接口,基本类型或void)的超类
String getTypeName()
为此类型的名称返回一个内容丰富的字符串。
TypeVariable<类<T>>[] getTypeParameters()
返回一个 TypeVariable对象的数组,它们以声明顺序表示由此 GenericDeclaration对象表示的通用声明声明的类型变量。
boolean isAnnotation()
如果此 对象表示注释类型,则返回true。
boolean isAnnotationPresent(类<? extends Annotation> annotationClass)
如果此元素上 存在指定类型的注释,则返回true,否则返回false。
boolean isAnonymousClass()
返回 true当且仅当基础类是匿名类时。
boolean isArray()
确定此 对象是否表示数组类。
boolean isAssignableFrom(类<?> cls)
确定由此 对象表示的类或接口是否与由指定的Class 表示的类或接口相同或是超类或 接口。
boolean isEnum()
当且仅当该类在源代码中被声明为枚举时才返回true。
boolean isInstance(Object obj)
确定指定的Object是否与此 Object表示的对象分配
boolean isInterface()
确定指定 对象表示接口类型。
boolean isLocalClass()
返回 true当且仅当基础类是本地类时。
boolean isMemberClass()
返回 true当且仅当基础类是成员类时。
boolean isPrimitive()
确定指定 对象表示一个基本类型。
boolean isSynthetic()
如果这个类是一个合成类,返回true ; 返回false其他。
T newInstance()
创建由此 对象表示的类的新实例。
String toGenericString()
返回描述此 的字符串,包括有关修饰符和类型参数的信息。
String toString()
将对象转换为字符串。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

测试代码使用的Person类

public class Person {
    public int num;
    public String str;

    public Person() {System.out.println("空参数构造方法");}

    public Person(int num, String str) {
        super();
        this.num = num;
        this.str = str;
    }

    private Person(String str,int num) {
        super();
        this.num = num;
        this.str = str;
    }

    public String toString() {
        return "Get [num=" + num + ", str=" + str + "]";
    }

    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }

}

得到Class对象

  • 通过Object类中的getClass()方法
  • 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
  • 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
  • 第三种和前两种的区别是,前两种你必须明确Person类型.后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
  • 因为一个.class文件在内存里只生成一个Class对象,所以无论那一种方法得到Class对象,得到的都是同一个对象。
public class Test {
    public static void main(String[] args) throws Exception {
        // 1: 通过Object类中的getObject()方法
         Person p1 = new Person();
         Class c1 = p1.getClass();
         System.out.println("c1 = "+ c1);

        // 2: 通过 类名.class 获取到字节码文件对象
         Class c2 = Person.class;
         System.out.println("c2 = "+ c2);

        // 3: 反射中的方法
        Class c3 = Class.forName("Person");// 包名.类名 如果没有包名直接写类名
        System.out.println("c3 = " + c3);
    }
}

获取所有权限为public的构造方法

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        //使用class文件对象,获取类中的构造方法
        //  Constructor[]  getConstructors() 获取class文件对象中的所有公共的构造方法
        Constructor[] cons = c.getConstructors();
        for(Constructor con : cons){
            System.out.println(con);
        }
    }
}

获取public权限对应参数的构造方法并使用

  • 无参数构造方法
public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        //获取指定的构造方法 如果getConstructor()不写参数,就获得无参数构造方法
        Constructor con =  c.getConstructor();
        //运行空参数构造方法,Constructor类方法 newInstance()运行获取到的构造方法
        Object obj = con.newInstance();
    }
}
  • 有参数构造方法
public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        //获取指定的有参数构造方法
        Constructor con =  c.getConstructor(int.class,String.class);
        //运行有参数构造方法,Constructor类方法 newInstance()运行获取到的构造方法
        Object obj = con.newInstance(1,"a");
        System.out.println(obj.toString());
    }
}
  • 获取无参构造方法并使用快捷方式
public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        // Class类中定义方法, T newInstance() 直接创建被反射类的对象实例
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

获取所有权限参构造方法

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        //Constructor[] getDeclaredConstructors()获取所有的构造方法,包括私有的
        Constructor[] cons = c.getDeclaredConstructors();
        for(Constructor con : cons){
            System.out.println(con);
        }
    }
}

获取所有权限对应参数参构造方法并使用

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");

        //Constructor getDeclaredConstructor(Class...c)获取到指定参数列表的构造方法
        Constructor con = c.getDeclaredConstructor(String.class,int.class);

        //Constructor类,父类AccessibleObject,定义方法setAccessible(boolean b)
        con.setAccessible(true);

        Object obj = con.newInstance("lisi",18);
        System.out.println(obj);
    }
}

获取权限为public的成员属性并修改

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        //获取成员变量 Class类的方法 getFields() class文件中的所有公共的成员变量
        //返回值是Field[]    Field类描述成员变量对象的类
        Field[] fields = c.getFields();
        for(Field f : fields){
            System.out.println(f);
        }
    }
}
  • 获取成员属性并修改
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        Object obj = c.newInstance();

        //获取指定的成员变量 String str
        //Class类的方法  Field getField(传递字符串类型的变量名) 获取指定的成员变量
        Field field = c.getField("str");

        //Field类的方法 void set(Object obj, Object value) ,修改成员变量的值
        //Object obj 必须有对象的支持,  Object value 修改后的值
        field.set(obj,"王五");
        System.out.println(obj);
    }
}
  • 如果想得到所有的成员变量(包括私有的,如果要进行修改私有成员变量,要先进行public void setAccessible(boolean flag) 并且设置为true)
  • public Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
  • public Field[] getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

获取权限为public的成员方法

import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        Object obj = c.newInstance();
        //获取class对象中的成员方法
        // Method[] getMethods()获取的是class文件中的所有公共成员方法,包括继承的
        // Method类是描述成员方法的对象
        Method[] methods = c.getMethods();
        for(Method m : methods){
            System.out.println(m);
        }
    }
}
  • 获取空参数成员方法并运行
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        Object obj = c.newInstance();

        //获取指定的方法toString运行
        // Method getMethod(String methodName,Class...c)
        // methodName获取的方法名  c 方法的参数列表
        Method method = c.getMethod("toString");
        //使用Method类中的方法,运行获取到的方法toString
        //Object invoke(Object obj, Object...o)
        System.out.println(method.invoke(obj));
    }
}
  • 获取有参数成员方法并运行
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("Person");
        Object obj = c.newInstance();

        //获取指定的方法setNum运行
        // Method getMethod(String methodName,Class...c)
        // methodName获取的方法名  c 方法的参数列表
        Method method = c.getMethod("setNum",int.class);
        //使用Method类中的方法,运行获取到的方法setNum
        //Object invoke(Object obj, Object...o)
        method.invoke(obj,1);

        //验证是否成功
        method = c.getMethod("toString");
        System.out.println(method.invoke(obj));
    }
}
  • 得到全部的成员方法(包括私有的,如果要使用私有成员方法,要先进行public void setAccessible(boolean flag)并且设置为true)
  • public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
  • public Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

泛型擦除

  • 伪泛型:在编译后的.class文件里面是没有泛型的。类型为Object。
  • 用反射的方法绕过编译,得到Class文件对象,直接调用add方法。
import java.lang.reflect.Method;
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) throws Exception {
        ArrayList<String> array  = new ArrayList<String>();
        array.add("a");
        //反射方式,获取出集合ArrayList类的class文件对象
        Class c = array.getClass();
        //获取ArrayList.class文件中的方法add
        Method method = c.getMethod("add",Object.class);
        //使用invoke运行ArrayList方法add
        method.invoke(array, 150);
        method.invoke(array, 1500);
        method.invoke(array, 15000);
        System.out.println(array);
    }
}

通过配置文件来决定运行的步骤

  • 通过配置文件得到类名和要运行的方法名,用反射的操作类名得到对象和调用方法
  • 准备配置文件,键值对
  • IO流读取配置文件 Reader
  • 文件中的键值对存储到集合中 Properties 集合保存的键值对,就是类名和方法名
  • 反射获取指定类的class文件对象
  • class文件对象,获取指定的方法
  • 运行方法
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {
    public static void main(String[] args) throws Exception {
        //IO流读取配置文件
        FileReader r = new FileReader("src\\config.properties");
        //创建集合对象
        Properties pro = new Properties();
        //调用集合方法load,传递流对象
        pro.load(r);
        r.close();
        //通过键获取值
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        //反射获取指定类的class文件对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        //获取指定的方法名
        Method method = c.getMethod(methodName);
        System.out.println(method.invoke(obj));
    }
}
// config.properties内容
/*
className=Person
methodName=toString
*/