您的位置:首页 > 编程语言 > Java开发

java反射机制笔记

2016-03-04 11:25 489 查看
概念:类和对象之间的关系--》类是对象的一种描述,对象是一类实物的一个具体实例子
反射:得到元数据的行为,得到类 中所有成员和行为德尔一种机制
Class是对一切类的行为和状态的抽象
Class类:用于描述一切类/接口/枚举/注解,枚举是一种类,注解是一种接口。
Class实例:就是指JVM(java虚拟机)中的一份字节码,
为了明确区分Class实例表示的是哪个类的字节码.Class类提供了泛型。

1.对象有编译类型和运行类型:
Object obj = new Date();
编译类型为:Object
运行类型为(就是对象的真实类型):Date
需求:根据对象obj调用Date类中的一个方法toLocaleString();
如果直接写成 obj.toLocaleString();//编译出错
原因是在编译过程中会检查编译类型是否有该方法。
编译类型Object没有该方法就会编译不通过
解决方案:强制转换obj为Date类型,前提是必须知道对象的真实类型是什么?
Date d = (Date)obj;
d.toLocaleString();//YES
如何得到Class的实例:
1.类名.class
2.Class.forName(String className);//根据一个类的全限定名来构造Class对象
3.每一个对象都有一个getClass方法,返回对象的运行时类型。该方法存在于超类Object中,java中所有类都继承于Object
java中九个预定义的Class实例:
八种基本数据类型没有类的权限定名,也没有getClass方法该如何获取基本数据类型的Class实例
byte short char int float double long boolean void关键字
上述八种基本数据类型和void关键字都有class属性
int的Class对象:Class clazz = int.class;
void关键字的Class对象 Class clazz = void.class;
所有的数据类型都有class属性,表示的都是Class对象
在java中基本数据类型与它的包装类不是同一种数据类型
验证:在eclipse中创建两个方法,
public void show(int i){}
public void show(Integer i){}//两个方法在eclipse中编译通过说明Integer和int不是同一种数据类型
在八大基本数据类型的包装类型中都有一个常量TYPE,表示其基本数据类型的Class实例
即Integer.TYPE == int.class 但是Integer.class != int.class;Integer.TYPE != Integer.class
所有具有相同元素类型和维数的数组才共享同一份字节码(Class对象)

获取class实例代码:
public class ClassInstanceDemo {

public static void main(String[] args) throws ClassNotFoundException {
//获取Class实例的第一种方法 类名.class
Class<Date> clazz1 = Date.class;

//获取Class实例第二种方法 Class.forName(String className)
Class<?> clazz2 = Class.forName("java.util.Date");

//获取Class实例第三种方法,对象.getClass();得到对象的运行时类型
Class<?> clazz3 = new String().getClass();
Class<?> clazz4 = new Date().getClass();

System.out.println(clazz1==clazz2);//true 一个类的字节码在JVM中有且只有一份。
System.out.println(clazz2 == clazz3);//false
System.out.println(clazz1 == clazz3);//false

//clazz1==clazz2==clazz4 因为表示的都是JVM中共同的一份字节码(Date.class)

}
}
获取构造器并创建对象实例代码:

package cn.bsxy.clazz;

import java.lang.reflect.Constructor;

class User{
public User(){
System.out.println("User ...user()");
}

public User(String name){
System.out.println("User..."+name);
}

private User(String name,int age){
System.out.println("User...userName:"+name+" UserAge:"+age);
}
}
/**
*
* @author smartluobo
* 功能:通过类反射机制获取类的构造器,使用获取的构造器创建该类的对象
*
*/
public class ReflectConstructorDemo {
public static void main(String[] args) throws Exception {
Class<User> clazz = User.class;
//clazz.newInstance()该方法是Class类中的方法,该方法调用当前class描述类的默认无参构造方法创建对象
//众所周知java中没创建一个类,jdk默认提供了该类的一个无参构造方法,但是一旦我们重新写了该类的有参构造方法。
//该类的jdk提供的无参构造方法将被覆盖,因此当我们写了该类的有参构造方法后没有手动声明无参构造方法使用clazz.newInstance();
//方法会抛异常,同时也不能使用clazz.getConstructor()和clazz.getDeclaredConstructor()方法
User u0 = clazz.newInstance();
//getConstructors()获取当前class描述类的所有public修饰的构造器
Constructor[] cs = clazz.getConstructors();
for (Constructor c : cs) {
// System.out.println(c);
}
System.out.println("*************============**************");
//getDeclaredConstructors()获取当前class描述类的所有构造器(忽略访问权限)
cs = clazz.getDeclaredConstructors();
for (Constructor c : cs) {
// System.out.println(c);
}
System.out.println("*************============**************");
//getConstructor() 获取当前class描述类的默认无参构造器
Constructor<User> c = clazz.getConstructor();
//newInstance()使用获取的构造器创建对象
User u = c.newInstance();
System.out.println(u);
//getConstructor(Class<?>... parameterTypes)获取指定参数类型的构造器,类的所有构造器名称和类名相同
//该方法只能获取public修饰的构造器,若指定的构造器乃私有则会抛出异常
//因此指定某个构造器只需指定参数列表即可,参数列表分为三个维度(参数类型,参数个数,和参数顺序)
//记住该方法的参数不是构造器的参数。而是构造器参数类型的Class实例
Constructor<User> c1 = clazz.getConstructor(String.class);
User u2 = c1.newInstance("jony");
//getDeclaredConstructor(Class<?>... parameterTypes) 该方法是对上一个方法的补充,当前class表述类的所有构造器
Constructor<User> c3 = clazz.getDeclaredConstructor(String.class,int.class);
//我们都知道要在类中使用其他类的私有构造方法来创建对象是编译不通过的因此getDeclaredConstructor(Class<?>... parameterTypes)
//虽然能获取到当前class描述的类的所有构造方法(忽略访问权限)但是如果我们拿到指定的构造器是private修饰我们依然不能用该构造方法创建对象
//此时我们需要通过 Constructor.setAccessible(true);来设置运行时忽略安全性。
c3.setAccessible(true);
User u3 = c3.newInstance("monkey",12);
/**
* Class<T> clazz = T.class;
* 总结:通过反射机制获取类的构造器的方法有四个
* 1.clazz.getConstructors();获取所有public修饰的构造器
* 2.clazz.getDeclaredConstructors();获取所有构造器 忽略访问权限
* 3.clazz.getConstructor(Class<T>...parameterType);获取指定的public修饰的构造器
* 4.clazz.getDeclaredConstructor(Class<T>...parameterType)获取指定的构造器忽视访问权限
* 通过反射机制创建当前class描述类的对象的方式两种
* 1.clazz.newInstance();调用当前class描述类的无参构造方法创建对象
* 2.Constructor<T> c = clazz.getConstructor(Class<T>...parameterType)
* T t = c.newInstance(参数);获取指定的构造器传入相关参数创建对象
*/

}

}

获取方法和属性执行方法和对属性取值和赋值代码:

package cn.bsxy.clazz;

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

class SuperEmp{

private int sex;
public String department;
public void sayHiSupper(){
System.out.println("this SuperEmp say Hi");
}
private void sayHiPrivate(){

}

}
class Emp extends SuperEmp{

private String name;
public int age;

public void sayHi(){
System.out.println("this Emp say Hi...");
}

public void sayHi(String name){
System.out.println("this Emp to **"+name+"** say Hi");
}

public void sayHello(String name){
System.out.println("this Emp to **"+name+"** say Hello");
}

public void sayByeBye(String name,int time){
System.out.println("this Emp to **"+name+"** say byebye"+"this time = "+time);
}
private void sayHiPrivate(){
System.out.println("this method is a private method");
}
public static void staticMethod(){
System.out.println("this method is a static method");
}
public void varParamMethod(int... a){
if(null != a && a.length > 0){
System.out.println("success a.length="+a.length);
}else{
System.out.println("faile");

}
}

@Override
public String toString() {
return "name:"+name+"******age:"+age;
}
}
public class ReflectMethodDemo {

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

//testReflectMethod();
testReflectField();
}

public static void testReflectField()throws Exception{
Class<Emp> clazz = Emp.class;
/**
* clazz.getFields();方法获取当前class描述类和该类的父类的所有public修饰的属性
*/
Field[] fs = clazz.getFields();
for (Field field : fs) {
System.out.println(field);
}
//clazz.getDeclaredFields();方法获取当前class描述类的所有属性忽视访问权限
System.out.println("*************============**************");
fs = clazz.getDeclaredFields();
for (Field field : fs) {
System.out.println(field);
}
System.out.println("*************============**************");
//clazz.getDeclaredField(String fieldName)获取指定名称的字段,该类所有字段
//clazz.getField(String fieldName) 获取指定名称的字段,该类及父类所有public修饰的字段
Field fName = clazz.getDeclaredField("name");
System.out.println(fName);
System.out.println("*************============**************");
Field fAge = clazz.getField("age");
System.out.println(fAge);
System.out.println("*************============**************");
/**
* 字段和方法一样底层都从属于对象,因此要对拿到的字段进行赋值或者取值
* 都必须先获取到对象,利用反射创建对象
*/
Emp e = clazz.newInstance();
fAge.set(e, 18);
System.out.println(e);
System.out.println("*************============**************");
//对对象私有属性进行赋值时需要先调用field.setAccessible(true);
fName.setAccessible(true);
fName.set(e, "jony");
System.out.println(e);
int age = fAge.getInt(e);
System.out.println(age);
String name = (String) fName.get(e);
System.out.println(name);
}

public static void testReflectMethod() throws Exception{
/**
* 通过反射获取方法
*/
//获取类在JVM中的字节码 即Class实例
Class<Emp> clazz = Emp.class;
/**
* 通过反射获取当前class描述类的方法集合
* clazz.getMethods()方法获取该类和其直接间接父类所声明的所有public修饰的方法
* clazz.getDeclaredMethods();该方法获取当前class描述类的所有方法(忽视访问权限)
* 但是放方法不能获取继承过来的所有方法,包括直接父类和间接父类的公共或私有方法
*/
Method[] ms = clazz.getMethods();
for (Method method : ms) {
// System.out.println(method);
}
ms = clazz.getDeclaredMethods();
for (Method method : ms) {
System.out.println(method);
}
System.out.println("*************============**************");
/**
* 在java的同一个类中通过方法签名来唯一确定一个方法
* 方法签名包括方法名称和参数列表,参数列表分为(参数类型,参数个数,参数顺序)
* clazz.getMethod(String methodName,Class...)
* 该方法通过指定的方法签名获取方法,methodName方法名称,Class...可变参数类型的class
* 可以获取该类的public修饰的方法亦获取直接或间接父类public修饰的方法
*/
Method m = clazz.getMethod("sayHi");
System.out.println(m);
System.out.println("*************============**************");
m = clazz.getMethod("sayHi", String.class);
System.out.println(m);
System.out.println("*************============**************");
m = clazz.getMethod("sayHiSupper");//获取父类方法
System.out.println(m);
System.out.println("*************============**************");
/**
* clazz.getDeclaredMethod(String, Class...)
* 该方法通过方法签名获取指定方法
* 能获取该类的所有方法,包括公共的和私有的 但是不能获取直接父类和间接父类的方法
*/
m = clazz.getDeclaredMethod("sayHello", String.class);
System.out.println(m);
System.out.println("*************============**************");
m = clazz.getDeclaredMethod("sayByeBye", String.class,int.class);
/**
* 执行方法,java中所有的非静态方法,
* 在上面获取到指定方法的基础上我们要做的是执行方法。
* java中类的非静态方法都从属与该类的对象。因此需要执行方法我们需要先拿到对象。
* 通过获取指定构造器创建对象,也可使用Class的newInstance()调用默认无参构造方法创建对象。
*/
Emp e = clazz.newInstance();
/**
* Method中的invoke方法需要床底两个参数,一个是该方法底层从属的对象obj,另一个是可变参数即执行该方法的实际参数.
* 当方法的参数是非可变参数的时候我们可以直接传值如m.invoke(e, "lucy",17);
* 亦可对参数进行包装m.invoke(e, new Object[]{"lucy",18});
* 但是当方法的参数是可变参数,获取指定方法时,方法实际参数的class传值等于该类型的数组类型的class
* 如方法声明 public void varParamMethod(int... a)
* 获取该方法时使用clazz.getDeclaredMethod("varParamMethod", int[].class);获取
* 执行该方法时方法的实际参数为实际参数类型的数组类型,如执行varParamMethod()方法的实际参数
* 为new int[]{1},为了加强通用性我们一般在参数数组类型的外层再加上一层包装
* new Object[]{new int[]{1,2,3}}
*/
System.out.println("*************============**************");
m.invoke(e, "lucy",17);
m.invoke(e, new Object[]{"lucy",18});
m = clazz.getDeclaredMethod("varParamMethod", int[].class);
m.invoke(e,new int[]{1});
m.invoke(e, new Object[]{new int[]{1,2,3}});
System.out.println("*************============**************");
m = clazz.getDeclaredMethod("staticMethod");
System.out.println(m);
//调用静态方法,传入的方法的底层从属对象可以写成null
m.invoke(null);
System.out.println("*************============**************");
m = clazz.getDeclaredMethod("sayHiPrivate");
//当获取的是一个私有方法时要执行该方法必须在执行该方法之前执行m.setAccessible(true);
m.setAccessible(true);
m.invoke(e);

}
/**
* 使用反射调用可变参数的方法
* 对于数组类型的引用类型的参数,底层会自动解包,为了解决该问题。我们使用Object的一维数组把实际参数包装起来
* 以后使用反射调用invoke方法,在传递实际参数的时候,无论是基本数据类型还是引用数据类型,也无论是可变参数类型还是
* 不变参数类型,都是用一个规定:一起实际参数都包装在Object的一维数组中,就是通用的
* 如:m.invoke(方法底层所属对象,new Object[]{实际参数});通用
*/
}

本文出自 “smartluobo” 博客,请务必保留此出处http://smartluobo.blog.51cto.com/6152293/1747496
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: