类的加载
类初始化时机
反射
Class类的使用
动态加载类
方法的反射
获取构造方法并使用
获取构造方法创建对象
获取私有构造方法创建对象
获取成员变量并使用
成员变量进行赋值和获取值操作
获取成员方法并使用
创建对象调用指定的private方法
加载:指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。
连接:验证,是否有正确的内部结构,并和其他类协调一致。准备,负责为类的静态成员分配内存,并设置默认初始化值。解析,将类的二进制数据中的符号引用替换为直接引用。
初始化:类进行初始化操作。
创建类的实例
类的静态变量,或者为静态变量赋值
类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
反射:
(定义)在运行时,而非编译时,动态获取类型的信息,比如接口信息,成员信息,方法信息,构造方法信息,再根据这些动态获取到的信息来创建对象,访问、修改成员,使用方法。
Class类:
Class没有公共的构造方法,所有的类都是它的实例类。
public class ClassDemo {
public static void main (String[] args){
Person person=new Person();
}
}
class Person{}
对于上述示例代码,person是Person类的实例对象,Person类是Class类的一个实例对象。但是Class类的实例对象不是new出来的。
3种获取Class类的实例对象的方式:
//方式1:在知道类名的情况下,调用该类的隐式的静态成员变量class
Class c1=Person.class;
//方式2:在知道实例对象名的情况下调用对象的getClass()方法
Class c2=person.getClass();
**上述的c1、c2表示了Person类的类类型。且上述2种方式需要明确Person类型。**方式1通过“类名.class”方式获取到字节码文件对象。方式2通过Object类中的getClass()方法,返回Object类的运行时类。
方式3:我们在JDBC连接MySQL连接驱动的时候会看到Class.forName(…).通过Class类的forName()方法创建,但是需要捕获异常。
//方式3:Class.forName()方式创建
Class c3=null;
try{
c3=Class.forName("Person");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
针对以上的3种方式创建的对象,我们可输出一下判断他们是否完全相等。
System.out.println(c1 == c2 && c2 == c3);//true
结果:
当然,我们也完全可以通过类的类类型创建该类的实例对象,调用类类型的newInstance()方法(需要做类型强转和捕获异常)
public class ClassDemo {
public static void main (String[] args){
Person person=new Person();
Class c1 = Person.class;
Class c2 = person.getClass();
Class c3 = null;
try {
c3=Class.forName("Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c1==c2&& c2==c3);
//通过类的类类型创建该类的实例对象,调用类类型的newInstance()方法
try {
Person person1= (Person) c1.newInstance();
person1.show();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Person{
public void show(){
System.out.println("通过类类型也可以创建实例对象!");
}
}
上述代码运行后能正常打印。
Class类还提供了很多获取成员信息的方法(成员方法、变量、构造方法),可以查阅文档。
Class 是个泛型类,有一个类型参数,getClass()并不知道具体的类型,只能返回Class>.(Java中?表示不确定,Kotlin中,用*表示不确定)
接口也有Class 对象。
基本类型没有getClass()方法,但都有对应的Class对象。void作为特殊的返回类型,也有Class对象。
Class intCls=int.class;
Class byteCls=byte.class;
//基本类型还有Double,Character等....
//特殊的返回类型Void:
Class voidCls=void.class;
对于数组,每种类型的数组都有对应的Class对象,每个 维度 都有一个。
//不使用动态加载
if("Person".equals(args[0])){
Person person = new Person();
}
if("You".equals(args[0])){
You you =new You();
}
上述代码中的**args[0]是一个命令行参数,动态传递进来的。**上述代码要求这2个类都得存在,如果有一个不存在,即使用不到这个类,也会影响整个程序。所以下方介绍的动态加载很好的解决这一问题:
**动态加载:**在调用的时候再去加载类。
try{
Class class = Class.forName(args[0]);
Person person= (Person)class.newInstance();
}
此处并不知道传递进来的是什么类型的,在强转的时候,在创建所有要用到的类(比如You类实现Person接口)的时候统一实现Person接口即可(泛型)。于是只有当用到才会调用此类。
下面的代码为简单的反射:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ClassDemo {
public static void main (String[] args){
Person person=new Person();
Class c =person.getClass();
try {
// Method method=c.getMethod("show",new Class[]{int.class,int.class});
Method method=c.getMethod("show",int.class,int.class);
method.invoke(person,2,3);
Method method1=c.getMethod("show");
method1.invoke(person);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class Person{
// public void show(){
// System.out.println("通过类类型也可以创建实例对象!");
// }
public void show(){
System.out.println("无参方法");
}
public void show(int a, int b){
System.out.println(a+b);
}
}
//获取public修饰, 指定参数类型所对应的构造方法
public Constructor getConstructor(Class>... parameterTypes)
//获取指定参数类型所对应的构造方法(包含私有的)
public Constructor getDeclaredConstructor(Class>... parameterTypes)
//获取所有的public 修饰的构造方法
public Constructor>[] getConstructors()
//获取所有的构造方法(包含私有的)
public Constructor>[] getDeclaredConstructors()
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
//获取Class对象
Class c = Class.forName("cn.itcast_01_Reflect.Person");//包名.类名
//获取所有的构造方法
//这里不止有一个构造方法,所以运用集合来存储
Constructor[] cons = c.getDeclaredConstructors();
//用循环来打印方法
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("------------------------");
//获取一个构造方法,这里getConstructor的参数就是指定构造方法的参数。
//public Person()
Constructor con1 = c.getConstructor(null);
System.out.println(con1);
//public Person(String name)
Constructor con2 = c.getConstructor(String.class);
System.out.println(con2);
//private Person(String name, int age)
Constructor con3 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(con3);
//public Person(String name, int age, String address)
Constructor con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(con4);
}
}
//参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。
public void setAccessible(boolean flag) throws SecurityException
//获取指定的 public修饰的变量
public Field getField(String name)
//获取指定的任意变量
public Field getDeclaredField(String name)
//获取所有public 修饰的变量
public Field[] getFields()
//获取所有的 变量 (包含私有)
public Field[] getDeclaredFields()
public class FieldDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
//获取Class对象
Class c = Class.forName("cn.itcast_01_Reflect.Person");
//获取成员变量
//多个变量,运用数组来存储
Field[] fields = c.getDeclaredFields();
//遍历数组,访问数组
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-----------------");
//一个变量
//public int age;
Field ageField = c.getField("age");
System.out.println(ageField);
//private String address,这里是私有变量,也能被获取。
Field addressField = c.getDeclaredField("address");
System.out.println(addressField);
}
}
//在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
public void set(Object obj, Object value)
//返回指定对象obj中,获取对象的值
public Object get(Object obj)
//1,获取Class对象
Class c = Class.forName("cn.itcast_01_Reflect.Person");
//2,获取构造方法
//public Person(String name, int age, String address)
//这里要传入对应构造方法的参数类型的class对象!!!!!!!!!
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3,通过构造方法,创建对象
Object obj = con.newInstance("小明", 23, "哈尔滨");
//4,获取指定的方法
//public void method1() 没有返回值没有参数的方法
//Method m1 = c.getMethod("method1", null);
//public String method4(String name)
Method m4 = c.getMethod("method4", String.class);
//5,执行找到的方法
//m1.invoke(obj, null);
Object result = m4.invoke(obj, "itcast");
//获取 public 修饰的方法
public Method getMethod(String name, Class>... parameterTypes)
//获取任意的方法,包含私有的
public Method getDeclaredMethod(String name, Class>... parameterTypes)
//参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
//这里因为返回不只一个方法,因此运用Method的数组来存放。类似前面的。
//获取本类与父类中所有public 修饰的方法
public Method[] getMethods()
//获取本类中所有的方法(包含私有的)
public Method[] getDeclaredMethods()
//执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定
public Object invoke(Object obj, Object... args)
1):获取Class对象
2):获取构造方法
3):通过构造方法,创建对象
4):获取指定的方法
5):开启暴力访问
6):执行找到的方法
也就是运用 setAccessible(true); 开启暴力访问。