您的位置:首页 > 职场人生

黑马程序员--java基础日记--反射

2015-10-15 12:37 447 查看
-----------android培训java培训、java学习型技术博客、期待与您交流!------------

反射

一、概念

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

动态获取类中信息,就是java反射。可以理解为对类的解剖。如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术。

类加载器

加载器的作用是将.class的文件加载进内存,也可将普通文件中的信息加载进内存。

分类

<1>Bootstrap ClassLoader 根类加载器

负责Java核心类的加载

<2>Extension ClassLoader 扩展类加载器

负责JRE扩展目录中jar包的加载。

<3>System ClassLoader 系统类加载器

负责JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和路径

二、Class类

Class类由多个java类共性抽取而来。Class是Java程序中各个Java类的总称

用于描述字节码的类就是Class类,创建对象,可以提取字节码文件中的内容,如字段、构造函数、一般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。

1、获取字节码对应的实例对象(Class类型)的三种方法

(1)、Class clazz=类名.class;

这种方法只需要用到类名就可以了,因为任何数据类型都具备一个静态的属性.class来获取其对应的Class对象,但是还是要明确用到类中的静态成员。

(2)、Class clazz=对象.getClass();

这种方法是调用了Object类中的getClass()方法。这种方法每次都需要知道具体的类并且创建该类对象,以及调用getClass()方法。非常的繁琐和不利于后期程序的扩展性。

(3)、Class clazz=Class.forName("包名.类名");//className

这种方法是最为常用的方法,该方法不需要创建对象,也不用调用对象的方法,只需要通过给定的类的字符串名称就可以获取该类。更为方便,扩展性更强。这种方法在以后的开发中非常的常见应该重点掌握。

如:通过class新建一个无参数构造函数:Person p=(Person) Class.forName("com.itheima.Person").newInstance();

2、通过反射获取构造方法(Constructor类)

public Constructor[] getConstructors():所有公共构造方法
public Constructor[] getDeclaredConstructors():所有构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)获取单个构造方法 ; 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
(1)、获取共有构造方法并新建对象

Class c = Class.forName(className);
Constructor con = c.getConstructor();// 返回的是构造方法对象
Object obj = con.newInstance();
(2)、获取私有构造方法并新建对象

Class c = Class.forName(className);
Constructor con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);//设置暴力访问标志位true,则不进行访问权限检查
Object obj = con.newInstance("hello");


实例1:获取Person类中的构造方法

package com.heima;

import java.lang.reflect.Constructor;

class Person{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.age=age;
this.name=name;
}
private Person(String name){
this.name=name;
}
public void show(){
System.out.println("hello,how are you??");
}
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;
}
public String toString(){
return "name:"+name+"age:"+age;

}
}

public class ConstructorDemo {

public static void main(String[] args) throws Exception {
Class p = Class.forName("com.heima.Person");//<span style="font-family:'Courier New' !important;color:#0800;FONT-SIZE: 12px !important; LINE-HEIGHT: 1.5 !important">获取Class字节码文件对象</span>
//获取所有共有构造方法
Constructor[] con = p.getConstructors();
for(Object obj : con){
System.out.println(obj);
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//获取所有构造方法
Constructor[] con2=p.getDeclaredConstructors();
for(Object obj : con2){
System.out.println(obj);
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//获取指定的构造方法
Constructor con3=p.getConstructor(String.class,int.class);
System.out.println(con3);
}

}

结果如图:



实例2、用反射创建Person实例对象

//方式一:直接用字节码对象的newInstance()创建空参实例对象
Class p = Class.forName("com.heima.Person");
Person p1 = (Person)p.newInstance();
//方式二:获取Constructor对象,通过Constructor对象的newInstance()创建实例对象
Constructor con = p.getConstructor();
Person p2 = (Person) con.newInstance();
//方式三:通过获取Constructor对象,创建指定参数的对象
Constructor con1 = p.getConstructor(String.class,int.class);
Person p3 = (Person) con.newInstance();


3、获取Class的字段(Field类)

获取所有成员:getFields,getDeclaredFields

获取单个成员:getField,getDeclaredField

修改成员的值:set(Object obj,Object value)

获取并修改指定成员变量的值的步骤 :

(1)、首先需要创建对象,因为获取和修改成员变量的值都需要明确修改的是哪个对象身上的

(2)、获取字节码对象,用其getDeclaredField(String name)方法来获取Field对象

(3)、通过Field对象的set(Object obj,Object value)来设置指定对象上的指定成员变量。 在设置之前一般都需要暴力访问,因为成员变量一般都是私有的。

(4)、通过Field对象中的get(Object obj)获取其身上的字段值。

实例:获取Person中的成员变量。

public class ConstructorDemo {

public static void main(String[] args) throws Exception {

Class p = Class.forName("com.heima.Person");//获取Class字节码文件对象

Field[] f = p.getDeclaredFields();//获取所有变量
for(Object obj : f){
System.out.println(obj);
}

Field f1 = p.getDeclaredField("name");	//获取指定变量
System.out.println(f1);

Person p1 = (Person)p.getConstructor(String.class,int.class).newInstance("小明",21);//新建对象
Field f2 = p.getDeclaredField("name");//获取指定的变量
f2.setAccessible(true);//设置暴力访问标记
f2.set(p1, "小红");//将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
System.out.println(f2.get(p1));//返回指定对象上此 Field 表示的字段的值。
}

}

4、获取Class中的成员方法(Method类)

Method类代表某个类中的一个成员方法。调用某个对象身上的方法,要先得到方法,再针对某个对象调用。

public Method getMethod(String name, Class<?>... parameterTypes);//获取指定名称,参数的公有方法对象
public Method getDeclaredMethod(String name, Class<?>...) //获取指定名称,参数的声明方法对象
public Method[] getMethods(); //获取类中的所有共有方法
public Method[] getDeclaredMethods();//获取所有声明方法
void setAccessible(boolean flag);//设置暴力访问标记
实例1:获取Person类中成员方法,并调用

package com.heima;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Person{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.age=age;
this.name=name;
}
private Person(String name){
this.name=name;
}
public void show(String str){
System.out.println("hello:"+str +",how are you??");
}
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;
}
public String toString(){
return "name:"+name+"age:"+age;

}
public static void print(){
System.out.println("print is run!!");
}
private void phone(String str){
System.out.println(str+" is run!!");
}
}

public class ConstructorDemo {

public static void main(String[] args) throws Exception {

Class p = Class.forName("com.heima.Person");//获取Class字节码文件对象

Method[] m = p.getDeclaredMethods();//获取所有声明的方法
for(Object obj : m){
System.out.println(obj);
}

System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
Person p1 = (Person)p.newInstance();//获取指定的方法
Method m1 = p.getMethod("show",String.class);
m1.invoke(p1,"hui");

Method m3 = p.getDeclaredMethod("phone",String.class);//获取指定的私有方法
m3.setAccessible(true);
m3.invoke(p1, "iphone");

System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
Method m2 = p.getMethod("print", null);//调用静态方法的时候不需要对象,因为静态优先于对象存在。
m2.invoke(null, null);
}

}


实例2、通过反射越过泛型检查。

向ArrayList<String>对象中添加一个Integer数据

package com.heima;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class test {
public static void main(String[] args) throws Exception {
ArrayList<String> al= new ArrayList<String>();// 创建一个ArrayList<String>对象
al.add("lisi");//如果这里直接写al.add(Integer),是不会编译通过的,因为会有类型错误
Class cl = al.getClass(); // 获取ArrayList的Class对象
Method add = cl.getMethod("add", Object.class); // 获取add()方法
add.invoke(al, 5);// 调用add方法添加元素

System.out.println(al);
}
}



动态代理

代理:就是本来是自己做的事情,却请了别人来做,被请的人就是代理对象。

动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。通过实现InvocationHandler接口复写invoke方法来实现。

Java中通过Proxy类中的newProxyInstance方法来创建动态代理,不过只能针对接口做动态代理。

package com.heima;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
interface Student{
public void work();
}
class stu implements Student{
@Override
public void work() {

System.out.println("工作ing");
}

}

class myInvocationHandler implements InvocationHandler{
private Object  target;
public myInvocationHandler(Object  target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] arg2)
throws Throwable {
System.out.println("上班打卡");
method.invoke(target, arg2);//执行被代理target对象的方法
System.out.println("下班打卡");
return null;
}

}
public class test  {
public static void main(String[] args) throws Exception {
stu s = new stu();
myInvocationHandler m = new myInvocationHandler(s);
Student u = (Student)Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), m);
u.work();

}
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: