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

java程序员从笨鸟到菜鸟之(四十五)反射初涉

2017-12-14 18:25 477 查看
反射概念

粗---先会使用

1  什么是java语言反射机制?

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

       反射就是通过获取到该类的字节码文件对象---->即:Class类对象,通过Class类对象获取该类里面的一些属性(成员变量)、构造方法、成员方法等----如何获取等

说明:两种理解方式均可

2  面试题:如何获取类的字节码文件对象?并且有几种方式呢?

1)Object类中的getClass()方法,表示正在运行的Class类型的实例

2)数据类型的class属性  举例:String.class、Student.class

3)Class类中的特有方法:forName(String className)---(重点,数据库加载驱动:Drivers)---静态方法获得类名对应的Class对象

说明:开发中常使用的方式,因为第三种里面的参数是一个字符串;一个Class对象表示特定类的对象(属性)

注意:第三种方式的是类的全路径名称的字符串形式

实例1 

package demo;

public class Demo1 {

public static void main(String[] args) throws ClassNotFoundException {
Person person=new Person();
Class<Person> person1=Person.class;//得到Person.class字节码文件对象
Class person2=person.getClass();
Class<?> person3 = Class.forName("demo.Person");//加载并且获得Person.class字节码文件对象
//如何方式3找不到类的全限定名:出现java.lang.ClassNotFoundException异常
}
}


说明1:出现黄色警告线原因---Class类实际是一个泛型类,大多数情况可以忽略类型参数,而使用原始的Class类;

说明2:三种方式都是Person类的字节码文件,所以三者通过"=="判断都是true;

注意:一个Class对象实际是一个类型,而此类型未必是一个类;int不是类,但int.class是一个Class类型的对象

回顾:在反射之前,在启动程序时,包含main方法的类首先被加载,它会加载所有需要的类,被加载的类又会继续加载它需要的类,以此类推;对于大型应用程序来说,会消耗很多时间,用户不耐烦。

反射应用:给用户一个启动速度比较快的幻觉---首先保证包含main方法的类没有显示地引用其它的类,在显示一个启动画面,然后通过调用Class.forName手工地加载其它的类

Class类

在程序运行期间(前提),Java运行时系统始终为所有的对象维护着一个被称为运行时的标识类型,这个信息(标识类型的信息)跟踪着每个对象所属的类;虚拟机利用运行时类型信息选择相应的方法执行。

理解:虚拟机为每个类型管理一个Class对象;注意:不是每个对象,通过"=="运算符的比较也可以看出。

那么如何访问这些信息?通过专门的java类访问这些信息,保存这些信息的类被称为Class。

常用获取构造器的方法

(1)getName---返回类的全限定类名----补充的

(2)public Constructor<?>[] getConstructors()---返回的是一个公共构造方法所在数组

(3)public Constructor<?>[] getDeclaredConstructors():获取的是当前字节码文件对象中所有的(静态的,私有的,受保护的)构造方法数组

(4)public Constructor<T> getConstructor(Class<?>... parameterTypes):它反映此 Class 对象所表示的类的指定公共构造方法

参数说明:参数为对应构造方法的数据类型的字节码文件对象,形如String.class或int.class;可变参数类型

(5)public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):表示获取类或者接口中的指定的构造方法

实例2

package demo;

import java.lang.reflect.Constructor;

public class Demo2 {

public static void main(String[] args) throws  Exception{
//方式1---public Constructor<?>[] getConstructors(),返回的是一个构造方法所在数组
//加"s"获取所有的公共的(public修饰的)构造方法
Constructor<?>[] constructors = person.getConstructors();
//遍历构造方法
for(Constructor con:constructors){
System.out.println(con);
}
System.out.println("---------------------");
//方式2
Constructor<?>[] dc = person.getDeclaredConstructors();
for(Constructor con:dc){
System.out.println(con);
}
System.out.println("----------------------");
//方式3---获取某一个公共的构造方法
Constructor<?> con = person.getConstructor(String.class,String.class);
System.out.println(con);
//方式4---获取某个构造方法,我们就获取个私有的方法
Constructor<?> dcr = person.getDeclaredConstructor(String.class,int.class);
System.out.println(dcr);
//了解一下打印的内容:public demo.Person(java.lang.String,int)
}
}
动态的创建一个类的实例----不经过编译时期的检查(翻墙)

(6)public 类名 newInstance():

说明:可以直接通过Class的newInstance()的方法,特点:对没有参数的默认构造器初始化并创建对象

除了Class的此方法,还有没有其它的途径创建对象?当然有

Constructor类

理解上:其实类似于将int类型封装成Integer类那样,将构造方法封装成Constructor类

(1)public T newInstance(Object... initargs):

说明:传递的是实际参数(可变参数),该方法等效于创建一个类的对象(动态)

实例3 创建对象动态和静态(公共的构造方法)

package demo;

import java.lang.reflect.Constructor;

public class Demo3 {

public static void main(String[] args) throws Exception {
//(1)静态
Person person = new Person();
//System.out.println(person);
//(2)动态
Person person2 = Person.class.newInstance();
System.out.println(person==person2);
//(3)动态
Class<?> person3 = Class.forName("demo.Person");
Constructor<?> dc = person3.getDeclaredConstructor();
Object obj = dc.newInstance();//最常用的,返回值也最特殊
//(4)动态
Class<? extends Person> class1 = person.getClass();
Constructor<? extends Person> dc1 = class1.getDeclaredConstructor();
Person person4 = dc1.newInstance();

}
}


注意:(2)和(3)以及(4)的newInstance()方法的返回值

那么问题来了,私有的构造方法能否创建对象?

(2)public void setAccessible(boolean flag):在访问的时候取消java语言访问检查(强制性)---常常是私有的构造方法;常常"fla
dcee
g=true" ----在给构造创建实例对象之前就应该取消检查

实例4

package demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo4 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//动态的创建类对象,并初始化(私有的和公共的)
//1:私有的方式
String str="demo.Person";
Class<?> c = Class.forName(str);
Constructor<?> dc = c.getDeclaredConstructor(String.class,int.class);
dc.setAccessible(true);//私有的构造方法不能直接访问
Object obj = dc.newInstance("小王",27);
System.out.println(obj);//调用Person类的toString()方法
//2:公共的方式
Constructor<?> con = c.getConstructor(String.class,String.class);
Object ne = con.newInstance("小王","0012");
System.out.println(ne);
}
}

通过反射获取成员变量并使用:成员变量--->Field

(1)获取所有的公共的成员变量public Field[] getFields()

说明:所有的公共的可访问的字段,返回的是Field对象数组

(2)public Field[] getDeclaredFields()

说明:获取当前字节码文件对象中所有的公共的或者私有的字段

(3)public Field getField(String name)

说明:获取公共的指定的字段,参数为当前成员变量名称的字符串形式

(4)public Field getDeclaredField(String name)

说明:获取类或接口中已经声明的指定的字段(一般私有的)

注意1:如果给当前obj实例对象设置一个参数,将指定对象变量上此 Field对象表示的字段设置为指定的新值;

public void set(Object obj, Object value)----给obj实例对象里面的成员变量设置一个实际参数;obj为动态的对象(forName创建的形式)

注意2:如果想对私有成员变量设置值,必须在设置值之前取消Java语言的访问检查:Field类的方法---setAccessible(true);

注意3:输出每个对象不是地址的原因:多态

实例5

package demo;

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

/**
* 通过反射获取成员变量并使用
* 成员变量--->Field
*
* @author Orange
* @version 1.8
*/
public class Demo5 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//(1)创建Class对象
Class<?> person = Class.forName("demo.Person");
//(2)获取所有的公共的成员变量public Field[] getFields():所有的公共的可访问的字段,返回的是Field对象数组
Field[] fields = person.getFields();
//遍历
for(Field fie:fields){
System.out.println(fie);
}
System.out.println("--------------------------");
//(3)public Field[] getDeclaredFields():获取当前字节码文件对象中所有的公共的或者私有的字段
Field[] df = person.getDeclaredFields();
for(Field fie:df){
System.out.println(fie);
}
//(4)重点---获取单个公共的成员变量(Field类对象)
//public Field getField(String name):获取公共的指定的字段,参数为当前成员变量名称"address"
Field field = person.getField("age");
//(5)动态创建一个对象
Constructor<?> con = person.getConstructor();
Object obj = con.newInstance();
//有了Field对象,给当前obj实例对象设置一个参数
//将指定对象变量上此 Field对象表示的字段设置为指定的新值public void set(Object obj, Object value)
//给obj实例对象里面的成员变量设置一个实际参数---->value
field.set(obj, 27);
System.out.println(obj);
//(6)私有的成员变量
Field de = person.getDeclaredField("name");
de.setAccessible(true);
de.set(obj, "小王");
System.out.println(obj);
}
}

调用对象的成员方法----Class类的方法

(1)  public Method[] getDeclaredMethods():

说明:得到此类对象本身的所有方法(公共,受保护、默认、私有),但不包括继承其它的方法(不管什么方式)

(2)  public Method[] getMethods():获取当前该字节码文件对象(Person.class)中自己本身以及它父类中所有的公共成员方法(包括继承)

说明:除了自身的公共方法以外,还有继承的公共方法

(3)  public Method getMethod(String name,Class<?>... parameterTypes):指定公共成员方法

参数1:表示方法名字符串形式
参数2:该方法的参数类型的Class对象(数据类型的class属性) String.class(非实参)

说明:返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法(无继承的方法,自身类中特有的公共方法)。

(4)  public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

说明:得到对象(无继承方法)反映此 Class 对象所表示的类或接口的指定已声明方法(可以私有化)

Method类

(1)  public Object invoke(Object obj, Object... args)----对封装的方法进行调用→真正调用方法

      参数1:表示当前针对哪个以实例对象进行方法的调用----疑问静态方法不需要obj吧!!!

      参数2:当前调用该方法的时候里面传递的实际参

说明:唯一有参数的,参数需要指明调用的动态对象,然后传递参数才能调用此对象的方法。

注意:如果是私有的方法,必须取消Java语言的访问检查---主要针对私有的方法---setAccessible(true)

实例6 通过反射获取成员变量并使用,之前使用对象调用成员方法

package demo;

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

public class Demo6 {
/**
* 需求:
* 通过反射获取成员变量并使用,之前使用对象调用成员方法
* 类似:
* Person p = new Person() ;
* p.show() ;
*/
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class<?> person = Class.forName("demo.Person");
Constructor<?> con = person.getConstructor();
Object instance = con.newInstance();//动态的创建了一个对象
Method[] met = person.getMethods();
Method me = person.getMethod("method1");
Object inv = me.invoke(instance);
System.out.println(inv);

}
}
反射的应用

reflect_handler的应用---体会

动态代理:

我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?

属性集合类----动态代理的形式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  反射
相关文章推荐