三、获取Class对象的三种方式
加载XX.class文件进内存时就被封装成了对象,该对象就是字节码文件对象。如何获取Class对象呢?
方式一:对象.getClass()
通过对象的getClass方法进行获取。
如:Class clazz=new Person().getClass();//Person是一个类名
麻烦之处:每次都需要具体的类和该类的对象,以及调用getClass方法。
方式二:类名.class
任何数据类型都具备着一个静态的属性class,这个属性直接获取到该类型的对应Class对象。
如:Class clazz=Person.class;//Person是一个类名
比第一种较为简单,不用创建对象,不用调用getClass方法,但是还是要使用具体的类,和该类中的一个静态属性class完成。
方式三:Class.forName("路径");
这种方式较为简单,只要知道类的名称即可。不需要使用该类,也不需要去调用具体的属性和行为。就可以获取到Class对象了。
如:Class clazz=Class.forName("包名.Person");//Person是一个类名
这种方式仅知道类名就可以获取到该类字节码对象的方式,更有利于扩展。
四、 Class 类中的方法Object newInstance()
创建此Class对象所表示的类的一个新实例。
2.获取Class对象static Class forName(String className)
返回与给定字符串名的类或接口的相关联的Class对象。
Constructor getConstructor()
返回Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。
Method getMethod(String name,Class… parameterTypes)
返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法
Method对象中的方法invoke(调用该方法的对象,传入的参数值);
5. Field getField(String name)
返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。
6. 获取实体类名。
String getName()
以String形式返回此Class对象所表示的实体名称。
代码实例:
package cn.reflect;
public class Person {
public static String name = "李多";
public int age;
public Person(){
System.out.println( "this");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println(this.name);
}
// 非静态,无参
public void show(){
System.out.println("show 姓名:"+this.name+"年龄:"+this.age);
}
public void showNo(String name){
System.out.println("show姓名:"+name);
}
//静态,不带参
public static void staticShow(){
System.out.println("show姓名:");
}
//静态,带参
public static void staticPar(String name ,int age){
System.out.println("姓名:"+name+"--------年龄:"+age);
}
}
package cn.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectMethodDemo {
public static void main(String[] args) throws Exception {
// 一、访问非静态,不带参方法
// method();
//二、访问静态,不带参
// method2();
// 三、访问非静态,带参方法
method3();
//四、访问静态带参
method4();
}
private static void method4() throws Exception{
String s = "cn.reflect.Person";
Class clazz = Class.forName(s);
Method m = clazz.getMethod("staticPar", String.class,int.class);
m.setAccessible(true);
m.invoke(null, "李瑁4",24);
}
private static void method3() throws Exception {
String str = "cn.reflect.Person";
//获取字节码文件对象
Class clazz = Class.forName(str);
//创建构造
//Constructor con = clazz.getConstructor();
//实例化对象
Object obj = clazz.newInstance();
//传入方法名,参数类型
Method m = clazz.getMethod("showNo", String.class) ;
m.invoke(obj, "李瑁2");
}
private static void method2() throws Exception {
String str = "cn.reflect.Person";
//获取字节码文件对象
Class clazz = Class.forName(str);
Method m = clazz.getMethod("staticShow", null);
m.invoke(null, null);
}
private static void method() throws Exception {
String str = "cn.reflect.Person";
//获取字节码文件对象
Class clazz = Class.forName(str);
//创建构造
Constructor con = clazz.getConstructor(String.class,int.class);
//实例化对象
Object obj = con.newInstance("李瑁",24);
//传入方法名,参数类型
Method m = clazz.getMethod("show", null) ;
m.invoke(obj, null);
}
}
package cn.reflect;
import java.lang.reflect.Field;
public class ReflectFieldDemo {
public static void main(String[] args) throws Exception{
// 普通的属性
// 私有的属性
// 静态的属性
fieldMethod();
}
private static void fieldMethod() throws Exception {
//获取字节码文件对象
Class clazz = Class.forName("cn.reflect.Person");
//Object obj = clazz.newInstance();
//获取对应属性的对象
Field f = clazz.getField("name"); //访问公共
//Field f = clazz.getDeclaredField("name");// 可以访问公共、保护、默认、私有。
//f.setAccessible(true);
String name = (String) f.get(null);
System.out.println(name);
}
}
二、内省
对程序类部进行检查,主要用来操作JavaBean
javaBean是一种特殊的Java类,主要用来传递数据信息。体现:将类中的属性为私有的,对外提供公共的访问方法。
一个标准的JavaBean代码:
package com.itheima;
public class Person {
private String name;
private int age;
public Person (String name, int age ) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
简单的内省:
package com.itheima;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpectorTest {
/**
* 简单的内省操作。
* @param args
* @throws IntrospectionException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void main(String[] args) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Person p = new Person("limao", 24);
// 属性的名字
String propertyName = "name";
// 创建内省对象
getProperty(p, propertyName);
String propertyAge = "age";
Object newAge = 21;
setProperty(p, propertyAge, newAge);
}
// 操作set方法
private static void setProperty(Person p, String propertyAge,Object age)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyAge, p.getClass());
Method setAge= pd.getWriteMethod();
setAge.invoke(p, age);
System.out.println(p.getAge());
}
// 操作get方法
private static void getProperty(Object obj, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj.getClass());
// 获取name的get方法
Method getName = pd.getReadMethod();
Object ob = getName.invoke( obj );
System.out.println(ob);
}
}
BeanUtils等工具包封装了内省的操作:我们开发的时候只需要导入BeanUtils包,就可以使用setProperty这些方法。
里面提供的功能更加的全面,数据类型的自动转换。
BeanUtils工具包中还有一个工具类PropertyUtils,用法跟BeanUtils一样。区别:
1)BeanUtils会对JavaBean的属性的类型进行转换,如属性本身是integer,会转换为String。
2)PropertyUtils以属性本身的类型进行操作。
当我们使用一个类,如果这个类还未加载到内存中,系统会通过加载、连接、初始化对类进行初始化。
1、类加载:指的是将类的class文件读入JVM,并为之创建一个Class对象。
2、类连接:指的是把类的二进制数据合并到JRE中,这又分为3个阶段:
a)、校验:检查载入Class文件数据的正确性。
b)、准备:给类的静态变量分配存储空间,并进行默认初始化。
c)、解析:将类的二进制数据中的符号引用替换成直接引用。
3、初始化:对类的静态变量、静态初始化块进行初始化。
注意:使用ClassLoader()方法,只是加载该类,并未初始化
java.lang.ClassLoader
类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class
类的一个实例。系统提供的类加载器主要有下面三个:
- 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自
java.lang.ClassLoader
。 - 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()
来获取它。:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象method: 指代的是我们所要调用真实对象的某个方法的Method对象args: 指代的是调用真实对象某个方法时接受的参数
package com.itheima.Refeclt;
import java.lang.reflect.Proxy;
public class DynamicProxy {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
RealSubject sub = new RealSubject();
SubInterface pro = (SubInterface )Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
new Class[] {Subject.class, SubInterface.class},
new ProxyHandler( sub ) );
pro.Hello();
// pro.Hello();
}
}
package com.itheima.Refeclt;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler{
private Object obj = null;
public ProxyHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
Thread.sleep(10);
method.invoke(obj, args);
long endTime = System.currentTimeMillis();
System.out.println("代理类执行的时间: "+(endTime-startTime));
return null;
}
}
package com.itheima.Refeclt;
interface Subject {
void dosomething();
}
package com.itheima.Refeclt;
public interface SubInterface {
void Hello();
}