文章参考(必须先看文章):
对象的创建过程
new创建对象的过程:
- JVM申请一块内存空间
- JVM调用类私有的构造器并传入ClassLoader(类加载器)
- 类加载器加载对应类的字节码,JVM创建Class对象
首先,在很多初学者的印象中,类和对象的关系是这样的:
虽然知道源代码经过javac命令编译后会在磁盘中得到字节码文件(.class文件),也知道java命令会启动JVM将字节码文件加载进内存,但也仅仅止步于此了。至于从字节码文件加载进内存到堆中产生对象,期间具体发生了什么,他们并不清楚。
所谓“万物皆对象”,字节码文件也难逃“被对象”的命运。它被加载进内存后,JVM为其创建了一个对象,以后所有该类的实例,皆以它为模板。这个对象叫Class对象,它是Class类的实例。
大家想想,Class类是用来描述所有类的,比如Person类,Student类...那我如何通过Class类创建Person类的Class对象呢?这样吗:
Class clazz = new Class();
好像不对吧,我说这是Student类的Class对象也行啊。有点晕了...
其实,程序员是无法自己new一个Class对象的,它仅由JVM创建。
- Class类的构造器是private的,杜绝了外界通过new创建Class对象的可能。当程序需要某个类时,JVM自己会调用这个构造器,并传入ClassLoader(类加载器),让它去加载字节码文件到内存,然后JVM为其创建对应的Class对象
- 为了方便区分,Class对象的表示法为:Class<String>,Class<Person>
所以借此机会,我们不妨换种方式看待类和对象:
也就是说,要得到一个类的实例,关键是先得到该类的Class对象!只不过new这个关键字实在太方便,为我们隐藏了底层很多细节,我在刚开始学习Java时甚至没意识到Class对象的存在。
Java反射机制
JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
java中那些对象可以有Class对象
- Class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
- interface:接口
- [ ]:数组
- enum:枚举
- annotation:注解@interface
- 基本数据类型
- void
Class c1 = Object.class; //类 class java.lang.Object
Class c2 = Comparable.class;//接口 interface java.lang.Comparable
Class c3 = String[].class; //一维数组 class [Ljava.lang.String;
Class c4 = int[][].class;//二维数组 class [[I
Class c5 = Override.class;//注解 interface java.lang.Override
Class c6 = ElementType.class;//枚举 class java.lang.annotation.ElementType
Class c7 = Integer.class;//基本数据类型 class java.lang.Integer
Class c8 = void.class;//void void
Class c9 = Class.class;//class class java.lang.Class
获得Class对象主要有三种方法
(1)Object-->getClass
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
(3)通过class类的静态方法:forName(String className)(最常用)
package fanshe;
public class Fanshe {
public static void main(String[] args) {
//第一种方式获取Class对象
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
//第三种方式获取Class对象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
创建实例:通过反射来生成对象主要有两种方法:
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。
Class<?> c = String.class;
Object str = c.newInstance();
(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。
//获取String的Class对象
Class<?> str = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=c.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance(“hello reflection”);
反射机制常用的类:
- Java.lang.Class;
- Java.lang.reflect.Constructor;
- Java.lang.reflect.Field;
- Java.lang.reflect.Method;
- java.lang.reflect.AccessibleObject
- java.lang.reflect.Proxy
- Java.lang.reflect.Modifier;
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
* 3).调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
/*
* 获取成员变量并调用:
*
* 1.批量的
* 1).Field[] getFields():获取所有的"公有字段"
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).public Field getField(String fieldName):获取某个"公有的"字段;
* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
*
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*/
/*
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
):
*/
public class MethodClass {
public static void main(String[] args) throws Exception {
Class aClass = Class.forName("ArrayGrammer.Person");
// 获取public成员,获取值 设置值
Field name = aClass.getField("name");
Person person= (Person) aClass.newInstance();
name.set(person,"鲍鱼");
String o = (String)name.get(person);
// 获取private成员,获取值,设置值
Field age = aClass.getDeclaredField("age");
age.setAccessible(true);
age.set(person,12);
Integer integer = (Integer)age.get(person);
// 获取所有成员
Field[] declaredFields = aClass.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
//1.获取Class对象
Class stuClass = Class.forName("fanshe.method.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = stuClass.getMethod("show1", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}
}
JDK的动态代理
能否不写代理类,而直接得到代理Class对象,然后根据它创建代理实例(反射)。(核心思想)
实现相同接口:
保证代理对象的内部结构和目标对象一致,这样我们对代理对象的操作最终都可以通过invoke方法转移到目标对象身上,代理对象只需专注于增强代码的编写
静态代理
假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志。
你如何在不修改已有代码的前提下,完成这个需求?
我首先想到的是静态代理。具体做法是:
1.为现有的每一个类都编写一个对应的代理类,并且让它实现和目标类相同的接口(假设都有)
2.在创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。也就是说,代理对象 = 增强代码 + 目标对象(原对象)。有了代理对象后,就不用原对象了
动态代理(能否不写代理类,而直接得到代理Class对象,然后根据它创建代理实例(反射))
- Proxy有个静态方法:getProxyClass(ClassLoader, interfaces),只要你给它传入类加载器和一组接口,它就给你返回代理Class对象。
- 调用代理对象的方法,最终都会调用InvocationHandler的invoke()方法。在invoke方法中调用目标对象的方法和增强代码
Proxy.newProxyInstance实现JDK动态代理
- 创建一个接口
public interface Ocean {
void oceanfood();
}
- 创建接口的实现类
public class Fish implements Ocean{
@Override
public void oceanfood() {
System.out.println("鱼吃草");
}
}
- 不创建代理类的情况下,获取代理对象实例
public class DynamicProxyObj {
public static void main(String[] args) {
Ocean proxyInstance = (Ocean)Proxy.newProxyInstance(Fish.class.getClassLoader(), Fish.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("在方法前执行");
Fish fish = new Fish();
Object invoke = method.invoke(fish);
System.out.println("在方法后执行");
return invoke;
}
});
proxyInstance.oceanfood();
}
}